Ver código fonte

view_index_metadata and manage privs now grant access to field capabilities (#67392)

This PR extends the `view_index_metadata` and `manage` index privileges to also
grant access to the field capabilities API.

The field capabilities API shows the same information (in a slightly different format)
as the field mapping API which is already granted by the said privileges.

Closes #66867
Albert Zaharovits 4 anos atrás
pai
commit
4d03817dc2

+ 6 - 6
x-pack/docs/en/security/authorization/privileges.asciidoc

@@ -264,8 +264,8 @@ No privilege to read or write index data or otherwise manage the index.
 
 `manage`::
 All `monitor` privileges plus index and data stream administration (aliases,
-analyze, cache clear, close, delete, exists, flush, mapping, open, force merge,
-refresh, settings, search shards, templates, validate).
+analyze, cache clear, close, delete, exists, flush, mapping, open, field capabilties,
+force merge, refresh, settings, search shards, templates, validate query).
 
 `manage_follow_index`::
 All actions that are required to manage the lifecycle of a follower index, which
@@ -295,10 +295,10 @@ clear_scroll, search, suggest, tv).
 Read-only access to the search action from a <<cross-cluster-configuring,remote cluster>>.
 
 `view_index_metadata`::
-Read-only access to index and data stream metadata (aliases, aliases exists,
-get index, get data stream, exists, field mappings, mappings, search shards,
-type exists, validate, warmers, settings, ilm). This privilege is available
-for use primarily by {kib} users.
+Read-only access to index and data stream metadata (aliases, exists,
+field capabilities, field mappings, get index, get data stream, ilm explain,
+mappings, search shards, settings, validate query).
+This privilege is available for use primarily by {kib} users.
 
 `write`::
 Privilege to perform all write operations to documents, which includes the

+ 4 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java

@@ -21,6 +21,7 @@ import org.elasticsearch.action.admin.indices.mapping.put.AutoPutMappingAction;
 import org.elasticsearch.action.admin.indices.resolve.ResolveIndexAction;
 import org.elasticsearch.action.admin.indices.settings.get.GetSettingsAction;
 import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryAction;
+import org.elasticsearch.action.fieldcaps.FieldCapabilitiesAction;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.xpack.core.action.CreateDataStreamAction;
 import org.elasticsearch.xpack.core.action.DeleteDataStreamAction;
@@ -62,13 +63,14 @@ public final class IndexPrivilege extends Privilege {
     private static final Automaton WRITE_AUTOMATON = patterns("indices:data/write/*", AutoPutMappingAction.NAME);
     private static final Automaton MONITOR_AUTOMATON = patterns("indices:monitor/*");
     private static final Automaton MANAGE_AUTOMATON =
-            unionAndMinimize(Arrays.asList(MONITOR_AUTOMATON, patterns("indices:admin/*")));
+            unionAndMinimize(Arrays.asList(MONITOR_AUTOMATON, patterns("indices:admin/*", FieldCapabilitiesAction.NAME + "*")));
     private static final Automaton CREATE_INDEX_AUTOMATON = patterns(CreateIndexAction.NAME, AutoCreateAction.NAME,
             CreateDataStreamAction.NAME);
     private static final Automaton DELETE_INDEX_AUTOMATON = patterns(DeleteIndexAction.NAME, DeleteDataStreamAction.NAME);
     private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, GetIndexAction.NAME,
             GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME, ClusterSearchShardsAction.NAME, ValidateQueryAction.NAME + "*",
-            GetSettingsAction.NAME, ExplainLifecycleAction.NAME, GetDataStreamAction.NAME, ResolveIndexAction.NAME);
+            GetSettingsAction.NAME, ExplainLifecycleAction.NAME, GetDataStreamAction.NAME, ResolveIndexAction.NAME,
+            FieldCapabilitiesAction.NAME + "*");
     private static final Automaton MANAGE_FOLLOW_INDEX_AUTOMATON = patterns(PutFollowAction.NAME, UnfollowAction.NAME,
         CloseIndexAction.NAME + "*");
     private static final Automaton MANAGE_LEADER_INDEX_AUTOMATON = patterns(ForgetFollowerAction.NAME + "*");

+ 35 - 2
x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/IndexPrivilegeIntegTests.java

@@ -73,10 +73,12 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "  indices:\n" +
             "    - names: 'b'\n" +
             "      privileges: [ monitor ]\n" +
-            "maintenance_a_role:\n" +
+            "maintenance_a_view_meta_b_role:\n" +
             "  indices:\n" +
             "    - names: 'a'\n" +
             "      privileges: [ maintenance ]\n" +
+            "    - names: '*b'\n" +
+            "      privileges: [ view_index_metadata ]\n" +
             "read_write_a_role:\n" +
             "  indices:\n" +
             "    - names: 'a'\n" +
@@ -104,7 +106,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "read_write_all_role:u12\n" +
             "create_c_role:u11\n" +
             "monitor_b_role:u14\n" +
-            "maintenance_a_role:u15\n" +
+            "maintenance_a_view_meta_b_role:u15\n" +
             "read_write_a_role:u12\n" +
             "delete_b_role:u11\n" +
             "index_a_role:u13\n";
@@ -174,6 +176,8 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsAllowed("u1",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u1", randomFrom("GET", "POST"), "/" + "b" + "/_field_caps?fields=*");
+        assertAccessIsDenied("u1", randomFrom("GET", "POST"), "/" + "c" + "/_field_caps?fields=*");
     }
 
     public void testUserU2() throws Exception {
@@ -191,6 +195,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsAllowed("u2",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u2", randomFrom("GET", "POST"), "/" + "c" + "/_field_caps?fields=*");
     }
 
     public void testUserU3() throws Exception {
@@ -229,6 +234,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsAllowed("u4",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u2", randomFrom("GET", "POST"), "/" + "c" + "/_field_caps?fields=*");
     }
 
     public void testUserU5() throws Exception {
@@ -278,6 +284,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsDenied("u7",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u7", randomFrom("GET", "POST"), "/" + randomIndex() + "/_field_caps?fields=*");
     }
 
     public void testUserU8() throws Exception {
@@ -309,6 +316,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsAllowed("u9",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u9", randomFrom("GET", "POST"), "/" + "c" + "/_field_caps?fields=*");
     }
 
     public void testUserU11() throws Exception {
@@ -334,6 +342,8 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsDenied("u11",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u11", randomFrom("GET", "POST"), "/" + "b" + "/_field_caps?fields=*");
+        assertAccessIsDenied("u11", randomFrom("GET", "POST"), "/" + "c" + "/_field_caps?fields=*");
     }
 
     public void testUserU12() throws Exception {
@@ -373,6 +383,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
         assertBodyHasAccessIsDenied("u13", "PUT", "/b/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsAllowed("u13",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u13", randomFrom("GET", "POST"), "/" + "a" + "/_field_caps?fields=*");
     }
 
     public void testUserU14() throws Exception {
@@ -395,11 +406,31 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
             "/" + randomIndex() + "/_bulk", "{ \"index\" : { \"_id\" : \"123\" } }\n{ \"foo\" : \"bar\" }\n");
         assertAccessIsAllowed("u14",
             "GET", "/" + randomIndex() + "/_mtermvectors", "{ \"docs\" : [ { \"_id\": \"1\" }, { \"_id\": \"2\" } ] }");
+        assertAccessIsDenied("u14", randomFrom("GET", "POST"), "/" + "b" + "/_field_caps?fields=*");
     }
 
     public void testUserU15() throws Exception {
         assertUserIsAllowed("u15", "maintenance", "a");
         assertUserIsDenied("u15", "crud", "a");
+        assertUserIsDenied("u15", "maintenance", "b");
+        assertUserIsDenied("u15", "crud", "b");
+        assertAccessIsDenied("u15", randomFrom("GET", "POST"), "/" + "a" + "/_field_caps?fields=*");
+        assertAccessIsAllowed("u15", randomFrom("GET", "POST"), "/" + "b" + "/_field_caps?fields=*");
+        assertAccessIsDenied("u15", "GET", "/_alias/" + "a");
+        assertAccessIsAllowed("u15", "GET", "/_alias/" + "b*");
+        assertAccessIsDenied("u15", "GET", "/" + "a" + (randomBoolean() ? "" : "/_settings"));
+        assertAccessIsAllowed("u15", "GET", "/" + "b" + (randomBoolean() ? "" : "/_settings"));
+        assertAccessIsDenied("u15", "GET", "/" + "a" + "/_mapping" + (randomBoolean() ? "" : "/field/name"));
+        assertAccessIsAllowed("u15", "GET", "/" + "b" + "/_mapping" + (randomBoolean() ? "" : "/field/name"));
+        assertAccessIsDenied("u15", "GET", "/" + "a" + "/_validate/query?q=name:elasticsearch");
+        assertAccessIsAllowed("u15", "GET", "/" + "b" + "/_validate/query?q=name:elasticsearch");
+        assertAccessIsDenied("u15", "GET", "/_resolve/index/" + "a");
+        assertAccessIsAllowed("u15", "GET", "/_resolve/index/" + "b");
+        assertAccessIsAllowed("u15", randomFrom("GET", "POST"), "/" + "a" + "/_search_shards");
+        assertAccessIsAllowed("u15", randomFrom("GET", "POST"), "/" + "b" + "/_search_shards");
+        // the ILM and data streams plugins reside in a separate project
+        // the view_index_metadata permission also grants the get data stream and ILM explain APIs
+        // but I don't feel compelled to add those as dependencies for this IT only
     }
 
     public void testThatUnknownUserIsRejectedProperly() throws Exception {
@@ -468,6 +499,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
                     assertNoTimeout(client().admin().cluster().prepareHealth(index).setWaitForGreenStatus().get());
                     assertAccessIsAllowed(user, "GET", "/" + index + "/_mapping/field/name");
                     assertAccessIsAllowed(user, "GET", "/" + index + "/_settings");
+                    assertAccessIsAllowed(user, randomFrom("GET", "POST"), "/" + index + "/_field_caps?fields=*");
                 } else {
                     assertAccessIsDenied(user, "DELETE", "/" + index);
                     assertUserIsDenied(user, "create_index", index);
@@ -523,6 +555,7 @@ public class IndexPrivilegeIntegTests extends AbstractPrivilegeTestCase {
                     assertAccessIsAllowed("admin", "GET", "/" + index + "/_doc/1");
                     assertAccessIsAllowed(user, "GET", "/" + index + "/_explain/1", "{ \"query\" : { \"match_all\" : {} } }");
                     assertAccessIsAllowed(user, "GET", "/" + index + "/_termvectors/1");
+                    assertAccessIsAllowed(user, randomFrom("GET", "POST"), "/" + index + "/_field_caps?fields=*");
                     assertUserIsAllowed(user, "search", index);
                 } else {
                     assertAccessIsDenied(user, "GET", "/" + index + "/_count");