From 67204a9c0522322d691483c78a7ecb51c4e1a936 Mon Sep 17 00:00:00 2001 From: Nikolaj Volgushev Date: Fri, 28 Oct 2022 13:40:25 +0200 Subject: [PATCH] `buildFromRoleDescriptor` uses application privilege look up (#91152) Building a role from a single role descriptor previously only worked correctly for application privileges with wildcard patterns. This PR adds support for concrete name look up and ports the relevant tests. Relates: #91107 (comment) --- .../core/security/authz/permission/Role.java | 22 +- .../authz/permission/SimpleRoleTests.java | 86 ++++++++ .../authz/store/ReservedRolesStoreTests.java | 200 ++++++++++++++---- .../service/ElasticServiceAccountsTests.java | 19 +- .../authz/store/CompositeRolesStoreTests.java | 13 ++ 5 files changed, 292 insertions(+), 48 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java index 7a412f33b88a6..d654746c6a13e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java @@ -318,6 +318,15 @@ static SimpleRole buildFromRoleDescriptor( final RoleDescriptor roleDescriptor, final FieldPermissionsCache fieldPermissionsCache, final RestrictedIndices restrictedIndices + ) { + return buildFromRoleDescriptor(roleDescriptor, fieldPermissionsCache, restrictedIndices, List.of()); + } + + static SimpleRole buildFromRoleDescriptor( + final RoleDescriptor roleDescriptor, + final FieldPermissionsCache fieldPermissionsCache, + final RestrictedIndices restrictedIndices, + final Collection storedApplicationPrivilegeDescriptors ) { // TODO handle this when we introduce remote index privileges for built-in users and roles. That's the only production code // using this builder @@ -344,14 +353,11 @@ static SimpleRole buildFromRoleDescriptor( } for (RoleDescriptor.ApplicationResourcePrivileges applicationPrivilege : roleDescriptor.getApplicationPrivileges()) { - builder.addApplicationPrivilege( - new ApplicationPrivilege( - applicationPrivilege.getApplication(), - Sets.newHashSet(applicationPrivilege.getPrivileges()), - applicationPrivilege.getPrivileges() - ), - Sets.newHashSet(applicationPrivilege.getResources()) - ); + ApplicationPrivilege.get( + applicationPrivilege.getApplication(), + Sets.newHashSet(applicationPrivilege.getPrivileges()), + storedApplicationPrivilegeDescriptors + ).forEach(priv -> builder.addApplicationPrivilege(priv, Sets.newHashSet(applicationPrivilege.getResources()))); } final String[] rdRunAs = roleDescriptor.getRunAs(); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/SimpleRoleTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/SimpleRoleTests.java index 6f2f9bfb69ba2..b6c9ffd8fb4bf 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/SimpleRoleTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/SimpleRoleTests.java @@ -11,13 +11,19 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; +import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege; +import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutionException; import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -56,4 +62,84 @@ public void testHasPrivilegesCache() throws ExecutionException { assertThat(role.checkPrivilegesWithCache(privilegesToCheck), equalTo(privilegesCheckResult)); } + public void testBuildFromRoleDescriptorWithApplicationPrivileges() { + final boolean wildcardApplication = randomBoolean(); + final boolean wildcardPrivileges = randomBoolean(); + final boolean wildcardResources = randomBoolean(); + final RoleDescriptor.ApplicationResourcePrivileges applicationPrivilege = RoleDescriptor.ApplicationResourcePrivileges.builder() + .application(wildcardApplication ? "*" : randomAlphaOfLengthBetween(5, 12)) + // concrete privileges need to be prefixed with lower case letters to be considered valid, so use "app" + .privileges(wildcardPrivileges ? "*" : "app" + randomAlphaOfLengthBetween(5, 12)) + .resources(wildcardResources ? new String[] { "*" } : generateRandomStringArray(6, randomIntBetween(4, 8), false, false)) + .build(); + + final String allowedApplicationActionPattern = randomAlphaOfLengthBetween(5, 12); + final SimpleRole role = Role.buildFromRoleDescriptor( + new RoleDescriptor( + "r1", + null, + null, + new RoleDescriptor.ApplicationResourcePrivileges[] { applicationPrivilege }, + null, + null, + null, + null + ), + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + wildcardPrivileges + ? List.of() + : List.of( + new ApplicationPrivilegeDescriptor( + applicationPrivilege.getApplication(), + Arrays.stream(applicationPrivilege.getPrivileges()).iterator().next(), + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) + ); + assertThat( + "expected grant for role with application privilege to be: " + applicationPrivilege, + role.application() + .grants( + new ApplicationPrivilege( + wildcardApplication ? randomAlphaOfLengthBetween(1, 10) : applicationPrivilege.getApplication(), + wildcardPrivileges ? Set.of(randomAlphaOfLengthBetween(1, 10)) : Set.of(applicationPrivilege.getPrivileges()), + wildcardPrivileges ? randomAlphaOfLengthBetween(1, 10) : allowedApplicationActionPattern + ), + wildcardResources ? randomAlphaOfLengthBetween(1, 10) : randomFrom(applicationPrivilege.getResources()) + ), + is(true) + ); + // This gives decent but not complete coverage of denial cases; for any non-wildcard field we pick a mismatched value to force a + // denial + assertThat( + "expected grant for role with application privilege to be: " + applicationPrivilege, + role.application() + .grants( + new ApplicationPrivilege( + false == wildcardApplication + ? randomValueOtherThan(applicationPrivilege.getApplication(), () -> randomAlphaOfLengthBetween(1, 10)) + : randomAlphaOfLengthBetween(1, 10), + false == wildcardPrivileges + ? randomValueOtherThan( + Set.of(applicationPrivilege.getPrivileges()), + () -> Set.of(randomAlphaOfLengthBetween(1, 10)) + ) + : Set.of(randomAlphaOfLengthBetween(1, 10)), + false == wildcardPrivileges + ? randomValueOtherThan(allowedApplicationActionPattern, () -> randomAlphaOfLengthBetween(1, 10)) + : randomAlphaOfLengthBetween(1, 10) + ), + false == wildcardResources + ? randomValueOtherThanMany( + it -> List.of(applicationPrivilege.getResources()).contains(it), + () -> randomAlphaOfLengthBetween(1, 10) + ) + : randomAlphaOfLengthBetween(1, 10) + ), + // If all are wildcards, then we necessarily get a grant, otherwise expect a denial + is(wildcardApplication && wildcardPrivileges && wildcardResources) + ); + } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index 36508470897c0..7ba4281d0994b 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -218,6 +218,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES; @@ -1142,7 +1143,13 @@ public void testKibanaAdminRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); - Role kibanaAdminRole = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + Role kibanaAdminRole = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of(new ApplicationPrivilegeDescriptor("kibana-.kibana", "all", Set.of(allowedApplicationActionPattern), Map.of())) + ); assertThat(kibanaAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(kibanaAdminRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(kibanaAdminRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -1166,7 +1173,10 @@ public void testKibanaAdminRole() { final String application = "kibana-.kibana"; assertThat(kibanaAdminRole.application().grants(new ApplicationPrivilege(application, "app-foo", "foo"), "*"), is(false)); - assertThat(kibanaAdminRole.application().grants(new ApplicationPrivilege(application, "app-all", "all"), "*"), is(true)); + assertThat( + kibanaAdminRole.application().grants(new ApplicationPrivilege(application, "app-all", allowedApplicationActionPattern), "*"), + is(true) + ); final String applicationWithRandomIndex = "kibana-.kibana_" + randomAlphaOfLengthBetween(8, 24); assertThat( @@ -1186,7 +1196,13 @@ public void testKibanaUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role kibanaUserRole = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + Role kibanaUserRole = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of(new ApplicationPrivilegeDescriptor("kibana-.kibana", "all", Set.of(allowedApplicationActionPattern), Map.of())) + ); assertThat(kibanaUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(kibanaUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(kibanaUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -1206,15 +1222,23 @@ public void testKibanaUserRole() { ); final String randomApplication = "kibana-" + randomAlphaOfLengthBetween(8, 24); - assertThat(kibanaUserRole.application().grants(new ApplicationPrivilege(randomApplication, "app-random", "all"), "*"), is(false)); + assertThat( + kibanaUserRole.application() + .grants(new ApplicationPrivilege(randomApplication, "app-random", allowedApplicationActionPattern), "*"), + is(false) + ); final String application = "kibana-.kibana"; assertThat(kibanaUserRole.application().grants(new ApplicationPrivilege(application, "app-foo", "foo"), "*"), is(false)); - assertThat(kibanaUserRole.application().grants(new ApplicationPrivilege(application, "app-all", "all"), "*"), is(true)); + assertThat( + kibanaUserRole.application().grants(new ApplicationPrivilege(application, "app-all", allowedApplicationActionPattern), "*"), + is(true) + ); final String applicationWithRandomIndex = "kibana-.kibana_" + randomAlphaOfLengthBetween(8, 24); assertThat( - kibanaUserRole.application().grants(new ApplicationPrivilege(applicationWithRandomIndex, "app-random-index", "all"), "*"), + kibanaUserRole.application() + .grants(new ApplicationPrivilege(applicationWithRandomIndex, "app-random-index", allowedApplicationActionPattern), "*"), is(false) ); @@ -1230,10 +1254,20 @@ public void testMonitoringUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); Role monitoringUserRole = Role.buildFromRoleDescriptor( roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), - RESTRICTED_INDICES + RESTRICTED_INDICES, + List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplicationWithRandomIndex, + "reserved_monitoring", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) ); assertThat(monitoringUserRole.cluster().check(MainAction.NAME, request, authentication), is(true)); assertThat(monitoringUserRole.cluster().check(XPackInfoAction.NAME, request, authentication), is(true)); @@ -1295,14 +1329,16 @@ public void testMonitoringUserRole() { assertNoAccessAllowed(monitoringUserRole, TestRestrictedIndices.SAMPLE_RESTRICTED_NAMES); assertNoAccessAllowed(monitoringUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); - final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat( monitoringUserRole.application().grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false) ); assertThat( monitoringUserRole.application() - .grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_monitoring", "reserved_monitoring"), "*"), + .grants( + new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_monitoring", allowedApplicationActionPattern), + "*" + ), is(true) ); @@ -1310,7 +1346,7 @@ public void testMonitoringUserRole() { assertThat(monitoringUserRole.application().grants(new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false)); assertThat( monitoringUserRole.application() - .grants(new ApplicationPrivilege(otherApplication, "app-reserved_monitoring", "reserved_monitoring"), "*"), + .grants(new ApplicationPrivilege(otherApplication, "app-reserved_monitoring", allowedApplicationActionPattern), "*"), is(false) ); } @@ -2138,7 +2174,21 @@ public void testAPMUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); + Role role = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplicationWithRandomIndex, + "reserved_ml_apm_user", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) + ); assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false)); assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 12)), is(false)); @@ -2163,12 +2213,11 @@ public void testAPMUserRole() { assertOnlyReadAllowed(role, "observability-annotations"); - final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false)); assertThat( role.application() .grants( - new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml_apm_user", "reserved_ml_apm_user"), + new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml_apm_user", allowedApplicationActionPattern), "*" ), is(true) @@ -2177,7 +2226,8 @@ public void testAPMUserRole() { final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24); assertThat(role.application().grants(new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false)); assertThat( - role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml_apm_user", "reserved_ml_apm_user"), "*"), + role.application() + .grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml_apm_user", allowedApplicationActionPattern), "*"), is(false) ); } @@ -2190,8 +2240,21 @@ public void testMachineLearningAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - Role role = Role.buildFromRoleDescriptor(roleDescriptor, fieldPermissionsCache, RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); + Role role = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplicationWithRandomIndex, + "reserved_ml_admin", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) + ); assertRoleHasManageMl(role); assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false)); @@ -2208,18 +2271,20 @@ public void testMachineLearningAdminRole() { assertNoAccessAllowed(role, TestRestrictedIndices.SAMPLE_RESTRICTED_NAMES); assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); - final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false)); assertThat( role.application() - .grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml_admin"), "*"), + .grants( + new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", allowedApplicationActionPattern), + "*" + ), is(true) ); final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24); assertThat(role.application().grants(new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false)); assertThat( - role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml_admin"), "*"), + role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", allowedApplicationActionPattern), "*"), is(false) ); } @@ -2301,8 +2366,21 @@ public void testMachineLearningUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - Role role = Role.buildFromRoleDescriptor(roleDescriptor, fieldPermissionsCache, RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); + Role role = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplicationWithRandomIndex, + "reserved_ml_user", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) + ); assertThat(role.cluster().check(CloseJobAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(DeleteCalendarAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(DeleteCalendarEventAction.NAME, request, authentication), is(false)); @@ -2372,18 +2450,20 @@ public void testMachineLearningUserRole() { assertNoAccessAllowed(role, TestRestrictedIndices.SAMPLE_RESTRICTED_NAMES); assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); - final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false)); assertThat( role.application() - .grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml_user"), "*"), + .grants( + new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", allowedApplicationActionPattern), + "*" + ), is(true) ); final String otherApplication = "logstash-" + randomAlphaOfLengthBetween(8, 24); assertThat(role.application().grants(new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false)); assertThat( - role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml_user"), "*"), + role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", allowedApplicationActionPattern), "*"), is(false) ); } @@ -2405,7 +2485,19 @@ public void testTransformAdminRole() { assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); } - Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); + List lookup = roleDescriptor.getName().equals("data_frame_transforms_admin") + ? List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplicationWithRandomIndex, + "reserved_ml_user", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) + : List.of(); + Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES, lookup); assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true)); @@ -2426,7 +2518,6 @@ public void testTransformAdminRole() { assertNoAccessAllowed(role, TestRestrictedIndices.SAMPLE_RESTRICTED_NAMES); assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); - final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat( role.application().grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false) @@ -2435,7 +2526,10 @@ public void testTransformAdminRole() { if (roleDescriptor.getName().equals("data_frame_transforms_admin")) { assertThat( role.application() - .grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml_user"), "*"), + .grants( + new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", allowedApplicationActionPattern), + "*" + ), is(true) ); } @@ -2444,7 +2538,8 @@ public void testTransformAdminRole() { assertThat(role.application().grants(new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false)); if (roleDescriptor.getName().equals("data_frame_transforms_admin")) { assertThat( - role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml_user"), "*"), + role.application() + .grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", allowedApplicationActionPattern), "*"), is(false) ); } @@ -2468,7 +2563,19 @@ public void testTransformUserRole() { assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); } - Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); + List lookup = roleDescriptor.getName().equals("data_frame_transforms_user") + ? List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplicationWithRandomIndex, + "reserved_ml_user", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) + : List.of(); + Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES, lookup); assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true)); @@ -2494,7 +2601,6 @@ public void testTransformUserRole() { assertNoAccessAllowed(role, TestRestrictedIndices.SAMPLE_RESTRICTED_NAMES); assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); - final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat( role.application().grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-foo", "foo"), "*"), is(false) @@ -2503,7 +2609,10 @@ public void testTransformUserRole() { if (roleDescriptor.getName().equals("data_frame_transforms_user")) { assertThat( role.application() - .grants(new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", "reserved_ml_user"), "*"), + .grants( + new ApplicationPrivilege(kibanaApplicationWithRandomIndex, "app-reserved_ml", allowedApplicationActionPattern), + "*" + ), is(true) ); } @@ -2512,7 +2621,8 @@ public void testTransformUserRole() { assertThat(role.application().grants(new ApplicationPrivilege(otherApplication, "app-foo", "foo"), "*"), is(false)); if (roleDescriptor.getName().equals("data_frame_transforms_user")) { assertThat( - role.application().grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", "reserved_ml_user"), "*"), + role.application() + .grants(new ApplicationPrivilege(otherApplication, "app-reserved_ml", allowedApplicationActionPattern), "*"), is(false) ); } @@ -2593,7 +2703,13 @@ public void testPredefinedViewerRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + Role role = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of(new ApplicationPrivilegeDescriptor("kibana-.kibana", "read", Set.of(allowedApplicationActionPattern), Map.of())) + ); // No cluster privileges assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); @@ -2637,7 +2753,10 @@ public void testPredefinedViewerRole() { assertNoAccessAllowed(role, "." + randomAlphaOfLengthBetween(6, 10)); assertNoAccessAllowed(role, "ilm-history-" + randomIntBetween(0, 5)); // Check application privileges - assertThat(role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-read", "read"), "*"), is(true)); + assertThat( + role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-read", allowedApplicationActionPattern), "*"), + is(true) + ); assertThat(role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-all", "all"), "*"), is(false)); assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 20)), is(false)); @@ -2651,7 +2770,13 @@ public void testPredefinedEditorRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.buildFromRoleDescriptor(roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), RESTRICTED_INDICES); + final String allowedApplicationActionPattern = "example/custom/action/*"; + Role role = Role.buildFromRoleDescriptor( + roleDescriptor, + new FieldPermissionsCache(Settings.EMPTY), + RESTRICTED_INDICES, + List.of(new ApplicationPrivilegeDescriptor("kibana-.kibana", "all", Set.of(allowedApplicationActionPattern), Map.of())) + ); // No cluster privileges assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); @@ -2701,7 +2826,10 @@ public void testPredefinedEditorRole() { assertNoAccessAllowed(role, "ilm-history-" + randomIntBetween(0, 5)); // Check application privileges - assertThat(role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-all", "all"), "*"), is(true)); + assertThat( + role.application().grants(new ApplicationPrivilege("kibana-.kibana", "kibana-all", allowedApplicationActionPattern), "*"), + is(true) + ); assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 20)), is(false)); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java index 3088631f9117b..70906a7f8683d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java @@ -111,6 +111,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege; +import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.user.KibanaSystemUser; import org.elasticsearch.xpack.core.security.user.User; @@ -119,6 +120,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES; import static org.hamcrest.Matchers.containsString; @@ -147,10 +149,20 @@ public void testKibanaSystemPrivileges() { } public void testElasticFleetServerPrivileges() { + final String allowedApplicationActionPattern = "example/custom/action/*"; + final String kibanaApplication = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final Role role = Role.buildFromRoleDescriptor( ElasticServiceAccounts.ACCOUNTS.get("elastic/fleet-server").roleDescriptor(), new FieldPermissionsCache(Settings.EMPTY), - RESTRICTED_INDICES + RESTRICTED_INDICES, + List.of( + new ApplicationPrivilegeDescriptor( + kibanaApplication, + "reserved_fleet-setup", + Set.of(allowedApplicationActionPattern), + Map.of() + ) + ) ); final Authentication authentication = AuthenticationTestHelper.builder().serviceAccount().build(); assertThat( @@ -236,10 +248,9 @@ public void testElasticFleetServerPrivileges() { assertThat(role.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(apmSampledTracesIndex), is(false)); assertThat(role.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(apmSampledTracesIndex), is(false)); - final String kibanaApplication = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); final String privilegeName = randomAlphaOfLengthBetween(3, 16); assertThat( - role.application().grants(new ApplicationPrivilege(kibanaApplication, privilegeName, "reserved_fleet-setup"), "*"), + role.application().grants(new ApplicationPrivilege(kibanaApplication, privilegeName, allowedApplicationActionPattern), "*"), is(true) ); @@ -247,7 +258,7 @@ public void testElasticFleetServerPrivileges() { + "-" + randomAlphaOfLengthBetween(8, 24); assertThat( - role.application().grants(new ApplicationPrivilege(otherApplication, privilegeName, "reserved_fleet-setup"), "*"), + role.application().grants(new ApplicationPrivilege(otherApplication, privilegeName, allowedApplicationActionPattern), "*"), is(false) ); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index b9dc7bd1da2d7..60d83aa001bee 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -491,6 +491,19 @@ private void trySuccessfullyLoadSuperuserRole(CompositeRolesStore compositeRoles final Role role = future.actionGet(); assertThat(role.names(), arrayContaining("superuser")); assertThat(role.application().getApplicationNames(), containsInAnyOrder("*")); + assertThat( + role.application() + .grants( + new ApplicationPrivilege( + randomAlphaOfLengthBetween(2, 10), + randomAlphaOfLengthBetween(2, 10), + randomAlphaOfLengthBetween(2, 10) + ), + "*" + ), + is(true) + ); + assertThat(role.cluster().privileges(), containsInAnyOrder(ClusterPrivilegeResolver.ALL)); assertThat(role.indices().check(SearchAction.NAME), Matchers.is(true)); assertThat(role.indices().check(IndexAction.NAME), Matchers.is(true));