---
setup:
  - skip:
      features: [headers, transform_and_set]

  - do:
      cluster.health:
          wait_for_status: yellow

  - do:
      security.put_role:
        name: "api_key_granter_role"
        body:  >
            {
              "cluster": ["grant_api_key"]
            }
  - do:
      security.put_role:
        name: "api_key_grant_target_role"
        body: >
          {
            "cluster": ["manage_token"],
            "indices": [
              {
                "names": "index",
                "privileges": ["all"]
              }
            ]
          }
  - do:
      security.put_role:
        name: "api_key_grant_runas_role"
        body: >
          {
            "run_as": ["api_key_grant_target_user"]
          }

  - do:
      security.put_user:
        username: "api_key_granter"
        body:  >
          {
            "password" : "x-pack-test-password",
            "roles" : [ "api_key_granter_role" ],
            "full_name" : "API key granter"
          }

  - do:
      security.put_user:
        username: "api_key_grant_target_user"
        body: >
          {
            "password" : "x-pack-test-password-2",
            "roles" : [ "api_key_grant_target_role" ],
            "full_name" : "API key grant target user"
          }
  - do:
      security.put_user:
        username: "api_key_grant_runas_user"
        body: >
          {
            "password" : "x-pack-test-password-3",
            "roles" : [ "api_key_grant_runas_role" ],
            "full_name" : "API key grant runas user"
          }

---
teardown:
  - do:
      security.delete_role:
        name: "api_key_granter_role"
        ignore: 404

  - do:
      security.delete_role:
        name: "api_key_grant_target_role"
        ignore: 404

  - do:
      security.delete_role:
        name: "api_key_grant_runas_role"
        ignore: 404

  - do:
      security.delete_user:
        username: "api_key_granter"
        ignore: 404

  - do:
      security.delete_user:
        username: "api_key_grant_target_user"
        ignore: 404

  - do:
      security.delete_user:
        username: "api_key_grant_runas_user"
        ignore: 404

---
"Test grant api key with password":
  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudGVyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_granter
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "my-api-key",
              "expiration": "1d",
              "role_descriptors": {
                "role": {
                  "index": [
                    {
                      "names": ["index"],
                      "privileges": ["read"]
                    }
                  ]
                }
              }
            },
            "grant_type": "password",
            "username": "api_key_grant_target_user",
            "password": "x-pack-test-password-2"
          }
  - match: { name: "my-api-key" }
  - is_true: id
  - is_true: api_key
  - is_true: expiration
  - set: { id: api_key_id }
  - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
  - match: { encoded: $login_creds }

  - do:
      headers:
        Authorization: ApiKey ${login_creds}
      security.authenticate: {}

  - match: { username: "api_key_grant_target_user" }
  - length: { roles: 0 }
  - match: { authentication_realm.name: "_es_api_key" }
  - match: { authentication_realm.type: "_es_api_key" }
  - match: { api_key.id: "${api_key_id}" }
  - match: { api_key.name: "my-api-key" }

  - do:
      security.clear_api_key_cache:
        ids: "${api_key_id}"

  - match: { _nodes.failed: 0 }

---
"Test grant api key with token":
  - do:
      security.get_token:
        body:
          grant_type: "password"
          username: "api_key_grant_target_user"
          password: "x-pack-test-password-2"

  - match: { type: "Bearer" }
  - is_true: access_token
  - set: { access_token: token }

  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudGVyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_granter
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "my-api-key",
              "expiration": "1d",
              "role_descriptors": {
                "role": {
                  "index": [
                    {
                      "names": ["index"],
                      "privileges": ["read"]
                    }
                  ]
                }
              }
            },
            "grant_type": "access_token",
            "access_token": "$token"
          }
  - match: { name: "my-api-key" }
  - is_true: id
  - is_true: api_key
  - is_true: expiration
  - set: { id: api_key_id }
  - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
  - match: { encoded: $login_creds }

  # verify the granted API Key
  - do:
      headers:
        Authorization: ApiKey ${login_creds}
      security.authenticate: {}

  - match: { username: "api_key_grant_target_user" }
  - length: { roles: 0 }
  - match: { authentication_realm.name: "_es_api_key" }
  - match: { authentication_realm.type: "_es_api_key" }
  - match: { api_key.id: "${api_key_id}" }
  - match: { api_key.name: "my-api-key" }

  - do:
      security.clear_api_key_cache:
        ids: "${api_key_id}"

  - match: { _nodes.failed: 0 }

---
"Test grant api key with non-JWT token and client authentication fails":
  - do:
      security.get_token:
        body:
          grant_type: "password"
          username: "api_key_grant_target_user"
          password: "x-pack-test-password-2"

  - match: { type: "Bearer" }
  - is_true: access_token
  - set: { access_token: token }

  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudGVyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_granter
      catch: bad_request
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "wrong-api-key"
            },
            "grant_type": "access_token",
            "access_token": "$token",
            "client_authentication": {
              "scheme": "SharedSecret",
              "value": "whatever"
            }
          }

  - match: { "error.type": "security_exception" }
  - match:
      "error.reason": "[client_authentication] not supported with the supplied access_token type"

---
"Test grant api key forbidden":
  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudF90YXJnZXRfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZC0y" # api_key_grant_target_user
      catch: forbidden
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "my-api-key",
              "expiration": "1d",
              "role_descriptors": {
                "role": {
                  "index": [
                    {
                      "names": ["index"],
                      "privileges": ["read"]
                    }
                  ]
                }
              }
            },
            "grant_type": "password",
            "username": "api_key_granter",
            "password": "x-pack-test-password"
          }
  - match: { "error.type": "security_exception" }
  - match:
      "error.reason": "action [cluster:admin/xpack/security/api_key/grant] is unauthorized for user [api_key_grant_target_user] with effective roles [api_key_grant_target_role], this action is granted by the cluster privileges [grant_api_key,manage_api_key,manage_security,all]"

---
"Test grant api key forbidden with manage_own_api_key privilege":
  # Give api_key_grant_target_user manage_own_api_key privilege
  - do:
      security.put_role:
        name: "api_key_grant_target_role"
        body: >
          {
            "cluster": ["manage_own_api_key"],
            "indices": [
              {
                "names": "index",
                "privileges": ["all"]
              }
            ]
          }

  # Can create for self
  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudF90YXJnZXRfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZC0y" # api_key_grant_target_user
      security.create_api_key:
        body: >
          {
            "name": "my-api-key"
          }
  - match: { name: "my-api-key" }
  - is_true: id
  - set: { id: api_key_id }

  # Can't grant to self
  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudF90YXJnZXRfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZC0y" # api_key_grant_target_user
      catch: forbidden
      security.grant_api_key:
        body: >
          {
            "api_key": {
              "name": "my-api-key"
            },
            "grant_type": "password",
            "username": "api_key_grant_target_user",
            "password": "x-pack-test-password-2"
          }
  - match: { "error.type": "security_exception" }
  - match:
      "error.reason": "action [cluster:admin/xpack/security/api_key/grant] is unauthorized for user [api_key_grant_target_user] with effective roles [api_key_grant_target_role], this action is granted by the cluster privileges [grant_api_key,manage_api_key,manage_security,all]"

  # Can't grant to others
  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudF90YXJnZXRfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZC0y" # api_key_grant_target_user
      catch: forbidden
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "my-api-key"
            },
            "grant_type": "password",
            "username": "api_key_granter",
            "password": "x-pack-test-password"
          }
  - match: { "error.type": "security_exception" }
  - match:
      "error.reason": "action [cluster:admin/xpack/security/api_key/grant] is unauthorized for user [api_key_grant_target_user] with effective roles [api_key_grant_target_role], this action is granted by the cluster privileges [grant_api_key,manage_api_key,manage_security,all]"

  - do:
      security.clear_api_key_cache:
        ids: "${api_key_id}"

  - match: { _nodes.failed: 0 }

---
"Test grant api key with password and run_as":
  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudGVyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_granter
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "my-api-key-with-password-runas"
            },
            "grant_type": "password",
            "username": "api_key_grant_runas_user",
            "password": "x-pack-test-password-3",
            "run_as": "api_key_grant_target_user"
          }
  - match: { name: "my-api-key-with-password-runas" }
  - is_true: id
  - is_true: api_key
  - set: { id: api_key_id }
  - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
  - match: { encoded: $login_creds }

  - do:
      headers:
        Authorization: ApiKey ${login_creds}
      security.authenticate: {}

  - match: { username: "api_key_grant_target_user" }
  - length: { roles: 0 }
  - match: { authentication_realm.name: "_es_api_key" }
  - match: { authentication_realm.type: "_es_api_key" }
  - match: { api_key.id: "${api_key_id}" }
  - match: { api_key.name: "my-api-key-with-password-runas" }

---
"Test grant api key with access token and run_as":
  - do:
      security.get_token:
        body:
          grant_type: "password"
          username: "api_key_grant_runas_user"
          password: "x-pack-test-password-3"

  - match: { type: "Bearer" }
  - is_true: access_token
  - set: { access_token: token }

  - do:
      headers:
        Authorization: "Basic YXBpX2tleV9ncmFudGVyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_granter
      security.grant_api_key:
        body:  >
          {
            "api_key": {
              "name": "my-api-key-with-access-token-runas"
            },
            "grant_type": "access_token",
            "access_token": "$token",
            "run_as": "api_key_grant_target_user"
          }
  - match: { name: "my-api-key-with-access-token-runas" }
  - is_true: id
  - is_true: api_key
  - set: { id: api_key_id }
  - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
  - match: { encoded: $login_creds }

  - do:
      headers:
        Authorization: ApiKey ${login_creds}
      security.authenticate: {}

  - match: { username: "api_key_grant_target_user" }
  - length: { roles: 0 }
  - match: { authentication_realm.name: "_es_api_key" }
  - match: { authentication_realm.type: "_es_api_key" }
  - match: { api_key.id: "${api_key_id}" }
  - match: { api_key.name: "my-api-key-with-access-token-runas" }
