Pārlūkot izejas kodu

Docs and yaml tests for viewing API key's limited-by (#89443)

This PR updates relevant docs and yaml tests to cover the new feature
of viewing API key's limited-by role descriptors introduced in #89273

Relates: #89058
Yang Wang 3 gadi atpakaļ
vecāks
revīzija
e276fd9506

+ 5 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/security.get_api_key.json

@@ -40,6 +40,11 @@
         "type":"boolean",
         "default":false,
         "description":"flag to query API keys owned by the currently authenticated user"
+      },
+      "with_limited_by":{
+        "type":"boolean",
+        "default":false,
+        "description": "flag to show the limited-by role descriptors of API Keys"
       }
     }
   }

+ 7 - 1
rest-api-spec/src/main/resources/rest-api-spec/api/security.query_api_keys.json

@@ -21,7 +21,13 @@
         }
       ]
     },
-    "params":{},
+    "params":{
+      "with_limited_by":{
+        "type":"boolean",
+        "default":false,
+        "description": "flag to show the limited-by role descriptors of API Keys"
+      }
+    },
     "body":{
       "description":"From, size, query, sort and search_after",
       "required":false

+ 92 - 29
x-pack/docs/en/rest-api/security/get-api-keys.asciidoc

@@ -27,7 +27,7 @@ regardless of ownership.
 The information for the API keys created by
 <<security-api-create-api-key,create API Key>> can be retrieved using this API.
 
-[[security-api-get-api-key-path-params]]
+[[security-api-get-api-key-query-params]]
 ==== {api-path-parms-title}
 
 The following parameters can be specified in the query parameters of a GET request and
@@ -55,6 +55,12 @@ by the currently authenticated user. Defaults to false.
 The 'realm_name' or 'username' parameters cannot be specified when this
 parameter is set to 'true' as they are assumed to be the currently authenticated ones.
 
+`with_limited_by`::
+(Optional, Boolean) A boolean flag to return the snapshot of the owner user's role descriptors
+associated with the API key. An API key's actual permission is the intersection of
+its <<api-key-role-descriptors,assigned role descriptors>> and the owner user's role descriptors
+(effectively limited by it). An API key must have `manage_api_key` or higher privileges to retrieve the limited-by role descriptors of any API key, including itself.
+
 NOTE: When none of the parameters "id", "name", "username" and "realm_name"
 are specified, and the "owner" is set to false then it will retrieve all API
 keys if the user is authorized. If the user is not authorized to retrieve other user's
@@ -70,7 +76,10 @@ If you create an API key as follows:
 POST /_security/api_key
 {
   "name": "my-api-key",
-  "role_descriptors": {}
+  "role_descriptors": {},
+  "metadata": {
+    "application": "myapp"
+  }
 }
 ------------------------------------------------------------
 
@@ -94,11 +103,80 @@ You can use the following example to retrieve the API key by ID:
 
 [source,console]
 --------------------------------------------------
-GET /_security/api_key?id=VuaCfGcBCdbkQm-e5aOx
+GET /_security/api_key?id=VuaCfGcBCdbkQm-e5aOx&with_limited_by=true
 --------------------------------------------------
 // TEST[s/VuaCfGcBCdbkQm-e5aOx/$body.id/]
 // TEST[continued]
 
+A successful call returns a JSON structure that contains the information of the API key:
+
+[source,js]
+--------------------------------------------------
+{
+  "api_keys": [ <1>
+    {
+      "id": "VuaCfGcBCdbkQm-e5aOx", <2>
+      "name": "my-api-key", <3>
+      "creation": 1548550550158, <4>
+      "expiration": 1548551550158, <5>
+      "invalidated": false, <6>
+      "username": "myuser", <7>
+      "realm": "native1", <8>
+      "metadata": { <9>
+        "application": "myapp"
+      },
+      "role_descriptors": { }, <10>
+      "limited_by": [  <11>
+        {
+          "role-power-user": {
+            "cluster": [
+              "monitor"
+            ],
+            "indices": [
+              {
+                "names": [
+                  "*"
+                ],
+                "privileges": [
+                  "read"
+                ],
+                "allow_restricted_indices": false
+              }
+            ],
+            "applications": [ ],
+            "run_as": [ ],
+            "metadata": { },
+            "transient_metadata": {
+              "enabled": true
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
+--------------------------------------------------
+// NOTCONSOLE
+<1> The list of API keys that were retrieved for this request.
+<2> Id for the API key
+<3> Name of the API key
+<4> Creation time for the API key in milliseconds
+<5> Optional expiration time for the API key in milliseconds
+<6> Invalidation status for the API key. If the key has been invalidated, it has
+a value of `true`. Otherwise, it is `false`.
+<7> Principal for which this API key was created
+<8> Realm name of the principal for which this API key was created
+<9> Metadata of the API key
+<10> The role descriptors assigned to this API key when it was <<api-key-role-descriptors,created>>
+or last <<security-api-update-api-key-api-key-role-descriptors,updated>>.
+An empty role descriptor means the API key inherits the owner user's
+permissions.
+<11> The owner user's permissions associated with the API key.
+It is a point-in-time snapshot captured at <<security-api-create-api-key,creation>> and
+subsequent <<security-api-update-api-key,updates>>. An API key's
+effective permissions are an intersection of its assigned privileges and
+the owner user's permissions.
+
 You can use the following example to retrieve the API key by name:
 
 [source,console]
@@ -183,19 +261,19 @@ A successful call returns a JSON structure that contains the information of one
 [source,js]
 --------------------------------------------------
 {
-  "api_keys": [ <1>
+  "api_keys": [
     {
-      "id": "0GF5GXsBCXxz2eDxWwFN", <2>
-      "name": "hadoop_myuser_key", <3>
-      "creation": 1548550550158, <4>
-      "expiration": 1548551550158, <5>
-      "invalidated": false, <6>
-      "username": "myuser", <7>
-      "realm": "native1", <8>
-      "metadata": { <9>
+      "id": "0GF5GXsBCXxz2eDxWwFN",
+      "name": "hadoop_myuser_key",
+      "creation": 1548550550158,
+      "expiration": 1548551550158,
+      "invalidated": false,
+      "username": "myuser",
+      "realm": "native1",
+      "metadata": {
         "application": "myapp"
       },
-      "role_descriptors": { <10>
+      "role_descriptors": {
         "role-a": {
           "cluster": [
             "monitor"
@@ -228,25 +306,10 @@ A successful call returns a JSON structure that contains the information of one
       "username": "user-y",
       "realm": "realm-2",
       "metadata": {},
-      "role_descriptors": { } <11>
+      "role_descriptors": { }
     }
   ]
 }
 --------------------------------------------------
 // NOTCONSOLE
 
-<1> The list of API keys that were retrieved for this request.
-<2> Id for the API key
-<3> Name of the API key
-<4> Creation time for the API key in milliseconds
-<5> Optional expiration time for the API key in milliseconds
-<6> Invalidation status for the API key. If the key has been invalidated, it has
-a value of `true`. Otherwise, it is `false`.
-<7> Principal for which this API key was created
-<8> Realm name of the principal for which this API key was created
-<9> Metadata of the API key
-<10> The role descriptors assigned to this API key when it was <<api-key-role-descriptors,created>>
-or last <<security-api-update-api-key-api-key-role-descriptors,updated>>. The API key's
-effective permissions are an intersection of its assigned privileges and the point-in-time snapshot of
-the owner user's permissions.
-<11> An empty role descriptor means the API key inherits the owner user's permissions.

+ 66 - 1
x-pack/docs/en/rest-api/security/query-api-key.asciidoc

@@ -33,6 +33,16 @@ Use this API to retrieve the API keys created with the
 <<security-api-create-api-key,create API key API>> in a paginated manner.
 You can optionally filter the results with a query.
 
+[[security-api-query-api-key-query-params]]
+==== {api-path-parms-title}
+
+`with_limited_by`::
+(Optional, Boolean) A boolean flag to return the snapshot of the owner user's role descriptors
+associated with the API key. An API key's actual permission is the intersection of
+its <<api-key-role-descriptors,assigned role descriptors>> and the owner user's role descriptors
+(effectively limited by it). An API key cannot retrieve any API key's limited-by role descriptors
+(including itself) unless it has `manage_api_key` or higher privileges.
+
 [[security-api-query-api-key-request-body]]
 ==== {api-request-body-title}
 
@@ -229,7 +239,7 @@ Use the information from the response to retrieve the API key by ID:
 
 [source,console]
 ----
-GET /_security/_query/api_key
+GET /_security/_query/api_key?with_limited_by=true
 {
   "query": {
     "ids": {
@@ -243,6 +253,61 @@ GET /_security/_query/api_key
 // TEST[s/VuaCfGcBCdbkQm-e5aOx/$body.id/]
 // TEST[continued]
 
+A successful call returns a JSON structure for API key information including its limited-by role descriptors:
+
+[source,js]
+--------------------------------------------------
+{
+  "api_keys": [
+    {
+      "id": "VuaCfGcBCdbkQm-e5aOx",
+      "name": "application-key-1",
+      "creation": 1548550550158,
+      "expiration": 1548551550158,
+      "invalidated": false,
+      "username": "myuser",
+      "realm": "native1",
+      "metadata": {
+        "application": "my-application"
+      },
+      "role_descriptors": { },
+      "limited_by": [ <1>
+        {
+          "role-power-user": {
+            "cluster": [
+              "monitor"
+            ],
+            "indices": [
+              {
+                "names": [
+                  "*"
+                ],
+                "privileges": [
+                  "read"
+                ],
+                "allow_restricted_indices": false
+              }
+            ],
+            "applications": [ ],
+            "run_as": [ ],
+            "metadata": { },
+            "transient_metadata": {
+              "enabled": true
+            }
+          }
+        }
+      ]
+    }
+  ]
+}
+--------------------------------------------------
+// NOTCONSOLE
+<1> The owner user's permissions associated with the API key.
+It is a point-in-time snapshot captured at <<security-api-create-api-key,creation>> and
+subsequent <<security-api-update-api-key,updates>>. An API key's
+effective permissions are an intersection of its assigned privileges and
+the owner user's permissions.
+
 You can also retrieve the API key by name:
 
 [source,console]

+ 188 - 12
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/api_key/40_view_role_descriptors.yml

@@ -30,16 +30,31 @@ setup:
 
   - do:
       security.put_user:
-        username: "api_key_user"
+        username: "api_key_admin"
         body:  >
           {
             "password" : "x-pack-test-password",
             "roles" : [ "admin_role" ],
-            "full_name" : "API key user"
+            "full_name" : "API key admin"
           }
 
+  - do:
+      security.put_role:
+        name: "user_role"
+        body: >
+          {
+            "cluster": ["manage_own_api_key"]
+          }
 
-
+  - do:
+      security.put_user:
+        username: "api_key_user"
+        body: >
+          {
+            "password" : "x-pack-test-password",
+            "roles" : [ "user_role" ],
+            "full_name" : "API key user"
+          }
 ---
 teardown:
   - do:
@@ -47,6 +62,16 @@ teardown:
         name: "admin_role"
         ignore: 404
 
+  - do:
+      security.delete_user:
+        username: "api_key_admin"
+        ignore: 404
+
+  - do:
+      security.delete_role:
+        name: "user_role"
+        ignore: 404
+
   - do:
       security.delete_user:
         username: "api_key_user"
@@ -59,7 +84,7 @@ teardown:
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.create_api_key:
         body:  >
           {
@@ -71,15 +96,16 @@ teardown:
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.get_api_key:
         id: "$api_key_id_0"
   - match: { "api_keys.0.id": "$api_key_id_0" }
   - match: { "api_keys.0.role_descriptors": { } }
+  - is_false: api_keys.0.limited_by
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.create_api_key:
         body:  >
           {
@@ -92,15 +118,16 @@ teardown:
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.get_api_key:
         id: "$api_key_id_1"
   - match: { "api_keys.0.id": "$api_key_id_1" }
   - match: { "api_keys.0.role_descriptors": { } }
+  - is_false: api_keys.0.limited_by
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.create_api_key:
         body:  >
           {
@@ -123,7 +150,7 @@ teardown:
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.get_api_key:
         id: "$api_key_id_2"
   - match: { "api_keys.0.id": "$api_key_id_2" }
@@ -152,10 +179,11 @@ teardown:
     }
   }
   }
+  - is_false: api_keys.0.limited_by
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.create_api_key:
         body:  >
           {
@@ -188,7 +216,7 @@ teardown:
 
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.get_api_key:
         id: "$api_key_id_3"
   - match: { "api_keys.0.id": "$api_key_id_3" }
@@ -239,11 +267,12 @@ teardown:
     }
   }
   }
+  - is_false: api_keys.0.limited_by
 
   # Query API keys
   - do:
       headers:
-        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
       security.query_api_keys:
         body: >
           {
@@ -253,8 +282,10 @@ teardown:
   - match: { count: 4 }
   - match: { "api_keys.0.name": "key-0-with-implicit-inherit" }
   - match: { "api_keys.0.role_descriptors": { } }
+  - is_false: api_keys.0.limited_by
   - match: { "api_keys.1.name": "key-1-with-explicit-inherit" }
   - match: { "api_keys.1.role_descriptors": { } }
+  - is_false: api_keys.1.limited_by
   - match: { "api_keys.2.name": "key-2-with-single-assigned-role-descriptor"}
   - match: { "api_keys.2.role_descriptors": {
     "role-a": {
@@ -281,6 +312,7 @@ teardown:
     }
   }
   }
+  - is_false: api_keys.2.limited_by
   - match: { "api_keys.3.name": "key-3-with-multiple-assigned-role-descriptors"}
   - match: { "api_keys.3.role_descriptors": {
     "role-a": {
@@ -329,4 +361,148 @@ teardown:
     }
   }
   }
+  - is_false: api_keys.3.limited_by
+
+---
+"Test API key limited-by role descriptors in Get and Query responses":
+  - skip:
+      features: transform_and_set
 
+  - do:
+      headers:
+        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+      security.create_api_key:
+        body:  >
+          {
+            "name": "key-0-limited-by-api-key-user"
+          }
+  - match: { name: "key-0-limited-by-api-key-user" }
+  - is_true: id
+  - set: { id: api_key_id_0 }
+  - set: { encoded: login_creds_0 }
+
+  # The key cannot view its own limited-by because it has only manage_own_api_key privileges
+  - do:
+      catch: forbidden
+      headers:
+        Authorization: ApiKey ${login_creds_0}
+      security.get_api_key:
+        id: "$api_key_id_0"
+        with_limited_by: true
+
+  - do:
+      headers:
+        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
+      security.get_api_key:
+        id: "$api_key_id_0"
+        owner: true
+        with_limited_by: true
+  - match: { "api_keys.0.id": "$api_key_id_0" }
+  - is_true: api_keys.0.role_descriptors
+  - length: { "api_keys.0.limited_by": 1 }
+  - match: { "api_keys.0.limited_by.0": {
+    "user_role" : {
+      "cluster" : [
+        "manage_own_api_key"
+      ],
+      "indices" : [ ],
+      "applications" : [ ],
+      "run_as" : [ ],
+      "metadata" : { },
+      "transient_metadata" : {
+        "enabled" : true
+      }
+    }
+  }
+  }
+
+  # Query API keys
+  - do:
+      headers:
+        Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_admin
+      security.query_api_keys:
+        with_limited_by: true
+        body: >
+          {
+            "query": { "term": { "name": "key-0-limited-by-api-key-user" }}
+          }
+  - match: { total: 1 }
+  - match: { count: 1 }
+  - match: { "api_keys.0.name": "key-0-limited-by-api-key-user" }
+  - match: { "api_keys.0.id": "$api_key_id_0" }
+  - is_true: api_keys.0.role_descriptors
+  - length: { "api_keys.0.limited_by": 1 }
+  - match: { "api_keys.0.limited_by.0": {
+    "user_role" : {
+      "cluster" : [
+        "manage_own_api_key"
+      ],
+      "indices" : [ ],
+      "applications" : [ ],
+      "run_as" : [ ],
+      "metadata" : { },
+      "transient_metadata" : {
+        "enabled" : true
+      }
+    }
+  }
+  }
+
+  - do:
+      headers:
+        Authorization: "Basic YXBpX2tleV9hZG1pbjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # api_key_admin
+      security.create_api_key:
+        body:  >
+          {
+            "name": "key-1-limited-by-api-key-admin"
+          }
+  - match: { name: "key-1-limited-by-api-key-admin" }
+  - is_true: id
+  - set: { id: api_key_id_1 }
+  - set: { encoded: login_creds_1 }
+
+  # This key can view its own limited-by because it inherits manage_api_key privileges
+  - do:
+      headers:
+        Authorization: ApiKey ${login_creds_1}
+      security.get_api_key:
+        id: "$api_key_id_1"
+        with_limited_by: true
+  - match: { "api_keys.0.id": "$api_key_id_1" }
+  - is_true: api_keys.0.role_descriptors
+  - length: { "api_keys.0.limited_by": 1 }
+  - match: { "api_keys.0.limited_by.0": {
+    "admin_role" : {
+      "cluster" : [
+        "manage_api_key"
+      ],
+      "indices" : [
+        {
+          "names" : [
+            "*"
+          ],
+          "privileges" : [
+            "all"
+          ],
+          "allow_restricted_indices" : false
+        }
+      ],
+      "applications" : [
+        {
+          "application" : "myapp",
+          "privileges" : [
+            "*"
+          ],
+          "resources" : [
+            "*"
+          ]
+        }
+      ],
+      "run_as" : [ ],
+      "metadata" : { },
+      "transient_metadata" : {
+        "enabled" : true
+      }
+    }
+  }
+  }