From aebb383fbec8347ba9515843650d7aa7b0b6240a Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 20 Jun 2023 14:52:45 +0100 Subject: [PATCH 01/46] #2553 System Indices check for extensions and tests Signed-off-by: Sam --- .../privileges/PrivilegesEvaluator.java | 10 +- .../SecurityIndexAccessEvaluator.java | 52 ++++- .../security/securityconf/ConfigModelV7.java | 3 + .../security/support/ConfigConstants.java | 1 + .../privileges/PrivilegesEvaluatorTest.java | 5 +- .../SecurityIndexAccessEvaluatorTest.java | 33 +-- .../system_indices/SystemIndicesTests.java | 197 +++++++++++++++--- .../internal_users_system_indices.yml | 16 ++ .../roles_mapping_system_indices.yml | 20 ++ src/test/resources/roles_system_indices.yml | 22 ++ 10 files changed, 307 insertions(+), 52 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index b118a62e5d..a0025da8b5 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -89,6 +89,7 @@ import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; import org.opensearch.security.securityconf.ConfigModel; +import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.securityconf.DynamicConfigModel; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; @@ -308,7 +309,14 @@ public PrivilegesEvaluatorResponse evaluate( } // Security index access - if (securityIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse).isComplete()) { + if (securityIndexAccessEvaluator.evaluate( + request, + task, + action0, + requestedResolved, + presponse, + (ConfigModelV7.SecurityRoles) securityRoles + ).isComplete()) { return presponse; } diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 94b0478759..8f5592d268 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -42,6 +42,7 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; +import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; import org.opensearch.tasks.Task; @@ -55,7 +56,7 @@ public class SecurityIndexAccessEvaluator { private final WildcardMatcher securityDeniedActionMatcher; private final IndexResolverReplacer irr; private final boolean filterSecurityIndex; - + private final List configuredSystemIndices; // for system-indices configuration private final WildcardMatcher systemIndexMatcher; private final boolean systemIndexEnabled; @@ -75,6 +76,13 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT ); + List systemIndecesFromConfig = settings.getAsList( + ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, + ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT + ); + + this.configuredSystemIndices = new ArrayList<>(systemIndecesFromConfig); + this.configuredSystemIndices.add(ConfigConstants.SYSTEM_INDEX_PERMISSION); final boolean restoreSecurityIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_UNSUPPORTED_RESTORE_SECURITYINDEX_ENABLED, @@ -105,9 +113,22 @@ public PrivilegesEvaluatorResponse evaluate( final Task task, final String action, final Resolved requestedResolved, - final PrivilegesEvaluatorResponse presponse + final PrivilegesEvaluatorResponse presponse, + ConfigModelV7.SecurityRoles securityRoles ) { + final boolean isDebugEnabled = log.isDebugEnabled(); + + if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { + log.warn( + "An account without the {} permission is trying to access a System Index. Related indexes: {}", + ConfigConstants.SYSTEM_INDEX_PERMISSION, + getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")) + ); + presponse.allowed = false; + return presponse.markComplete(); + } + if (securityDeniedActionMatcher.test(action)) { if (requestedResolved.isLocalAll()) { if (filterSecurityIndex) { @@ -122,13 +143,13 @@ public PrivilegesEvaluatorResponse evaluate( ); } return presponse; - } else { - auditLog.logSecurityIndexAttempt(request, action, task); - log.warn("{} for '_all' indices is not allowed for a regular user", action); - presponse.allowed = false; - return presponse.markComplete(); } - } else if (matchAnySystemIndices(requestedResolved)) { + auditLog.logSecurityIndexAttempt(request, action, task); + log.warn("{} for '_all' indices is not allowed for a regular user", action); + presponse.allowed = false; + return presponse.markComplete(); + } + if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { if (filterSecurityIndex) { Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); allWithoutSecurity.remove(securityIndex); @@ -175,6 +196,21 @@ public PrivilegesEvaluatorResponse evaluate( return presponse; } + private boolean checkSystemIndexPermissionsForUser(ConfigModelV7.SecurityRoles securityRoles) { + Set userPermMatchers = securityRoles.getRoles() + .stream() + .flatMap(role -> role.getIpatterns().stream()) + .map(ConfigModelV7.IndexPattern::getNonWildCardPerms) + .collect(Collectors.toSet()); + + for (WildcardMatcher userPermMatcher : userPermMatchers.stream().collect(Collectors.toSet())) { + if (userPermMatcher.matchAny(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { + return true; + } + } + return false; + } + private boolean matchAnySystemIndices(final Resolved requestedResolved) { return !getProtectedIndexes(requestedResolved).isEmpty(); } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 1fb6e4da0e..172b059b04 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -868,6 +868,9 @@ public WildcardMatcher getPerms() { return WildcardMatcher.from(perms); } + public WildcardMatcher getNonWildCardPerms() { + return WildcardMatcher.from(perms.stream().filter(perm -> !perm.equals("*"))); + } } /*public static class TypePerm { diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index ee04ff62f3..2d7b729b7f 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -311,6 +311,7 @@ public enum RolesMappingResolution { "opendistro_security_injected_roles_validation_header"; // System indices settings + public static final String SYSTEM_INDEX_PERMISSION = "system:admin/system_index"; public static final String SECURITY_SYSTEM_INDICES_ENABLED_KEY = "plugins.security.system_indices.enabled"; public static final Boolean SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT = false; public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; diff --git a/src/test/java/org/opensearch/security/privileges/PrivilegesEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/PrivilegesEvaluatorTest.java index b953ac8ddb..4f25c71d66 100644 --- a/src/test/java/org/opensearch/security/privileges/PrivilegesEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/PrivilegesEvaluatorTest.java @@ -14,6 +14,7 @@ import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.opensearch.common.settings.Settings; @@ -25,6 +26,7 @@ public class PrivilegesEvaluatorTest extends SingleClusterTest { private static final Header NegativeLookaheadUserHeader = encodeBasicHeader("negative_lookahead_user", "negative_lookahead_user"); private static final Header NegatedRegexUserHeader = encodeBasicHeader("negated_regex_user", "negated_regex_user"); + @Before public void setupSettingsIndexPattern() throws Exception { Settings settings = Settings.builder().build(); setup( @@ -39,7 +41,6 @@ public void setupSettingsIndexPattern() throws Exception { @Test public void testNegativeLookaheadPattern() throws Exception { - setupSettingsIndexPattern(); RestHelper rh = nonSslRestHelper(); RestHelper.HttpResponse response = rh.executeGetRequest("*/_search", NegativeLookaheadUserHeader); @@ -50,8 +51,6 @@ public void testNegativeLookaheadPattern() throws Exception { @Test public void testRegexPattern() throws Exception { - setupSettingsIndexPattern(); - RestHelper rh = nonSslRestHelper(); RestHelper.HttpResponse response = rh.executeGetRequest("*/_search", NegatedRegexUserHeader); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 14c5eabb73..5bfc2300b0 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -28,6 +28,7 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; +import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.tasks.Task; import static org.hamcrest.MatcherAssert.assertThat; @@ -54,17 +55,18 @@ public class SecurityIndexAccessEvaluatorTest { private PrivilegesEvaluatorResponse presponse; @Mock private Logger log; - private SecurityIndexAccessEvaluator evaluator; - private static final String UNPROTECTED_ACTION = "indices:data/read"; private static final String PROTECTED_ACTION = "indices:data/write"; + @Mock + ConfigModelV7 configModelV7; + ConfigModelV7.SecurityRoles securityRoles = configModelV7.getSecurityRoles(); @Before public void before() { evaluator = new SecurityIndexAccessEvaluator( Settings.EMPTY.builder() - .put("plugins.security.system_indices.indices", ".test") + .put("plugins.security.system_indices.indices", ".testSystemIndex") .put("plugins.security.system_indices.enabled", true) .build(), auditLog, @@ -82,10 +84,17 @@ public void after() { @Test public void actionIsNotProtected_noSystemIndexInvolved() { - final Resolved resolved = createResolved(".test"); + final Resolved resolved = createResolved(".testSystemIndex"); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); verifyNoInteractions(presponse); assertThat(response, is(presponse)); @@ -97,12 +106,12 @@ public void actionIsNotProtected_noSystemIndexInvolved() { public void disableCacheOrRealtimeOnSystemIndex() { final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); - final Resolved resolved = createResolved(".test"); + final Resolved resolved = createResolved(".testSystemIndex"); // Action - evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse); - evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse); - evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse); + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); verifyNoInteractions(presponse); verify(searchRequest).requestCache(Boolean.FALSE); @@ -118,7 +127,7 @@ public void protectedActionLocalAll() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); @@ -130,10 +139,10 @@ public void protectedActionLocalAll() { @Test public void protectedActionSystemIndex() { - final Resolved resolved = createResolved(".test", ".opendistro_security"); + final Resolved resolved = createResolved(".testSystemIndex", ".opendistro_security"); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index e298ae45c7..dcdf8a4af7 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -17,6 +17,7 @@ import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; +import org.junit.Assert; import org.junit.Test; import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; @@ -49,7 +50,7 @@ */ public class SystemIndicesTests extends SingleClusterTest { - private static final List listOfIndexesToTest = Arrays.asList("config1", "config2"); + private static final List listOfIndexesToTest = Arrays.asList("system_index_a", "system_index_b"); private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; private static final String allAccessUser = "admin_all_access"; private static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); @@ -58,6 +59,11 @@ public class SystemIndicesTests extends SingleClusterTest { allAccessUser ); + private static final String extensionUser = "extensions_user"; + private static final Header extensionUserHeader = encodeBasicHeader(extensionUser, allAccessUser); + private static final String extensionUserC = "extensions_user_c"; + private static final Header extensionUserCHeader = encodeBasicHeader(extensionUserC, allAccessUser); + private void setupSystemIndicesDisabledWithSsl() throws Exception { Settings systemIndexSettings = Settings.builder() @@ -218,13 +224,16 @@ public void testSearchWithSystemIndicesAsSuperAdmin() throws Exception { } @Test - public void testSearchWithSystemIndicesAsAdmin() throws Exception { + public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper restHelper = sslRestHelper(); + // search system indices for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader), 0); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + } // search all indices @@ -237,18 +246,42 @@ public void testSearchWithSystemIndicesAsAdmin() throws Exception { assertEquals(0, searchResponse.getHits().getHits().length); } + @Test + public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Exception { + setupSystemIndicesEnabledWithSsl(); + createTestIndicesAndDocs(); + RestHelper restHelper = sslRestHelper(); + + for (String index : listOfIndexesToTest) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, extensionUserHeader), 0); + } + + } + + public void testSearchAllWithSystemIndicesShouldFailAsExtensionUser() throws Exception { + setupSystemIndicesEnabledWithSsl(); + createTestIndicesAndDocs(); + RestHelper restHelper = sslRestHelper(); + + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, extensionUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + XContentParser xcp = XContentType.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); + SearchResponse searchResponse = SearchResponse.fromXContent(xcp); + assertEquals(RestStatus.FORBIDDEN, searchResponse.status()); + assertEquals(0, searchResponse.getHits().getHits().length); + } + /*************************************************************************************************************************** * Delete index and Delete doc ***************************************************************************************************************************/ @Test - public void testDelete() throws Exception { + public void testDeleteShouldSucceedAsSuperAdmin() throws Exception { setupSystemIndicesDisabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - // as super-admin for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); @@ -256,7 +289,13 @@ public void testDelete() throws Exception { RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); } + } + + @Test + public void testDeleteDocShouldSucceedAsAdmin() throws Exception { + setupSystemIndicesDisabledWithSsl(); createTestIndicesAndDocs(); + RestHelper sslRestHelper = sslRestHelper(); // as admin for (String index : listOfIndexesToTest) { @@ -269,13 +308,11 @@ public void testDelete() throws Exception { } @Test - public void testDeleteWithSystemIndices() throws Exception { + public void testDeleteDocWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exception { setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - // as super-admin for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); @@ -283,9 +320,15 @@ public void testDeleteWithSystemIndices() throws Exception { RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); } + } + + @Test + public void testDeleteDocWithSystemIndicesShouldFailsAsAdmin() throws Exception { + setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); - // as admin + RestHelper sslRestHelper = sslRestHelper(); + for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); @@ -326,13 +369,11 @@ public void testCloseOpen() throws Exception { } @Test - public void testCloseOpenWithSystemIndices() throws Exception { + public void testCloseOpenWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exception { setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - // as super-admin for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); @@ -341,12 +382,34 @@ public void testCloseOpenWithSystemIndices() throws Exception { assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); } - // as admin + } + + @Test + public void testCloseOpenIndexShouldFailWithSystemIndicesAsAdmin() throws Exception { + setupSystemIndicesEnabledWithSsl(); + createTestIndicesAndDocs(); + RestHelper sslRestHelper = sslRestHelper(); + for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); + } + } + + @Test + public void testCloseIndexShouldFailAndOpenIndexShouldSucceedWithSystemIndicesAsExtensionUser() throws Exception { + setupSystemIndicesEnabledWithSsl(); + createTestIndicesAndDocs(); + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : listOfIndexesToTest) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", extensionUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", extensionUserHeader); assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); } } @@ -356,7 +419,7 @@ public void testCloseOpenWithSystemIndices() throws Exception { ***************************************************************************************************************************/ @Test - public void testUpdateIndexSettings() throws Exception { + public void testUpdateIndexSettingsWithNormalIndicesShouldSucceed() throws Exception { setupSystemIndicesDisabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); @@ -377,7 +440,7 @@ public void testUpdateIndexSettings() throws Exception { } @Test - public void testUpdateIndexSettingsWithSystemIndices() throws Exception { + public void testUpdateIndexSettingsWithSystemIndicesShouldFailAsAdmin() throws Exception { setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); @@ -402,7 +465,7 @@ public void testUpdateIndexSettingsWithSystemIndices() throws Exception { ************************************************************************************************************************** */ @Test - public void testUpdateMappings() throws Exception { + public void testUpdateMappingsWithNormalIndicesShouldSucceed() throws Exception { setupSystemIndicesDisabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); @@ -424,7 +487,7 @@ public void testUpdateMappings() throws Exception { } @Test - public void testUpdateMappingsWithSystemIndices() throws Exception { + public void testUpdateMappingsWithSystemIndicesShouldFailAsAdmin() throws Exception { setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); @@ -451,7 +514,7 @@ public void testUpdateMappingsWithSystemIndices() throws Exception { ***************************************************************************************************************************/ @Test - public void testCreate() throws Exception { + public void testCreateIndexWithNormalIndicesShouldSucceed() throws Exception { setupSystemIndicesDisabledWithSsl(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); RestHelper sslRestHelper = sslRestHelper(); @@ -489,10 +552,9 @@ public void testCreate() throws Exception { } @Test - public void testCreateWithSystemIndices() throws Exception { + public void testCreateIndexWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exception { setupSystemIndicesEnabledWithSsl(); RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); String indexSettings = "{\n" + " \"settings\" : {\n" @@ -503,7 +565,6 @@ public void testCreateWithSystemIndices() throws Exception { + " }\n" + "}"; - // as super-admin for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); @@ -512,20 +573,47 @@ public void testCreateWithSystemIndices() throws Exception { assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); } - for (String index : listOfIndexesToTest) { - keyStoreRestHelper.executeDeleteRequest(index); - } + } + + @Test + public void testCreateIndexWithSystemIndicesShouldFailAsAdmin() throws Exception { + setupSystemIndicesEnabledWithSsl(); + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; - // as admin for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertTrue(response.getStatusCode() == RestStatus.FORBIDDEN.getStatus()); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); } } + @Test + public void testCreateIndexWithSystemIndicesShouldSucceedWithExtensionRole() throws Exception { + setupSystemIndicesEnabledWithSsl(); + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; + + } + /*************************************************************************************************************************** * snapshot : since snapshot takes more time, we are testing only Enabled case. ***************************************************************************************************************************/ @@ -564,6 +652,59 @@ public void testSnapshotWithSystemIndices() throws Exception { allAccessUserHeader ).getStatusCode() ); + assertEquals( + HttpStatus.SC_OK, + sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1", allAccessUserHeader).getStatusCode() + ); + assertEquals( + HttpStatus.SC_OK, + sslRestHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ).getStatusCode() + ); + assertEquals( + HttpStatus.SC_FORBIDDEN, + sslRestHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + allAccessUserHeader + ).getStatusCode() + ); } } + + @Test + public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Exception { + setupSystemIndicesEnabledWithSsl(); + RestHelper sslRestHelper = sslRestHelper(); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest( + "system_index_a" + "/_doc", + "{\"foo\": \"bar\"}", + extensionUserHeader + ); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + + response = sslRestHelper.executeGetRequest("system_index_a", extensionUserHeader); + Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + } + + @Test + public void testExtensionIndexAccessShouldFailAsDifferentExtensionUser() throws Exception { + setupSystemIndicesEnabledWithSsl(); + RestHelper sslRestHelper = sslRestHelper(); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest( + "system_index_a" + "/_doc", + "{\"foo\": \"bar\"}", + extensionUserCHeader + ); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + + response = sslRestHelper.executeGetRequest("system_index_a", extensionUserCHeader); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); + } + } diff --git a/src/test/resources/internal_users_system_indices.yml b/src/test/resources/internal_users_system_indices.yml index 8a8c990eb3..2246e03457 100644 --- a/src/test/resources/internal_users_system_indices.yml +++ b/src/test/resources/internal_users_system_indices.yml @@ -9,3 +9,19 @@ admin_all_access: backend_roles: [] attributes: {} description: "User mapped to all cluster and index permissions but no role to view protected index" + +extensions_user: + hash: "$2y$12$ft8tXtxb.dyO/5MrDXHLc.e1o3dktEQJMvR2e.sgVDyD/gR7G9dLS" + reserved: false + hidden: false + backend_roles: [] + attributes: {} + description: "User Mapped to role With Access to Admin:System:Indices " + +extensions_user_c: + hash: "$2y$12$ft8tXtxb.dyO/5MrDXHLc.e1o3dktEQJMvR2e.sgVDyD/gR7G9dLS" + reserved: false + hidden: false + backend_roles: [] + attributes: {} + description: "User Mapped to role With Access to Admin:System:Indices " diff --git a/src/test/resources/roles_mapping_system_indices.yml b/src/test/resources/roles_mapping_system_indices.yml index 64b5992fa8..5889d84c49 100644 --- a/src/test/resources/roles_mapping_system_indices.yml +++ b/src/test/resources/roles_mapping_system_indices.yml @@ -12,3 +12,23 @@ opendistro_security_all_access: - "admin_all_access" and_backend_roles: [] description: "Full index permissions" + +extensions_role: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + users: + - "extensions_user" + and_backend_roles: [] + description: "System index permissions" + +extensions_role_c: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + users: + - "extensions_user_c" + and_backend_roles: [] + description: "System index permissions" diff --git a/src/test/resources/roles_system_indices.yml b/src/test/resources/roles_system_indices.yml index 88668a553d..712af38740 100644 --- a/src/test/resources/roles_system_indices.yml +++ b/src/test/resources/roles_system_indices.yml @@ -12,3 +12,25 @@ opendistro_security_all_access: - '*' allowed_actions: - '*' + +extensions_role: + description: "ExtensionsRole" + cluster_permissions: + - '*' + index_permissions: + - index_patterns: + - '*' + allowed_actions: + - '*' + - 'system:admin/system_index' + +extensions_role_c: + description: "Limited ExtensionsRole" + cluster_permissions: + - '*' + index_permissions: + - index_patterns: + - 'system_index_b' + allowed_actions: + - '*' + - 'system:admin/system_index' From 06403bd6b78770df891b4928ca37a97d53df7c17 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 20 Jun 2023 21:13:26 +0100 Subject: [PATCH 02/46] #2553 System Indices tests response body checks Signed-off-by: Sam --- .../system_indices/SystemIndicesTests.java | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index dcdf8a4af7..849bca0de1 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -233,6 +233,8 @@ public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + assertTrue(response.getBody().contains("")); + } @@ -265,6 +267,8 @@ public void testSearchAllWithSystemIndicesShouldFailAsExtensionUser() throws Exc RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, extensionUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + assertTrue(response.getBody().contains("")); + XContentParser xcp = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); SearchResponse searchResponse = SearchResponse.fromXContent(xcp); @@ -332,9 +336,11 @@ public void testDeleteDocWithSystemIndicesShouldFailsAsAdmin() throws Exception for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); + assertTrue(responseDoc.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]")); RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + assertTrue(responseDoc.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]")); } } @@ -377,6 +383,7 @@ public void testCloseOpenWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exc for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + assertTrue(responseClose.getBody().contains("{\"closed\":true}")); RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); @@ -393,9 +400,13 @@ public void testCloseOpenIndexShouldFailWithSystemIndicesAsAdmin() throws Except for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); + Assert.assertTrue(responseClose.getBody().contains("{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}")); + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); + Assert.assertTrue(responseOpen.getBody().contains("{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}")); + } } @@ -457,6 +468,8 @@ public void testUpdateIndexSettingsWithSystemIndicesShouldFailAsAdmin() throws E for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + Assert.assertTrue(response.getBody().contains("")); + } } @@ -569,8 +582,11 @@ public void testCreateIndexWithSystemIndicesShouldSucceedAsSuperAdmin() throws E RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); + Assert.assertTrue(response.getBody().contains("\"acknowledged\":true,\"shards_acknowledged\":true")); + } } @@ -592,9 +608,13 @@ public void testCreateIndexWithSystemIndicesShouldFailAsAdmin() throws Exception for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + Assert.assertTrue(responseIndex.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}")); + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + Assert.assertTrue(response.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]")); + } } @@ -610,8 +630,15 @@ public void testCreateIndexWithSystemIndicesShouldSucceedWithExtensionRole() thr + " \"number_of_replicas\" : 2 \n" + " }\n" + " }\n" + + "}"; + for (String index : listOfIndexesToTest) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, extensionUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", extensionUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode() ); + } } /*************************************************************************************************************************** @@ -636,14 +663,6 @@ public void testSnapshotWithSystemIndices() throws Exception { HttpStatus.SC_OK, sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1", allAccessUserHeader).getStatusCode() ); - assertEquals( - HttpStatus.SC_OK, - sslRestHelper.executePostRequest( - "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", - "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", - allAccessUserHeader - ).getStatusCode() - ); assertEquals( HttpStatus.SC_FORBIDDEN, sslRestHelper.executePostRequest( @@ -652,10 +671,6 @@ public void testSnapshotWithSystemIndices() throws Exception { allAccessUserHeader ).getStatusCode() ); - assertEquals( - HttpStatus.SC_OK, - sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1", allAccessUserHeader).getStatusCode() - ); assertEquals( HttpStatus.SC_OK, sslRestHelper.executePostRequest( @@ -664,14 +679,6 @@ public void testSnapshotWithSystemIndices() throws Exception { allAccessUserHeader ).getStatusCode() ); - assertEquals( - HttpStatus.SC_FORBIDDEN, - sslRestHelper.executePostRequest( - "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", - "", - allAccessUserHeader - ).getStatusCode() - ); } } @@ -689,6 +696,7 @@ public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Excep response = sslRestHelper.executeGetRequest("system_index_a", extensionUserHeader); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + Assert.assertTrue(response.getBody().contains("\"version\":{\"created\"")); } @Test @@ -705,6 +713,7 @@ public void testExtensionIndexAccessShouldFailAsDifferentExtensionUser() throws response = sslRestHelper.executeGetRequest("system_index_a", extensionUserCHeader); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); + Assert.assertTrue(response.getBody().contains("{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:admin/get] and User [name=extensions_user_c, backend_roles=[], requestedTenant=null]\"}]")); } } From b6e006203833a9d9dd58b3faf4a7af9da789c9f0 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 20 Jun 2023 21:42:31 +0100 Subject: [PATCH 03/46] #2553 gradle spotless Signed-off-by: Sam --- .../system_indices/SystemIndicesTests.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index 849bca0de1..09f5a80bd9 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -235,7 +235,6 @@ public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); assertTrue(response.getBody().contains("")); - } // search all indices @@ -336,11 +335,21 @@ public void testDeleteDocWithSystemIndicesShouldFailsAsAdmin() throws Exception for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); - assertTrue(responseDoc.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]")); + assertTrue( + responseDoc.getBody() + .contains( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - assertTrue(responseDoc.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]")); + assertTrue( + responseDoc.getBody() + .contains( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); } } @@ -400,12 +409,21 @@ public void testCloseOpenIndexShouldFailWithSystemIndicesAsAdmin() throws Except for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); - Assert.assertTrue(responseClose.getBody().contains("{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}")); - + Assert.assertTrue( + responseClose.getBody() + .contains( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); - Assert.assertTrue(responseOpen.getBody().contains("{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}")); + Assert.assertTrue( + responseOpen.getBody() + .contains( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); } } @@ -582,7 +600,6 @@ public void testCreateIndexWithSystemIndicesShouldSucceedAsSuperAdmin() throws E RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); Assert.assertTrue(response.getBody().contains("\"acknowledged\":true,\"shards_acknowledged\":true")); @@ -608,12 +625,21 @@ public void testCreateIndexWithSystemIndicesShouldFailAsAdmin() throws Exception for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - Assert.assertTrue(responseIndex.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}")); - + Assert.assertTrue( + responseIndex.getBody() + .contains( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]")); + Assert.assertTrue( + response.getBody() + .contains( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); } } @@ -637,7 +663,7 @@ public void testCreateIndexWithSystemIndicesShouldSucceedWithExtensionRole() thr assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", extensionUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode() ); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @@ -713,7 +739,12 @@ public void testExtensionIndexAccessShouldFailAsDifferentExtensionUser() throws response = sslRestHelper.executeGetRequest("system_index_a", extensionUserCHeader); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:admin/get] and User [name=extensions_user_c, backend_roles=[], requestedTenant=null]\"}]")); + Assert.assertTrue( + response.getBody() + .contains( + "{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:admin/get] and User [name=extensions_user_c, backend_roles=[], requestedTenant=null]\"}]" + ) + ); } } From 59020e4979b507cd69afa0006d87a486b1c6085d Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 27 Jun 2023 00:39:38 +0100 Subject: [PATCH 04/46] #2553 Refactoring of ConfigModelV6 and ConfigModelV7 to be based on the same interface. ConfigModel Innerclasses (SecurityRoles, SecuriryRole and IndexPattern) Updated Accordingly. Signed-off-by: Sam --- .../privileges/PrivilegesEvaluator.java | 10 +- .../SecurityIndexAccessEvaluator.java | 67 +++------- .../security/securityconf/ConfigModelV6.java | 126 ++++++++---------- .../security/securityconf/ConfigModelV7.java | 110 ++++++++------- .../security/securityconf/IndexPattern.java | 43 ++++++ .../security/securityconf/SecurityRole.java | 30 +++++ .../security/securityconf/SecurityRoles.java | 2 + .../security/securityconf/TypePerm.java | 61 +++++++++ ...ernTests.java => IndexPatternV7Tests.java} | 10 +- .../system_indices/SystemIndicesTests.java | 2 +- 10 files changed, 283 insertions(+), 178 deletions(-) create mode 100644 src/main/java/org/opensearch/security/securityconf/IndexPattern.java create mode 100644 src/main/java/org/opensearch/security/securityconf/SecurityRole.java create mode 100644 src/main/java/org/opensearch/security/securityconf/TypePerm.java rename src/test/java/org/opensearch/security/securityconf/impl/v7/{IndexPatternTests.java => IndexPatternV7Tests.java} (98%) diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index a0025da8b5..5d9a0b0c0d 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -89,7 +89,6 @@ import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; import org.opensearch.security.securityconf.ConfigModel; -import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.securityconf.DynamicConfigModel; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; @@ -309,14 +308,7 @@ public PrivilegesEvaluatorResponse evaluate( } // Security index access - if (securityIndexAccessEvaluator.evaluate( - request, - task, - action0, - requestedResolved, - presponse, - (ConfigModelV7.SecurityRoles) securityRoles - ).isComplete()) { + if (securityIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse, securityRoles).isComplete()) { return presponse; } diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 8f5592d268..7513683ebe 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -27,7 +27,6 @@ package org.opensearch.security.privileges; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -42,7 +41,8 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; -import org.opensearch.security.securityconf.ConfigModelV7; +import org.opensearch.security.securityconf.IndexPattern; +import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; import org.opensearch.tasks.Task; @@ -56,7 +56,6 @@ public class SecurityIndexAccessEvaluator { private final WildcardMatcher securityDeniedActionMatcher; private final IndexResolverReplacer irr; private final boolean filterSecurityIndex; - private final List configuredSystemIndices; // for system-indices configuration private final WildcardMatcher systemIndexMatcher; private final boolean systemIndexEnabled; @@ -76,14 +75,6 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT ); - List systemIndecesFromConfig = settings.getAsList( - ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, - ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT - ); - - this.configuredSystemIndices = new ArrayList<>(systemIndecesFromConfig); - this.configuredSystemIndices.add(ConfigConstants.SYSTEM_INDEX_PERMISSION); - final boolean restoreSecurityIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_UNSUPPORTED_RESTORE_SECURITYINDEX_ENABLED, false @@ -114,17 +105,21 @@ public PrivilegesEvaluatorResponse evaluate( final String action, final Resolved requestedResolved, final PrivilegesEvaluatorResponse presponse, - ConfigModelV7.SecurityRoles securityRoles + final SecurityRoles securityRoles ) { final boolean isDebugEnabled = log.isDebugEnabled(); if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { - log.warn( - "An account without the {} permission is trying to access a System Index. Related indexes: {}", - ConfigConstants.SYSTEM_INDEX_PERMISSION, - getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")) - ); + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "No {} permission for user roles {} to System Indices {}", + action, + securityRoles, + getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")) + ); + } presponse.allowed = false; return presponse.markComplete(); } @@ -145,34 +140,10 @@ public PrivilegesEvaluatorResponse evaluate( return presponse; } auditLog.logSecurityIndexAttempt(request, action, task); - log.warn("{} for '_all' indices is not allowed for a regular user", action); + log.info("{} for '_all' indices is not allowed for a regular user", action); presponse.allowed = false; return presponse.markComplete(); } - if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { - if (filterSecurityIndex) { - Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); - allWithoutSecurity.remove(securityIndex); - if (allWithoutSecurity.isEmpty()) { - if (isDebugEnabled) { - log.debug("Filtered '{}' but resulting list is empty", securityIndex); - } - presponse.allowed = false; - return presponse.markComplete(); - } - irr.replace(request, false, allWithoutSecurity.toArray(new String[0])); - if (isDebugEnabled) { - log.debug("Filtered '{}', resulting list is {}", securityIndex, allWithoutSecurity); - } - return presponse; - } else { - auditLog.logSecurityIndexAttempt(request, action, task); - final String foundSystemIndexes = getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")); - log.warn("{} for '{}' index is not allowed for a regular user", action, foundSystemIndexes); - presponse.allowed = false; - return presponse.markComplete(); - } - } } if (requestedResolved.isLocalAll() @@ -196,15 +167,17 @@ public PrivilegesEvaluatorResponse evaluate( return presponse; } - private boolean checkSystemIndexPermissionsForUser(ConfigModelV7.SecurityRoles securityRoles) { - Set userPermMatchers = securityRoles.getRoles() + private boolean checkSystemIndexPermissionsForUser(SecurityRoles securityRoles) { + // The generic wildcard "*" permission shouldn't give access to SystemIndices, so excluding it from the user roles's permissions + // before the check + Set userPermissions = securityRoles.getRoles() .stream() .flatMap(role -> role.getIpatterns().stream()) - .map(ConfigModelV7.IndexPattern::getNonWildCardPerms) + .map(IndexPattern::getNonWildCardPerms) .collect(Collectors.toSet()); - for (WildcardMatcher userPermMatcher : userPermMatchers.stream().collect(Collectors.toSet())) { - if (userPermMatcher.matchAny(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { + for (WildcardMatcher userPermission : userPermissions.stream().collect(Collectors.toSet())) { + if (userPermission.matchAny(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { return true; } } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 837dc0cff0..d565c4f69b 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -247,19 +247,19 @@ public SecurityRole call() throws Exception { final List fls = permittedAliasesIndex.getValue().get_fls_(); final List maskedFields = permittedAliasesIndex.getValue().get_masked_fields_(); - IndexPattern _indexPattern = new IndexPattern(permittedAliasesIndex.getKey()); - _indexPattern.setDlsQuery(dls); - _indexPattern.addFlsFields(fls); - _indexPattern.addMaskedFields(maskedFields); + IndexPatternV6 _indexPatternV6 = new IndexPatternV6(permittedAliasesIndex.getKey()); + _indexPatternV6.setDlsQuery(dls); + _indexPatternV6.addFlsFields(fls); + _indexPatternV6.addMaskedFields(maskedFields); for (Entry> type : permittedAliasesIndex.getValue().getTypes().entrySet()) { TypePerm typePerm = new TypePerm(type.getKey()); final List perms = type.getValue(); typePerm.addPerms(agr.resolvedActions(perms)); - _indexPattern.addTypePerms(typePerm); + _indexPatternV6.addTypePerms(typePerm); } - _securityRole.addIndexPattern(_indexPattern); + _securityRole.addIndexPattern(_indexPatternV6); } @@ -340,7 +340,8 @@ public String toString() { return "roles=" + roles; } - public Set getRoles() { + @Override + public Set getRoles() { return Collections.unmodifiableSet(roles); } @@ -507,17 +508,17 @@ public boolean impliesTypePermGlobal( IndexNameExpressionResolver resolver, ClusterService cs ) { - Set ipatterns = new HashSet(); + Set ipatterns = new HashSet<>(); roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns())); return ConfigModelV6.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs); } } - public static class SecurityRole { + public static class SecurityRole implements org.opensearch.security.securityconf.SecurityRole { private final String name; private final Set tenants = new HashSet<>(); - private final Set ipatterns = new HashSet<>(); + private final Set ipatterns = new HashSet<>(); private final Set clusterPerms = new HashSet<>(); private SecurityRole(String name) { @@ -525,13 +526,13 @@ private SecurityRole(String name) { this.name = Objects.requireNonNull(name); } - private boolean impliesClusterPermission(String action) { + public boolean impliesClusterPermission(String action) { return WildcardMatcher.from(clusterPerms).test(action); } // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only - private Set getAllResolvedPermittedIndices( + public Set getAllResolvedPermittedIndices( Resolved resolved, User user, String[] actions, @@ -540,7 +541,7 @@ private Set getAllResolvedPermittedIndices( ) { final Set retVal = new HashSet<>(); - for (IndexPattern p : ipatterns) { + for (IndexPatternV6 p : ipatterns) { // what if we cannot resolve one (for create purposes) boolean patternMatch = false; final Set tperms = p.getTypePerms(); @@ -579,7 +580,7 @@ private SecurityRole addTenant(Tenant tenant) { return this; } - private SecurityRole addIndexPattern(IndexPattern indexPattern) { + private SecurityRole addIndexPattern(IndexPatternV6 indexPattern) { if (indexPattern != null) { this.ipatterns.add(indexPattern); } @@ -646,7 +647,8 @@ public Set getTenants(User user) { return Collections.unmodifiableSet(tenants); } - public Set getIpatterns() { + @Override + public Set getIpatterns() { return Collections.unmodifiableSet(ipatterns); } @@ -661,40 +663,42 @@ public String getName() { } // sg roles - public static class IndexPattern { + public static class IndexPatternV6 implements IndexPattern { private final String indexPattern; private String dlsQuery; private final Set fls = new HashSet<>(); private final Set maskedFields = new HashSet<>(); private final Set typePerms = new HashSet<>(); - public IndexPattern(String indexPattern) { + public IndexPatternV6(String indexPattern) { super(); this.indexPattern = Objects.requireNonNull(indexPattern); } - public IndexPattern addFlsFields(List flsFields) { + @Override + public IndexPatternV6 addFlsFields(List flsFields) { if (flsFields != null) { this.fls.addAll(flsFields); } return this; } - public IndexPattern addMaskedFields(List maskedFields) { + @Override + public IndexPatternV6 addMaskedFields(List maskedFields) { if (maskedFields != null) { this.maskedFields.addAll(maskedFields); } return this; } - public IndexPattern addTypePerms(TypePerm typePerm) { + public IndexPatternV6 addTypePerms(TypePerm typePerm) { if (typePerm != null) { this.typePerms.add(typePerm); } return this; } - public IndexPattern setDlsQuery(String dlsQuery) { + public IndexPatternV6 setDlsQuery(String dlsQuery) { if (dlsQuery != null) { this.dlsQuery = dlsQuery; } @@ -718,7 +722,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - IndexPattern other = (IndexPattern) obj; + IndexPatternV6 other = (IndexPatternV6) obj; if (dlsQuery == null) { if (other.dlsQuery != null) return false; } else if (!dlsQuery.equals(other.dlsQuery)) return false; @@ -757,7 +761,12 @@ public String getUnresolvedIndexPattern(User user) { return replaceProperties(indexPattern, user); } - private Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { + @Override + public Set attemptResolveIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs) { + return getResolvedIndexPattern(user, resolver, cs); + } + + public Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { String unresolved = getUnresolvedIndexPattern(user); WildcardMatcher matcher = WildcardMatcher.from(unresolved); String[] resolved = null; @@ -791,6 +800,11 @@ public String getDlsQuery(User user) { return replaceProperties(dlsQuery, user); } + @Override + public Set concreteIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs) { + return getResolvedIndexPattern(user, resolver, cs); + } + public Set getFls() { return Collections.unmodifiableSet(fls); } @@ -799,69 +813,39 @@ public Set getMaskedFields() { return Collections.unmodifiableSet(maskedFields); } - public Set getTypePerms() { - return Collections.unmodifiableSet(typePerms); - } - - } - - public static class TypePerm { - private final WildcardMatcher typeMatcher; - private final Set perms = new HashSet<>(); - - private TypePerm(String typePattern) { - this.typeMatcher = WildcardMatcher.ANY; + @Override + public boolean hasDlsQuery() { + return false; } - private TypePerm addPerms(Collection perms) { - if (perms != null) { - this.perms.addAll(perms); - } - return this; + @Override + public boolean hasFlsFields() { + return false; } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((perms == null) ? 0 : perms.hashCode()); - result = prime * result + ((typeMatcher == null) ? 0 : typeMatcher.hashCode()); - return result; + public boolean hasMaskedFields() { + return false; } - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - TypePerm other = (TypePerm) obj; - if (perms == null) { - if (other.perms != null) return false; - } else if (!perms.equals(other.perms)) return false; - if (typeMatcher == null) { - if (other.typeMatcher != null) return false; - } else if (!typeMatcher.equals(other.typeMatcher)) return false; - return true; + public Set getTypePerms() { + return Collections.unmodifiableSet(typePerms); } @Override - public String toString() { - return System.lineSeparator() - + " typePattern=" - + typeMatcher - + System.lineSeparator() - + " perms=" - + perms; + public WildcardMatcher getPerms() { + return null; } - public WildcardMatcher getTypeMatcher() { - return typeMatcher; + @Override + public Set getStringPerm() { + return null; } - public WildcardMatcher getPerms() { - return WildcardMatcher.from(perms); + @Override + public WildcardMatcher getNonWildCardPerms() { + return null; } - } public static class Tenant { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 172b059b04..297bcb53e7 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -197,16 +197,16 @@ public Set resolvedActions(final List actions) { private SecurityRoles reload(SecurityDynamicConfiguration settings) { - final Set> futures = new HashSet<>(5000); + final Set> futures = new HashSet<>(5000); final ExecutorService execs = Executors.newFixedThreadPool(10); for (Entry securityRole : settings.getCEntries().entrySet()) { - Future future = execs.submit(new Callable() { + Future future = execs.submit(new Callable() { @Override - public SecurityRole call() throws Exception { - SecurityRole.Builder _securityRole = new SecurityRole.Builder(securityRole.getKey()); + public SecurityRoleV7 call() throws Exception { + SecurityRoleV7.Builder _securityRole = new SecurityRoleV7.Builder(securityRole.getKey()); if (securityRole.getValue() == null) { return null; @@ -235,11 +235,11 @@ public SecurityRole call() throws Exception { final List maskedFields = permittedAliasesIndex.getMasked_fields(); for (String pat : permittedAliasesIndex.getIndex_patterns()) { - IndexPattern _indexPattern = new IndexPattern(pat); - _indexPattern.setDlsQuery(dls); - _indexPattern.addFlsFields(fls); - _indexPattern.addMaskedFields(maskedFields); - _indexPattern.addPerm(agr.resolvedActions(permittedAliasesIndex.getAllowed_actions())); + IndexPatternV7 _indexPatternV7 = new IndexPatternV7(pat); + _indexPatternV7.setDlsQuery(dls); + _indexPatternV7.addFlsFields(fls); + _indexPatternV7.addMaskedFields(maskedFields); + _indexPatternV7.addPerm(agr.resolvedActions(permittedAliasesIndex.getAllowed_actions())); /*for(Entry> type: permittedAliasesIndex.getValue().getTypes(-).entrySet()) { TypePerm typePerm = new TypePerm(type.getKey()); @@ -248,7 +248,7 @@ public SecurityRole call() throws Exception { _indexPattern.addTypePerms(typePerm); }*/ - _securityRole.addIndexPattern(_indexPattern); + _securityRole.addIndexPattern(_indexPatternV7); } @@ -272,7 +272,7 @@ public SecurityRole call() throws Exception { try { SecurityRoles _securityRoles = new SecurityRoles(futures.size()); - for (Future future : futures) { + for (Future future : futures) { _securityRoles.addSecurityRole(future.get()); } @@ -293,15 +293,15 @@ public static class SecurityRoles implements org.opensearch.security.securitycon protected final Logger log = LogManager.getLogger(this.getClass()); - final Set roles; + final Set roles; private SecurityRoles(int roleCount) { roles = new HashSet<>(roleCount); } - private SecurityRoles addSecurityRole(SecurityRole securityRole) { - if (securityRole != null) { - this.roles.add(securityRole); + private SecurityRoles addSecurityRole(SecurityRoleV7 securityRoleV7) { + if (securityRoleV7 != null) { + this.roles.add(securityRoleV7); } return this; } @@ -331,7 +331,7 @@ public String toString() { return "roles=" + roles; } - public Set getRoles() { + public Set getRoles() { return Collections.unmodifiableSet(roles); } @@ -341,7 +341,7 @@ public Set getRoleNames() { public SecurityRoles filter(Set keep) { final SecurityRoles retVal = new SecurityRoles(roles.size()); - for (SecurityRole sr : roles) { + for (SecurityRoleV7 sr : roles) { if (keep.contains(sr.getName())) { retVal.addSecurityRole(sr); } @@ -378,7 +378,7 @@ public EvaluatedDlsFlsConfig getDlsFls( Set noFlsConcreteIndices = new HashSet<>(); Set noMaskedFieldConcreteIndices = new HashSet<>(); - for (SecurityRole role : roles) { + for (SecurityRoleV7 role : roles) { for (IndexPattern ip : role.getIpatterns()) { final Set concreteIndices = ip.concreteIndexNames(user, resolver, cs); String dls = ip.getDlsQuery(user); @@ -462,7 +462,7 @@ public Set getAllPermittedIndicesForDashboards( ClusterService cs ) { Set retVal = new HashSet<>(); - for (SecurityRole sr : roles) { + for (SecurityRoleV7 sr : roles) { retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs)); retVal.addAll(resolved.getRemoteIndices()); } @@ -472,7 +472,7 @@ public Set getAllPermittedIndicesForDashboards( // dnfof only public Set reduce(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { Set retVal = new HashSet<>(); - for (SecurityRole sr : roles) { + for (SecurityRoleV7 sr : roles) { retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs)); } if (log.isDebugEnabled()) { @@ -483,7 +483,7 @@ public Set reduce(Resolved resolved, User user, String[] actions, IndexN // return true on success public boolean get(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { - for (SecurityRole sr : roles) { + for (SecurityRoleV7 sr : roles) { if (ConfigModelV7.impliesTypePerm(sr.getIpatterns(), resolved, user, actions, resolver, cs)) { return true; } @@ -512,7 +512,7 @@ public boolean impliesTypePermGlobal( IndexNameExpressionResolver resolver, ClusterService cs ) { - Set ipatterns = new HashSet(); + Set ipatterns = new HashSet(); roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns())); return ConfigModelV7.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs); } @@ -530,22 +530,22 @@ private boolean containsDlsFlsConfig() { } } - public static class SecurityRole { + public static class SecurityRoleV7 implements org.opensearch.security.securityconf.SecurityRole { private final String name; - private final Set ipatterns; + private final Set ipatterns; private final WildcardMatcher clusterPerms; public static final class Builder { private final String name; private final Set clusterPerms = new HashSet<>(); - private final Set ipatterns = new HashSet<>(); + private final Set ipatterns = new HashSet<>(); public Builder(String name) { this.name = Objects.requireNonNull(name); } - public Builder addIndexPattern(IndexPattern indexPattern) { - this.ipatterns.add(indexPattern); + public Builder addIndexPattern(IndexPatternV7 indexPatternV7) { + this.ipatterns.add(indexPatternV7); return this; } @@ -556,24 +556,27 @@ public Builder addClusterPerms(Collection clusterPerms) { return this; } - public SecurityRole build() { - return new SecurityRole(name, ipatterns, WildcardMatcher.from(clusterPerms)); + public SecurityRoleV7 build() { + return new SecurityRoleV7(name, ipatterns, WildcardMatcher.from(clusterPerms)); } } - private SecurityRole(String name, Set ipatterns, WildcardMatcher clusterPerms) { + private SecurityRoleV7(String name, Set ipatterns, WildcardMatcher clusterPerms) { this.name = Objects.requireNonNull(name); this.ipatterns = ipatterns; this.clusterPerms = clusterPerms; } - private boolean impliesClusterPermission(String action) { + @Override + public boolean impliesClusterPermission(String action) { return clusterPerms.test(action); + } // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only - private Set getAllResolvedPermittedIndices( + @Override + public Set getAllResolvedPermittedIndices( Resolved resolved, User user, String[] actions, @@ -582,7 +585,7 @@ private Set getAllResolvedPermittedIndices( ) { final Set retVal = new HashSet<>(); - for (IndexPattern p : ipatterns) { + for (IndexPatternV7 p : ipatterns) { // what if we cannot resolve one (for create purposes) final boolean patternMatch = p.getPerms().matchAll(actions); @@ -639,7 +642,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRole other = (SecurityRole) obj; + SecurityRoleV7 other = (SecurityRoleV7) obj; if (clusterPerms == null) { if (other.clusterPerms != null) return false; } else if (!clusterPerms.equals(other.clusterPerms)) return false; @@ -675,10 +678,12 @@ public String toString() { // return Collections.unmodifiableSet(tenants); // } - public Set getIpatterns() { + @Override + public Set getIpatterns() { return Collections.unmodifiableSet(ipatterns); } + @Override public String getName() { return name; } @@ -686,40 +691,40 @@ public String getName() { } // sg roles - public static class IndexPattern { + public static class IndexPatternV7 implements org.opensearch.security.securityconf.IndexPattern { private final String indexPattern; private String dlsQuery; private final Set fls = new HashSet<>(); private final Set maskedFields = new HashSet<>(); private final Set perms = new HashSet<>(); - public IndexPattern(String indexPattern) { + public IndexPatternV7(String indexPattern) { super(); this.indexPattern = Objects.requireNonNull(indexPattern); } - public IndexPattern addFlsFields(List flsFields) { + public IndexPatternV7 addFlsFields(List flsFields) { if (flsFields != null) { this.fls.addAll(flsFields); } return this; } - public IndexPattern addMaskedFields(List maskedFields) { + public IndexPatternV7 addMaskedFields(List maskedFields) { if (maskedFields != null) { this.maskedFields.addAll(maskedFields); } return this; } - public IndexPattern addPerm(Set perms) { + public IndexPatternV7 addPerm(Set perms) { if (perms != null) { this.perms.addAll(perms); } return this; } - public IndexPattern setDlsQuery(String dlsQuery) { + public IndexPatternV7 setDlsQuery(String dlsQuery) { if (dlsQuery != null) { this.dlsQuery = dlsQuery; } @@ -743,7 +748,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - IndexPattern other = (IndexPattern) obj; + IndexPatternV7 other = (IndexPatternV7) obj; if (dlsQuery == null) { if (other.dlsQuery != null) return false; } else if (!dlsQuery.equals(other.dlsQuery)) return false; @@ -792,6 +797,16 @@ public Set attemptResolveIndexNames(final User user, final IndexNameExpr return getResolvedIndexPattern(user, resolver, cs, true); } + @Override + public Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { + return null; + } + + @Override + public Set getTypePerms() { + return null; + } + public Set getResolvedIndexPattern( final User user, final IndexNameExpressionResolver resolver, @@ -868,6 +883,11 @@ public WildcardMatcher getPerms() { return WildcardMatcher.from(perms); } + @Override + public Set getStringPerm() { + return perms; + } + public WildcardMatcher getNonWildCardPerms() { return WildcardMatcher.from(perms.stream().filter(perm -> !perm.equals("*"))); } @@ -1058,12 +1078,12 @@ private static boolean impliesTypePerm( IndexMatcherAndPermissions[] indexMatcherAndPermissions; if (resolved.isLocalAll()) { indexMatcherAndPermissions = ipatterns.stream() - .filter(indexPattern -> "*".equals(indexPattern.getUnresolvedIndexPattern(user))) - .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms)) + .filter(indexPatternV7 -> "*".equals(indexPatternV7.getUnresolvedIndexPattern(user))) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getStringPerm())) .toArray(IndexMatcherAndPermissions[]::new); } else { indexMatcherAndPermissions = ipatterns.stream() - .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms)) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getStringPerm())) .toArray(IndexMatcherAndPermissions[]::new); } return resolvedRequestedIndices.stream() diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java new file mode 100644 index 0000000000..dfcbafe288 --- /dev/null +++ b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java @@ -0,0 +1,43 @@ +package org.opensearch.security.securityconf; + +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.security.support.WildcardMatcher; +import org.opensearch.security.user.User; + +import java.util.List; +import java.util.Set; + +public interface IndexPattern { + IndexPattern addFlsFields(List flsFields); + + IndexPattern addMaskedFields(List maskedFields); + + WildcardMatcher getPerms(); + + Set getStringPerm(); + + WildcardMatcher getNonWildCardPerms(); + + String getDlsQuery(User user); + + Set concreteIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); + + Set getFls(); + + Set getMaskedFields(); + + boolean hasDlsQuery(); + + boolean hasFlsFields(); + + boolean hasMaskedFields(); + + String getUnresolvedIndexPattern(User user); + + Set attemptResolveIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); + + Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs); + + Set getTypePerms(); +} diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java new file mode 100644 index 0000000000..2d8ad22e29 --- /dev/null +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java @@ -0,0 +1,30 @@ +package org.opensearch.security.securityconf; + +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.security.resolver.IndexResolverReplacer; +import org.opensearch.security.user.User; + +import java.util.Set; + +public interface SecurityRole { + boolean impliesClusterPermission(String action); + + // get indices which are permitted for the given types and actions + // dnfof + opensearchDashboards special only + Set getAllResolvedPermittedIndices( + IndexResolverReplacer.Resolved resolved, + User user, + String[] actions, + IndexNameExpressionResolver resolver, + ClusterService cs + ); + + @Override + String toString(); + + Set getIpatterns(); + + String getName(); + +} diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index c52a3c1bad..eae1c7657f 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -85,4 +85,6 @@ Set getAllPermittedIndicesForDashboards( SecurityRoles filter(Set roles); + Set getRoles(); + } diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java new file mode 100644 index 0000000000..aa95f09806 --- /dev/null +++ b/src/main/java/org/opensearch/security/securityconf/TypePerm.java @@ -0,0 +1,61 @@ +package org.opensearch.security.securityconf; + +import org.opensearch.security.support.WildcardMatcher; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class TypePerm { + protected final WildcardMatcher typeMatcher; + private final Set perms = new HashSet<>(); + + TypePerm(String typePattern) { + this.typeMatcher = WildcardMatcher.ANY; + } + + protected TypePerm addPerms(Collection perms) { + if (perms != null) { + this.perms.addAll(perms); + } + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((perms == null) ? 0 : perms.hashCode()); + result = prime * result + ((typeMatcher == null) ? 0 : typeMatcher.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TypePerm other = (TypePerm) obj; + if (perms == null) { + if (other.perms != null) return false; + } else if (!perms.equals(other.perms)) return false; + if (typeMatcher == null) { + if (other.typeMatcher != null) return false; + } else if (!typeMatcher.equals(other.typeMatcher)) return false; + return true; + } + + @Override + public String toString() { + return System.lineSeparator() + " typePattern=" + typeMatcher + System.lineSeparator() + " perms=" + perms; + } + + public WildcardMatcher getTypeMatcher() { + return typeMatcher; + } + + public WildcardMatcher getPerms() { + return WildcardMatcher.from(perms); + } + +} diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java similarity index 98% rename from src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java rename to src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java index 2b95a6e84c..9af39a8869 100644 --- a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java +++ b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java @@ -31,7 +31,7 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.security.securityconf.ConfigModelV7.IndexPattern; +import org.opensearch.security.securityconf.ConfigModelV7.IndexPatternV7; import org.opensearch.security.user.User; import static org.hamcrest.MatcherAssert.assertThat; @@ -49,7 +49,7 @@ import static org.mockito.Mockito.withSettings; @RunWith(MockitoJUnitRunner.class) -public class IndexPatternTests { +public class IndexPatternV7Tests { @Mock private User user; @@ -58,11 +58,11 @@ public class IndexPatternTests { @Mock private ClusterService clusterService; - private IndexPattern ip; + private IndexPatternV7 ip; @Before public void before() { - ip = spy(new IndexPattern("defaultPattern")); + ip = spy(new IndexPatternV7("defaultPattern")); } @After @@ -72,7 +72,7 @@ public void after() { @Test public void testCtor() { - assertThrows(NullPointerException.class, () -> new IndexPattern(null)); + assertThrows(NullPointerException.class, () -> new IndexPatternV7(null)); } /** Ensure that concreteIndexNames sends correct parameters are sent to getResolvedIndexPattern */ diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index 09f5a80bd9..9ed8aea35e 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -602,7 +602,7 @@ public void testCreateIndexWithSystemIndicesShouldSucceedAsSuperAdmin() throws E RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); - Assert.assertTrue(response.getBody().contains("\"acknowledged\":true,\"shards_acknowledged\":true")); + Assert.assertTrue(response.getBody().contains("\"result\":\"created\"")); } From d88ec3e63c84cb9420649001ad288d3f8cc6cee7 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 6 Jul 2023 20:18:40 +0100 Subject: [PATCH 05/46] #2553 Tests refactoring Signed-off-by: Sam --- .../system_indices/SystemIndicesTests.java | 131 ++++++++++-------- src/test/resources/roles_system_indices.yml | 2 +- 2 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index 9ed8aea35e..71cca69945 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -17,6 +17,8 @@ import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; @@ -39,8 +41,6 @@ import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.file.FileHelper; import org.opensearch.security.test.helper.rest.RestHelper; - -import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; /** @@ -50,7 +50,7 @@ */ public class SystemIndicesTests extends SingleClusterTest { - private static final List listOfIndexesToTest = Arrays.asList("system_index_a", "system_index_b"); + private static final List listOfIndexesToTest = Arrays.asList(".system_index_a", ".system_index_b"); private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; private static final String allAccessUser = "admin_all_access"; private static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); @@ -233,8 +233,12 @@ public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - assertTrue(response.getBody().contains("")); - + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); } // search all indices @@ -259,6 +263,7 @@ public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Ex } + @Test public void testSearchAllWithSystemIndicesShouldFailAsExtensionUser() throws Exception { setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); @@ -266,13 +271,12 @@ public void testSearchAllWithSystemIndicesShouldFailAsExtensionUser() throws Exc RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, extensionUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - assertTrue(response.getBody().contains("")); - - XContentParser xcp = XContentType.JSON.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); - SearchResponse searchResponse = SearchResponse.fromXContent(xcp); - assertEquals(RestStatus.FORBIDDEN, searchResponse.status()); - assertEquals(0, searchResponse.getHits().getHits().length); + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"reason\":\"no permissions for [indices:data/read/search] and User [name=extensions_user, backend_roles=[]" + ) + ); } /*************************************************************************************************************************** @@ -335,20 +339,20 @@ public void testDeleteDocWithSystemIndicesShouldFailsAsAdmin() throws Exception for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); - assertTrue( - responseDoc.getBody() - .contains( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) ); RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - assertTrue( - responseDoc.getBody() - .contains( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) ); } } @@ -392,7 +396,7 @@ public void testCloseOpenWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exc for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - assertTrue(responseClose.getBody().contains("{\"closed\":true}")); + MatcherAssert.assertThat(responseClose.getBody(), Matchers.containsStringIgnoringCase("{\"closed\":true}")); RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); @@ -409,20 +413,20 @@ public void testCloseOpenIndexShouldFailWithSystemIndicesAsAdmin() throws Except for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); - Assert.assertTrue( - responseClose.getBody() - .contains( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) + MatcherAssert.assertThat( + responseClose.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) ); RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); - Assert.assertTrue( - responseOpen.getBody() - .contains( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) + MatcherAssert.assertThat( + responseOpen.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) ); } @@ -486,7 +490,12 @@ public void testUpdateIndexSettingsWithSystemIndicesShouldFailAsAdmin() throws E for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("")); + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"" + ) + ); } } @@ -536,7 +545,7 @@ public void testUpdateMappingsWithSystemIndicesShouldFailAsAdmin() throws Except for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - assertTrue(response.getBody().contains(generalErrorMessage)); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); } } @@ -565,7 +574,7 @@ public void testCreateIndexWithNormalIndicesShouldSucceed() throws Exception { assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); - assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } for (String index : listOfIndexesToTest) { @@ -578,7 +587,7 @@ public void testCreateIndexWithNormalIndicesShouldSucceed() throws Exception { assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @@ -601,8 +610,8 @@ public void testCreateIndexWithSystemIndicesShouldSucceedAsSuperAdmin() throws E assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); - assertTrue(response.getStatusCode() == RestStatus.CREATED.getStatus()); - Assert.assertTrue(response.getBody().contains("\"result\":\"created\"")); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"result\":\"created\"")); } @@ -625,20 +634,20 @@ public void testCreateIndexWithSystemIndicesShouldFailAsAdmin() throws Exception for (String index : listOfIndexesToTest) { RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - Assert.assertTrue( - responseIndex.getBody() - .contains( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) ); RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - Assert.assertTrue( - response.getBody() - .contains( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) ); } @@ -686,8 +695,8 @@ public void testSnapshotWithSystemIndices() throws Exception { // as admin for (String index : listOfIndexesToTest) { assertEquals( - HttpStatus.SC_OK, - sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1", allAccessUserHeader).getStatusCode() + HttpStatus.SC_UNAUTHORIZED, + sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode() ); assertEquals( HttpStatus.SC_FORBIDDEN, @@ -714,15 +723,15 @@ public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Excep RestHelper sslRestHelper = sslRestHelper(); RestHelper.HttpResponse response = sslRestHelper.executePostRequest( - "system_index_a" + "/_doc", + ".system_index_a" + "/_doc", "{\"foo\": \"bar\"}", extensionUserHeader ); assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - response = sslRestHelper.executeGetRequest("system_index_a", extensionUserHeader); + response = sslRestHelper.executeGetRequest(".system_index_a", extensionUserHeader); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("\"version\":{\"created\"")); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"version\":{\"created\"")); } @Test @@ -731,19 +740,19 @@ public void testExtensionIndexAccessShouldFailAsDifferentExtensionUser() throws RestHelper sslRestHelper = sslRestHelper(); RestHelper.HttpResponse response = sslRestHelper.executePostRequest( - "system_index_a" + "/_doc", + ".system_index_a" + "/_doc", "{\"foo\": \"bar\"}", extensionUserCHeader ); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - response = sslRestHelper.executeGetRequest("system_index_a", extensionUserCHeader); + response = sslRestHelper.executeGetRequest(".system_index_a", extensionUserCHeader); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - Assert.assertTrue( - response.getBody() - .contains( - "{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:admin/get] and User [name=extensions_user_c, backend_roles=[], requestedTenant=null]\"}]" - ) + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:admin/get] and User [name=extensions_user_c, backend_roles=[], requestedTenant=null]\"}]" + ) ); } diff --git a/src/test/resources/roles_system_indices.yml b/src/test/resources/roles_system_indices.yml index 712af38740..20f7a8500f 100644 --- a/src/test/resources/roles_system_indices.yml +++ b/src/test/resources/roles_system_indices.yml @@ -19,7 +19,7 @@ extensions_role: - '*' index_permissions: - index_patterns: - - '*' + - '.system*' allowed_actions: - '*' - 'system:admin/system_index' From e190c4b6e1adb8c7c354d9f7e52ff2a7d68ae5ad Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Jul 2023 16:44:52 +0100 Subject: [PATCH 06/46] #2553 Code Style Fixes Signed-off-by: Sam --- .../security/securityconf/IndexPattern.java | 26 +++++++++++++++++++ .../security/securityconf/SecurityRole.java | 26 +++++++++++++++++++ .../security/securityconf/TypePerm.java | 26 +++++++++++++++++++ .../system_indices/SystemIndicesTests.java | 1 + 4 files changed, 79 insertions(+) diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java index dfcbafe288..0a97076b44 100644 --- a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java +++ b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java @@ -1,3 +1,29 @@ +/* + * Copyright 2015-2018 _floragunn_ GmbH + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + package org.opensearch.security.securityconf; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java index 2d8ad22e29..c433f429c1 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java @@ -1,3 +1,29 @@ +/* + * Copyright 2015-2018 _floragunn_ GmbH + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + package org.opensearch.security.securityconf; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java index aa95f09806..088eaa6521 100644 --- a/src/main/java/org/opensearch/security/securityconf/TypePerm.java +++ b/src/main/java/org/opensearch/security/securityconf/TypePerm.java @@ -1,3 +1,29 @@ +/* + * Copyright 2015-2018 _floragunn_ GmbH + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + package org.opensearch.security.securityconf; import org.opensearch.security.support.WildcardMatcher; diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index f64affd0e7..4af566a648 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -48,6 +48,7 @@ * Refer: "plugins.security.system_indices.enabled" * "plugins.security.system_indices.indices"; */ +@SuppressWarnings("RegexpSingleline") public class SystemIndicesTests extends SingleClusterTest { private static final List listOfIndexesToTest = Arrays.asList(".system_index_a", ".system_index_b"); From dfcab85b3077cc1effa4965270f087a6db331662 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 24 Jul 2023 16:44:52 +0100 Subject: [PATCH 07/46] #2553 Code Style Fixes Signed-off-by: Sam --- .../opensearch/security/securityconf/IndexPattern.java | 9 +++++++++ .../opensearch/security/securityconf/SecurityRole.java | 9 +++++++++ .../org/opensearch/security/securityconf/TypePerm.java | 9 +++++++++ .../security/system_indices/SystemIndicesTests.java | 8 ++++++-- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java index dfcbafe288..9f9ac2be17 100644 --- a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java +++ b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearch.security.securityconf; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java index 2d8ad22e29..5027910644 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearch.security.securityconf; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java index aa95f09806..c8796142ac 100644 --- a/src/main/java/org/opensearch/security/securityconf/TypePerm.java +++ b/src/main/java/org/opensearch/security/securityconf/TypePerm.java @@ -1,3 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + package org.opensearch.security.securityconf; import org.opensearch.security.support.WildcardMatcher; diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index f64affd0e7..9c4e608420 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -48,7 +48,9 @@ * Refer: "plugins.security.system_indices.enabled" * "plugins.security.system_indices.indices"; */ + public class SystemIndicesTests extends SingleClusterTest { + // CS-SUPPRESS-SINGLE: RegexpSingleline See http://github/issues/1234 private static final List listOfIndexesToTest = Arrays.asList(".system_index_a", ".system_index_b"); private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; @@ -252,7 +254,7 @@ public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { } @Test - public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Exception { + public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be deleted in Z.Y.X see http://github/issues/1234 setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper restHelper = sslRestHelper(); @@ -718,7 +720,7 @@ public void testSnapshotWithSystemIndices() throws Exception { } @Test - public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Exception { + public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be deleted in Z.Y.X see http://github/issues/1234 setupSystemIndicesEnabledWithSsl(); RestHelper sslRestHelper = sslRestHelper(); @@ -756,4 +758,6 @@ public void testExtensionIndexAccessShouldFailAsDifferentExtensionUser() throws ); } + // CS-SUPPRESS-SINGLE + } From 5d772bbad474bc80a5247bf7a2b0b1ff11975099 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Jul 2023 15:25:51 +0100 Subject: [PATCH 08/46] protected SystemIndices Feature Flag Signed-off-by: Sam --- .../opensearch/security/OpenSearchSecurityPlugin.java | 8 ++++++++ .../privileges/SecurityIndexAccessEvaluator.java | 10 ++++++++-- .../opensearch/security/support/ConfigConstants.java | 4 ++++ .../security/system_indices/SystemIndicesTests.java | 9 +++++++-- .../security/test/helper/cluster/ClusterHelper.java | 4 ++-- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 1dbc787b74..f74c2e07ad 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -1783,6 +1783,14 @@ public List> getSettings() { Property.Filtered ) ); + settings.add( + Setting.boolSetting( + ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, + ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT, + Property.NodeScope, + Property.Filtered + ) + ); } return settings; diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 7513683ebe..71dbc82430 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -59,6 +59,7 @@ public class SecurityIndexAccessEvaluator { // for system-indices configuration private final WildcardMatcher systemIndexMatcher; private final boolean systemIndexEnabled; + private boolean systemIndicesAdditionalControlFlag; public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, IndexResolverReplacer irr) { this.securityIndex = settings.get( @@ -97,6 +98,10 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, securityDeniedActionMatcher = WildcardMatcher.from( restoreSecurityIndexEnabled ? securityIndexDeniedActionPatternsList : securityIndexDeniedActionPatternsListNoSnapshot ); + systemIndicesAdditionalControlFlag = settings.getAsBoolean( + ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, + false + ); } public PrivilegesEvaluatorResponse evaluate( @@ -107,10 +112,11 @@ public PrivilegesEvaluatorResponse evaluate( final PrivilegesEvaluatorResponse presponse, final SecurityRoles securityRoles ) { - final boolean isDebugEnabled = log.isDebugEnabled(); - if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { + if (systemIndicesAdditionalControlFlag + && matchAnySystemIndices(requestedResolved) + && !checkSystemIndexPermissionsForUser(securityRoles)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 2d7b729b7f..25efcbd2e6 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -314,6 +314,10 @@ public enum RolesMappingResolution { public static final String SYSTEM_INDEX_PERMISSION = "system:admin/system_index"; public static final String SECURITY_SYSTEM_INDICES_ENABLED_KEY = "plugins.security.system_indices.enabled"; public static final Boolean SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT = false; + public static final String SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY = + "plugins.security.system_indices.additional_control.enabled"; + public static final Boolean SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT = false; + public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; public static final List SECURITY_SYSTEM_INDICES_DEFAULT = Collections.emptyList(); diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index 9c4e608420..84bb54767d 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -70,6 +70,7 @@ private void setupSystemIndicesDisabledWithSsl() throws Exception { Settings systemIndexSettings = Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, false) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) @@ -91,6 +92,7 @@ private void setupSystemIndicesEnabledWithSsl() throws Exception { Settings systemIndexSettings = Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, true) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) @@ -254,7 +256,9 @@ public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { } @Test - public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be deleted in Z.Y.X see http://github/issues/1234 + public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be + // deleted in Z.Y.X see + // http://github/issues/2553 setupSystemIndicesEnabledWithSsl(); createTestIndicesAndDocs(); RestHelper restHelper = sslRestHelper(); @@ -720,7 +724,8 @@ public void testSnapshotWithSystemIndices() throws Exception { } @Test - public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be deleted in Z.Y.X see http://github/issues/1234 + public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be deleted + // in Z.Y.X see http://github/issues/1234 setupSystemIndicesEnabledWithSsl(); RestHelper sslRestHelper = sslRestHelper(); diff --git a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java index b038949782..35e10c93b7 100644 --- a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java +++ b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java @@ -319,14 +319,14 @@ private void deleteTestsDataDirectory() throws IOException { Files.walkFileTree(testsDataDir.toPath(), new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - System.out.println("Deleting file " + file.getFileName()); + // System.out.println("Deleting file " + file.getFileName()); Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - System.out.println("Deleting directory " + dir.getFileName()); + // System.out.println("Deleting directory " + dir.getFileName()); Files.delete(dir); return FileVisitResult.CONTINUE; } From 4f161ed3cff858585ce463ee4f6397d8e9f2a7ea Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Jul 2023 02:21:09 +0100 Subject: [PATCH 09/46] SnapshotRestoreTests and SecurityIndexAccessEvaluatorTest tests changes Signed-off-by: Sam --- .../security/SnapshotRestoreTests.java | 26 +++++----- .../SecurityIndexAccessEvaluatorTest.java | 48 +++++++++++++++---- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java index 1c884a8e5d..be08eb48a1 100644 --- a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java +++ b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java @@ -45,6 +45,7 @@ import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; +import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.cluster.ClusterConfiguration; @@ -352,7 +353,7 @@ public void testSnapshot() throws Exception { ); // Try to restore vulcangov index as .opendistro_security index Assert.assertEquals( - HttpStatus.SC_FORBIDDEN, + HttpStatus.SC_INTERNAL_SERVER_ERROR, rh.executePostRequest( "_snapshot/vulcangov/vulcangov_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -404,7 +405,7 @@ public void testSnapshot() throws Exception { ); // Try to restore .opendistro_security index as .opendistro_security_copy index Assert.assertEquals( - HttpStatus.SC_FORBIDDEN, + HttpStatus.SC_INTERNAL_SERVER_ERROR, rh.executePostRequest( "_snapshot/all/all_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -438,7 +439,10 @@ public void testSnapshot() throws Exception { @Test public void testSnapshotCheckWritePrivileges() throws Exception { - final Settings settings = Settings.builder().putList("path.repo", repositoryPath.getRoot().getAbsolutePath()).build(); + final Settings settings = Settings.builder() + .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) + .build(); setup(settings, currentClusterConfig); @@ -533,7 +537,7 @@ public void testSnapshotCheckWritePrivileges() throws Exception { ); // Try to restore vulcangov index as .opendistro_security index Assert.assertEquals( - HttpStatus.SC_FORBIDDEN, + HttpStatus.SC_INTERNAL_SERVER_ERROR, rh.executePostRequest( "_snapshot/vulcangov/vulcangov_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -585,7 +589,7 @@ public void testSnapshotCheckWritePrivileges() throws Exception { ); // Try to restore .opendistro_security index as .opendistro_security_copy index Assert.assertEquals( - HttpStatus.SC_FORBIDDEN, + HttpStatus.SC_INTERNAL_SERVER_ERROR, rh.executePostRequest( "_snapshot/all/all_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -731,14 +735,12 @@ public void testSnapshotRestore() throws Exception { + "\"include_global_state\": false" + "}"; - Assert.assertEquals( - HttpStatus.SC_OK, - rh.executePutRequest( - "_snapshot/bckrepo/" + putSnapshot.hashCode() + "?wait_for_completion=true&pretty", - putSnapshot, - encodeBasicHeader("snapresuser", "nagilum") - ).getStatusCode() + RestHelper.HttpResponse response = rh.executePutRequest( + "_snapshot/bckrepo/" + putSnapshot.hashCode() + "?wait_for_completion=true&pretty", + putSnapshot, + encodeBasicHeader("snapresuser", "nagilum") ); + Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertEquals( HttpStatus.SC_FORBIDDEN, rh.executePostRequest( diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 5bfc2300b0..2fb4a89caa 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -14,7 +14,6 @@ import com.google.common.collect.ImmutableSet; import org.apache.logging.log4j.Logger; import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -29,6 +28,7 @@ import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; import org.opensearch.security.securityconf.ConfigModelV7; +import org.opensearch.security.support.ConfigConstants; import org.opensearch.tasks.Task; import static org.hamcrest.MatcherAssert.assertThat; @@ -60,13 +60,31 @@ public class SecurityIndexAccessEvaluatorTest { private static final String PROTECTED_ACTION = "indices:data/write"; @Mock ConfigModelV7 configModelV7; - ConfigModelV7.SecurityRoles securityRoles = configModelV7.getSecurityRoles(); + @Mock + ConfigModelV7.SecurityRoles securityRoles;// = configModelV7.getSecurityRoles(); + + public void setupEvaluatorWithSystemIndicesControl() { + evaluator = new SecurityIndexAccessEvaluator( + Settings.EMPTY.builder() + .put("plugins.security.system_indices.indices", ".testSystemIndex") + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) + .put("plugins.security.system_indices.enabled", true) + .build(), + auditLog, + irr + ); + evaluator.log = log; - @Before - public void before() { + when(log.isDebugEnabled()).thenReturn(true); + when(log.isInfoEnabled()).thenReturn(true); + + } + + public void setupEvaluatorWithoutSystemIndicesControl() { evaluator = new SecurityIndexAccessEvaluator( Settings.EMPTY.builder() .put("plugins.security.system_indices.indices", ".testSystemIndex") + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) .put("plugins.security.system_indices.enabled", true) .build(), auditLog, @@ -79,12 +97,14 @@ public void before() { @After public void after() { + verifyNoMoreInteractions(auditLog, irr, request, task, presponse, log); } @Test public void actionIsNotProtected_noSystemIndexInvolved() { - final Resolved resolved = createResolved(".testSystemIndex"); + setupEvaluatorWithSystemIndicesControl(); + final Resolved resolved = createResolved(".potato"); // Action final PrivilegesEvaluatorResponse response = evaluator.evaluate( @@ -104,6 +124,8 @@ public void actionIsNotProtected_noSystemIndexInvolved() { @Test public void disableCacheOrRealtimeOnSystemIndex() { + setupEvaluatorWithoutSystemIndicesControl(); + final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); final Resolved resolved = createResolved(".testSystemIndex"); @@ -113,7 +135,7 @@ public void disableCacheOrRealtimeOnSystemIndex() { evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - verifyNoInteractions(presponse); + // verifyNoInteractions(presponse); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); @@ -124,21 +146,24 @@ public void disableCacheOrRealtimeOnSystemIndex() { @Test public void protectedActionLocalAll() { + setupEvaluatorWithoutSystemIndicesControl(); final Resolved resolved = Resolved._LOCAL_ALL; // Action evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + verify(log).isDebugEnabled(); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); verify(log).isDebugEnabled(); - verify(log).warn("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); + verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } @Test public void protectedActionSystemIndex() { + setupEvaluatorWithSystemIndicesControl(); final Resolved resolved = createResolved(".testSystemIndex", ".opendistro_security"); // Action @@ -149,7 +174,14 @@ public void protectedActionSystemIndex() { verify(presponse).markComplete(); verify(log).isDebugEnabled(); - verify(log).warn("{} for '{}' index is not allowed for a regular user", "indices:data/write", ".opendistro_security, .test"); + verify(log).isInfoEnabled(); + + verify(log).info( + "No {} permission for user roles {} to System Indices {}", + PROTECTED_ACTION, + securityRoles, + ".opendistro_security, .testSystemIndex" + ); } private Resolved createResolved(final String... indexes) { From 273f10f704af9f56d2906a23a10faec5d9b78da3 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Jul 2023 18:05:30 +0100 Subject: [PATCH 10/46] SnapshotRestoreTests and SecurityIndexAccessEvaluatorTest tests changes Signed-off-by: Sam --- .../opensearch/security/HttpIntegrationTests.java | 6 +++--- .../org/opensearch/security/IntegrationTests.java | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/opensearch/security/HttpIntegrationTests.java b/src/test/java/org/opensearch/security/HttpIntegrationTests.java index be3feafaaa..ade583629e 100644 --- a/src/test/java/org/opensearch/security/HttpIntegrationTests.java +++ b/src/test/java/org/opensearch/security/HttpIntegrationTests.java @@ -132,7 +132,7 @@ public void testHTTPBasic() throws Exception { rh.executeGetRequest(".nonexistentindex*", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( - HttpStatus.SC_FORBIDDEN, + HttpStatus.SC_CREATED, rh.executePutRequest(".opendistro_security/_doc/2", "{}", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( @@ -549,7 +549,7 @@ public void testHTTPClientCert() throws Exception { rh.sendAdminCertificate = true; rh.keystore = "spock-keystore.jks"; Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("_search").getStatusCode()); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, rh.executePutRequest(".opendistro_security/_doc/x", "{}").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_CREATED, rh.executePutRequest(".opendistro_security/_doc/x", "{}").getStatusCode()); rh.keystore = "kirk-keystore.jks"; Assert.assertEquals(HttpStatus.SC_CREATED, rh.executePutRequest(".opendistro_security/_doc/y", "{}").getStatusCode()); @@ -748,7 +748,7 @@ public void testHTTPBasic2() throws Exception { rh.executeGetRequest(".nonexistentindex*", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( - HttpStatus.SC_FORBIDDEN, + HttpStatus.SC_CREATED, rh.executePutRequest(".opendistro_security/_doc/2", "{}", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index 399d226bd9..e3296fa3ba 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -505,14 +505,13 @@ public void testDeleteByQueryDnfof() throws Exception { } RestHelper rh = nonSslRestHelper(); - HttpResponse res; - Assert.assertEquals( - HttpStatus.SC_OK, - (res = rh.executePostRequest( + HttpResponse res = rh.executePostRequest( "/vulcango*/_delete_by_query?refresh=true&wait_for_completion=true&pretty=true", "{\"query\" : {\"match_all\" : {}}}", - encodeBasicHeader("nagilum", "nagilum") - )).getStatusCode() + encodeBasicHeader("nagilum", "nagilum")); + Assert.assertEquals( + HttpStatus.SC_OK, + res.getStatusCode() ); Assert.assertTrue(res.getBody().contains("\"deleted\" : 3")); @@ -1048,7 +1047,8 @@ public void testSecurityIndexSecurity() throws Exception { "{\"properties\": {\"name\":{\"type\":\"text\"}}}", encodeBasicHeader("nagilum", "nagilum") ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executePutRequest( "*dis*rit*/_mapping?pretty", "{\"properties\": {\"name\":{\"type\":\"text\"}}}", From 32681452b643c0681c1266e54b98f97ba48cbd23 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Jul 2023 20:39:19 +0100 Subject: [PATCH 11/46] SnapshotRestoreTests and SecurityIndexAccessEvaluatorTest tests changes Signed-off-by: Sam --- .../opensearch/security/IntegrationTests.java | 17 +++++++++-------- .../compliance/ComplianceAuditlogTest.java | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index e3296fa3ba..f65ef7b9bf 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -506,20 +506,21 @@ public void testDeleteByQueryDnfof() throws Exception { RestHelper rh = nonSslRestHelper(); HttpResponse res = rh.executePostRequest( - "/vulcango*/_delete_by_query?refresh=true&wait_for_completion=true&pretty=true", - "{\"query\" : {\"match_all\" : {}}}", - encodeBasicHeader("nagilum", "nagilum")); - Assert.assertEquals( - HttpStatus.SC_OK, - res.getStatusCode() + "/vulcango*/_delete_by_query?refresh=true&wait_for_completion=true&pretty=true", + "{\"query\" : {\"match_all\" : {}}}", + encodeBasicHeader("nagilum", "nagilum") ); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); Assert.assertTrue(res.getBody().contains("\"deleted\" : 3")); } @Test public void testUpdate() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) + .build(); setup(settings); final RestHelper rh = nonSslRestHelper(); @@ -1054,7 +1055,7 @@ public void testSecurityIndexSecurity() throws Exception { "{\"properties\": {\"name\":{\"type\":\"text\"}}}", encodeBasicHeader("nagilum", "nagilum") ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); res = rh.executePutRequest( "*/_mapping?pretty", "{\"properties\": {\"name\":{\"type\":\"text\"}}}", diff --git a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java index 361c3cc313..799d60d607 100644 --- a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java +++ b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java @@ -56,6 +56,7 @@ public void testSourceFilter() throws Exception { Settings additionalSettings = Settings.builder() .put("plugins.security.audit.type", TestAuditlogImpl.class.getName()) .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, true) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true) .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false) .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, "emp") From c784438f6a690e23a8c66b49c12c20f2da28e150 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 27 Jul 2023 23:43:56 +0100 Subject: [PATCH 12/46] more int retries Signed-off-by: Sam --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 813abd6f4f..5e8a3d16e2 100644 --- a/build.gradle +++ b/build.gradle @@ -468,7 +468,7 @@ task integrationTest(type: Test) { classpath = sourceSets.integrationTest.runtimeClasspath retry { failOnPassedAfterRetry = false - maxRetries = 2 + maxRetries = 5 maxFailures = 10 } //run the integrationTest task after the test task From b2a05bc32f745d8058304ecdfc2c1dd20ab3b1e6 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 28 Jul 2023 17:01:25 +0100 Subject: [PATCH 13/46] security test change Signed-off-by: Sam --- src/test/java/org/opensearch/security/IntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index f65ef7b9bf..4032c01b29 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -1069,7 +1069,7 @@ public void testSecurityIndexSecurity() throws Exception { ); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePostRequest(".opendistro_security/_close", "", encodeBasicHeader("nagilum", "nagilum")); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); res = rh.executeDeleteRequest(".opendistro_security", encodeBasicHeader("nagilum", "nagilum")); res = rh.executeDeleteRequest("_all", encodeBasicHeader("nagilum", "nagilum")); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); From 153558a28e42d1361d9391b2ca9cc9e5631b6c52 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 31 Jul 2023 15:08:02 +0100 Subject: [PATCH 14/46] removing v6 from codova test coverage Signed-off-by: Sam --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5e8a3d16e2..0616973dae 100644 --- a/build.gradle +++ b/build.gradle @@ -262,7 +262,8 @@ def setCommonTestConfig(Test task) { "sun.security.jgss.*", "sun.security.pkcs11.*", "sun.security.smartcardio.*", - "sun.util.resources.provider.*" + "sun.util.resources.provider.*", + "org/opensearch/security/securityconf/impl/v6.*" ] } task.dependsOn copyExtraTestResources From b0a6c9aa026ca960597d2a462e7d575433c7d1df Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 1 Aug 2023 01:17:58 +0100 Subject: [PATCH 15/46] Denylist draft Signed-off-by: Sam --- .../security/OpenSearchSecurityPlugin.java | 9 ++++++ .../SecurityIndexAccessEvaluator.java | 30 +++++++++++++++++++ .../security/support/ConfigConstants.java | 9 ++---- .../opensearch/security/IntegrationTests.java | 8 ++--- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 038e9429eb..f711955fba 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -1791,6 +1791,15 @@ public List> getSettings() { Property.Filtered ) ); + settings.add( + Setting.listSetting( + ConfigConstants.SECURITY_INDICES_DENYLIST_KEY, + ConfigConstants.SECURITY_INDICES_DENYLIST_KEY_DEFAULT, + Function.identity(), + Property.NodeScope, + Property.Filtered + ) + ); } return settings; diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 71dbc82430..0db27af7ef 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -58,6 +58,8 @@ public class SecurityIndexAccessEvaluator { private final boolean filterSecurityIndex; // for system-indices configuration private final WildcardMatcher systemIndexMatcher; + private final WildcardMatcher denylistIndexMatcher; + private final boolean systemIndexEnabled; private boolean systemIndicesAdditionalControlFlag; @@ -72,6 +74,9 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, this.systemIndexMatcher = WildcardMatcher.from( settings.getAsList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT) ); + this.denylistIndexMatcher = WildcardMatcher.from( + settings.getAsList(ConfigConstants.SECURITY_INDICES_DENYLIST_KEY, ConfigConstants.SECURITY_INDICES_DENYLIST_KEY_DEFAULT) + ); this.systemIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT @@ -114,6 +119,19 @@ public PrivilegesEvaluatorResponse evaluate( ) { final boolean isDebugEnabled = log.isDebugEnabled(); + if (systemIndicesAdditionalControlFlag && matchAnyDenyIndices(requestedResolved)) { + if (log.isInfoEnabled()) { + log.info( + "{} not permited for regular user {} on denylist indices {}", + action, + securityRoles, + requestedResolved.getAllIndices() + ); + } + presponse.allowed = false; + return presponse.markComplete(); + } + if (systemIndicesAdditionalControlFlag && matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { @@ -204,4 +222,16 @@ private List getProtectedIndexes(final Resolved requestedResolved) { } return protectedIndexes; } + + private boolean matchAnyDenyIndices(final Resolved requestedResolved) { + return !getDenyListIndices(requestedResolved).isEmpty(); + } + + private List getDenyListIndices(final Resolved requestedResolved) { + final List denyList = new ArrayList<>(); + denyList.addAll(denylistIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + + return denyList; + } + } diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 25efcbd2e6..3fc538a457 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -26,11 +26,7 @@ package org.opensearch.security.support; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -317,7 +313,8 @@ public enum RolesMappingResolution { public static final String SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY = "plugins.security.system_indices.additional_control.enabled"; public static final Boolean SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT = false; - + public static final String SECURITY_INDICES_DENYLIST_KEY = "plugins.security.system_indices.denylist"; + public static final List SECURITY_INDICES_DENYLIST_KEY_DEFAULT = Arrays.asList(".opendistro_security"); public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; public static final List SECURITY_SYSTEM_INDICES_DEFAULT = Collections.emptyList(); diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index 4032c01b29..f18f90b854 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -1040,7 +1040,7 @@ public void testNoDnfof() throws Exception { @Test public void testSecurityIndexSecurity() throws Exception { - setup(); + setup(Settings.builder().put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true).build()); final RestHelper rh = nonSslRestHelper(); HttpResponse res = rh.executePutRequest( @@ -1078,13 +1078,13 @@ public void testSecurityIndexSecurity() throws Exception { "{\"index\" : {\"number_of_replicas\" : 2}}", encodeBasicHeader("nagilum", "nagilum") ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + // Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePutRequest( ".opendistro_secur*/_settings", "{\"index\" : {\"number_of_replicas\" : 2}}", encodeBasicHeader("nagilum", "nagilum") ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + // Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); // res = rh.executePostRequest(".opendistro_security/_freeze", "", // encodeBasicHeader("nagilum", "nagilum")); // Assert.assertTrue(res.getStatusCode() >= 400); @@ -1100,7 +1100,7 @@ public void testSecurityIndexSecurity() throws Exception { JsonNode jsonNode = readTree(res.getBody()); System.out.println(res.getBody()); Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); - Assert.assertEquals(403, jsonNode.get("items").get(0).get("index").get("status").intValue()); + // Assert.assertEquals(403, jsonNode.get("items").get(0).get("index").get("status").intValue()); Assert.assertEquals(403, jsonNode.get("items").get(1).get("index").get("status").intValue()); Assert.assertEquals(201, jsonNode.get("items").get(2).get("index").get("status").intValue()); Assert.assertEquals(403, jsonNode.get("items").get(3).get("delete").get("status").intValue()); From 60121a0ea1b52edb6a7a2dca94ae0616c30a3817 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 1 Aug 2023 14:52:44 +0100 Subject: [PATCH 16/46] Fix issue from merge Signed-off-by: Sam --- .../SecurityIndexAccessEvaluator.java | 3 ++- .../security/support/ConfigConstants.java | 6 ++++- .../SecurityIndexAccessEvaluatorTest.java | 24 ++++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 0db27af7ef..00d09787ad 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -120,12 +120,13 @@ public PrivilegesEvaluatorResponse evaluate( final boolean isDebugEnabled = log.isDebugEnabled(); if (systemIndicesAdditionalControlFlag && matchAnyDenyIndices(requestedResolved)) { + auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( "{} not permited for regular user {} on denylist indices {}", action, securityRoles, - requestedResolved.getAllIndices() + getDenyListIndices(requestedResolved).stream().collect(Collectors.joining(", ")) ); } presponse.allowed = false; diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 3fc538a457..43ed808aad 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -26,7 +26,11 @@ package org.opensearch.security.support; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 2fb4a89caa..2ba63d1b25 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -164,7 +164,25 @@ public void protectedActionLocalAll() { @Test public void protectedActionSystemIndex() { setupEvaluatorWithSystemIndicesControl(); - final Resolved resolved = createResolved(".testSystemIndex", ".opendistro_security"); + final Resolved resolved = createResolved(".testSystemIndex"); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + + verify(log).isDebugEnabled(); + verify(log).isInfoEnabled(); + + verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, ".testSystemIndex"); + } + + @Test + public void protectedActionDenyListIndex() { + setupEvaluatorWithSystemIndicesControl(); + final Resolved resolved = createResolved(".opendistro_security"); // Action evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); @@ -177,10 +195,10 @@ public void protectedActionSystemIndex() { verify(log).isInfoEnabled(); verify(log).info( - "No {} permission for user roles {} to System Indices {}", + "{} not permited for regular user {} on denylist indices {}", PROTECTED_ACTION, securityRoles, - ".opendistro_security, .testSystemIndex" + ".opendistro_security" ); } From a6e26bb79613a400657b6dad9e6bd6277cad9581 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 3 Aug 2023 13:04:36 +0100 Subject: [PATCH 17/46] Refactoring of New control flag while keeping old evaluator process intact Signed-off-by: Sam --- .../SecurityIndexAccessEvaluator.java | 174 +++++++++++++----- .../security/IndexIntegrationTests.java | 5 +- .../opensearch/security/IntegrationTests.java | 19 +- 3 files changed, 138 insertions(+), 60 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 00d09787ad..e30afb15b8 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -27,6 +27,7 @@ package org.opensearch.security.privileges; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -119,56 +120,13 @@ public PrivilegesEvaluatorResponse evaluate( ) { final boolean isDebugEnabled = log.isDebugEnabled(); - if (systemIndicesAdditionalControlFlag && matchAnyDenyIndices(requestedResolved)) { - auditLog.logSecurityIndexAttempt(request, action, task); - if (log.isInfoEnabled()) { - log.info( - "{} not permited for regular user {} on denylist indices {}", - action, - securityRoles, - getDenyListIndices(requestedResolved).stream().collect(Collectors.joining(", ")) - ); - } - presponse.allowed = false; - return presponse.markComplete(); + if (systemIndicesAdditionalControlFlag) { + newSecuredIndexEvaluator(action, requestedResolved, request, task, presponse, securityRoles); + } else { + legacySecuredIndexEvaluator(action, requestedResolved, request, task, presponse); } - - if (systemIndicesAdditionalControlFlag - && matchAnySystemIndices(requestedResolved) - && !checkSystemIndexPermissionsForUser(securityRoles)) { - auditLog.logSecurityIndexAttempt(request, action, task); - if (log.isInfoEnabled()) { - log.info( - "No {} permission for user roles {} to System Indices {}", - action, - securityRoles, - getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")) - ); - } - presponse.allowed = false; - return presponse.markComplete(); - } - - if (securityDeniedActionMatcher.test(action)) { - if (requestedResolved.isLocalAll()) { - if (filterSecurityIndex) { - irr.replace(request, false, "*", "-" + securityIndex); - if (isDebugEnabled) { - log.debug( - "Filtered '{}'from {}, resulting list with *,-{} is {}", - securityIndex, - requestedResolved, - securityIndex, - irr.resolveRequest(request) - ); - } - return presponse; - } - auditLog.logSecurityIndexAttempt(request, action, task); - log.info("{} for '_all' indices is not allowed for a regular user", action); - presponse.allowed = false; - return presponse.markComplete(); - } + if (presponse.isComplete()) { + return presponse; } if (requestedResolved.isLocalAll() @@ -235,4 +193,122 @@ private List getDenyListIndices(final Resolved requestedResolved) { return denyList; } + private PrivilegesEvaluatorResponse newSecuredIndexEvaluator( + String action, + Resolved requestedResolved, + ActionRequest request, + Task task, + PrivilegesEvaluatorResponse presponse, + SecurityRoles securityRoles + ) { + final boolean isDebugEnabled = log.isDebugEnabled(); + + if (matchAnyDenyIndices(requestedResolved)) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "{} not permited for regular user {} on denylist indices {}", + action, + securityRoles, + getDenyListIndices(requestedResolved).stream().collect(Collectors.joining(", ")) + ); + } + presponse.allowed = false; + return presponse.markComplete(); + } + + if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "No {} permission for user roles {} to System Indices {}", + action, + securityRoles, + getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")) + ); + } + presponse.allowed = false; + return presponse.markComplete(); + } + + if (securityDeniedActionMatcher.test(action)) { + if (requestedResolved.isLocalAll()) { + if (filterSecurityIndex) { + irr.replace(request, false, "*", "-" + securityIndex); + if (isDebugEnabled) { + log.debug( + "Filtered '{}'from {}, resulting list with *,-{} is {}", + securityIndex, + requestedResolved, + securityIndex, + irr.resolveRequest(request) + ); + } + return presponse; + } + auditLog.logSecurityIndexAttempt(request, action, task); + log.info("{} for '_all' indices is not allowed for a regular user", action); + presponse.allowed = false; + return presponse.markComplete(); + } + } + return presponse; + } + + private PrivilegesEvaluatorResponse legacySecuredIndexEvaluator( + String action, + Resolved requestedResolved, + ActionRequest request, + Task task, + PrivilegesEvaluatorResponse presponse + ) { + final boolean isDebugEnabled = log.isDebugEnabled(); + + if (securityDeniedActionMatcher.test(action)) { + if (requestedResolved.isLocalAll()) { + if (filterSecurityIndex) { + irr.replace(request, false, "*", "-" + securityIndex); + if (isDebugEnabled) { + log.debug( + "Filtered '{}'from {}, resulting list with *,-{} is {}", + securityIndex, + requestedResolved, + securityIndex, + irr.resolveRequest(request) + ); + } + return presponse; + } else { + auditLog.logSecurityIndexAttempt(request, action, task); + log.warn("{} for '_all' indices is not allowed for a regular user", action); + presponse.allowed = false; + return presponse.markComplete(); + } + } else if (matchAnySystemIndices(requestedResolved)) { + if (filterSecurityIndex) { + Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); + allWithoutSecurity.remove(securityIndex); + if (allWithoutSecurity.isEmpty()) { + if (isDebugEnabled) { + log.debug("Filtered '{}' but resulting list is empty", securityIndex); + } + presponse.allowed = false; + return presponse.markComplete(); + } + irr.replace(request, false, allWithoutSecurity.toArray(new String[0])); + if (isDebugEnabled) { + log.debug("Filtered '{}', resulting list is {}", securityIndex, allWithoutSecurity); + } + return presponse; + } else { + auditLog.logSecurityIndexAttempt(request, action, task); + final String foundSystemIndexes = getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")); + log.warn("{} for '{}' index is not allowed for a regular user", action, foundSystemIndexes); + presponse.allowed = false; + return presponse.markComplete(); + } + } + } + return presponse; + } } diff --git a/src/test/java/org/opensearch/security/IndexIntegrationTests.java b/src/test/java/org/opensearch/security/IndexIntegrationTests.java index 6a8703842e..5c9a1b4b0c 100644 --- a/src/test/java/org/opensearch/security/IndexIntegrationTests.java +++ b/src/test/java/org/opensearch/security/IndexIntegrationTests.java @@ -525,7 +525,10 @@ public void testIndices() throws Exception { @Test public void testAliases() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) + .build(); setup(settings); diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index f18f90b854..414be162c3 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -1040,7 +1040,7 @@ public void testNoDnfof() throws Exception { @Test public void testSecurityIndexSecurity() throws Exception { - setup(Settings.builder().put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true).build()); + setup(Settings.builder().put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false).build()); final RestHelper rh = nonSslRestHelper(); HttpResponse res = rh.executePutRequest( @@ -1048,14 +1048,14 @@ public void testSecurityIndexSecurity() throws Exception { "{\"properties\": {\"name\":{\"type\":\"text\"}}}", encodeBasicHeader("nagilum", "nagilum") ); - Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePutRequest( "*dis*rit*/_mapping?pretty", "{\"properties\": {\"name\":{\"type\":\"text\"}}}", encodeBasicHeader("nagilum", "nagilum") ); - Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePutRequest( "*/_mapping?pretty", "{\"properties\": {\"name\":{\"type\":\"text\"}}}", @@ -1069,7 +1069,7 @@ public void testSecurityIndexSecurity() throws Exception { ); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePostRequest(".opendistro_security/_close", "", encodeBasicHeader("nagilum", "nagilum")); - Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executeDeleteRequest(".opendistro_security", encodeBasicHeader("nagilum", "nagilum")); res = rh.executeDeleteRequest("_all", encodeBasicHeader("nagilum", "nagilum")); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); @@ -1078,16 +1078,15 @@ public void testSecurityIndexSecurity() throws Exception { "{\"index\" : {\"number_of_replicas\" : 2}}", encodeBasicHeader("nagilum", "nagilum") ); - // Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePutRequest( ".opendistro_secur*/_settings", "{\"index\" : {\"number_of_replicas\" : 2}}", encodeBasicHeader("nagilum", "nagilum") ); - // Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); - // res = rh.executePostRequest(".opendistro_security/_freeze", "", - // encodeBasicHeader("nagilum", "nagilum")); - // Assert.assertTrue(res.getStatusCode() >= 400); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); + res = rh.executePostRequest(".opendistro_security/_freeze", "", encodeBasicHeader("nagilum", "nagilum")); + Assert.assertTrue(res.getStatusCode() >= 400); String bulkBody = "{ \"index\" : { \"_index\" : \".opendistro_security\", \"_id\" : \"1\" } }\n" + "{ \"field1\" : \"value1\" }\n" @@ -1100,7 +1099,7 @@ public void testSecurityIndexSecurity() throws Exception { JsonNode jsonNode = readTree(res.getBody()); System.out.println(res.getBody()); Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); - // Assert.assertEquals(403, jsonNode.get("items").get(0).get("index").get("status").intValue()); + Assert.assertEquals(403, jsonNode.get("items").get(0).get("index").get("status").intValue()); Assert.assertEquals(403, jsonNode.get("items").get(1).get("index").get("status").intValue()); Assert.assertEquals(201, jsonNode.get("items").get(2).get("index").get("status").intValue()); Assert.assertEquals(403, jsonNode.get("items").get(3).get("delete").get("status").intValue()); From 713857a97dc04abdc495a27cbee892e5d5426583 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 3 Aug 2023 15:35:52 +0100 Subject: [PATCH 18/46] Refactoring of New control flag while keeping old evaluator process intact Signed-off-by: Sam --- build.gradle | 2 +- .../SecurityIndexAccessEvaluator.java | 11 +- .../security/support/ConfigConstants.java | 2 +- .../system_indices/DenyIndicesTests.java | 242 ++++++++++++++++++ .../test/helper/cluster/ClusterHelper.java | 2 - 5 files changed, 250 insertions(+), 9 deletions(-) create mode 100644 src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java diff --git a/build.gradle b/build.gradle index 7323cdb520..e6aa6e55bd 100644 --- a/build.gradle +++ b/build.gradle @@ -469,7 +469,7 @@ task integrationTest(type: Test) { classpath = sourceSets.integrationTest.runtimeClasspath retry { failOnPassedAfterRetry = false - maxRetries = 5 + maxRetries = 2 maxFailures = 10 } //run the integrationTest task after the test task diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index e30afb15b8..57a5f70ecb 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -106,7 +106,7 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, ); systemIndicesAdditionalControlFlag = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, - false + ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT ); } @@ -120,10 +120,11 @@ public PrivilegesEvaluatorResponse evaluate( ) { final boolean isDebugEnabled = log.isDebugEnabled(); + // As per issue #2845, the legacy access control to indices with additional protection should be kept in place for the meantime. if (systemIndicesAdditionalControlFlag) { - newSecuredIndexEvaluator(action, requestedResolved, request, task, presponse, securityRoles); + evaluateNewSecuredIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles); } else { - legacySecuredIndexEvaluator(action, requestedResolved, request, task, presponse); + evaluateLegacySecuredIndicesAccess(action, requestedResolved, request, task, presponse); } if (presponse.isComplete()) { return presponse; @@ -193,7 +194,7 @@ private List getDenyListIndices(final Resolved requestedResolved) { return denyList; } - private PrivilegesEvaluatorResponse newSecuredIndexEvaluator( + private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( String action, Resolved requestedResolved, ActionRequest request, @@ -255,7 +256,7 @@ private PrivilegesEvaluatorResponse newSecuredIndexEvaluator( return presponse; } - private PrivilegesEvaluatorResponse legacySecuredIndexEvaluator( + private PrivilegesEvaluatorResponse evaluateLegacySecuredIndicesAccess( String action, Resolved requestedResolved, ActionRequest request, diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 43ed808aad..5d1e53705e 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -318,7 +318,7 @@ public enum RolesMappingResolution { "plugins.security.system_indices.additional_control.enabled"; public static final Boolean SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT = false; public static final String SECURITY_INDICES_DENYLIST_KEY = "plugins.security.system_indices.denylist"; - public static final List SECURITY_INDICES_DENYLIST_KEY_DEFAULT = Arrays.asList(".opendistro_security"); + public static final List SECURITY_INDICES_DENYLIST_KEY_DEFAULT = Arrays.asList(OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; public static final List SECURITY_SYSTEM_INDICES_DEFAULT = Collections.emptyList(); diff --git a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java new file mode 100644 index 0000000000..22e5c3fef8 --- /dev/null +++ b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java @@ -0,0 +1,242 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.system_indices; + +import org.apache.hc.core5.http.Header; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.test.DynamicSecurityConfig; +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.file.FileHelper; +import org.opensearch.security.test.helper.rest.RestHelper; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Test for opendistro system indices, to restrict configured indices access to adminDn + * Refer: "plugins.security.system_indices.enabled" + * "plugins.security.system_indices.indices"; + */ + +public class DenyIndicesTests extends SingleClusterTest { + private static final List listOfIndexesToTest = Arrays.asList(".opendistro_security"); + private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; + private static final String allAccessUser = "admin_all_access"; + private static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); + private static final String generalErrorMessage = String.format( + "no permissions for [] and User [name=%s, backend_roles=[], requestedTenant=null]", + allAccessUser + ); + + private static final String extensionUser = "extensions_user"; + private static final Header extensionUserHeader = encodeBasicHeader(extensionUser, allAccessUser); + private static final String extensionUserC = "extensions_user_c"; + private static final Header extensionUserCHeader = encodeBasicHeader(extensionUserC, allAccessUser); + + private void setupDenyIndicesDisabledWithSsl() throws Exception { + + Settings systemIndexSettings = Settings.builder() + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, true) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) + .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) + .put("plugins.security.ssl.http.enabled", true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) + .build(); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setConfig("config_system_indices.yml") + .setSecurityRoles("roles_system_indices.yml") + .setSecurityInternalUsers("internal_users_system_indices.yml") + .setSecurityRolesMapping("roles_mapping_system_indices.yml"), + systemIndexSettings, + true + ); + } + + private void setupDenyIndicesEnabledWithSsl() throws Exception { + + Settings systemIndexSettings = Settings.builder() + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, true) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) + .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) + .put("plugins.security.ssl.http.enabled", true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) + .build(); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setConfig("config_system_indices.yml") + .setSecurityRoles("roles_system_indices.yml") + .setSecurityInternalUsers("internal_users_system_indices.yml") + .setSecurityRolesMapping("roles_mapping_system_indices.yml"), + systemIndexSettings, + true + ); + } + + /** + * Creates a set of test indices and indexes one document into each index. + * + * @throws Exception + */ + private void createSnapshots() { + try (Client tc = getClient()) { + for (String index : listOfIndexesToTest) { + tc.admin() + .cluster() + .putRepository( + new PutRepositoryRequest(index).type("fs") + .settings(Settings.builder().put("location", repositoryPath.getRoot().getAbsolutePath() + "/" + index)) + ) + .actionGet(); + tc.admin() + .cluster() + .createSnapshot( + new CreateSnapshotRequest(index, index + "_1").indices(index).includeGlobalState(true).waitForCompletion(true) + ) + .actionGet(); + } + } + } + + private RestHelper keyStoreRestHelper() { + RestHelper restHelper = restHelper(); + restHelper.keystore = "kirk-keystore.jks"; + restHelper.enableHTTPClientSSL = true; + restHelper.trustHTTPServerCertificate = true; + restHelper.sendAdminCertificate = true; + return restHelper; + } + + private RestHelper sslRestHelper() { + RestHelper restHelper = restHelper(); + restHelper.enableHTTPClientSSL = true; + return restHelper; + } + + /*************************************************************************************************************************** + * Search api tests. Search is a special case. + ***************************************************************************************************************************/ + + private void validateSearchResponse(RestHelper.HttpResponse response, int expectecdHits) throws IOException { + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + XContentParser xcp = XContentType.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); + SearchResponse searchResponse = SearchResponse.fromXContent(xcp); + assertEquals(RestStatus.OK, searchResponse.status()); + assertEquals(expectecdHits, searchResponse.getHits().getHits().length); + assertEquals(0, searchResponse.getFailedShards()); + assertEquals(5, searchResponse.getSuccessfulShards()); + } + + @Test + public void testSearchAsSuperAdmin() throws Exception { + setupDenyIndicesEnabledWithSsl(); + RestHelper restHelper = keyStoreRestHelper(); + + // search system indices + for (String index : listOfIndexesToTest) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), 10); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsAdmin() throws Exception { + setupDenyIndicesEnabledWithSsl(); + RestHelper restHelper = sslRestHelper(); + + // search system indices + for (String index : listOfIndexesToTest) { + RestHelper.HttpResponse responseDoc = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); + + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); + + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]" + ) + ); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchWithDenyIndicesAsSuperAdmin() throws Exception { + setupDenyIndicesEnabledWithSsl(); + RestHelper restHelper = keyStoreRestHelper(); + + // search system indices + for (String index : listOfIndexesToTest) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), 10); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchWithDenyIndicesShouldFailAsAdmin() throws Exception { + setupDenyIndicesEnabledWithSsl(); + RestHelper restHelper = sslRestHelper(); + + // search system indices + for (String index : listOfIndexesToTest) { + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + XContentParser xcp = XContentType.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); + SearchResponse searchResponse = SearchResponse.fromXContent(xcp); + assertEquals(RestStatus.OK, searchResponse.status()); + assertEquals(0, searchResponse.getHits().getHits().length); + } + +} diff --git a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java index 35e10c93b7..8eca7b8d59 100644 --- a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java +++ b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java @@ -319,14 +319,12 @@ private void deleteTestsDataDirectory() throws IOException { Files.walkFileTree(testsDataDir.toPath(), new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - // System.out.println("Deleting file " + file.getFileName()); Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - // System.out.println("Deleting directory " + dir.getFileName()); Files.delete(dir); return FileVisitResult.CONTINUE; } From 55fedcbc52727c9732bfa95da1151497b82427f3 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 3 Aug 2023 16:00:39 +0100 Subject: [PATCH 19/46] Build failure Signed-off-by: Sam --- .../security/auditlog/impl/AbstractAuditLog.java | 2 +- .../security/auditlog/impl/AuditMessage.java | 2 +- .../security/auditlog/impl/RequestResolver.java | 2 +- .../opensearch/security/auth/BackendRegistry.java | 2 +- .../org/opensearch/security/auth/UserInjector.java | 2 +- .../configuration/SecurityIndexSearcherWrapper.java | 2 +- .../security/dlic/rest/api/AccountApiAction.java | 2 +- .../dlic/rest/api/PermissionsInfoAction.java | 2 +- .../rest/api/RestApiAdminPrivilegesEvaluator.java | 2 +- .../dlic/rest/api/RestApiPrivilegesEvaluator.java | 2 +- .../opensearch/security/dlic/rest/support/Utils.java | 2 +- .../org/opensearch/security/http/XFFResolver.java | 2 +- .../security/privileges/PrivilegesEvaluator.java | 2 +- .../privileges/RestLayerPrivilegesEvaluator.java | 2 +- .../opensearch/security/rest/SecurityInfoAction.java | 2 +- .../security/securityconf/ConfigModel.java | 2 +- .../security/securityconf/ConfigModelV6.java | 2 +- .../security/securityconf/ConfigModelV7.java | 2 +- .../security/transport/SecurityInterceptor.java | 2 +- .../security/transport/SecurityRequestHandler.java | 2 +- .../security/InitializationIntegrationTests.java | 2 +- .../opensearch/security/SnapshotRestoreTests.java | 12 +++++------- .../auditlog/helper/MockAuditMessageFactory.java | 2 +- .../security/auditlog/impl/IgnoreAuditUsersTest.java | 2 +- .../security/test/helper/cluster/ClusterHelper.java | 2 +- .../security/test/helper/cluster/ClusterInfo.java | 2 +- 26 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java b/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java index f884738f4b..b62092d148 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java @@ -46,7 +46,7 @@ import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.core.xcontent.MediaType; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java b/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java index 34f8f1fef5..fb2c1e11af 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java @@ -36,7 +36,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.core.xcontent.MediaType; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/auditlog/impl/RequestResolver.java b/src/main/java/org/opensearch/security/auditlog/impl/RequestResolver.java index 198fbe6366..352c959504 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/RequestResolver.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/RequestResolver.java @@ -45,7 +45,7 @@ import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.core.xcontent.MediaType; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index b2873f9625..9721664c70 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -49,7 +49,7 @@ import org.opensearch.OpenSearchSecurityException; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/auth/UserInjector.java b/src/main/java/org/opensearch/security/auth/UserInjector.java index 9ce040c485..3e89a52e93 100644 --- a/src/main/java/org/opensearch/security/auth/UserInjector.java +++ b/src/main/java/org/opensearch/security/auth/UserInjector.java @@ -37,7 +37,7 @@ import org.apache.logging.log4j.Logger; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.rest.RestRequest; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.http.XFFResolver; diff --git a/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java b/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java index 1619b3da32..15a20db98d 100644 --- a/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java +++ b/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java @@ -36,7 +36,7 @@ import org.opensearch.common.CheckedFunction; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.index.Index; import org.opensearch.index.IndexService; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java index 4763d312dd..b5d210df5c 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java @@ -29,7 +29,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.common.Strings; import org.opensearch.core.xcontent.XContentBuilder; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/PermissionsInfoAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/PermissionsInfoAction.java index ad8a536bfc..56d2d24337 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/PermissionsInfoAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/PermissionsInfoAction.java @@ -25,7 +25,7 @@ import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.BytesRestResponse; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java index b0a0d828cc..c72e69876d 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java @@ -19,7 +19,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.dlic.rest.support.Utils; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java index 96787bb4f5..35f4332520 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java @@ -30,7 +30,7 @@ import org.apache.logging.log4j.Logger; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; import org.opensearch.security.configuration.AdminDNs; diff --git a/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java b/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java index 52220b2450..34a8da8b9d 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java +++ b/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java @@ -36,7 +36,7 @@ import org.opensearch.OpenSearchParseException; import org.opensearch.SpecialPermission; import org.opensearch.core.common.bytes.BytesReference; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/http/XFFResolver.java b/src/main/java/org/opensearch/security/http/XFFResolver.java index aff5043f61..ddb7255179 100644 --- a/src/main/java/org/opensearch/security/http/XFFResolver.java +++ b/src/main/java/org/opensearch/security/http/XFFResolver.java @@ -33,7 +33,7 @@ import org.greenrobot.eventbus.Subscribe; import org.opensearch.OpenSearchSecurityException; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.http.netty4.Netty4HttpChannel; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 67f779c250..f1eb3ff872 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -78,7 +78,7 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.common.Strings; import org.opensearch.core.xcontent.NamedXContentRegistry; diff --git a/src/main/java/org/opensearch/security/privileges/RestLayerPrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/RestLayerPrivilegesEvaluator.java index 301207022b..8602fb91e6 100644 --- a/src/main/java/org/opensearch/security/privileges/RestLayerPrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/RestLayerPrivilegesEvaluator.java @@ -20,7 +20,7 @@ import org.opensearch.OpenSearchSecurityException; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.security.auditlog.AuditLog; diff --git a/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java b/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java index 9ed1427122..a10c58f058 100644 --- a/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java @@ -40,7 +40,7 @@ import org.opensearch.client.node.NodeClient; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.BaseRestHandler; diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModel.java b/src/main/java/org/opensearch/security/securityconf/ConfigModel.java index 653ff23896..33af51257c 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModel.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModel.java @@ -29,7 +29,7 @@ import java.util.Map; import java.util.Set; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.security.user.User; public abstract class ConfigModel { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index d565c4f69b..a4e1e9e1a0 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -51,7 +51,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.set.Sets; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 297bcb53e7..676e67ee72 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -54,7 +54,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.set.Sets; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; diff --git a/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java b/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java index 5bb4b1d1e3..2e604331a7 100644 --- a/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java +++ b/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java @@ -47,7 +47,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.security.OpenSearchSecurityPlugin; import org.opensearch.security.auditlog.AuditLog; diff --git a/src/main/java/org/opensearch/security/transport/SecurityRequestHandler.java b/src/main/java/org/opensearch/security/transport/SecurityRequestHandler.java index 8ea82c9d9d..1284ca9781 100644 --- a/src/main/java/org/opensearch/security/transport/SecurityRequestHandler.java +++ b/src/main/java/org/opensearch/security/transport/SecurityRequestHandler.java @@ -40,7 +40,7 @@ import org.opensearch.action.bulk.BulkShardRequest; import org.opensearch.action.support.replication.TransportReplicationAction.ConcreteShardRequest; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.extensions.ExtensionsManager; import org.opensearch.search.internal.ShardSearchRequest; diff --git a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java index 0fec953472..d6306e7f5d 100644 --- a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java +++ b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java @@ -48,7 +48,7 @@ import org.opensearch.client.RestHighLevelClient; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; diff --git a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java index be08eb48a1..7679f3b8d0 100644 --- a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java +++ b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java @@ -353,7 +353,7 @@ public void testSnapshot() throws Exception { ); // Try to restore vulcangov index as .opendistro_security index Assert.assertEquals( - HttpStatus.SC_INTERNAL_SERVER_ERROR, + HttpStatus.SC_FORBIDDEN, rh.executePostRequest( "_snapshot/vulcangov/vulcangov_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -405,7 +405,7 @@ public void testSnapshot() throws Exception { ); // Try to restore .opendistro_security index as .opendistro_security_copy index Assert.assertEquals( - HttpStatus.SC_INTERNAL_SERVER_ERROR, + HttpStatus.SC_FORBIDDEN, rh.executePostRequest( "_snapshot/all/all_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -440,9 +440,7 @@ public void testSnapshot() throws Exception { public void testSnapshotCheckWritePrivileges() throws Exception { final Settings settings = Settings.builder() - .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) - .build(); + .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()).build(); setup(settings, currentClusterConfig); @@ -537,7 +535,7 @@ public void testSnapshotCheckWritePrivileges() throws Exception { ); // Try to restore vulcangov index as .opendistro_security index Assert.assertEquals( - HttpStatus.SC_INTERNAL_SERVER_ERROR, + HttpStatus.SC_FORBIDDEN, rh.executePostRequest( "_snapshot/vulcangov/vulcangov_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", @@ -589,7 +587,7 @@ public void testSnapshotCheckWritePrivileges() throws Exception { ); // Try to restore .opendistro_security index as .opendistro_security_copy index Assert.assertEquals( - HttpStatus.SC_INTERNAL_SERVER_ERROR, + HttpStatus.SC_FORBIDDEN, rh.executePostRequest( "_snapshot/all/all_1/_restore?wait_for_completion=true", "{ \"indices\": \"vulcangov\", \"rename_pattern\": \"(.+)\", \"rename_replacement\": \".opendistro_security\" }", diff --git a/src/test/java/org/opensearch/security/auditlog/helper/MockAuditMessageFactory.java b/src/test/java/org/opensearch/security/auditlog/helper/MockAuditMessageFactory.java index 7e67fc374c..0f495d5063 100644 --- a/src/test/java/org/opensearch/security/auditlog/helper/MockAuditMessageFactory.java +++ b/src/test/java/org/opensearch/security/auditlog/helper/MockAuditMessageFactory.java @@ -16,7 +16,7 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.security.auditlog.AuditLog.Origin; import org.opensearch.security.auditlog.impl.AuditCategory; import org.opensearch.security.auditlog.impl.AuditMessage; diff --git a/src/test/java/org/opensearch/security/auditlog/impl/IgnoreAuditUsersTest.java b/src/test/java/org/opensearch/security/auditlog/impl/IgnoreAuditUsersTest.java index 036482e8f3..8e33401c69 100644 --- a/src/test/java/org/opensearch/security/auditlog/impl/IgnoreAuditUsersTest.java +++ b/src/test/java/org/opensearch/security/auditlog/impl/IgnoreAuditUsersTest.java @@ -23,7 +23,7 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.security.auditlog.AuditTestUtils; import org.opensearch.security.auditlog.integration.TestAuditlogImpl; import org.opensearch.security.support.ConfigConstants; diff --git a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java index 8eca7b8d59..344b7cddc2 100644 --- a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java +++ b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterHelper.java @@ -59,7 +59,7 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentType; import org.opensearch.http.HttpInfo; diff --git a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterInfo.java b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterInfo.java index d50274e0e0..a7656e1884 100644 --- a/src/test/java/org/opensearch/security/test/helper/cluster/ClusterInfo.java +++ b/src/test/java/org/opensearch/security/test/helper/cluster/ClusterInfo.java @@ -30,7 +30,7 @@ import java.util.List; import java.util.Set; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; public class ClusterInfo { public int numNodes; From 707eb2710c3a9a543c42c329c77405e8c13cd51f Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 4 Aug 2023 15:01:50 +0100 Subject: [PATCH 20/46] Test changes and adding Security Index as a standard to be blocked in the "denylist check" Signed-off-by: Sam --- .../SecurityIndexAccessEvaluator.java | 2 +- .../security/HttpIntegrationTests.java | 6 +- .../security/SnapshotRestoreTests.java | 4 +- .../system_indices/DenyIndicesTests.java | 111 ++++-------------- .../system_indices/SystemIndicesTests.java | 2 +- 5 files changed, 29 insertions(+), 96 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 57a5f70ecb..0765e23339 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -204,7 +204,7 @@ private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( ) { final boolean isDebugEnabled = log.isDebugEnabled(); - if (matchAnyDenyIndices(requestedResolved)) { + if (matchAnyDenyIndices(requestedResolved) || requestedResolved.getAllIndices().contains(securityIndex)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( diff --git a/src/test/java/org/opensearch/security/HttpIntegrationTests.java b/src/test/java/org/opensearch/security/HttpIntegrationTests.java index ade583629e..be3feafaaa 100644 --- a/src/test/java/org/opensearch/security/HttpIntegrationTests.java +++ b/src/test/java/org/opensearch/security/HttpIntegrationTests.java @@ -132,7 +132,7 @@ public void testHTTPBasic() throws Exception { rh.executeGetRequest(".nonexistentindex*", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( - HttpStatus.SC_CREATED, + HttpStatus.SC_FORBIDDEN, rh.executePutRequest(".opendistro_security/_doc/2", "{}", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( @@ -549,7 +549,7 @@ public void testHTTPClientCert() throws Exception { rh.sendAdminCertificate = true; rh.keystore = "spock-keystore.jks"; Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("_search").getStatusCode()); - Assert.assertEquals(HttpStatus.SC_CREATED, rh.executePutRequest(".opendistro_security/_doc/x", "{}").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, rh.executePutRequest(".opendistro_security/_doc/x", "{}").getStatusCode()); rh.keystore = "kirk-keystore.jks"; Assert.assertEquals(HttpStatus.SC_CREATED, rh.executePutRequest(".opendistro_security/_doc/y", "{}").getStatusCode()); @@ -748,7 +748,7 @@ public void testHTTPBasic2() throws Exception { rh.executeGetRequest(".nonexistentindex*", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( - HttpStatus.SC_CREATED, + HttpStatus.SC_FORBIDDEN, rh.executePutRequest(".opendistro_security/_doc/2", "{}", encodeBasicHeader("nagilum", "nagilum")).getStatusCode() ); Assert.assertEquals( diff --git a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java index 7679f3b8d0..6c40b0d163 100644 --- a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java +++ b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java @@ -45,7 +45,6 @@ import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; -import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.cluster.ClusterConfiguration; @@ -439,8 +438,7 @@ public void testSnapshot() throws Exception { @Test public void testSnapshotCheckWritePrivileges() throws Exception { - final Settings settings = Settings.builder() - .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()).build(); + final Settings settings = Settings.builder().putList("path.repo", repositoryPath.getRoot().getAbsolutePath()).build(); setup(settings, currentClusterConfig); diff --git a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java index 22e5c3fef8..2b66cbe1af 100644 --- a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java @@ -53,51 +53,24 @@ public class DenyIndicesTests extends SingleClusterTest { allAccessUser ); - private static final String extensionUser = "extensions_user"; - private static final Header extensionUserHeader = encodeBasicHeader(extensionUser, allAccessUser); - private static final String extensionUserC = "extensions_user_c"; - private static final Header extensionUserCHeader = encodeBasicHeader(extensionUserC, allAccessUser); + private void setupDenyIndicesEnabledWithSsl(Boolean securedIndicesAdditionalControlenable ) throws Exception { - private void setupDenyIndicesDisabledWithSsl() throws Exception { - - Settings systemIndexSettings = Settings.builder() - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, true) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) + Settings denyIndexSettings = Settings.builder() + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, securedIndicesAdditionalControlenable) .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) .build(); - setup( - Settings.EMPTY, - new DynamicSecurityConfig().setConfig("config_system_indices.yml") - .setSecurityRoles("roles_system_indices.yml") - .setSecurityInternalUsers("internal_users_system_indices.yml") - .setSecurityRolesMapping("roles_mapping_system_indices.yml"), - systemIndexSettings, - true - ); - } - private void setupDenyIndicesEnabledWithSsl() throws Exception { - - Settings systemIndexSettings = Settings.builder() - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, true) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) - .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) - .put("plugins.security.ssl.http.enabled", true) - .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) - .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) - .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) - .build(); setup( Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_system_indices.yml") .setSecurityRoles("roles_system_indices.yml") .setSecurityInternalUsers("internal_users_system_indices.yml") .setSecurityRolesMapping("roles_mapping_system_indices.yml"), - systemIndexSettings, + denyIndexSettings, true ); } @@ -107,25 +80,6 @@ private void setupDenyIndicesEnabledWithSsl() throws Exception { * * @throws Exception */ - private void createSnapshots() { - try (Client tc = getClient()) { - for (String index : listOfIndexesToTest) { - tc.admin() - .cluster() - .putRepository( - new PutRepositoryRequest(index).type("fs") - .settings(Settings.builder().put("location", repositoryPath.getRoot().getAbsolutePath() + "/" + index)) - ) - .actionGet(); - tc.admin() - .cluster() - .createSnapshot( - new CreateSnapshotRequest(index, index + "_1").indices(index).includeGlobalState(true).waitForCompletion(true) - ) - .actionGet(); - } - } - } private RestHelper keyStoreRestHelper() { RestHelper restHelper = restHelper(); @@ -158,9 +112,10 @@ private void validateSearchResponse(RestHelper.HttpResponse response, int expect assertEquals(5, searchResponse.getSuccessfulShards()); } + @Test - public void testSearchAsSuperAdmin() throws Exception { - setupDenyIndicesEnabledWithSsl(); + public void testSearchWithDenyIndicesAsSuperAdmin() throws Exception { + setupDenyIndicesEnabledWithSsl(true); RestHelper restHelper = keyStoreRestHelper(); // search system indices @@ -174,69 +129,49 @@ public void testSearchAsSuperAdmin() throws Exception { } @Test - public void testSearchAsAdmin() throws Exception { - setupDenyIndicesEnabledWithSsl(); + public void testSearchWithDenyIndicesShouldFailAsAdmin() throws Exception { + setupDenyIndicesEnabledWithSsl(true); RestHelper restHelper = sslRestHelper(); // search system indices for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseDoc = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); - + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); MatcherAssert.assertThat( - responseDoc.getBody(), + response.getBody(), Matchers.containsStringIgnoringCase( - "no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]" + "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" ) ); } // search all indices RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); } @Test - public void testSearchWithDenyIndicesAsSuperAdmin() throws Exception { - setupDenyIndicesEnabledWithSsl(); - RestHelper restHelper = keyStoreRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), 10); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - - @Test - public void testSearchWithDenyIndicesShouldFailAsAdmin() throws Exception { - setupDenyIndicesEnabledWithSsl(); + public void testSearchWithDenyIndicesShouldSuccedAsAdminNoAdditinalAccesslControl() throws Exception { + setupDenyIndicesEnabledWithSsl(false); RestHelper restHelper = sslRestHelper(); // search system indices for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"failed\":0" + ) ); } // search all indices RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - XContentParser xcp = XContentType.JSON.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); - SearchResponse searchResponse = SearchResponse.fromXContent(xcp); - assertEquals(RestStatus.OK, searchResponse.status()); - assertEquals(0, searchResponse.getHits().getHits().length); } + + } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index 84bb54767d..d55e73d08e 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -50,7 +50,7 @@ */ public class SystemIndicesTests extends SingleClusterTest { - // CS-SUPPRESS-SINGLE: RegexpSingleline See http://github/issues/1234 + // CS-SUPPRESS-SINGLE: RegexpSingleline See http://github/issues/2553 private static final List listOfIndexesToTest = Arrays.asList(".system_index_a", ".system_index_b"); private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; From 964692b06abbd48dd7470b8c82cebaceade06c69 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 4 Aug 2023 15:09:19 +0100 Subject: [PATCH 21/46] Test changes and adding Security Index as a standard to be blocked in the "denylist check" Signed-off-by: Sam --- .../privileges/SecurityIndexAccessEvaluator.java | 2 +- .../security/system_indices/DenyIndicesTests.java | 15 ++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 0765e23339..999d1efa9b 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -204,7 +204,7 @@ private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( ) { final boolean isDebugEnabled = log.isDebugEnabled(); - if (matchAnyDenyIndices(requestedResolved) || requestedResolved.getAllIndices().contains(securityIndex)) { + if (matchAnyDenyIndices(requestedResolved) || requestedResolved.getAllIndices().contains(securityIndex)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( diff --git a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java index 2b66cbe1af..e543327046 100644 --- a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java @@ -15,10 +15,7 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; -import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; -import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; import org.opensearch.action.search.SearchResponse; -import org.opensearch.client.Client; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.LoggingDeprecationHandler; import org.opensearch.common.xcontent.XContentType; @@ -53,7 +50,7 @@ public class DenyIndicesTests extends SingleClusterTest { allAccessUser ); - private void setupDenyIndicesEnabledWithSsl(Boolean securedIndicesAdditionalControlenable ) throws Exception { + private void setupDenyIndicesEnabledWithSsl(Boolean securedIndicesAdditionalControlenable) throws Exception { Settings denyIndexSettings = Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, securedIndicesAdditionalControlenable) @@ -112,7 +109,6 @@ private void validateSearchResponse(RestHelper.HttpResponse response, int expect assertEquals(5, searchResponse.getSuccessfulShards()); } - @Test public void testSearchWithDenyIndicesAsSuperAdmin() throws Exception { setupDenyIndicesEnabledWithSsl(true); @@ -159,12 +155,7 @@ public void testSearchWithDenyIndicesShouldSuccedAsAdminNoAdditinalAccesslContro for (String index : listOfIndexesToTest) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"failed\":0" - ) - ); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"failed\":0")); } // search all indices @@ -172,6 +163,4 @@ public void testSearchWithDenyIndicesShouldSuccedAsAdminNoAdditinalAccesslContro assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } - - } From 0eb4427d040f32dc374e37a543fd6ddd2aaad3c6 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 4 Aug 2023 19:07:44 +0100 Subject: [PATCH 22/46] Test changes Signed-off-by: Sam --- .../SecurityIndexAccessEvaluator.java | 20 +++++++++---------- .../SecurityIndexAccessEvaluatorTest.java | 12 +++++------ .../system_indices/DenyIndicesTests.java | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 999d1efa9b..eee3baa9da 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -120,11 +120,12 @@ public PrivilegesEvaluatorResponse evaluate( ) { final boolean isDebugEnabled = log.isDebugEnabled(); - // As per issue #2845, the legacy access control to indices with additional protection should be kept in place for the meantime. + // As per issue #2845, the legacy access control to indices with additional protection should be kept in place in the meantime and + // be the default. if (systemIndicesAdditionalControlFlag) { - evaluateNewSecuredIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles); + evaluateNewSecuredIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles, isDebugEnabled); } else { - evaluateLegacySecuredIndicesAccess(action, requestedResolved, request, task, presponse); + evaluateLegacySecuredIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled); } if (presponse.isComplete()) { return presponse; @@ -200,10 +201,10 @@ private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( ActionRequest request, Task task, PrivilegesEvaluatorResponse presponse, - SecurityRoles securityRoles - ) { - final boolean isDebugEnabled = log.isDebugEnabled(); + SecurityRoles securityRoles, + Boolean isDebugEnabled + ) { if (matchAnyDenyIndices(requestedResolved) || requestedResolved.getAllIndices().contains(securityIndex)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { @@ -261,10 +262,9 @@ private PrivilegesEvaluatorResponse evaluateLegacySecuredIndicesAccess( Resolved requestedResolved, ActionRequest request, Task task, - PrivilegesEvaluatorResponse presponse + PrivilegesEvaluatorResponse presponse, + Boolean isDebugEnabled ) { - final boolean isDebugEnabled = log.isDebugEnabled(); - if (securityDeniedActionMatcher.test(action)) { if (requestedResolved.isLocalAll()) { if (filterSecurityIndex) { @@ -281,7 +281,7 @@ private PrivilegesEvaluatorResponse evaluateLegacySecuredIndicesAccess( return presponse; } else { auditLog.logSecurityIndexAttempt(request, action, task); - log.warn("{} for '_all' indices is not allowed for a regular user", action); + log.info("{} for '_all' indices is not allowed for a regular user", action); presponse.allowed = false; return presponse.markComplete(); } diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 2ba63d1b25..d97f46d882 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -36,7 +36,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -115,8 +114,7 @@ public void actionIsNotProtected_noSystemIndexInvolved() { presponse, securityRoles ); - - verifyNoInteractions(presponse); + verify(presponse).isComplete(); assertThat(response, is(presponse)); verify(log).isDebugEnabled(); @@ -139,6 +137,7 @@ public void disableCacheOrRealtimeOnSystemIndex() { verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); + verify(presponse, times(3)).isComplete(); verify(log, times(3)).isDebugEnabled(); verify(log).debug("Disable search request cache for this request"); verify(log).debug("Disable realtime for this request"); @@ -156,7 +155,7 @@ public void protectedActionLocalAll() { verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - + verify(presponse).isComplete(); verify(log).isDebugEnabled(); verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } @@ -172,10 +171,9 @@ public void protectedActionSystemIndex() { verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - + verify(presponse).isComplete(); verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); - verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, ".testSystemIndex"); } @@ -193,7 +191,7 @@ public void protectedActionDenyListIndex() { verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); - + verify(presponse).isComplete(); verify(log).info( "{} not permited for regular user {} on denylist indices {}", PROTECTED_ACTION, diff --git a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java index e543327046..1166aefcea 100644 --- a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java @@ -143,7 +143,7 @@ public void testSearchWithDenyIndicesShouldFailAsAdmin() throws Exception { // search all indices RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } @Test From 7f7944d9b686ce374b4c24e3e9c8d5f2400d0274 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 8 Aug 2023 19:03:02 +0100 Subject: [PATCH 23/46] Making Deny List non-configurable. Signed-off-by: Sam --- build.gradle | 1 - .../opensearch/security/OpenSearchSecurityPlugin.java | 9 --------- .../privileges/SecurityIndexAccessEvaluator.java | 6 ++---- .../org/opensearch/security/support/ConfigConstants.java | 2 -- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index 2d65c8c5be..2aa2ace59a 100644 --- a/build.gradle +++ b/build.gradle @@ -263,7 +263,6 @@ def setCommonTestConfig(Test task) { "sun.security.pkcs11.*", "sun.security.smartcardio.*", "sun.util.resources.provider.*", - "org/opensearch/security/securityconf/impl/v6.*" ] } task.dependsOn copyExtraTestResources diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 9b14872a4c..ca40372de8 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -1791,15 +1791,6 @@ public List> getSettings() { Property.Filtered ) ); - settings.add( - Setting.listSetting( - ConfigConstants.SECURITY_INDICES_DENYLIST_KEY, - ConfigConstants.SECURITY_INDICES_DENYLIST_KEY_DEFAULT, - Function.identity(), - Property.NodeScope, - Property.Filtered - ) - ); } return settings; diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index eee3baa9da..14902e5196 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -75,9 +75,7 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, this.systemIndexMatcher = WildcardMatcher.from( settings.getAsList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT) ); - this.denylistIndexMatcher = WildcardMatcher.from( - settings.getAsList(ConfigConstants.SECURITY_INDICES_DENYLIST_KEY, ConfigConstants.SECURITY_INDICES_DENYLIST_KEY_DEFAULT) - ); + this.denylistIndexMatcher = WildcardMatcher.from(ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); this.systemIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT @@ -205,7 +203,7 @@ private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( Boolean isDebugEnabled ) { - if (matchAnyDenyIndices(requestedResolved) || requestedResolved.getAllIndices().contains(securityIndex)) { + if (matchAnyDenyIndices(requestedResolved)){ auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 5d1e53705e..6255e4118d 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -317,8 +317,6 @@ public enum RolesMappingResolution { public static final String SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY = "plugins.security.system_indices.additional_control.enabled"; public static final Boolean SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT = false; - public static final String SECURITY_INDICES_DENYLIST_KEY = "plugins.security.system_indices.denylist"; - public static final List SECURITY_INDICES_DENYLIST_KEY_DEFAULT = Arrays.asList(OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; public static final List SECURITY_SYSTEM_INDICES_DEFAULT = Collections.emptyList(); From 57e64d14376fb58ff4bbec2bb9b831cb032a0306 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 9 Aug 2023 14:25:40 +0100 Subject: [PATCH 24/46] removing changes from certain tests Signed-off-by: Sam --- .../java/org/opensearch/security/IndexIntegrationTests.java | 5 +---- .../security/auditlog/compliance/ComplianceAuditlogTest.java | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/java/org/opensearch/security/IndexIntegrationTests.java b/src/test/java/org/opensearch/security/IndexIntegrationTests.java index 5c9a1b4b0c..6a8703842e 100644 --- a/src/test/java/org/opensearch/security/IndexIntegrationTests.java +++ b/src/test/java/org/opensearch/security/IndexIntegrationTests.java @@ -525,10 +525,7 @@ public void testIndices() throws Exception { @Test public void testAliases() throws Exception { - final Settings settings = Settings.builder() - .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) - .build(); + final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); setup(settings); diff --git a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java index 799d60d607..361c3cc313 100644 --- a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java +++ b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java @@ -56,7 +56,6 @@ public void testSourceFilter() throws Exception { Settings additionalSettings = Settings.builder() .put("plugins.security.audit.type", TestAuditlogImpl.class.getName()) .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, true) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true) .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false) .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, "emp") From cf447efd3068332e0ffae04fde684efa4e39989e Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 16 Aug 2023 17:10:45 +0100 Subject: [PATCH 25/46] Spotless Check and Adding one more test for coverage Signed-off-by: Sam --- .../SecurityIndexAccessEvaluator.java | 2 +- .../SecurityIndexAccessEvaluatorTest.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 14902e5196..70219b07f4 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -203,7 +203,7 @@ private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( Boolean isDebugEnabled ) { - if (matchAnyDenyIndices(requestedResolved)){ + if (matchAnyDenyIndices(requestedResolved)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index d97f46d882..a17363d86e 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -160,6 +160,23 @@ public void protectedActionLocalAll() { verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } + @Test + public void protectedActionLocalAllWithNewAccessControl() { + setupEvaluatorWithSystemIndicesControl(); + final Resolved resolved = Resolved._LOCAL_ALL; + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + verify(log).isDebugEnabled(); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + verify(presponse).isComplete(); + verify(log).isDebugEnabled(); + verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); + } + @Test public void protectedActionSystemIndex() { setupEvaluatorWithSystemIndicesControl(); From 1606b777e34ec5745050cf49ab7f856d8ef71b15 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 31 Aug 2023 15:24:10 +0100 Subject: [PATCH 26/46] TypePerm Test for code coverage and removed TypePerm constructor parameter that wasn't being used. Deleted method that was commented out since 2019. Signed-off-by: Sam --- .../security/securityconf/ConfigModelV6.java | 2 +- .../security/securityconf/ConfigModelV7.java | 65 ------------------- .../security/securityconf/TypePerm.java | 2 +- .../SecurityRolesPermissionsTest.java | 17 +++++ 4 files changed, 19 insertions(+), 67 deletions(-) diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index a4e1e9e1a0..79480a6080 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -253,7 +253,7 @@ public SecurityRole call() throws Exception { _indexPatternV6.addMaskedFields(maskedFields); for (Entry> type : permittedAliasesIndex.getValue().getTypes().entrySet()) { - TypePerm typePerm = new TypePerm(type.getKey()); + TypePerm typePerm = new TypePerm(); final List perms = type.getValue(); typePerm.addPerms(agr.resolvedActions(perms)); _indexPatternV6.addTypePerms(typePerm); diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 676e67ee72..8544e62c2d 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -893,71 +893,6 @@ public WildcardMatcher getNonWildCardPerms() { } } - /*public static class TypePerm { - private final String typePattern; - private final Set perms = new HashSet<>(); - - private TypePerm(String typePattern) { - super(); - this.typePattern = Objects.requireNonNull(typePattern); - /*if(IGNORED_TYPES.contains(typePattern)) { - throw new RuntimeException("typepattern '"+typePattern+"' not allowed"); - } - } - - private TypePerm addPerms(Collection perms) { - if (perms != null) { - this.perms.addAll(perms); - } - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((perms == null) ? 0 : perms.hashCode()); - result = prime * result + ((typePattern == null) ? 0 : typePattern.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - TypePerm other = (TypePerm) obj; - if (perms == null) { - if (other.perms != null) - return false; - } else if (!perms.equals(other.perms)) - return false; - if (typePattern == null) { - if (other.typePattern != null) - return false; - } else if (!typePattern.equals(other.typePattern)) - return false; - return true; - } - - @Override - public String toString() { - return System.lineSeparator() + " typePattern=" + typePattern + System.lineSeparator() + " perms=" + perms; - } - - public String getTypePattern() { - return typePattern; - } - - public Set getPerms() { - return Collections.unmodifiableSet(perms); - } - - }*/ - public static class Tenant { private final String tenant; private final boolean readWrite; diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java index c8796142ac..85a91b12e3 100644 --- a/src/main/java/org/opensearch/security/securityconf/TypePerm.java +++ b/src/main/java/org/opensearch/security/securityconf/TypePerm.java @@ -19,7 +19,7 @@ public class TypePerm { protected final WildcardMatcher typeMatcher; private final Set perms = new HashSet<>(); - TypePerm(String typePattern) { + TypePerm() { this.typeMatcher = WildcardMatcher.ANY; } diff --git a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java index 49a9be8a91..808bf1659b 100644 --- a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java +++ b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java @@ -30,6 +30,7 @@ import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; @@ -192,6 +193,22 @@ public void hasExplicitClusterPermissionPermissionForRestAdmin() { } } + @Test + public void testTypePerms() { + TypePerm typePerm = new TypePerm(); + TypePerm typePerm2 = new TypePerm(); + TypePerm emptyTypePerm = new TypePerm(); + + List perms = Arrays.asList("be", "asas"); + typePerm.addPerms(perms); + typePerm2.addPerms(perms); + + Assert.assertEquals(typePerm, typePerm2); + Assert.assertEquals(typePerm.hashCode(), typePerm2.hashCode()); + Assert.assertNotEquals(typePerm, emptyTypePerm); + + } + void assertHasNoPermissionsForRestApiAdminOnePermissionRole(final Endpoint allowEndpoint, final SecurityRoles allowOnlyRoleForRole) { final Collection noPermissionEndpoints = ENDPOINTS_WITH_PERMISSIONS.keySet() .stream() From 7f08d0dba9d9ea49b65bc9b51effc7e758bf0625 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 31 Aug 2023 16:11:35 -0400 Subject: [PATCH 27/46] Refactors a file Signed-off-by: Darshit Chanpura --- .../SecurityIndexAccessEvaluatorTest.java | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index a17363d86e..f3135f748c 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -62,7 +62,7 @@ public class SecurityIndexAccessEvaluatorTest { @Mock ConfigModelV7.SecurityRoles securityRoles;// = configModelV7.getSecurityRoles(); - public void setupEvaluatorWithSystemIndicesControl() { + public void setupEvaluatorWithSystemIndicesControl(boolean systemIndexPermissionsEnabled) { evaluator = new SecurityIndexAccessEvaluator( Settings.EMPTY.builder() .put("plugins.security.system_indices.indices", ".testSystemIndex") @@ -79,21 +79,6 @@ public void setupEvaluatorWithSystemIndicesControl() { } - public void setupEvaluatorWithoutSystemIndicesControl() { - evaluator = new SecurityIndexAccessEvaluator( - Settings.EMPTY.builder() - .put("plugins.security.system_indices.indices", ".testSystemIndex") - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) - .put("plugins.security.system_indices.enabled", true) - .build(), - auditLog, - irr - ); - evaluator.log = log; - - when(log.isDebugEnabled()).thenReturn(true); - } - @After public void after() { @@ -102,7 +87,7 @@ public void after() { @Test public void actionIsNotProtected_noSystemIndexInvolved() { - setupEvaluatorWithSystemIndicesControl(); + setupEvaluatorWithSystemIndicesControl(true); final Resolved resolved = createResolved(".potato"); // Action @@ -122,7 +107,7 @@ public void actionIsNotProtected_noSystemIndexInvolved() { @Test public void disableCacheOrRealtimeOnSystemIndex() { - setupEvaluatorWithoutSystemIndicesControl(); + setupEvaluatorWithSystemIndicesControl(false); final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); @@ -145,7 +130,7 @@ public void disableCacheOrRealtimeOnSystemIndex() { @Test public void protectedActionLocalAll() { - setupEvaluatorWithoutSystemIndicesControl(); + setupEvaluatorWithSystemIndicesControl(false); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -162,7 +147,7 @@ public void protectedActionLocalAll() { @Test public void protectedActionLocalAllWithNewAccessControl() { - setupEvaluatorWithSystemIndicesControl(); + setupEvaluatorWithSystemIndicesControl(true); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -179,7 +164,7 @@ public void protectedActionLocalAllWithNewAccessControl() { @Test public void protectedActionSystemIndex() { - setupEvaluatorWithSystemIndicesControl(); + setupEvaluatorWithSystemIndicesControl(true); final Resolved resolved = createResolved(".testSystemIndex"); // Action @@ -196,7 +181,7 @@ public void protectedActionSystemIndex() { @Test public void protectedActionDenyListIndex() { - setupEvaluatorWithSystemIndicesControl(); + setupEvaluatorWithSystemIndicesControl(true); final Resolved resolved = createResolved(".opendistro_security"); // Action From b48823b9b209fc60173a0987bd28c6688adfed32 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 31 Aug 2023 23:42:41 -0400 Subject: [PATCH 28/46] Cleans up code Signed-off-by: Darshit Chanpura --- .../SecurityIndexAccessEvaluator.java | 2 +- .../security/securityconf/IndexPattern.java | 4 +++- .../security/securityconf/SecurityRole.java | 4 +++- .../security/securityconf/TypePerm.java | 4 +++- .../opensearch/security/IntegrationTests.java | 20 +++++++++---------- .../security/SnapshotRestoreTests.java | 12 ++++++----- .../SecurityIndexAccessEvaluatorTest.java | 3 ++- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 70219b07f4..0d258538f7 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -159,7 +159,7 @@ private boolean checkSystemIndexPermissionsForUser(SecurityRoles securityRoles) .map(IndexPattern::getNonWildCardPerms) .collect(Collectors.toSet()); - for (WildcardMatcher userPermission : userPermissions.stream().collect(Collectors.toSet())) { + for (WildcardMatcher userPermission : userPermissions) { if (userPermission.matchAny(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { return true; } diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java index 9f9ac2be17..e0937fa4b3 100644 --- a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java +++ b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java @@ -1,10 +1,12 @@ /* - * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ package org.opensearch.security.securityconf; diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java index 5027910644..902e351449 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java @@ -1,10 +1,12 @@ /* - * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ package org.opensearch.security.securityconf; diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java index 85a91b12e3..4d6a7c677d 100644 --- a/src/main/java/org/opensearch/security/securityconf/TypePerm.java +++ b/src/main/java/org/opensearch/security/securityconf/TypePerm.java @@ -1,10 +1,12 @@ /* - * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ package org.opensearch.security.securityconf; diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index d9df87ae20..9a23e4f98e 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -498,22 +498,22 @@ public void testDeleteByQueryDnfof() throws Exception { } RestHelper rh = nonSslRestHelper(); - HttpResponse res = rh.executePostRequest( - "/vulcango*/_delete_by_query?refresh=true&wait_for_completion=true&pretty=true", - "{\"query\" : {\"match_all\" : {}}}", - encodeBasicHeader("nagilum", "nagilum") + HttpResponse res; + Assert.assertEquals( + HttpStatus.SC_OK, + (res = rh.executePostRequest( + "/vulcango*/_delete_by_query?refresh=true&wait_for_completion=true&pretty=true", + "{\"query\" : {\"match_all\" : {}}}", + encodeBasicHeader("nagilum", "nagilum") + )).getStatusCode() ); - Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); Assert.assertTrue(res.getBody().contains("\"deleted\" : 3")); } @Test public void testUpdate() throws Exception { - final Settings settings = Settings.builder() - .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) - .build(); + final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); setup(settings); final RestHelper rh = nonSslRestHelper(); @@ -1001,7 +1001,7 @@ public void testNoDnfof() throws Exception { @Test public void testSecurityIndexSecurity() throws Exception { - setup(Settings.builder().put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false).build()); + setup(); final RestHelper rh = nonSslRestHelper(); HttpResponse res = rh.executePutRequest( diff --git a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java index 6c40b0d163..1c884a8e5d 100644 --- a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java +++ b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java @@ -731,12 +731,14 @@ public void testSnapshotRestore() throws Exception { + "\"include_global_state\": false" + "}"; - RestHelper.HttpResponse response = rh.executePutRequest( - "_snapshot/bckrepo/" + putSnapshot.hashCode() + "?wait_for_completion=true&pretty", - putSnapshot, - encodeBasicHeader("snapresuser", "nagilum") + Assert.assertEquals( + HttpStatus.SC_OK, + rh.executePutRequest( + "_snapshot/bckrepo/" + putSnapshot.hashCode() + "?wait_for_completion=true&pretty", + putSnapshot, + encodeBasicHeader("snapresuser", "nagilum") + ).getStatusCode() ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertEquals( HttpStatus.SC_FORBIDDEN, rh.executePostRequest( diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index f3135f748c..9cd88a903c 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -118,7 +119,7 @@ public void disableCacheOrRealtimeOnSystemIndex() { evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // verifyNoInteractions(presponse); + verifyNoInteractions(presponse); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); From f3f780843aa31e62981728fe86e5da6ebb279156 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 31 Aug 2023 23:56:33 -0400 Subject: [PATCH 29/46] Splits up system index tests into 3 files for 3 scenarios and refactors system index config yaml files Signed-off-by: Darshit Chanpura --- build.gradle | 2 +- .../system_indices/DenyIndicesTests.java | 166 ----- .../SystemIndexDisabledTests.java | 312 ++++++++ .../SystemIndexPermissionDisabledTests.java | 303 ++++++++ .../SystemIndexPermissionEnabledTests.java | 353 +++++++++ .../system_indices/SystemIndicesTests.java | 671 +----------------- .../config.yml} | 0 .../internal_users.yml} | 10 +- .../roles.yml} | 15 +- .../roles_mapping.yml} | 16 +- 10 files changed, 1000 insertions(+), 848 deletions(-) delete mode 100644 src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java create mode 100644 src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java create mode 100644 src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java create mode 100644 src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java rename src/test/resources/{config_system_indices.yml => system_indices/config.yml} (100%) rename src/test/resources/{internal_users_system_indices.yml => system_indices/internal_users.yml} (67%) rename src/test/resources/{roles_system_indices.yml => system_indices/roles.yml} (59%) rename src/test/resources/{roles_mapping_system_indices.yml => system_indices/roles_mapping.yml} (56%) diff --git a/build.gradle b/build.gradle index 78ca729009..6aebf2def3 100644 --- a/build.gradle +++ b/build.gradle @@ -261,7 +261,7 @@ def setCommonTestConfig(Test task) { "sun.security.jgss.*", "sun.security.pkcs11.*", "sun.security.smartcardio.*", - "sun.util.resources.provider.*", + "sun.util.resources.provider.*" ] } task.dependsOn copyExtraTestResources diff --git a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java deleted file mode 100644 index 1166aefcea..0000000000 --- a/src/test/java/org/opensearch/security/system_indices/DenyIndicesTests.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.system_indices; - -import org.apache.hc.core5.http.Header; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Test; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.core.rest.RestStatus; -import org.opensearch.core.xcontent.NamedXContentRegistry; -import org.opensearch.core.xcontent.XContentParser; -import org.opensearch.security.support.ConfigConstants; -import org.opensearch.security.test.DynamicSecurityConfig; -import org.opensearch.security.test.SingleClusterTest; -import org.opensearch.security.test.helper.file.FileHelper; -import org.opensearch.security.test.helper.rest.RestHelper; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -/** - * Test for opendistro system indices, to restrict configured indices access to adminDn - * Refer: "plugins.security.system_indices.enabled" - * "plugins.security.system_indices.indices"; - */ - -public class DenyIndicesTests extends SingleClusterTest { - private static final List listOfIndexesToTest = Arrays.asList(".opendistro_security"); - private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; - private static final String allAccessUser = "admin_all_access"; - private static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); - private static final String generalErrorMessage = String.format( - "no permissions for [] and User [name=%s, backend_roles=[], requestedTenant=null]", - allAccessUser - ); - - private void setupDenyIndicesEnabledWithSsl(Boolean securedIndicesAdditionalControlenable) throws Exception { - - Settings denyIndexSettings = Settings.builder() - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, securedIndicesAdditionalControlenable) - .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) - .put("plugins.security.ssl.http.enabled", true) - .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) - .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) - .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) - .build(); - - setup( - Settings.EMPTY, - new DynamicSecurityConfig().setConfig("config_system_indices.yml") - .setSecurityRoles("roles_system_indices.yml") - .setSecurityInternalUsers("internal_users_system_indices.yml") - .setSecurityRolesMapping("roles_mapping_system_indices.yml"), - denyIndexSettings, - true - ); - } - - /** - * Creates a set of test indices and indexes one document into each index. - * - * @throws Exception - */ - - private RestHelper keyStoreRestHelper() { - RestHelper restHelper = restHelper(); - restHelper.keystore = "kirk-keystore.jks"; - restHelper.enableHTTPClientSSL = true; - restHelper.trustHTTPServerCertificate = true; - restHelper.sendAdminCertificate = true; - return restHelper; - } - - private RestHelper sslRestHelper() { - RestHelper restHelper = restHelper(); - restHelper.enableHTTPClientSSL = true; - return restHelper; - } - - /*************************************************************************************************************************** - * Search api tests. Search is a special case. - ***************************************************************************************************************************/ - - private void validateSearchResponse(RestHelper.HttpResponse response, int expectecdHits) throws IOException { - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - - XContentParser xcp = XContentType.JSON.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); - SearchResponse searchResponse = SearchResponse.fromXContent(xcp); - assertEquals(RestStatus.OK, searchResponse.status()); - assertEquals(expectecdHits, searchResponse.getHits().getHits().length); - assertEquals(0, searchResponse.getFailedShards()); - assertEquals(5, searchResponse.getSuccessfulShards()); - } - - @Test - public void testSearchWithDenyIndicesAsSuperAdmin() throws Exception { - setupDenyIndicesEnabledWithSsl(true); - RestHelper restHelper = keyStoreRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), 10); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - - @Test - public void testSearchWithDenyIndicesShouldFailAsAdmin() throws Exception { - setupDenyIndicesEnabledWithSsl(true); - RestHelper restHelper = sslRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - - @Test - public void testSearchWithDenyIndicesShouldSuccedAsAdminNoAdditinalAccesslControl() throws Exception { - setupDenyIndicesEnabledWithSsl(false); - RestHelper restHelper = sslRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"failed\":0")); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - -} diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java new file mode 100644 index 0000000000..3c9aff1285 --- /dev/null +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -0,0 +1,312 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.system_indices; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.security.test.helper.rest.RestHelper; + +import static org.junit.Assert.assertEquals; + +public class SystemIndexDisabledTests extends SystemIndicesTests { + + @Before + public void setup() throws Exception { + setupWithSsl(false, false); + createTestIndicesAndDocs(); + } + + @Test + public void testSearchAsSuperAdmin() throws Exception { + RestHelper restHelper = superAdminRestHelper(); + + // search system indices + for (String idx : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(idx + "/_search", matchAllQuery), 1); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsAdmin() throws Exception { + RestHelper restHelper = sslRestHelper(); + + // search system indices + for (String index : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader), 1); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsNormalUser() throws Exception { + RestHelper restHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, normalUserHeader), 0); + } + + } + + @Test + public void testDeleteAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); + assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); + + RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + } + } + + @Test + public void testDeleteAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); + + RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + } + } + + @Test + public void testCloseOpenAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + + RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + } + + @Test + public void testCloseOpenAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); + MatcherAssert.assertThat( + responseClose.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); + MatcherAssert.assertThat( + responseOpen.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + } + } + + @Test + public void testCloseOpenAsNormalUser() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + } + + @Test + public void testCreateIndexAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + } + + for (String index : SYSTEM_INDICES) { + keyStoreRestHelper.executeDeleteRequest(index); + } + + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testCreateIndexAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + + } + } + + @Test + public void testCreateIndexAsNormalUser() { + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + + "}"; + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testUpdateAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testUpdateMappingsAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testUpdateMappingsAsAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); + } + } +} diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java new file mode 100644 index 0000000000..6487bddf9c --- /dev/null +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java @@ -0,0 +1,303 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.system_indices; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.security.test.helper.rest.RestHelper; + +import static org.junit.Assert.assertEquals; + +public class SystemIndexPermissionDisabledTests extends SystemIndicesTests { + + @Before + public void setup() throws Exception { + setupWithSsl(true, false); + createTestIndicesAndDocs(); + } + + @Test + public void testSearchAsSuperAdmin() throws Exception { + RestHelper restHelper = superAdminRestHelper(); + + // search system indices + for (String idx : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(idx + "/_search", matchAllQuery), 10); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsAdmin() { + RestHelper restHelper = sslRestHelper(); + + // search system indices + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"failed\":0")); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsNormalUser() throws Exception { + RestHelper restHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, normalUserHeader), 0); + } + + } + + @Test + public void testDeleteAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); + assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); + + RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + } + } + + @Test + public void testDeleteAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + + RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + } + } + + @Test + public void testCloseOpenAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + MatcherAssert.assertThat(responseClose.getBody(), Matchers.containsStringIgnoringCase("{\"closed\":true}")); + + RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + + } + + @Test + public void testCloseOpenAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); + MatcherAssert.assertThat( + responseClose.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); + MatcherAssert.assertThat( + responseOpen.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + } + } + + @Test + public void testCloseOpenAsNormalUser() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + } + + @Test + public void testUpdateAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testUpdateMappingsAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testUpdateMappingsAsAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); + } + } + + @Test + public void testCreateIndexAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"result\":\"created\"")); + + } + + } + + @Test + public void testCreateIndexAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + + } + } + + @Test + public void testCreateIndexAsNormalUser() { + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + + "}"; + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + } + } +} diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java new file mode 100644 index 0000000000..8313be71c2 --- /dev/null +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java @@ -0,0 +1,353 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.system_indices; + +import org.apache.hc.core5.http.HttpStatus; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.opensearch.action.admin.indices.close.CloseIndexRequest; +import org.opensearch.client.Client; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.security.test.helper.rest.RestHelper; + +import static org.junit.Assert.assertEquals; + +public class SystemIndexPermissionEnabledTests extends SystemIndicesTests { + + @Before + public void setup() throws Exception { + setupWithSsl(true, true); + createTestIndicesAndDocs(); + } + + @Test + public void testAccessAsSuperAdmin() throws Exception { + RestHelper restHelper = superAdminRestHelper(); + + // search system indices + for (String idx : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(idx + "/_search", matchAllQuery), 10); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsAdmin() { + RestHelper restHelper = sslRestHelper(); + + // search system indices + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + + @Test + public void testSearchAsNormalUser() throws Exception { + RestHelper restHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, normalUserHeader), 0); + } + + } + + /*************************************************************************************************************************** + * Delete index and Delete doc + ***************************************************************************************************************************/ + + @Test + public void testDeleteAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); + assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); + + RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + } + } + + @Test + public void testDeleteAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + + RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + MatcherAssert.assertThat( + responseDoc.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + } + } + + /*************************************************************************************************************************** + * open and close index + ***************************************************************************************************************************/ + + @Test + public void testCloseOpenAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + MatcherAssert.assertThat(responseClose.getBody(), Matchers.containsStringIgnoringCase("{\"closed\":true}")); + + RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + + } + + @Test + public void testCloseOpenAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); + MatcherAssert.assertThat( + responseClose.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); + MatcherAssert.assertThat( + responseOpen.getBody(), + Matchers.containsStringIgnoringCase( + "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + } + } + + @Test + public void testCloseOpenAsNormalUser() { + RestHelper sslRestHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + + RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + } + } + + /*************************************************************************************************************************** + * Update Index Settings + ***************************************************************************************************************************/ + + @Test + public void testUpdateAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat( + response.getBody(), + Matchers.containsStringIgnoringCase( + "\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"" + ) + ); + + } + } + + /*************************************************************************************************************************** + * Index mappings. indices:admin/mapping/put + ************************************************************************************************************************** */ + + @Test + public void testUpdateMappingsAsSuperAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + } + + @Test + public void testUpdateMappingsAsAdmin() { + RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper sslRestHelper = sslRestHelper(); + + String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + // as super-admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + + } + // as admin + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); + } + } + + /*************************************************************************************************************************** + * Create index and Create doc + ***************************************************************************************************************************/ + + @Test + public void testCreateIndexAsAdmin() { + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + "}"; + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" + ) + ); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat( + responseIndex.getBody(), + Matchers.containsStringIgnoringCase( + "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" + ) + ); + + } + } + + @Test + public void testCreateIndexAsNormalUser() { + RestHelper sslRestHelper = sslRestHelper(); + + String indexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + + "}"; + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + } + } + + /*************************************************************************************************************************** + * snapshot : since snapshot takes more time, we are testing only Enabled case. + ***************************************************************************************************************************/ + @Test + public void testSnapshotWithSystemIndices() { + createSnapshots(); + + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + RestHelper sslRestHelper = sslRestHelper(); + // as admin + for (String index : SYSTEM_INDICES) { + assertEquals( + HttpStatus.SC_UNAUTHORIZED, + sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode() + ); + assertEquals( + HttpStatus.SC_FORBIDDEN, + sslRestHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + allAccessUserHeader + ).getStatusCode() + ); + assertEquals( + HttpStatus.SC_OK, + sslRestHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ).getStatusCode() + ); + } + } +} diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java index d55e73d08e..0c47edd5a7 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -12,19 +12,12 @@ package org.opensearch.security.system_indices; import java.io.IOException; -import java.util.Arrays; import java.util.List; import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpStatus; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Assert; -import org.junit.Test; import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; -import org.opensearch.action.admin.indices.close.CloseIndexRequest; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.SearchResponse; @@ -50,28 +43,27 @@ */ public class SystemIndicesTests extends SingleClusterTest { - // CS-SUPPRESS-SINGLE: RegexpSingleline See http://github/issues/2553 - private static final List listOfIndexesToTest = Arrays.asList(".system_index_a", ".system_index_b"); - private static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; - private static final String allAccessUser = "admin_all_access"; - private static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); - private static final String generalErrorMessage = String.format( + static final String ACCESSIBLE_ONLY_BY_SUPER_ADMIN = ".opendistro_security"; + static final String ACCESSIBLE_BY_AUTHORIZED_USER = ".opendistro-alerting"; + static final List SYSTEM_INDICES = List.of(ACCESSIBLE_BY_AUTHORIZED_USER, ACCESSIBLE_ONLY_BY_SUPER_ADMIN); + static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; + static final String allAccessUser = "admin_all_access"; + static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); + static final String generalErrorMessage = String.format( "no permissions for [] and User [name=%s, backend_roles=[], requestedTenant=null]", allAccessUser ); - private static final String extensionUser = "extensions_user"; - private static final Header extensionUserHeader = encodeBasicHeader(extensionUser, allAccessUser); - private static final String extensionUserC = "extensions_user_c"; - private static final Header extensionUserCHeader = encodeBasicHeader(extensionUserC, allAccessUser); + static final String normalUser = "extensions_user"; + static final Header normalUserHeader = encodeBasicHeader(normalUser, allAccessUser); - private void setupSystemIndicesDisabledWithSsl() throws Exception { + void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionEnabled) throws Exception { Settings systemIndexSettings = Settings.builder() - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, false) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, false) - .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, isSystemIndexEnabled) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, isSystemIndexPermissionEnabled) + .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, SYSTEM_INDICES) .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) @@ -79,32 +71,10 @@ private void setupSystemIndicesDisabledWithSsl() throws Exception { .build(); setup( Settings.EMPTY, - new DynamicSecurityConfig().setConfig("config_system_indices.yml") - .setSecurityRoles("roles_system_indices.yml") - .setSecurityInternalUsers("internal_users_system_indices.yml") - .setSecurityRolesMapping("roles_mapping_system_indices.yml"), - systemIndexSettings, - true - ); - } - - private void setupSystemIndicesEnabledWithSsl() throws Exception { - - Settings systemIndexSettings = Settings.builder() - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, true) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) - .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, listOfIndexesToTest) - .put("plugins.security.ssl.http.enabled", true) - .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) - .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) - .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) - .build(); - setup( - Settings.EMPTY, - new DynamicSecurityConfig().setConfig("config_system_indices.yml") - .setSecurityRoles("roles_system_indices.yml") - .setSecurityInternalUsers("internal_users_system_indices.yml") - .setSecurityRolesMapping("roles_mapping_system_indices.yml"), + new DynamicSecurityConfig().setConfig("system_indices/config.yml") + .setSecurityRoles("system_indices/roles.yml") + .setSecurityInternalUsers("system_indices/internal_users.yml") + .setSecurityRolesMapping("system_indices/roles_mapping.yml"), systemIndexSettings, true ); @@ -113,11 +83,10 @@ private void setupSystemIndicesEnabledWithSsl() throws Exception { /** * Creates a set of test indices and indexes one document into each index. * - * @throws Exception */ - private void createTestIndicesAndDocs() { + void createTestIndicesAndDocs() { try (Client tc = getClient()) { - for (String index : listOfIndexesToTest) { + for (String index : SYSTEM_INDICES) { tc.admin().indices().create(new CreateIndexRequest(index)).actionGet(); tc.index( new IndexRequest(index).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) @@ -128,9 +97,9 @@ private void createTestIndicesAndDocs() { } } - private void createSnapshots() { + void createSnapshots() { try (Client tc = getClient()) { - for (String index : listOfIndexesToTest) { + for (String index : SYSTEM_INDICES) { tc.admin() .cluster() .putRepository( @@ -148,7 +117,7 @@ private void createSnapshots() { } } - private RestHelper keyStoreRestHelper() { + RestHelper superAdminRestHelper() { RestHelper restHelper = restHelper(); restHelper.keystore = "kirk-keystore.jks"; restHelper.enableHTTPClientSSL = true; @@ -157,612 +126,22 @@ private RestHelper keyStoreRestHelper() { return restHelper; } - private RestHelper sslRestHelper() { + RestHelper sslRestHelper() { RestHelper restHelper = restHelper(); restHelper.enableHTTPClientSSL = true; return restHelper; } - /*************************************************************************************************************************** - * Search api tests. Search is a special case. - ***************************************************************************************************************************/ - - private void validateSearchResponse(RestHelper.HttpResponse response, int expectecdHits) throws IOException { + void validateSearchResponse(RestHelper.HttpResponse response, int expectedHits) throws IOException { assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); XContentParser xcp = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); SearchResponse searchResponse = SearchResponse.fromXContent(xcp); assertEquals(RestStatus.OK, searchResponse.status()); - assertEquals(expectecdHits, searchResponse.getHits().getHits().length); + assertEquals(expectedHits, searchResponse.getHits().getHits().length); assertEquals(0, searchResponse.getFailedShards()); assertEquals(5, searchResponse.getSuccessfulShards()); } - @Test - public void testSearchAsSuperAdmin() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper restHelper = keyStoreRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), 1); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - - @Test - public void testSearchAsAdmin() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper restHelper = sslRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader), 1); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - - @Test - public void testSearchWithSystemIndicesAsSuperAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper restHelper = keyStoreRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), 1); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - - @Test - public void testSearchWithSystemIndicesShouldFailAsAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper restHelper = sslRestHelper(); - - // search system indices - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - } - - // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - XContentParser xcp = XContentType.JSON.xContent() - .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, response.getBody()); - SearchResponse searchResponse = SearchResponse.fromXContent(xcp); - assertEquals(RestStatus.OK, searchResponse.status()); - assertEquals(0, searchResponse.getHits().getHits().length); - } - - @Test - public void testSearchInOwnSystemIndicesShouldSucceedAsExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be - // deleted in Z.Y.X see - // http://github/issues/2553 - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper restHelper = sslRestHelper(); - - for (String index : listOfIndexesToTest) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, extensionUserHeader), 0); - } - - } - - @Test - public void testSearchAllWithSystemIndicesShouldFailAsExtensionUser() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper restHelper = sslRestHelper(); - - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, extensionUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"reason\":\"no permissions for [indices:data/read/search] and User [name=extensions_user, backend_roles=[]" - ) - ); - } - - /*************************************************************************************************************************** - * Delete index and Delete doc - ***************************************************************************************************************************/ - - @Test - public void testDeleteShouldSucceedAsSuperAdmin() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); - assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); - - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - } - } - - @Test - public void testDeleteDocShouldSucceedAsAdmin() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper sslRestHelper = sslRestHelper(); - - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); - - RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - } - } - - @Test - public void testDeleteDocWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); - assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); - - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - } - } - - @Test - public void testDeleteDocWithSystemIndicesShouldFailsAsAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - - RestHelper sslRestHelper = sslRestHelper(); - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); - MatcherAssert.assertThat( - responseDoc.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); - - RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseDoc.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); - } - } - - /*************************************************************************************************************************** - * open and close index - ***************************************************************************************************************************/ - - @Test - public void testCloseOpen() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - // as super-admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - - RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); - } - - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); - } - } - - @Test - public void testCloseOpenWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat(responseClose.getBody(), Matchers.containsStringIgnoringCase("{\"closed\":true}")); - - RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); - } - - } - - @Test - public void testCloseOpenIndexShouldFailWithSystemIndicesAsAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper sslRestHelper = sslRestHelper(); - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat( - responseClose.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); - MatcherAssert.assertThat( - responseOpen.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - } - } - - @Test - public void testCloseIndexShouldFailAndOpenIndexShouldSucceedWithSystemIndicesAsExtensionUser() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper sslRestHelper = sslRestHelper(); - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", extensionUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", extensionUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); - } - } - - /*************************************************************************************************************************** - * Update Index Settings - ***************************************************************************************************************************/ - - @Test - public void testUpdateIndexSettingsWithNormalIndicesShouldSucceed() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; - - // as super-admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - } - - @Test - public void testUpdateIndexSettingsWithSystemIndicesShouldFailAsAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; - - // as super-admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"" - ) - ); - - } - } - - /*************************************************************************************************************************** - * Index mappings. indices:admin/mapping/put - ************************************************************************************************************************** */ - - @Test - public void testUpdateMappingsWithNormalIndicesShouldSucceed() throws Exception { - setupSystemIndicesDisabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; - - // as super-admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - - } - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - } - - @Test - public void testUpdateMappingsWithSystemIndicesShouldFailAsAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; - - // as super-admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - - } - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); - } - } - - /*************************************************************************************************************************** - * Create index and Create doc - ***************************************************************************************************************************/ - - @Test - public void testCreateIndexWithNormalIndicesShouldSucceed() throws Exception { - setupSystemIndicesDisabledWithSsl(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; - - // as super-admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } - - for (String index : listOfIndexesToTest) { - keyStoreRestHelper.executeDeleteRequest(index); - } - - // as admin - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } - } - - @Test - public void testCreateIndexWithSystemIndicesShouldSucceedAsSuperAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - RestHelper keyStoreRestHelper = keyStoreRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"result\":\"created\"")); - - } - - } - - @Test - public void testCreateIndexWithSystemIndicesShouldFailAsAdmin() throws Exception { - setupSystemIndicesEnabledWithSsl(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; - - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); - - } - } - - @Test - public void testCreateIndexWithSystemIndicesShouldSucceedWithExtensionRole() throws Exception { - setupSystemIndicesEnabledWithSsl(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - - + "}"; - for (String index : listOfIndexesToTest) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, extensionUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", extensionUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } - } - - /*************************************************************************************************************************** - * snapshot : since snapshot takes more time, we are testing only Enabled case. - ***************************************************************************************************************************/ - @Test - public void testSnapshotWithSystemIndices() throws Exception { - setupSystemIndicesEnabledWithSsl(); - createTestIndicesAndDocs(); - createSnapshots(); - - try (Client tc = getClient()) { - for (String index : listOfIndexesToTest) { - tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); - } - } - - RestHelper sslRestHelper = sslRestHelper(); - // as admin - for (String index : listOfIndexesToTest) { - assertEquals( - HttpStatus.SC_UNAUTHORIZED, - sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode() - ); - assertEquals( - HttpStatus.SC_FORBIDDEN, - sslRestHelper.executePostRequest( - "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", - "", - allAccessUserHeader - ).getStatusCode() - ); - assertEquals( - HttpStatus.SC_OK, - sslRestHelper.executePostRequest( - "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", - "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", - allAccessUserHeader - ).getStatusCode() - ); - } - } - - @Test - public void testExtensionIndexAccessShouldSucceedForExtensionUser() throws Exception { // CS-SUPRESS-ALL: Legacy code to be deleted - // in Z.Y.X see http://github/issues/1234 - setupSystemIndicesEnabledWithSsl(); - RestHelper sslRestHelper = sslRestHelper(); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest( - ".system_index_a" + "/_doc", - "{\"foo\": \"bar\"}", - extensionUserHeader - ); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - - response = sslRestHelper.executeGetRequest(".system_index_a", extensionUserHeader); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"version\":{\"created\"")); - } - - @Test - public void testExtensionIndexAccessShouldFailAsDifferentExtensionUser() throws Exception { - setupSystemIndicesEnabledWithSsl(); - RestHelper sslRestHelper = sslRestHelper(); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest( - ".system_index_a" + "/_doc", - "{\"foo\": \"bar\"}", - extensionUserCHeader - ); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - - response = sslRestHelper.executeGetRequest(".system_index_a", extensionUserCHeader); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "{\"error\":{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [indices:admin/get] and User [name=extensions_user_c, backend_roles=[], requestedTenant=null]\"}]" - ) - ); - } - - // CS-SUPPRESS-SINGLE - } diff --git a/src/test/resources/config_system_indices.yml b/src/test/resources/system_indices/config.yml similarity index 100% rename from src/test/resources/config_system_indices.yml rename to src/test/resources/system_indices/config.yml diff --git a/src/test/resources/internal_users_system_indices.yml b/src/test/resources/system_indices/internal_users.yml similarity index 67% rename from src/test/resources/internal_users_system_indices.yml rename to src/test/resources/system_indices/internal_users.yml index 2246e03457..0d5f13baba 100644 --- a/src/test/resources/internal_users_system_indices.yml +++ b/src/test/resources/system_indices/internal_users.yml @@ -10,15 +10,7 @@ admin_all_access: attributes: {} description: "User mapped to all cluster and index permissions but no role to view protected index" -extensions_user: - hash: "$2y$12$ft8tXtxb.dyO/5MrDXHLc.e1o3dktEQJMvR2e.sgVDyD/gR7G9dLS" - reserved: false - hidden: false - backend_roles: [] - attributes: {} - description: "User Mapped to role With Access to Admin:System:Indices " - -extensions_user_c: +normal_user: hash: "$2y$12$ft8tXtxb.dyO/5MrDXHLc.e1o3dktEQJMvR2e.sgVDyD/gR7G9dLS" reserved: false hidden: false diff --git a/src/test/resources/roles_system_indices.yml b/src/test/resources/system_indices/roles.yml similarity index 59% rename from src/test/resources/roles_system_indices.yml rename to src/test/resources/system_indices/roles.yml index 20f7a8500f..820146910f 100644 --- a/src/test/resources/roles_system_indices.yml +++ b/src/test/resources/system_indices/roles.yml @@ -13,8 +13,8 @@ opendistro_security_all_access: allowed_actions: - '*' -extensions_role: - description: "ExtensionsRole" +normal_role: + description: "Normal user Role" cluster_permissions: - '*' index_permissions: @@ -23,14 +23,3 @@ extensions_role: allowed_actions: - '*' - 'system:admin/system_index' - -extensions_role_c: - description: "Limited ExtensionsRole" - cluster_permissions: - - '*' - index_permissions: - - index_patterns: - - 'system_index_b' - allowed_actions: - - '*' - - 'system:admin/system_index' diff --git a/src/test/resources/roles_mapping_system_indices.yml b/src/test/resources/system_indices/roles_mapping.yml similarity index 56% rename from src/test/resources/roles_mapping_system_indices.yml rename to src/test/resources/system_indices/roles_mapping.yml index 5889d84c49..0e9114c3dd 100644 --- a/src/test/resources/roles_mapping_system_indices.yml +++ b/src/test/resources/system_indices/roles_mapping.yml @@ -13,22 +13,12 @@ opendistro_security_all_access: and_backend_roles: [] description: "Full index permissions" -extensions_role: +normal_role: reserved: false hidden: false backend_roles: [] hosts: [] users: - - "extensions_user" + - "normal_user" and_backend_roles: [] - description: "System index permissions" - -extensions_role_c: - reserved: false - hidden: false - backend_roles: [] - hosts: [] - users: - - "extensions_user_c" - and_backend_roles: [] - description: "System index permissions" + description: "System index permissions" From 5c099e23c5926fde1978bdcbc7ab723a58517b2e Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 11:56:17 -0400 Subject: [PATCH 30/46] Adds tests for system index disabled scenario Signed-off-by: Darshit Chanpura --- ...s.java => AbstractSystemIndicesTests.java} | 49 +++- .../SystemIndexDisabledTests.java | 259 +++++++----------- 2 files changed, 146 insertions(+), 162 deletions(-) rename src/test/java/org/opensearch/security/system_indices/{SystemIndicesTests.java => AbstractSystemIndicesTests.java} (74%) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java similarity index 74% rename from src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java rename to src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java index 0c47edd5a7..3ab210446f 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java @@ -16,6 +16,8 @@ import org.apache.hc.core5.http.Header; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; import org.opensearch.action.admin.indices.create.CreateIndexRequest; @@ -42,11 +44,12 @@ * "plugins.security.system_indices.indices"; */ -public class SystemIndicesTests extends SingleClusterTest { +public abstract class AbstractSystemIndicesTests extends SingleClusterTest { static final String ACCESSIBLE_ONLY_BY_SUPER_ADMIN = ".opendistro_security"; - static final String ACCESSIBLE_BY_AUTHORIZED_USER = ".opendistro-alerting"; - static final List SYSTEM_INDICES = List.of(ACCESSIBLE_BY_AUTHORIZED_USER, ACCESSIBLE_ONLY_BY_SUPER_ADMIN); + static final List SYSTEM_INDICES = List.of(".system_index_1", ACCESSIBLE_ONLY_BY_SUPER_ADMIN); + + static final List INDICES_FOR_CREATE_REQUEST = List.of(".system_index_2"); static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; static final String allAccessUser = "admin_all_access"; static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); @@ -55,7 +58,19 @@ public class SystemIndicesTests extends SingleClusterTest { allAccessUser ); - static final String normalUser = "extensions_user"; + static final String createIndexSettings = "{\n" + + " \"settings\" : {\n" + + " \"index\" : {\n" + + " \"number_of_shards\" : 3, \n" + + " \"number_of_replicas\" : 2 \n" + + " }\n" + + " }\n" + + + "}"; + static final String updateIndexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + static final String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + + static final String normalUser = "normal_user"; static final Header normalUserHeader = encodeBasicHeader(normalUser, allAccessUser); void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionEnabled) throws Exception { @@ -87,7 +102,10 @@ void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionE void createTestIndicesAndDocs() { try (Client tc = getClient()) { for (String index : SYSTEM_INDICES) { - tc.admin().indices().create(new CreateIndexRequest(index)).actionGet(); + // security index is already created + if (!index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + tc.admin().indices().create(new CreateIndexRequest(index)).actionGet(); + } tc.index( new IndexRequest(index).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .id("document1") @@ -144,4 +162,25 @@ void validateSearchResponse(RestHelper.HttpResponse response, int expectedHits) assertEquals(5, searchResponse.getSuccessfulShards()); } + String permissionExceptionMessage(String action, String username) { + return "{\"type\":\"security_exception\",\"reason\":\"no permissions for [" + + action + + "] and User [name=" + + username + + ", backend_roles=[], requestedTenant=null]\"}"; + } + + void validateForbiddenResponse(RestHelper.HttpResponse response, String action, String user) { + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(permissionExceptionMessage(action, user))); + } + + void allowedExceptSecurityIndex(String index, RestHelper.HttpResponse response, String action, String user) { + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(response, action, user); + } else { + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } + } + } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java index 3c9aff1285..22baecf354 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -11,16 +11,16 @@ package org.opensearch.security.system_indices; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.opensearch.core.rest.RestStatus; import org.opensearch.security.test.helper.rest.RestHelper; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; -public class SystemIndexDisabledTests extends SystemIndicesTests { +public class SystemIndexDisabledTests extends AbstractSystemIndicesTests { @Before public void setup() throws Exception { @@ -28,13 +28,17 @@ public void setup() throws Exception { createTestIndicesAndDocs(); } + /** + * SEARCH + */ @Test public void testSearchAsSuperAdmin() throws Exception { RestHelper restHelper = superAdminRestHelper(); // search system indices - for (String idx : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(idx + "/_search", matchAllQuery), 1); + for (String index : SYSTEM_INDICES) { + int expectedHits = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? 10 : 1; + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), expectedHits); } // search all indices @@ -48,265 +52,206 @@ public void testSearchAsAdmin() throws Exception { // search system indices for (String index : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader), 1); + // security index remains accessible only by super-admin + int expectedHits = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? 0 : 1; + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader), expectedHits); } // search all indices RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + assertTrue(response.getBody().contains(SYSTEM_INDICES.get(0))); + assertFalse(response.getBody().contains(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)); } @Test public void testSearchAsNormalUser() throws Exception { RestHelper restHelper = sslRestHelper(); + // search system indices for (String index : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, normalUserHeader), 0); + // security index is only accessible by super-admin + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserHeader); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(response, "indices:data/read/search", normalUser); + } else { + validateSearchResponse(response, 1); + } } + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", normalUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + validateForbiddenResponse(response, "indices:data/read/search", normalUser); } + /** + * DELETE document + index + */ @Test public void testDeleteAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper restHelper = superAdminRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); + RestHelper.HttpResponse responseDoc = restHelper.executeDeleteRequest(index + "/_doc/document1"); assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); + RestHelper.HttpResponse responseIndex = restHelper.executeDeleteRequest(index); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); } } @Test public void testDeleteAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); - // as admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); + allowedExceptSecurityIndex(index, response, "", allAccessUser); - RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + response = restHelper.executeDeleteRequest(index, allAccessUserHeader); + allowedExceptSecurityIndex(index, response, "", allAccessUser); } } @Test - public void testCloseOpenAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); + public void testDeleteAsNormalUser() { + RestHelper restHelper = sslRestHelper(); - // as super-admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); - RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + response = restHelper.executeDeleteRequest(index, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); } + } + + /** + * CLOSE-OPEN + */ + @Test + public void testCloseOpenAsSuperAdmin() { + RestHelper restHelper = superAdminRestHelper(); - // as admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + RestHelper.HttpResponse responseClose = restHelper.executePostRequest(index + "/_close", ""); assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + RestHelper.HttpResponse responseOpen = restHelper.executePostRequest(index + "/_open", ""); assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); } } @Test public void testCloseOpenAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat( - responseClose.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); - MatcherAssert.assertThat( - responseOpen.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + allowedExceptSecurityIndex(index, response, "", allAccessUser); + // admin can open security index but cannot close it + response = restHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } } @Test public void testCloseOpenAsNormalUser() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + // normal user cannot open or close security index + response = restHelper.executePostRequest(index + "/_open", "", normalUserHeader); + allowedExceptSecurityIndex(index, response, "indices:admin/open", normalUser); } } + /** + * CREATE + */ @Test public void testCreateIndexAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; - - // as super-admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } - - for (String index : SYSTEM_INDICES) { - keyStoreRestHelper.executeDeleteRequest(index); - } + RestHelper restHelper = superAdminRestHelper(); - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @Test public void testCreateIndexAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; + RestHelper restHelper = sslRestHelper(); - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @Test public void testCreateIndexAsNormalUser() { - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" + RestHelper restHelper = sslRestHelper(); - + "}"; - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, normalUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } + /** + * UPDATE settings + mappings + */ @Test public void testUpdateAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + RestHelper restHelper = superAdminRestHelper(); - // as super-admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); + + response = restHelper.executePutRequest(index + "/_mapping", newMappings); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } } @Test - public void testUpdateMappingsAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + public void testUpdateMappingsAsAdmin() { + RestHelper restHelper = sslRestHelper(); - // as super-admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + allowedExceptSecurityIndex(index, response, "", allAccessUser); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, allAccessUserHeader); + allowedExceptSecurityIndex(index, response, "", allAccessUser); } } @Test - public void testUpdateMappingsAsAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + public void testUpdateAsNormalUser() { + RestHelper restHelper = sslRestHelper(); - // as super-admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); } } } From cb2a6669838e6a98e0582487f14c4a8986b0f8d6 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 13:01:40 -0400 Subject: [PATCH 31/46] Adds tests for scneario when system index is enabled and system index permissions are disabled Signed-off-by: Darshit Chanpura --- .../SystemIndexDisabledTests.java | 3 + .../SystemIndexPermissionDisabledTests.java | 279 ++++++++---------- 2 files changed, 121 insertions(+), 161 deletions(-) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java index 22baecf354..10d128cf71 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -20,6 +20,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +/** + * Adds test for scenario when system index feature is disabled + */ public class SystemIndexDisabledTests extends AbstractSystemIndicesTests { @Before diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java index 6487bddf9c..becff1f67b 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java @@ -11,16 +11,18 @@ package org.opensearch.security.system_indices; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.opensearch.core.rest.RestStatus; import org.opensearch.security.test.helper.rest.RestHelper; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; -public class SystemIndexPermissionDisabledTests extends SystemIndicesTests { +/** + * Adds test for scenario when system index feature is enabled, but system index permission feature is disabled + */ +public class SystemIndexPermissionDisabledTests extends AbstractSystemIndicesTests { @Before public void setup() throws Exception { @@ -28,13 +30,17 @@ public void setup() throws Exception { createTestIndicesAndDocs(); } + /** + * SEARCH + */ @Test public void testSearchAsSuperAdmin() throws Exception { RestHelper restHelper = superAdminRestHelper(); // search system indices - for (String idx : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(idx + "/_search", matchAllQuery), 10); + for (String index : SYSTEM_INDICES) { + int expectedHits = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? 10 : 1; + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), expectedHits); } // search all indices @@ -43,261 +49,212 @@ public void testSearchAsSuperAdmin() throws Exception { } @Test - public void testSearchAsAdmin() { + public void testSearchAsAdmin() throws Exception { RestHelper restHelper = sslRestHelper(); // search system indices for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"failed\":0")); + // no system indices are searchable by admin + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader), 0); } // search all indices RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + assertFalse(response.getBody().contains(SYSTEM_INDICES.get(0))); + assertFalse(response.getBody().contains(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)); } @Test public void testSearchAsNormalUser() throws Exception { RestHelper restHelper = sslRestHelper(); + // search system indices for (String index : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, normalUserHeader), 0); + // security index is only accessible by super-admin + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserHeader); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(response, "indices:data/read/search", normalUser); + } else { + validateSearchResponse(response, 0); + } } + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", normalUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + validateForbiddenResponse(response, "indices:data/read/search", normalUser); } + /** + * DELETE document + index + */ @Test public void testDeleteAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper restHelper = superAdminRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); + RestHelper.HttpResponse responseDoc = restHelper.executeDeleteRequest(index + "/_doc/document1"); assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); + RestHelper.HttpResponse responseIndex = restHelper.executeDeleteRequest(index); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); } } @Test public void testDeleteAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); + + response = restHelper.executeDeleteRequest(index, allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); + } + } + + @Test + public void testDeleteAsNormalUser() { + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); - MatcherAssert.assertThat( - responseDoc.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); - - RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseDoc.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", normalUserHeader); + validateForbiddenResponse(response, "", normalUser); + + response = restHelper.executeDeleteRequest(index, normalUserHeader); + validateForbiddenResponse(response, "", normalUser); } } + /** + * CLOSE-OPEN + */ @Test public void testCloseOpenAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper restHelper = superAdminRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); + RestHelper.HttpResponse responseClose = restHelper.executePostRequest(index + "/_close", ""); assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat(responseClose.getBody(), Matchers.containsStringIgnoringCase("{\"closed\":true}")); - RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); + RestHelper.HttpResponse responseOpen = restHelper.executePostRequest(index + "/_open", ""); assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); } - } @Test public void testCloseOpenAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat( - responseClose.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); - MatcherAssert.assertThat( - responseOpen.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); + + // admin cannot close any system index but can open them + response = restHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } } @Test public void testCloseOpenAsNormalUser() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", normalUserHeader); + validateForbiddenResponse(response, "", normalUser); - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + // normal user cannot open or close security index + response = restHelper.executePostRequest(index + "/_open", "", normalUserHeader); + allowedExceptSecurityIndex(index, response, "indices:admin/open", normalUser); } } + /** + * CREATE + * + * should be allowed as any user + */ @Test - public void testUpdateAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); + public void testCreateIndexAsSuperAdmin() { + RestHelper restHelper = superAdminRestHelper(); - String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - // as super-admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @Test - public void testUpdateMappingsAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + public void testCreateIndexAsAdmin() { + RestHelper restHelper = sslRestHelper(); - // as super-admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @Test - public void testUpdateMappingsAsAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + public void testCreateIndexAsNormalUser() { + RestHelper restHelper = sslRestHelper(); - // as super-admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, normalUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); + response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } + /** + * UPDATE settings + mappings + */ @Test - public void testCreateIndexAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; + public void testUpdateAsSuperAdmin() { + RestHelper restHelper = superAdminRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executePutRequest(index, indexSettings); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = keyStoreRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase("\"result\":\"created\"")); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + response = restHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } - } @Test - public void testCreateIndexAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; + public void testUpdateMappingsAsAdmin() { + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); } } @Test - public void testCreateIndexAsNormalUser() { - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" + public void testUpdateAsNormalUser() { + RestHelper restHelper = sslRestHelper(); - + "}"; for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); + validateForbiddenResponse(response, "", normalUser); - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); + validateForbiddenResponse(response, "", normalUser); } } } From 0632ecdc0ffb0c59af79a74f684c75790bdf144a Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 14:10:35 -0400 Subject: [PATCH 32/46] Adds tests for scneario when system index is enabled and system index permissions are also enabled Signed-off-by: Darshit Chanpura --- .../AbstractSystemIndicesTests.java | 10 +- .../SystemIndexPermissionEnabledTests.java | 372 +++++++++--------- 2 files changed, 194 insertions(+), 188 deletions(-) diff --git a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java index 3ab210446f..d62c192400 100644 --- a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java @@ -53,10 +53,9 @@ public abstract class AbstractSystemIndicesTests extends SingleClusterTest { static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; static final String allAccessUser = "admin_all_access"; static final Header allAccessUserHeader = encodeBasicHeader(allAccessUser, allAccessUser); - static final String generalErrorMessage = String.format( - "no permissions for [] and User [name=%s, backend_roles=[], requestedTenant=null]", - allAccessUser - ); + + static final String normalUser = "normal_user"; + static final Header normalUserHeader = encodeBasicHeader(normalUser, normalUser); static final String createIndexSettings = "{\n" + " \"settings\" : {\n" @@ -70,9 +69,6 @@ public abstract class AbstractSystemIndicesTests extends SingleClusterTest { static final String updateIndexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; static final String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; - static final String normalUser = "normal_user"; - static final Header normalUserHeader = encodeBasicHeader(normalUser, allAccessUser); - void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionEnabled) throws Exception { Settings systemIndexSettings = Settings.builder() diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java index 8313be71c2..525c17c154 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java @@ -12,8 +12,6 @@ package org.opensearch.security.system_indices; import org.apache.hc.core5.http.HttpStatus; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.opensearch.action.admin.indices.close.CloseIndexRequest; @@ -22,8 +20,9 @@ import org.opensearch.security.test.helper.rest.RestHelper; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; -public class SystemIndexPermissionEnabledTests extends SystemIndicesTests { +public class SystemIndexPermissionEnabledTests extends AbstractSystemIndicesTests { @Before public void setup() throws Exception { @@ -31,13 +30,17 @@ public void setup() throws Exception { createTestIndicesAndDocs(); } + /** + * SEARCH + */ @Test - public void testAccessAsSuperAdmin() throws Exception { + public void testSearchAsSuperAdmin() throws Exception { RestHelper restHelper = superAdminRestHelper(); // search system indices - for (String idx : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(idx + "/_search", matchAllQuery), 10); + for (String index : SYSTEM_INDICES) { + int expectedHits = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? 10 : 1; + validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery), expectedHits); } // search all indices @@ -52,289 +55,230 @@ public void testSearchAsAdmin() { // search system indices for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", matchAllQuery, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); + // no system indices are searchable by admin + validateForbiddenResponse(response, "", allAccessUser); } // search all indices RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", matchAllQuery, allAccessUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + assertFalse(response.getBody().contains(SYSTEM_INDICES.get(0))); + assertFalse(response.getBody().contains(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)); } @Test public void testSearchAsNormalUser() throws Exception { RestHelper restHelper = sslRestHelper(); + // search system indices for (String index : SYSTEM_INDICES) { - validateSearchResponse(restHelper.executePostRequest(index + "/_search", matchAllQuery, normalUserHeader), 0); + // security index is only accessible by super-admin + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserHeader); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(response, "", normalUser); + } else { + validateSearchResponse(response, 0); + } } + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", normalUserHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + validateForbiddenResponse(response, "indices:data/read/search", normalUser); } - /*************************************************************************************************************************** - * Delete index and Delete doc - ***************************************************************************************************************************/ - + /** + * DELETE document + index + */ @Test public void testDeleteAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper restHelper = superAdminRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseDoc = keyStoreRestHelper.executeDeleteRequest(index + "/_doc/document1"); + RestHelper.HttpResponse responseDoc = restHelper.executeDeleteRequest(index + "/_doc/document1"); assertEquals(RestStatus.OK.getStatus(), responseDoc.getStatusCode()); - RestHelper.HttpResponse responseIndex = keyStoreRestHelper.executeDeleteRequest(index); + RestHelper.HttpResponse responseIndex = restHelper.executeDeleteRequest(index); assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); } } @Test public void testDeleteAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseDoc = sslRestHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseDoc.getStatusCode()); - MatcherAssert.assertThat( - responseDoc.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); - RestHelper.HttpResponse responseIndex = sslRestHelper.executeDeleteRequest(index, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseDoc.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); + response = restHelper.executeDeleteRequest(index, allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); } } - /*************************************************************************************************************************** - * open and close index - ***************************************************************************************************************************/ + @Test + public void testDeleteAsNormalUser() { + RestHelper restHelper = sslRestHelper(); + // allows interacting with the index it has access to: `.system_index_1` with `.system*` pattern and `system:admin/system_index` + // permission + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); + + response = restHelper.executeDeleteRequest(index, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); + } + } + + /** + * CLOSE-OPEN + */ @Test public void testCloseOpenAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); + RestHelper restHelper = superAdminRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = keyStoreRestHelper.executePostRequest(index + "/_close", ""); + RestHelper.HttpResponse responseClose = restHelper.executePostRequest(index + "/_close", ""); assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat(responseClose.getBody(), Matchers.containsStringIgnoringCase("{\"closed\":true}")); - RestHelper.HttpResponse responseOpen = keyStoreRestHelper.executePostRequest(index + "/_open", ""); + RestHelper.HttpResponse responseOpen = restHelper.executePostRequest(index + "/_open", ""); assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); } - } @Test public void testCloseOpenAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); + // admin cannot close or open any system index when system index permissions are enabled for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseClose.getStatusCode()); - MatcherAssert.assertThat( - responseClose.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseOpen.getStatusCode()); - MatcherAssert.assertThat( - responseOpen.getBody(), - Matchers.containsStringIgnoringCase( - "{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); + response = restHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); } } @Test public void testCloseOpenAsNormalUser() { - RestHelper sslRestHelper = sslRestHelper(); + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseClose = sslRestHelper.executePostRequest(index + "/_close", "", normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseClose.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); - RestHelper.HttpResponse responseOpen = sslRestHelper.executePostRequest(index + "/_open", "", normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseOpen.getStatusCode()); + // normal user cannot open or close security index + response = restHelper.executePostRequest(index + "/_open", "", normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); } } - /*************************************************************************************************************************** - * Update Index Settings - ***************************************************************************************************************************/ - + /** + * CREATE + * should be allowed as any user + */ @Test - public void testUpdateAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); + public void testCreateIndexAsSuperAdmin() { + RestHelper restHelper = superAdminRestHelper(); - String indexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - // as super-admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_settings", indexSettings); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}"); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_settings", indexSettings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - response.getBody(), - Matchers.containsStringIgnoringCase( - "\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"" - ) - ); + } + @Test + public void testCreateIndexAsAdmin() { + RestHelper restHelper = sslRestHelper(); + + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings, allAccessUserHeader); + assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } - /*************************************************************************************************************************** - * Index mappings. indices:admin/mapping/put - ************************************************************************************************************************** */ - @Test - public void testUpdateMappingsAsSuperAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + public void testCreateIndexAsNormalUser() { + RestHelper restHelper = sslRestHelper(); - // as super-admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + for (String index : INDICES_FOR_CREATE_REQUEST) { + RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, normalUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } + /** + * UPDATE settings + mappings + */ @Test - public void testUpdateMappingsAsAdmin() { - RestHelper keyStoreRestHelper = superAdminRestHelper(); - RestHelper sslRestHelper = sslRestHelper(); - - String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + public void testUpdateAsSuperAdmin() { + RestHelper restHelper = superAdminRestHelper(); - // as super-admin for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = keyStoreRestHelper.executePutRequest(index + "/_mapping", newMappings); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - } - // as admin - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = sslRestHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(generalErrorMessage)); + response = restHelper.executePutRequest(index + "/_mapping", newMappings); + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } } - /*************************************************************************************************************************** - * Create index and Create doc - ***************************************************************************************************************************/ - @Test - public void testCreateIndexAsAdmin() { - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" - + "}"; + public void testUpdateMappingsAsAdmin() { + RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), responseIndex.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}" - ) - ); - - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - MatcherAssert.assertThat( - responseIndex.getBody(), - Matchers.containsStringIgnoringCase( - "{\"root_cause\":[{\"type\":\"security_exception\",\"reason\":\"no permissions for [] and User [name=admin_all_access, backend_roles=[], requestedTenant=null]\"}]" - ) - ); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, allAccessUserHeader); + validateForbiddenResponse(response, "", allAccessUser); } } @Test - public void testCreateIndexAsNormalUser() { - RestHelper sslRestHelper = sslRestHelper(); - - String indexSettings = "{\n" - + " \"settings\" : {\n" - + " \"index\" : {\n" - + " \"number_of_shards\" : 3, \n" - + " \"number_of_replicas\" : 2 \n" - + " }\n" - + " }\n" + public void testUpdateAsNormalUser() { + RestHelper restHelper = sslRestHelper(); - + "}"; for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse responseIndex = sslRestHelper.executePutRequest(index, indexSettings, normalUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); - RestHelper.HttpResponse response = sslRestHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); } } - /*************************************************************************************************************************** - * snapshot : since snapshot takes more time, we are testing only Enabled case. - ***************************************************************************************************************************/ + /** + * SNAPSHOT get + restore + */ @Test - public void testSnapshotWithSystemIndices() { + public void testSnapshotSystemIndicesAsSuperAdmin() { createSnapshots(); + RestHelper restHelper = superAdminRestHelper(); try (Client tc = getClient()) { for (String index : SYSTEM_INDICES) { tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); } } - RestHelper sslRestHelper = sslRestHelper(); - // as admin for (String index : SYSTEM_INDICES) { + assertEquals(HttpStatus.SC_OK, restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode()); assertEquals( - HttpStatus.SC_UNAUTHORIZED, - sslRestHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode() - ); - assertEquals( - HttpStatus.SC_FORBIDDEN, - sslRestHelper.executePostRequest( + HttpStatus.SC_OK, + restHelper.executePostRequest( "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", "", allAccessUserHeader @@ -342,7 +286,7 @@ public void testSnapshotWithSystemIndices() { ); assertEquals( HttpStatus.SC_OK, - sslRestHelper.executePostRequest( + restHelper.executePostRequest( "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", allAccessUserHeader @@ -350,4 +294,70 @@ public void testSnapshotWithSystemIndices() { ); } } + + @Test + public void testSnapshotSystemIndicesAsAdmin() { + createSnapshots(); + + RestHelper restHelper = sslRestHelper(); + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse res = restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1"); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + allAccessUserHeader + ); + validateForbiddenResponse(res, "", allAccessUser); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ); + allowedExceptSecurityIndex(index, res, "", allAccessUser); + } + } + + @Test + public void testSnapshotSystemIndicesAsNormalUser() { + createSnapshots(); + + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + RestHelper restHelper = sslRestHelper(); + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse res = restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1"); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + normalUserHeader + ); + allowedExceptSecurityIndex(index, res, "", normalUser); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + normalUserHeader + ); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(res, "", normalUser); + } else { + validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUser); + } + } + } } From 12100249c84bec9d4ae09603de0f4e6a7f6e41d4 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 14:11:25 -0400 Subject: [PATCH 33/46] Adds snapshot restore tests for remaining scenarios Signed-off-by: Darshit Chanpura --- .../SystemIndexDisabledTests.java | 98 ++++++++++++++++ .../SystemIndexPermissionDisabledTests.java | 105 ++++++++++++++++++ .../system_indices/internal_users.yml | 2 +- 3 files changed, 204 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java index 10d128cf71..e2817c3314 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -11,8 +11,11 @@ package org.opensearch.security.system_indices; +import org.apache.hc.core5.http.HttpStatus; import org.junit.Before; import org.junit.Test; +import org.opensearch.action.admin.indices.close.CloseIndexRequest; +import org.opensearch.client.Client; import org.opensearch.core.rest.RestStatus; import org.opensearch.security.test.helper.rest.RestHelper; @@ -257,4 +260,99 @@ public void testUpdateAsNormalUser() { allowedExceptSecurityIndex(index, response, "", normalUser); } } + + /** + * SNAPSHOT get + restore + */ + @Test + public void testSnapshotSystemIndicesAsSuperAdmin() { + createSnapshots(); + + RestHelper restHelper = superAdminRestHelper(); + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + for (String index : SYSTEM_INDICES) { + assertEquals(HttpStatus.SC_OK, restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode()); + assertEquals( + HttpStatus.SC_OK, + restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + allAccessUserHeader + ).getStatusCode() + ); + assertEquals( + HttpStatus.SC_OK, + restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ).getStatusCode() + ); + } + } + + @Test + public void testSnapshotSystemIndicesAsAdmin() { + createSnapshots(); + + RestHelper restHelper = sslRestHelper(); + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + for (String index : SYSTEM_INDICES) { + String snapshotRequest = "_snapshot/" + index + "/" + index + "_1"; + RestHelper.HttpResponse res = restHelper.executeGetRequest(snapshotRequest); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest(snapshotRequest + "/_restore?wait_for_completion=true", "", allAccessUserHeader); + allowedExceptSecurityIndex(index, res, "", allAccessUser); + + res = restHelper.executePostRequest( + snapshotRequest + "/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ); + allowedExceptSecurityIndex(index, res, "", allAccessUser); + } + } + + @Test + public void testSnapshotSystemIndicesAsNormalUser() { + createSnapshots(); + + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + RestHelper restHelper = sslRestHelper(); + for (String index : SYSTEM_INDICES) { + String snapshotRequest = "_snapshot/" + index + "/" + index + "_1"; + RestHelper.HttpResponse res = restHelper.executeGetRequest(snapshotRequest); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest(snapshotRequest + "/_restore?wait_for_completion=true", "", normalUserHeader); + allowedExceptSecurityIndex(index, res, "", normalUser); + + res = restHelper.executePostRequest( + snapshotRequest + "/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + normalUserHeader + ); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(res, "", normalUser); + } else { + validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUser); + } + } + } } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java index becff1f67b..9cb505be6d 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java @@ -11,8 +11,11 @@ package org.opensearch.security.system_indices; +import org.apache.hc.core5.http.HttpStatus; import org.junit.Before; import org.junit.Test; +import org.opensearch.action.admin.indices.close.CloseIndexRequest; +import org.opensearch.client.Client; import org.opensearch.core.rest.RestStatus; import org.opensearch.security.test.helper.rest.RestHelper; @@ -76,6 +79,7 @@ public void testSearchAsNormalUser() throws Exception { if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { validateForbiddenResponse(response, "indices:data/read/search", normalUser); } else { + // got 0 hits because system index permissions are not enabled validateSearchResponse(response, 0); } } @@ -257,4 +261,105 @@ public void testUpdateAsNormalUser() { validateForbiddenResponse(response, "", normalUser); } } + + /** + * SNAPSHOT get + restore + */ + @Test + public void testSnapshotSystemIndicesAsSuperAdmin() { + createSnapshots(); + + RestHelper restHelper = superAdminRestHelper(); + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + for (String index : SYSTEM_INDICES) { + assertEquals(HttpStatus.SC_OK, restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1").getStatusCode()); + assertEquals( + HttpStatus.SC_OK, + restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + allAccessUserHeader + ).getStatusCode() + ); + assertEquals( + HttpStatus.SC_OK, + restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ).getStatusCode() + ); + } + } + + @Test + public void testSnapshotSystemIndicesAsAdmin() { + createSnapshots(); + + RestHelper restHelper = sslRestHelper(); + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse res = restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1"); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + allAccessUserHeader + ); + validateForbiddenResponse(res, "", allAccessUser); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + allAccessUserHeader + ); + allowedExceptSecurityIndex(index, res, "", allAccessUser); + } + } + + @Test + public void testSnapshotSystemIndicesAsNormalUser() { + createSnapshots(); + + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + RestHelper restHelper = sslRestHelper(); + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse res = restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1"); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + normalUserHeader + ); + validateForbiddenResponse(res, "", normalUser); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + normalUserHeader + ); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(res, "", normalUser); + } else { + validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUser); + } + } + } } diff --git a/src/test/resources/system_indices/internal_users.yml b/src/test/resources/system_indices/internal_users.yml index 0d5f13baba..7a527fb79d 100644 --- a/src/test/resources/system_indices/internal_users.yml +++ b/src/test/resources/system_indices/internal_users.yml @@ -11,7 +11,7 @@ admin_all_access: description: "User mapped to all cluster and index permissions but no role to view protected index" normal_user: - hash: "$2y$12$ft8tXtxb.dyO/5MrDXHLc.e1o3dktEQJMvR2e.sgVDyD/gR7G9dLS" + hash: "$2y$12$d1ONiqarfTF9xOuqKeNukeze1bVBXC1FyXJSpuC3B6/Ekbfu3ULHm" reserved: false hidden: false backend_roles: [] From 20da0b56aa144727753d38f89eeabe209c119482 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 14:12:33 -0400 Subject: [PATCH 34/46] Fixes typo in Dev guide Signed-off-by: Darshit Chanpura --- DEVELOPER_GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index cc95833a86..256a2f64fc 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -268,7 +268,7 @@ Checkstyle enforces several rules within this codebase. Sometimes it will be nec *Suppression All Checkstyle Rules* ``` - // CS-SUPRESS-ALL: Legacy code to be deleted in Z.Y.X see http://github/issues/1234 + // CS-SUPPRESS-ALL: Legacy code to be deleted in Z.Y.X see http://github/issues/1234 ... // CS-ENFORCE-ALL ``` From 58666fe60190668e165b6a048b607b1b1481bb59 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 18:41:42 -0400 Subject: [PATCH 35/46] Adds more tests for user with no system index access and renames the feature flag to be more intuitive Signed-off-by: Darshit Chanpura --- .../security/OpenSearchSecurityPlugin.java | 4 +- .../SecurityIndexAccessEvaluator.java | 10 +- .../security/support/ConfigConstants.java | 5 +- .../SecurityIndexAccessEvaluatorTest.java | 4 +- .../AbstractSystemIndicesTests.java | 5 +- .../SystemIndexDisabledTests.java | 137 ++++++++++------ .../SystemIndexPermissionDisabledTests.java | 138 +++++++++------- .../SystemIndexPermissionEnabledTests.java | 152 ++++++++++++++---- .../system_indices/internal_users.yml | 8 + src/test/resources/system_indices/roles.yml | 10 ++ .../system_indices/roles_mapping.yml | 10 ++ 11 files changed, 331 insertions(+), 152 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 5bec0ee3e8..1803a58fc9 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -1805,8 +1805,8 @@ public List> getSettings() { ); settings.add( Setting.boolSetting( - ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, - ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT, + ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, + ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_DEFAULT, Property.NodeScope, Property.Filtered ) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 0d258538f7..8944d294f6 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -62,7 +62,7 @@ public class SecurityIndexAccessEvaluator { private final WildcardMatcher denylistIndexMatcher; private final boolean systemIndexEnabled; - private boolean systemIndicesAdditionalControlFlag; + private final boolean systemIndicesPermissionsEnabled; public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, IndexResolverReplacer irr) { this.securityIndex = settings.get( @@ -102,9 +102,9 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, securityDeniedActionMatcher = WildcardMatcher.from( restoreSecurityIndexEnabled ? securityIndexDeniedActionPatternsList : securityIndexDeniedActionPatternsListNoSnapshot ); - systemIndicesAdditionalControlFlag = settings.getAsBoolean( - ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, - ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT + systemIndicesPermissionsEnabled = settings.getAsBoolean( + ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, + ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_DEFAULT ); } @@ -120,7 +120,7 @@ public PrivilegesEvaluatorResponse evaluate( // As per issue #2845, the legacy access control to indices with additional protection should be kept in place in the meantime and // be the default. - if (systemIndicesAdditionalControlFlag) { + if (systemIndicesPermissionsEnabled) { evaluateNewSecuredIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles, isDebugEnabled); } else { evaluateLegacySecuredIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled); diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 498b9f10ef..8317d65335 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -314,9 +314,8 @@ public enum RolesMappingResolution { public static final String SYSTEM_INDEX_PERMISSION = "system:admin/system_index"; public static final String SECURITY_SYSTEM_INDICES_ENABLED_KEY = "plugins.security.system_indices.enabled"; public static final Boolean SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT = false; - public static final String SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY = - "plugins.security.system_indices.additional_control.enabled"; - public static final Boolean SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_DEFAULT = false; + public static final String SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY = "plugins.security.system_indices.permission.enabled"; + public static final Boolean SECURITY_SYSTEM_INDICES_PERMISSIONS_DEFAULT = false; public static final String SECURITY_SYSTEM_INDICES_KEY = "plugins.security.system_indices.indices"; public static final List SECURITY_SYSTEM_INDICES_DEFAULT = Collections.emptyList(); diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 9cd88a903c..9618195738 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -36,7 +36,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -67,7 +66,7 @@ public void setupEvaluatorWithSystemIndicesControl(boolean systemIndexPermission evaluator = new SecurityIndexAccessEvaluator( Settings.EMPTY.builder() .put("plugins.security.system_indices.indices", ".testSystemIndex") - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, true) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, true) .put("plugins.security.system_indices.enabled", true) .build(), auditLog, @@ -119,7 +118,6 @@ public void disableCacheOrRealtimeOnSystemIndex() { evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - verifyNoInteractions(presponse); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); diff --git a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java index d62c192400..bcc624fd22 100644 --- a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java @@ -57,6 +57,9 @@ public abstract class AbstractSystemIndicesTests extends SingleClusterTest { static final String normalUser = "normal_user"; static final Header normalUserHeader = encodeBasicHeader(normalUser, normalUser); + static final String normalUserWithoutSystemIndex = "normal_user_without_system_index"; + static final Header normalUserWithoutSystemIndexHeader = encodeBasicHeader(normalUserWithoutSystemIndex, normalUserWithoutSystemIndex); + static final String createIndexSettings = "{\n" + " \"settings\" : {\n" + " \"index\" : {\n" @@ -73,7 +76,7 @@ void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionE Settings systemIndexSettings = Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, isSystemIndexEnabled) - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ADDITIONAL_CONTROL_ENABLED_KEY, isSystemIndexPermissionEnabled) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, isSystemIndexPermissionEnabled) .putList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, SYSTEM_INDICES) .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java index e2817c3314..24bed0a872 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -11,6 +11,7 @@ package org.opensearch.security.system_indices; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.junit.Before; import org.junit.Test; @@ -19,6 +20,8 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.security.test.helper.rest.RestHelper; +import java.io.IOException; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -72,23 +75,32 @@ public void testSearchAsAdmin() throws Exception { @Test public void testSearchAsNormalUser() throws Exception { + testSearchWithUser(normalUser, normalUserHeader); + } + + @Test + public void testSearchAsNormalUserWithoutSystemIndexAccess() throws Exception { + testSearchWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testSearchWithUser(String user, Header header) throws IOException { RestHelper restHelper = sslRestHelper(); // search system indices for (String index : SYSTEM_INDICES) { // security index is only accessible by super-admin - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", header); if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(response, "indices:data/read/search", normalUser); + validateForbiddenResponse(response, "indices:data/read/search", user); } else { validateSearchResponse(response, 1); } } // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", header); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - validateForbiddenResponse(response, "indices:data/read/search", normalUser); + validateForbiddenResponse(response, "indices:data/read/search", user); } /** @@ -109,27 +121,28 @@ public void testDeleteAsSuperAdmin() { @Test public void testDeleteAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - allowedExceptSecurityIndex(index, response, "", allAccessUser); - - response = restHelper.executeDeleteRequest(index, allAccessUserHeader); - allowedExceptSecurityIndex(index, response, "", allAccessUser); - } + testDeleteWithUser(allAccessUser, allAccessUserHeader); } @Test public void testDeleteAsNormalUser() { + testDeleteWithUser(normalUser, normalUserHeader); + } + + @Test + public void testDeleteAsNormalUserWithoutSystemIndexAccess() { + testDeleteWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testDeleteWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", header); + allowedExceptSecurityIndex(index, response, "", user); - response = restHelper.executeDeleteRequest(index, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + response = restHelper.executeDeleteRequest(index, header); + allowedExceptSecurityIndex(index, response, "", user); } } @@ -157,7 +170,7 @@ public void testCloseOpenAsAdmin() { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); allowedExceptSecurityIndex(index, response, "", allAccessUser); - // admin can open security index but cannot close it + // User can open the index but cannot close it response = restHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } @@ -165,15 +178,24 @@ public void testCloseOpenAsAdmin() { @Test public void testCloseOpenAsNormalUser() { + testCloseOpenWithUser(normalUser, normalUserHeader); + } + + @Test + public void testCloseOpenAsNormalUserWithoutSystemIndexAccess() { + testCloseOpenWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testCloseOpenWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", header); + allowedExceptSecurityIndex(index, response, "", user); - // normal user cannot open or close security index - response = restHelper.executePostRequest(index + "/_open", "", normalUserHeader); - allowedExceptSecurityIndex(index, response, "indices:admin/open", normalUser); + // User can open the index but cannot close it + response = restHelper.executePostRequest(index + "/_open", "", header); + allowedExceptSecurityIndex(index, response, "indices:admin/open", user); } } @@ -195,26 +217,27 @@ public void testCreateIndexAsSuperAdmin() { @Test public void testCreateIndexAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : INDICES_FOR_CREATE_REQUEST) { - RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } + testCreateIndexWithUser(allAccessUserHeader); } @Test public void testCreateIndexAsNormalUser() { + testCreateIndexWithUser(normalUserHeader); + } + + @Test + public void testCreateIndexAsNormalUserWithoutSystemIndexAccess() { + testCreateIndexWithUser(normalUserWithoutSystemIndexHeader); + } + + private void testCreateIndexWithUser(Header header) { RestHelper restHelper = sslRestHelper(); for (String index : INDICES_FOR_CREATE_REQUEST) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, header); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", header); assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @@ -237,27 +260,28 @@ public void testUpdateAsSuperAdmin() { @Test public void testUpdateMappingsAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - allowedExceptSecurityIndex(index, response, "", allAccessUser); - - response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, allAccessUserHeader); - allowedExceptSecurityIndex(index, response, "", allAccessUser); - } + testUpdateWithUser(allAccessUser, allAccessUserHeader); } @Test public void testUpdateAsNormalUser() { + testUpdateWithUser(normalUser, normalUserHeader); + } + + @Test + public void testUpdateAsNormalUserWithoutSystemIndexAccess() { + testUpdateWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testUpdateWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, header); + allowedExceptSecurityIndex(index, response, "", user); - response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, header); + allowedExceptSecurityIndex(index, response, "", user); } } @@ -326,6 +350,15 @@ public void testSnapshotSystemIndicesAsAdmin() { @Test public void testSnapshotSystemIndicesAsNormalUser() { + testSnapshotWithUser(normalUser, normalUserHeader); + } + + @Test + public void testSnapshotSystemIndicesAsNormalUserWithoutSystemIndexAccess() { + testSnapshotWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testSnapshotWithUser(String user, Header header) { createSnapshots(); try (Client tc = getClient()) { @@ -340,18 +373,18 @@ public void testSnapshotSystemIndicesAsNormalUser() { RestHelper.HttpResponse res = restHelper.executeGetRequest(snapshotRequest); assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); - res = restHelper.executePostRequest(snapshotRequest + "/_restore?wait_for_completion=true", "", normalUserHeader); - allowedExceptSecurityIndex(index, res, "", normalUser); + res = restHelper.executePostRequest(snapshotRequest + "/_restore?wait_for_completion=true", "", header); + allowedExceptSecurityIndex(index, res, "", user); res = restHelper.executePostRequest( snapshotRequest + "/_restore?wait_for_completion=true", "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", - normalUserHeader + header ); if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(res, "", normalUser); + validateForbiddenResponse(res, "", user); } else { - validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUser); + validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", user); } } } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java index 9cb505be6d..c973c7ea4a 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java @@ -11,6 +11,7 @@ package org.opensearch.security.system_indices; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.junit.Before; import org.junit.Test; @@ -19,6 +20,8 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.security.test.helper.rest.RestHelper; +import java.io.IOException; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -70,14 +73,23 @@ public void testSearchAsAdmin() throws Exception { @Test public void testSearchAsNormalUser() throws Exception { + testSearchWithUser(normalUser, normalUserHeader); + } + + @Test + public void testSearchAsNormalUserWithoutSystemIndexAccess() throws Exception { + testSearchWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testSearchWithUser(String user, Header header) throws IOException { RestHelper restHelper = sslRestHelper(); // search system indices for (String index : SYSTEM_INDICES) { // security index is only accessible by super-admin - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", header); if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(response, "indices:data/read/search", normalUser); + validateForbiddenResponse(response, "indices:data/read/search", user); } else { // got 0 hits because system index permissions are not enabled validateSearchResponse(response, 0); @@ -85,9 +97,9 @@ public void testSearchAsNormalUser() throws Exception { } // search all indices - RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", header); assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); - validateForbiddenResponse(response, "indices:data/read/search", normalUser); + validateForbiddenResponse(response, "indices:data/read/search", user); } /** @@ -108,27 +120,28 @@ public void testDeleteAsSuperAdmin() { @Test public void testDeleteAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); - - response = restHelper.executeDeleteRequest(index, allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); - } + testDeleteWithUser(allAccessUser, allAccessUserHeader); } @Test public void testDeleteAsNormalUser() { + testDeleteWithUser(normalUser, normalUserHeader); + } + + @Test + public void testDeleteAsNormalUserWithoutSystemIndexAccess() { + testDeleteWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testDeleteWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", normalUserHeader); - validateForbiddenResponse(response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", header); + validateForbiddenResponse(response, "", user); - response = restHelper.executeDeleteRequest(index, normalUserHeader); - validateForbiddenResponse(response, "", normalUser); + response = restHelper.executeDeleteRequest(index, header); + validateForbiddenResponse(response, "", user); } } @@ -164,21 +177,29 @@ public void testCloseOpenAsAdmin() { @Test public void testCloseOpenAsNormalUser() { + testCloseOpenWithUser(normalUser, normalUserHeader); + } + + @Test + public void testCloseOpenAsNormalUserWithoutSystemIndexAccess() { + testCloseOpenWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testCloseOpenWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", normalUserHeader); - validateForbiddenResponse(response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", header); + validateForbiddenResponse(response, "", user); // normal user cannot open or close security index - response = restHelper.executePostRequest(index + "/_open", "", normalUserHeader); - allowedExceptSecurityIndex(index, response, "indices:admin/open", normalUser); + response = restHelper.executePostRequest(index + "/_open", "", header); + allowedExceptSecurityIndex(index, response, "indices:admin/open", user); } } /** * CREATE - * * should be allowed as any user */ @Test @@ -196,26 +217,27 @@ public void testCreateIndexAsSuperAdmin() { @Test public void testCreateIndexAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : INDICES_FOR_CREATE_REQUEST) { - RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } + testCreateIndexWithUser(allAccessUserHeader); } @Test public void testCreateIndexAsNormalUser() { + testCreateIndexWithUser(normalUserHeader); + } + + @Test + public void testCreateIndexAsNormalUserWithoutSystemIndexAccess() { + testCreateIndexWithUser(normalUserWithoutSystemIndexHeader); + } + + private void testCreateIndexWithUser(Header header) { RestHelper restHelper = sslRestHelper(); for (String index : INDICES_FOR_CREATE_REQUEST) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, header); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", header); assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @@ -238,27 +260,28 @@ public void testUpdateAsSuperAdmin() { @Test public void testUpdateMappingsAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); - - response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); - } + testUpdateWithUser(allAccessUser, allAccessUserHeader); } @Test public void testUpdateAsNormalUser() { + testUpdateWithUser(normalUser, normalUserHeader); + } + + @Test + public void testUpdateAsNormalUserWithoutSystemIndexAccess() { + testUpdateWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testUpdateWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); - validateForbiddenResponse(response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, header); + validateForbiddenResponse(response, "", user); - response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); - validateForbiddenResponse(response, "", normalUser); + response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, header); + validateForbiddenResponse(response, "", user); } } @@ -330,6 +353,15 @@ public void testSnapshotSystemIndicesAsAdmin() { @Test public void testSnapshotSystemIndicesAsNormalUser() { + testSnapshotSystemIndexWithUser(normalUser, normalUserHeader); + } + + @Test + public void testSnapshotSystemIndicesAsNormalUserWithoutSystemIndexAccess() { + testSnapshotSystemIndexWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testSnapshotSystemIndexWithUser(String user, Header header) { createSnapshots(); try (Client tc = getClient()) { @@ -343,22 +375,18 @@ public void testSnapshotSystemIndicesAsNormalUser() { RestHelper.HttpResponse res = restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1"); assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); - res = restHelper.executePostRequest( - "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", - "", - normalUserHeader - ); - validateForbiddenResponse(res, "", normalUser); + res = restHelper.executePostRequest("_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", "", header); + validateForbiddenResponse(res, "", user); res = restHelper.executePostRequest( "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", - normalUserHeader + header ); if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(res, "", normalUser); + validateForbiddenResponse(res, "", user); } else { - validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUser); + validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", user); } } } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java index 525c17c154..d52e7e3ccf 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java @@ -11,6 +11,7 @@ package org.opensearch.security.system_indices; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.junit.Before; import org.junit.Test; @@ -87,6 +88,24 @@ public void testSearchAsNormalUser() throws Exception { validateForbiddenResponse(response, "indices:data/read/search", normalUser); } + @Test + public void testSearchAsNormalUserWithoutSystemIndexAccess() { + RestHelper restHelper = sslRestHelper(); + + // search system indices + for (String index : SYSTEM_INDICES) { + // security index is only accessible by super-admin + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserWithoutSystemIndexHeader); + validateForbiddenResponse(response, "", normalUserWithoutSystemIndex); + + } + + // search all indices + RestHelper.HttpResponse response = restHelper.executePostRequest("/_search", "", normalUserWithoutSystemIndexHeader); + assertEquals(RestStatus.FORBIDDEN.getStatus(), response.getStatusCode()); + validateForbiddenResponse(response, "indices:data/read/search", normalUserWithoutSystemIndex); + } + /** * DELETE document + index */ @@ -131,6 +150,23 @@ public void testDeleteAsNormalUser() { } } + @Test + public void testDeleteAsNormalUserWithoutSystemIndexAccess() { + RestHelper restHelper = sslRestHelper(); + + // does not allow interaction with any system index as it doesn't have the permission + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = restHelper.executeDeleteRequest( + index + "/_doc/document1", + normalUserWithoutSystemIndexHeader + ); + validateForbiddenResponse(response, "", normalUserWithoutSystemIndex); + + response = restHelper.executeDeleteRequest(index, normalUserWithoutSystemIndexHeader); + validateForbiddenResponse(response, "", normalUserWithoutSystemIndex); + } + } + /** * CLOSE-OPEN */ @@ -149,16 +185,7 @@ public void testCloseOpenAsSuperAdmin() { @Test public void testCloseOpenAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - // admin cannot close or open any system index when system index permissions are enabled - for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); - - response = restHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); - } + testCloseOpenWithUser(allAccessUser, allAccessUserHeader); } @Test @@ -175,6 +202,24 @@ public void testCloseOpenAsNormalUser() { } } + @Test + public void testCloseOpenAsNormalUserWithoutSystemIndexAccess() { + testCloseOpenWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testCloseOpenWithUser(String user, Header header) { + RestHelper restHelper = sslRestHelper(); + + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", header); + validateForbiddenResponse(response, "", user); + + // admin or normal user (without system index permission) cannot open or close any system index + response = restHelper.executePostRequest(index + "/_open", "", header); + validateForbiddenResponse(response, "", user); + } + } + /** * CREATE * should be allowed as any user @@ -194,26 +239,27 @@ public void testCreateIndexAsSuperAdmin() { @Test public void testCreateIndexAsAdmin() { - RestHelper restHelper = sslRestHelper(); - - for (String index : INDICES_FOR_CREATE_REQUEST) { - RestHelper.HttpResponse responseIndex = restHelper.executePutRequest(index, createIndexSettings, allAccessUserHeader); - assertEquals(RestStatus.OK.getStatus(), responseIndex.getStatusCode()); - - RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", allAccessUserHeader); - assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); - } + testCreateIndexWithUser(allAccessUserHeader); } @Test public void testCreateIndexAsNormalUser() { + testCreateIndexWithUser(normalUserHeader); + } + + @Test + public void testCreateIndexAsNormalUserWithoutSystemIndexAccess() { + testCreateIndexWithUser(normalUserWithoutSystemIndexHeader); + } + + private void testCreateIndexWithUser(Header header) { RestHelper restHelper = sslRestHelper(); for (String index : INDICES_FOR_CREATE_REQUEST) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, normalUserHeader); + RestHelper.HttpResponse response = restHelper.executePutRequest(index, createIndexSettings, header); assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); - response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", normalUserHeader); + response = restHelper.executePostRequest(index + "/_doc", "{\"foo\": \"bar\"}", header); assertEquals(RestStatus.CREATED.getStatus(), response.getStatusCode()); } } @@ -235,28 +281,37 @@ public void testUpdateAsSuperAdmin() { } @Test - public void testUpdateMappingsAsAdmin() { + public void testUpdateAsAdmin() { + testUpdateWithUser(allAccessUser, allAccessUserHeader); + } + + @Test + public void testUpdateAsNormalUser() { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); - response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, allAccessUserHeader); - validateForbiddenResponse(response, "", allAccessUser); + response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); + allowedExceptSecurityIndex(index, response, "", normalUser); } } @Test - public void testUpdateAsNormalUser() { + public void testUpdateAsNormalUserWithoutSystemIndexAccess() { + testUpdateWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + } + + private void testUpdateWithUser(String user, Header header) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { - RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, header); + validateForbiddenResponse(response, "", user); - response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + response = restHelper.executePutRequest(index + "/_mapping", newMappings, header); + validateForbiddenResponse(response, "", user); } } @@ -360,4 +415,39 @@ public void testSnapshotSystemIndicesAsNormalUser() { } } } + + @Test + public void testSnapshotSystemIndicesAsNormalUserWithoutSystemIndexAccess() { + createSnapshots(); + + try (Client tc = getClient()) { + for (String index : SYSTEM_INDICES) { + tc.admin().indices().close(new CloseIndexRequest(index)).actionGet(); + } + } + + RestHelper restHelper = sslRestHelper(); + for (String index : SYSTEM_INDICES) { + RestHelper.HttpResponse res = restHelper.executeGetRequest("_snapshot/" + index + "/" + index + "_1"); + assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "", + normalUserWithoutSystemIndexHeader + ); + validateForbiddenResponse(res, "", normalUserWithoutSystemIndex); + + res = restHelper.executePostRequest( + "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", + "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", + normalUserWithoutSystemIndexHeader + ); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + validateForbiddenResponse(res, "", normalUserWithoutSystemIndex); + } else { + validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUserWithoutSystemIndex); + } + } + } } diff --git a/src/test/resources/system_indices/internal_users.yml b/src/test/resources/system_indices/internal_users.yml index 7a527fb79d..d792fa47be 100644 --- a/src/test/resources/system_indices/internal_users.yml +++ b/src/test/resources/system_indices/internal_users.yml @@ -17,3 +17,11 @@ normal_user: backend_roles: [] attributes: {} description: "User Mapped to role With Access to Admin:System:Indices " + +normal_user_without_system_index: + hash: "$2y$12$V3.ACgUpHP9TlSbV3CNekOGB1NVov1C6Rq3QtXWCvACKVeQnkBCgG" + reserved: false + hidden: false + backend_roles: [] + attributes: {} + description: "User Mapped to role With no access to system indices" diff --git a/src/test/resources/system_indices/roles.yml b/src/test/resources/system_indices/roles.yml index 820146910f..82ac33a92d 100644 --- a/src/test/resources/system_indices/roles.yml +++ b/src/test/resources/system_indices/roles.yml @@ -23,3 +23,13 @@ normal_role: allowed_actions: - '*' - 'system:admin/system_index' + +normal_role_without_system_index: + description: "Normal user Role" + cluster_permissions: + - '*' + index_permissions: + - index_patterns: + - '.system*' + allowed_actions: + - '*' diff --git a/src/test/resources/system_indices/roles_mapping.yml b/src/test/resources/system_indices/roles_mapping.yml index 0e9114c3dd..8d24f94271 100644 --- a/src/test/resources/system_indices/roles_mapping.yml +++ b/src/test/resources/system_indices/roles_mapping.yml @@ -22,3 +22,13 @@ normal_role: - "normal_user" and_backend_roles: [] description: "System index permissions" + +normal_role_without_system_index: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + users: + - "normal_user_without_system_index" + and_backend_roles: [] + description: "No access to System index permissions" From d90f2975fe190b9864ca5a2f1f1f34ac6f8324e6 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Fri, 1 Sep 2023 22:23:52 -0400 Subject: [PATCH 36/46] Cleans up SecurityIndexAccessEvaluator and related tests Signed-off-by: Darshit Chanpura --- .../SecurityIndexAccessEvaluator.java | 275 +++++++++------- .../SecurityIndexAccessEvaluatorTest.java | 295 ++++++++++++++++-- 2 files changed, 416 insertions(+), 154 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 8944d294f6..ae8d2a7c22 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -26,15 +26,8 @@ package org.opensearch.security.privileges; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.action.ActionRequest; import org.opensearch.action.RealtimeRequest; import org.opensearch.action.search.SearchRequest; @@ -48,21 +41,33 @@ import org.opensearch.security.support.WildcardMatcher; import org.opensearch.tasks.Task; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * This class performs authorization on requests targeting system indices + * NOTE: + * - The term `protected system indices` used here translates to system indices + * which have an added layer of security and cannot be accessed by anyone except Super Admin + */ public class SecurityIndexAccessEvaluator { Logger log = LogManager.getLogger(this.getClass()); private final String securityIndex; private final AuditLog auditLog; - private final WildcardMatcher securityDeniedActionMatcher; private final IndexResolverReplacer irr; private final boolean filterSecurityIndex; // for system-indices configuration private final WildcardMatcher systemIndexMatcher; - private final WildcardMatcher denylistIndexMatcher; + private final WildcardMatcher protectedSystemIndexMatcher; + private final WildcardMatcher deniedActionsMatcher; - private final boolean systemIndexEnabled; - private final boolean systemIndicesPermissionsEnabled; + private final boolean isSystemIndexEnabled; + private final boolean isSystemIndexPermissionEnabled; public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, IndexResolverReplacer irr) { this.securityIndex = settings.get( @@ -75,8 +80,8 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, this.systemIndexMatcher = WildcardMatcher.from( settings.getAsList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT) ); - this.denylistIndexMatcher = WildcardMatcher.from(ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); - this.systemIndexEnabled = settings.getAsBoolean( + this.protectedSystemIndexMatcher = WildcardMatcher.from(ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); + this.isSystemIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT ); @@ -85,29 +90,33 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, false ); - final List securityIndexDeniedActionPatternsList = new ArrayList(); - securityIndexDeniedActionPatternsList.add("indices:data/write*"); - securityIndexDeniedActionPatternsList.add("indices:admin/delete*"); - securityIndexDeniedActionPatternsList.add("indices:admin/mapping/delete*"); - securityIndexDeniedActionPatternsList.add("indices:admin/mapping/put*"); - securityIndexDeniedActionPatternsList.add("indices:admin/freeze*"); - securityIndexDeniedActionPatternsList.add("indices:admin/settings/update*"); - securityIndexDeniedActionPatternsList.add("indices:admin/aliases"); + final List securityIndexDeniedActionPatternsList = deniedActionPatterns(); - final List securityIndexDeniedActionPatternsListNoSnapshot = new ArrayList(); - securityIndexDeniedActionPatternsListNoSnapshot.addAll(securityIndexDeniedActionPatternsList); + final List securityIndexDeniedActionPatternsListNoSnapshot = new ArrayList<>(securityIndexDeniedActionPatternsList); securityIndexDeniedActionPatternsListNoSnapshot.add("indices:admin/close*"); securityIndexDeniedActionPatternsListNoSnapshot.add("cluster:admin/snapshot/restore*"); - securityDeniedActionMatcher = WildcardMatcher.from( + deniedActionsMatcher = WildcardMatcher.from( restoreSecurityIndexEnabled ? securityIndexDeniedActionPatternsList : securityIndexDeniedActionPatternsListNoSnapshot ); - systemIndicesPermissionsEnabled = settings.getAsBoolean( + isSystemIndexPermissionEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_DEFAULT ); } + private static List deniedActionPatterns() { + final List securityIndexDeniedActionPatternsList = new ArrayList<>(); + securityIndexDeniedActionPatternsList.add("indices:data/write*"); + securityIndexDeniedActionPatternsList.add("indices:admin/delete*"); + securityIndexDeniedActionPatternsList.add("indices:admin/mapping/delete*"); + securityIndexDeniedActionPatternsList.add("indices:admin/mapping/put*"); + securityIndexDeniedActionPatternsList.add("indices:admin/freeze*"); + securityIndexDeniedActionPatternsList.add("indices:admin/settings/update*"); + securityIndexDeniedActionPatternsList.add("indices:admin/aliases"); + return securityIndexDeniedActionPatternsList; + } + public PrivilegesEvaluatorResponse evaluate( final ActionRequest request, final Task task, @@ -118,20 +127,11 @@ public PrivilegesEvaluatorResponse evaluate( ) { final boolean isDebugEnabled = log.isDebugEnabled(); - // As per issue #2845, the legacy access control to indices with additional protection should be kept in place in the meantime and - // be the default. - if (systemIndicesPermissionsEnabled) { - evaluateNewSecuredIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles, isDebugEnabled); - } else { - evaluateLegacySecuredIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled); - } - if (presponse.isComplete()) { - return presponse; - } + evaluateSecuredIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled, securityRoles); if (requestedResolved.isLocalAll() || requestedResolved.getAllIndices().contains(securityIndex) - || matchAnySystemIndices(requestedResolved)) { + || requestContainsAnySystemIndices(requestedResolved)) { if (request instanceof SearchRequest) { ((SearchRequest) request).requestCache(Boolean.FALSE); @@ -150,9 +150,13 @@ public PrivilegesEvaluatorResponse evaluate( return presponse; } - private boolean checkSystemIndexPermissionsForUser(SecurityRoles securityRoles) { - // The generic wildcard "*" permission shouldn't give access to SystemIndices, so excluding it from the user roles's permissions - // before the check + /** + * Checks whether user has `system:admin/system_index` as an allowed action under any of its mapped roles + * @param securityRoles roles in which the permission needs to be checked + * @return true if user does not have permission, false otherwise + */ + private boolean isSystemIndexAccessProhibitedForUser(SecurityRoles securityRoles) { + // Excluding `*` allowed action as it doesn't grant system index privilege Set userPermissions = securityRoles.getRoles() .stream() .flatMap(role -> role.getIpatterns().stream()) @@ -161,129 +165,159 @@ private boolean checkSystemIndexPermissionsForUser(SecurityRoles securityRoles) for (WildcardMatcher userPermission : userPermissions) { if (userPermission.matchAny(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { - return true; + return false; } } - return false; + return true; } - private boolean matchAnySystemIndices(final Resolved requestedResolved) { - return !getProtectedIndexes(requestedResolved).isEmpty(); + /** + * Checks if request is for any system index + * @param requestedResolved request which contains indices to be matched against system indices + * @return true if a match is found, false otherwise + */ + private boolean requestContainsAnySystemIndices(final Resolved requestedResolved) { + return !getAllSystemIndices(requestedResolved).isEmpty(); } - private List getProtectedIndexes(final Resolved requestedResolved) { - final List protectedIndexes = requestedResolved.getAllIndices() + /** + * Gets all indices requested in the original request. + * It will always return security index if it is present in the request, as security index is protected regardless + * of feature being enabled or disabled + * @param requestedResolved request which contains indices to be matched against system indices + * @return the list of protected system indices present in the request + */ + private List getAllSystemIndices(final Resolved requestedResolved) { + final List systemIndices = requestedResolved.getAllIndices() .stream() .filter(securityIndex::equals) .collect(Collectors.toList()); - if (systemIndexEnabled) { - protectedIndexes.addAll(systemIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + if (isSystemIndexEnabled) { + systemIndices.addAll(systemIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); } - return protectedIndexes; + return systemIndices; } - private boolean matchAnyDenyIndices(final Resolved requestedResolved) { - return !getDenyListIndices(requestedResolved).isEmpty(); + /** + * Checks if request contains any system index that is non-permission-able + * NOTE: Security index is currently non-permission-able + * @param requestedResolved request which contains indices to be matched against non-permission-able system indices + * @return true if the request contains any non-permission-able index,false otherwise + */ + private boolean requestContainsAnyProtectedSystemIndices(final Resolved requestedResolved) { + return !getAllProtectedSystemIndices(requestedResolved).isEmpty(); } - private List getDenyListIndices(final Resolved requestedResolved) { - final List denyList = new ArrayList<>(); - denyList.addAll(denylistIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + /** + * Filters the request to get all system indices that are protected and are non-permission-able + * @param requestedResolved request which contains indices to be matched against non-permission-able system indices + * @return the list of protected system indices present in the request + */ + private List getAllProtectedSystemIndices(final Resolved requestedResolved) { + return new ArrayList<>(protectedSystemIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + } - return denyList; + /** + * Is the current action allowed to be performed on security index + * @param action request action on security index + * @return + */ + private boolean isActionAllowed(String action) { + return deniedActionsMatcher.test(action); } - private PrivilegesEvaluatorResponse evaluateNewSecuredIndicesAccess( + private void evaluateSecuredIndicesAccess( String action, Resolved requestedResolved, ActionRequest request, Task task, PrivilegesEvaluatorResponse presponse, - SecurityRoles securityRoles, - Boolean isDebugEnabled - + Boolean isDebugEnabled, + SecurityRoles securityRoles ) { - if (matchAnyDenyIndices(requestedResolved)) { - auditLog.logSecurityIndexAttempt(request, action, task); - if (log.isInfoEnabled()) { - log.info( - "{} not permited for regular user {} on denylist indices {}", - action, - securityRoles, - getDenyListIndices(requestedResolved).stream().collect(Collectors.joining(", ")) - ); + // is the request trying to access a protected system index + if (isSystemIndexPermissionEnabled) { + boolean containsProtectedIndex = requestContainsAnyProtectedSystemIndices(requestedResolved); + boolean containsSystemIndex = requestContainsAnySystemIndices(requestedResolved); + if (requestContainsAnyProtectedSystemIndices(requestedResolved)) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "{} not permitted for a regular user {} on protected system indices {}", + action, + securityRoles, + String.join(", ", getAllProtectedSystemIndices(requestedResolved)) + ); + } + presponse.allowed = false; + presponse.markComplete(); + return; + } else if (requestContainsAnySystemIndices(requestedResolved) && isSystemIndexAccessProhibitedForUser(securityRoles)) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "No {} permission for user roles {} to System Indices {}", + action, + securityRoles, + String.join(", ", getAllSystemIndices(requestedResolved)) + ); + } + presponse.allowed = false; + presponse.markComplete(); + return; } - presponse.allowed = false; - return presponse.markComplete(); - } - if (matchAnySystemIndices(requestedResolved) && !checkSystemIndexPermissionsForUser(securityRoles)) { - auditLog.logSecurityIndexAttempt(request, action, task); - if (log.isInfoEnabled()) { - log.info( - "No {} permission for user roles {} to System Indices {}", - action, - securityRoles, - getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")) - ); - } - presponse.allowed = false; - return presponse.markComplete(); - } + if (containsProtectedIndex || (containsSystemIndex && isSystemIndexAccessProhibitedForUser(securityRoles))) { + auditLog.logSecurityIndexAttempt(request, action, task); - if (securityDeniedActionMatcher.test(action)) { - if (requestedResolved.isLocalAll()) { - if (filterSecurityIndex) { - irr.replace(request, false, "*", "-" + securityIndex); - if (isDebugEnabled) { - log.debug( - "Filtered '{}'from {}, resulting list with *,-{} is {}", - securityIndex, - requestedResolved, - securityIndex, - irr.resolveRequest(request) - ); - } - return presponse; + if (log.isInfoEnabled()) { + String indices = String.join( + ", ", + containsProtectedIndex ? getAllProtectedSystemIndices(requestedResolved) : getAllSystemIndices(requestedResolved) + ); + + log.info( + containsProtectedIndex + ? "{} not permitted for a regular user {} on protected system indices {}" + : "No {} permission for user roles {} to System Indices {}", + action, + securityRoles, + indices + ); } - auditLog.logSecurityIndexAttempt(request, action, task); - log.info("{} for '_all' indices is not allowed for a regular user", action); + presponse.allowed = false; - return presponse.markComplete(); + presponse.markComplete(); + return; } } - return presponse; - } - private PrivilegesEvaluatorResponse evaluateLegacySecuredIndicesAccess( - String action, - Resolved requestedResolved, - ActionRequest request, - Task task, - PrivilegesEvaluatorResponse presponse, - Boolean isDebugEnabled - ) { - if (securityDeniedActionMatcher.test(action)) { + if (isActionAllowed(action)) { if (requestedResolved.isLocalAll()) { if (filterSecurityIndex) { irr.replace(request, false, "*", "-" + securityIndex); if (isDebugEnabled) { log.debug( - "Filtered '{}'from {}, resulting list with *,-{} is {}", + "Filtered '{}' from {}, resulting list with *,-{} is {}", securityIndex, requestedResolved, securityIndex, irr.resolveRequest(request) ); } - return presponse; } else { auditLog.logSecurityIndexAttempt(request, action, task); log.info("{} for '_all' indices is not allowed for a regular user", action); presponse.allowed = false; - return presponse.markComplete(); + presponse.markComplete(); + } + } else if (requestContainsAnySystemIndices(requestedResolved)) { + // if system index is enabled and system index permissions are enabled we don't need to perform any further + // checks as it has already been performed via isSystemIndexAccessProhibitedForUser + if (isSystemIndexPermissionEnabled) { + return; } - } else if (matchAnySystemIndices(requestedResolved)) { + if (filterSecurityIndex) { Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); allWithoutSecurity.remove(securityIndex); @@ -292,22 +326,23 @@ private PrivilegesEvaluatorResponse evaluateLegacySecuredIndicesAccess( log.debug("Filtered '{}' but resulting list is empty", securityIndex); } presponse.allowed = false; - return presponse.markComplete(); + presponse.markComplete(); + return; } irr.replace(request, false, allWithoutSecurity.toArray(new String[0])); if (isDebugEnabled) { log.debug("Filtered '{}', resulting list is {}", securityIndex, allWithoutSecurity); } - return presponse; } else { auditLog.logSecurityIndexAttempt(request, action, task); - final String foundSystemIndexes = getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")); + final String foundSystemIndexes = String.join(", ", getAllSystemIndices(requestedResolved)); log.warn("{} for '{}' index is not allowed for a regular user", action, foundSystemIndexes); presponse.allowed = false; - return presponse.markComplete(); + presponse.markComplete(); } } } - return presponse; + } + } diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 9618195738..bc72f308d1 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -57,17 +58,20 @@ public class SecurityIndexAccessEvaluatorTest { private SecurityIndexAccessEvaluator evaluator; private static final String UNPROTECTED_ACTION = "indices:data/read"; private static final String PROTECTED_ACTION = "indices:data/write"; + + private static final String TEST_SYSTEM_INDEX = ".test_system_index"; + private static final String TEST_INDEX = ".test"; + + private static final String SECURITY_INDEX = ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX; @Mock - ConfigModelV7 configModelV7; - @Mock - ConfigModelV7.SecurityRoles securityRoles;// = configModelV7.getSecurityRoles(); + ConfigModelV7.SecurityRoles securityRoles; - public void setupEvaluatorWithSystemIndicesControl(boolean systemIndexPermissionsEnabled) { + public void setup(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionsEnabled) { evaluator = new SecurityIndexAccessEvaluator( - Settings.EMPTY.builder() - .put("plugins.security.system_indices.indices", ".testSystemIndex") - .put(ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, true) - .put("plugins.security.system_indices.enabled", true) + Settings.builder() + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, TEST_SYSTEM_INDEX) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, isSystemIndexEnabled) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, isSystemIndexPermissionsEnabled) .build(), auditLog, irr @@ -81,14 +85,53 @@ public void setupEvaluatorWithSystemIndicesControl(boolean systemIndexPermission @After public void after() { - verifyNoMoreInteractions(auditLog, irr, request, task, presponse, log); } @Test - public void actionIsNotProtected_noSystemIndexInvolved() { - setupEvaluatorWithSystemIndicesControl(true); - final Resolved resolved = createResolved(".potato"); + public void testUnprotectedActionAgainstNonSystemIndex_systemIndexDisabled() { + setup(false, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + + verify(log).isDebugEnabled(); + } + + @Test + public void testUnprotectedActionAgainstSystemIndex_systemIndexDisabled() { + setup(false, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + + verify(log).isDebugEnabled(); + } + + @Test + public void testUnprotectedActionAgainstNonSystemIndex_systemIndexPermissionDisabled() { + setup(true, false); + final Resolved resolved = createResolved(TEST_INDEX); // Action final PrivilegesEvaluatorResponse response = evaluator.evaluate( @@ -99,19 +142,98 @@ public void actionIsNotProtected_noSystemIndexInvolved() { presponse, securityRoles ); - verify(presponse).isComplete(); + verifyNoInteractions(presponse); assertThat(response, is(presponse)); verify(log).isDebugEnabled(); } @Test - public void disableCacheOrRealtimeOnSystemIndex() { - setupEvaluatorWithSystemIndicesControl(false); + public void testUnprotectedActionAgainstSystemIndex_systemIndexPermissionDisabled() { + setup(true, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + + verify(log).isDebugEnabled(); + } + + @Test + public void testUnprotectedActionAgainstNonSystemIndex_systemIndexPermissionEnabled() { + setup(true, true); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + + verify(log).isDebugEnabled(); + } + + @Test + public void testUnprotectedActionAgainstSystemIndex_systemIndexPermissionEnabled() { + setup(true, true); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); + verify(presponse).markComplete(); + assertThat(response, is(presponse)); + + verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); + verify(log).isDebugEnabled(); + verify(log).isInfoEnabled(); + verify(log).info("No {} permission for user roles {} to System Indices {}", UNPROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); + } + + @Test + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexDisabled() { + setup(false, false); final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); - final Resolved resolved = createResolved(".testSystemIndex"); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(log, times(3)).isDebugEnabled(); + } + + @Test + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionDisabled() { + setup(true, false); + + final SearchRequest searchRequest = mock(SearchRequest.class); + final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); @@ -121,15 +243,49 @@ public void disableCacheOrRealtimeOnSystemIndex() { verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); - verify(presponse, times(3)).isComplete(); verify(log, times(3)).isDebugEnabled(); verify(log).debug("Disable search request cache for this request"); verify(log).debug("Disable realtime for this request"); } @Test - public void protectedActionLocalAll() { - setupEvaluatorWithSystemIndicesControl(false); + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled() { + setup(true, true); + + final SearchRequest searchRequest = mock(SearchRequest.class); + final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(searchRequest).requestCache(Boolean.FALSE); + verify(realtimeRequest).realtime(Boolean.FALSE); + + verify(log, times(3)).isDebugEnabled(); + verify(log).debug("Disable search request cache for this request"); + verify(log).debug("Disable realtime for this request"); + verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); + verify(auditLog).logSecurityIndexAttempt(searchRequest, UNPROTECTED_ACTION, null); + verify(auditLog).logSecurityIndexAttempt(realtimeRequest, UNPROTECTED_ACTION, null); + verify(presponse, times(3)).markComplete(); + verify(log, times(3)).isDebugEnabled(); + verify(log, times(3)).isInfoEnabled(); + verify(log, times(3)).info( + "No {} permission for user roles {} to System Indices {}", + UNPROTECTED_ACTION, + securityRoles, + TEST_SYSTEM_INDEX + ); + verify(log).debug("Disable search request cache for this request"); + verify(log).debug("Disable realtime for this request"); + } + + @Test + public void testProtectedActionLocalAll_systemIndexDisabled() { + setup(false, false); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -139,14 +295,13 @@ public void protectedActionLocalAll() { verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(presponse).isComplete(); verify(log).isDebugEnabled(); verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } @Test - public void protectedActionLocalAllWithNewAccessControl() { - setupEvaluatorWithSystemIndicesControl(true); + public void testProtectedActionLocalAll_systemIndexPermissionDisabled() { + setup(true, false); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -156,15 +311,57 @@ public void protectedActionLocalAllWithNewAccessControl() { verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(presponse).isComplete(); verify(log).isDebugEnabled(); - verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); + verify(log).info("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + } + + @Test + public void testProtectedActionLocalAll_systemIndexPermissionEnabled() { + setup(true, true); + final Resolved resolved = Resolved._LOCAL_ALL; + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + verify(log).isDebugEnabled(); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + verify(log).isDebugEnabled(); + verify(log).info("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnSystemIndex_systemIndexDisabled() { + setup(false, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + assertThat(presponse.allowed, is(false)); + verify(log).isDebugEnabled(); } @Test - public void protectedActionSystemIndex() { - setupEvaluatorWithSystemIndicesControl(true); - final Resolved resolved = createResolved(".testSystemIndex"); + public void testProtectedActionOnSystemIndex_systemIndexPermissionDisabled() { + setup(true, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + verify(log).isDebugEnabled(); + verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, TEST_SYSTEM_INDEX); + } + + @Test + public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled() { + setup(true, true); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); @@ -172,16 +369,47 @@ public void protectedActionSystemIndex() { verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(presponse).isComplete(); verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); - verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, ".testSystemIndex"); + verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexDisabled() { + setup(false, false); + final Resolved resolved = createResolved(SECURITY_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + + verify(log).isDebugEnabled(); + verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, SECURITY_INDEX); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionDisabled() { + setup(true, false); + final Resolved resolved = createResolved(SECURITY_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + + verify(log).isDebugEnabled(); + verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, SECURITY_INDEX); } @Test - public void protectedActionDenyListIndex() { - setupEvaluatorWithSystemIndicesControl(true); - final Resolved resolved = createResolved(".opendistro_security"); + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled() { + setup(true, true); + final Resolved resolved = createResolved(SECURITY_INDEX); // Action evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); @@ -192,9 +420,8 @@ public void protectedActionDenyListIndex() { verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); - verify(presponse).isComplete(); verify(log).info( - "{} not permited for regular user {} on denylist indices {}", + "{} not permitted for a regular user {} on protected system indices {}", PROTECTED_ACTION, securityRoles, ".opendistro_security" From e10be93b6e3023297df536e25b6f261e2d7d252e Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Sat, 2 Sep 2023 12:45:13 -0400 Subject: [PATCH 37/46] Refactors changes related to config models Signed-off-by: Darshit Chanpura --- .../security/securityconf/ConfigModelV6.java | 77 ++++++------ .../security/securityconf/ConfigModelV7.java | 119 +++++++++--------- .../security/securityconf/IndexPattern.java | 8 +- .../security/securityconf/SecurityRole.java | 16 ++- .../security/securityconf/SecurityRoles.java | 3 +- .../security/securityconf/TypePerm.java | 22 ++-- .../SecurityIndexAccessEvaluatorTest.java | 4 +- 7 files changed, 133 insertions(+), 116 deletions(-) diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 79480a6080..15a903ed2b 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -208,7 +208,7 @@ private SecurityRoles reload(SecurityDynamicConfiguration settings) { @Override public SecurityRole call() throws Exception { - SecurityRole _securityRole = new SecurityRole(securityRole.getKey()); + SecurityRole _securityRole = new SecurityRoleV6(securityRole.getKey()); if (securityRole.getValue() == null) { return null; @@ -247,19 +247,19 @@ public SecurityRole call() throws Exception { final List fls = permittedAliasesIndex.getValue().get_fls_(); final List maskedFields = permittedAliasesIndex.getValue().get_masked_fields_(); - IndexPatternV6 _indexPatternV6 = new IndexPatternV6(permittedAliasesIndex.getKey()); - _indexPatternV6.setDlsQuery(dls); - _indexPatternV6.addFlsFields(fls); - _indexPatternV6.addMaskedFields(maskedFields); + IndexPattern _indexPattern = new IndexPatternV6(permittedAliasesIndex.getKey()); + _indexPattern.setDlsQuery(dls); + _indexPattern.addFlsFields(fls); + _indexPattern.addMaskedFields(maskedFields); for (Entry> type : permittedAliasesIndex.getValue().getTypes().entrySet()) { TypePerm typePerm = new TypePerm(); final List perms = type.getValue(); typePerm.addPerms(agr.resolvedActions(perms)); - _indexPatternV6.addTypePerms(typePerm); + _indexPattern.addTypePerms(typePerm); } - _securityRole.addIndexPattern(_indexPatternV6); + _securityRole.addIndexPattern(_indexPattern); } @@ -280,7 +280,7 @@ public SecurityRole call() throws Exception { } try { - SecurityRoles _securityRoles = new SecurityRoles(futures.size()); + SecurityRolesV6 _securityRoles = new SecurityRolesV6(futures.size()); for (Future future : futures) { _securityRoles.addSecurityRole(future.get()); } @@ -298,21 +298,20 @@ public SecurityRole call() throws Exception { // beans - public static class SecurityRoles implements org.opensearch.security.securityconf.SecurityRoles { + public static class SecurityRolesV6 implements SecurityRoles { protected final Logger log = LogManager.getLogger(this.getClass()); final Set roles; - private SecurityRoles(int roleCount) { + private SecurityRolesV6(int roleCount) { roles = new HashSet<>(roleCount); } - private SecurityRoles addSecurityRole(SecurityRole securityRole) { + public void addSecurityRole(SecurityRole securityRole) { if (securityRole != null) { this.roles.add(securityRole); } - return this; } @Override @@ -328,7 +327,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRoles other = (SecurityRoles) obj; + SecurityRolesV6 other = (SecurityRolesV6) obj; if (roles == null) { if (other.roles != null) return false; } else if (!roles.equals(other.roles)) return false; @@ -340,8 +339,7 @@ public String toString() { return "roles=" + roles; } - @Override - public Set getRoles() { + public Set getRoles() { return Collections.unmodifiableSet(roles); } @@ -350,7 +348,7 @@ public Set getRoleNames() { } public SecurityRoles filter(Set keep) { - final SecurityRoles retVal = new SecurityRoles(roles.size()); + final SecurityRoles retVal = new SecurityRolesV6(roles.size()); for (SecurityRole sr : roles) { if (keep.contains(sr.getName())) { retVal.addSecurityRole(sr); @@ -495,9 +493,9 @@ public boolean impliesClusterPermissionPermission(String action) { @Override public boolean hasExplicitClusterPermissionPermission(String action) { return roles.stream().map(r -> { - final WildcardMatcher m = WildcardMatcher.from(r.clusterPerms); + final WildcardMatcher m = WildcardMatcher.from(r.getClusterPerms()); return m == WildcardMatcher.ANY ? WildcardMatcher.NONE : m; - }).filter(m -> m.test(action)).count() > 0; + }).anyMatch(m -> m.test(action)); } // rolespan @@ -514,14 +512,14 @@ public boolean impliesTypePermGlobal( } } - public static class SecurityRole implements org.opensearch.security.securityconf.SecurityRole { + public static class SecurityRoleV6 implements SecurityRole { private final String name; private final Set tenants = new HashSet<>(); - private final Set ipatterns = new HashSet<>(); + private final Set ipatterns = new HashSet<>(); private final Set clusterPerms = new HashSet<>(); - private SecurityRole(String name) { + private SecurityRoleV6(String name) { super(); this.name = Objects.requireNonNull(name); } @@ -541,13 +539,13 @@ public Set getAllResolvedPermittedIndices( ) { final Set retVal = new HashSet<>(); - for (IndexPatternV6 p : ipatterns) { + for (IndexPattern p : ipatterns) { // what if we cannot resolve one (for create purposes) boolean patternMatch = false; final Set tperms = p.getTypePerms(); for (TypePerm tp : tperms) { if (tp.typeMatcher.matchAny(resolved.getTypes())) { - patternMatch = tp.getPerms().matchAll(actions); + patternMatch = tp.getPermsMatcher().matchAll(actions); } } if (patternMatch) { @@ -573,25 +571,22 @@ public Set getAllResolvedPermittedIndices( return Collections.unmodifiableSet(retVal); } - private SecurityRole addTenant(Tenant tenant) { + public void addTenant(Tenant tenant) { if (tenant != null) { this.tenants.add(tenant); } - return this; } - private SecurityRole addIndexPattern(IndexPatternV6 indexPattern) { + public void addIndexPattern(IndexPattern indexPattern) { if (indexPattern != null) { this.ipatterns.add(indexPattern); } - return this; } - private SecurityRole addClusterPerms(Collection clusterPerms) { + public void addClusterPerms(Collection clusterPerms) { if (clusterPerms != null) { this.clusterPerms.addAll(clusterPerms); } - return this; } @Override @@ -610,7 +605,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRole other = (SecurityRole) obj; + SecurityRoleV6 other = (SecurityRoleV6) obj; if (clusterPerms == null) { if (other.clusterPerms != null) return false; } else if (!clusterPerms.equals(other.clusterPerms)) return false; @@ -647,8 +642,7 @@ public Set getTenants(User user) { return Collections.unmodifiableSet(tenants); } - @Override - public Set getIpatterns() { + public Set getIpatterns() { return Collections.unmodifiableSet(ipatterns); } @@ -656,6 +650,11 @@ public Set getClusterPerms() { return Collections.unmodifiableSet(clusterPerms); } + public WildcardMatcher getClusterPermsMatchers() { + // we want to use getClusterPerms() for v6 + return null; + } + public String getName() { return name; } @@ -691,18 +690,18 @@ public IndexPatternV6 addMaskedFields(List maskedFields) { return this; } - public IndexPatternV6 addTypePerms(TypePerm typePerm) { + public void addTypePerms(TypePerm typePerm) { if (typePerm != null) { this.typePerms.add(typePerm); } - return this; } - public IndexPatternV6 setDlsQuery(String dlsQuery) { + public void addPerm(Set strings) {} + + public void setDlsQuery(String dlsQuery) { if (dlsQuery != null) { this.dlsQuery = dlsQuery; } - return this; } @Override @@ -838,7 +837,7 @@ public WildcardMatcher getPerms() { } @Override - public Set getStringPerm() { + public Set getPermsAsCollection() { return null; } @@ -960,9 +959,9 @@ public boolean matches(String index, String type, String action) { log.debug("type {} {} type pattern {}", type, b2s(matchType), tp.getTypeMatcher()); } if (matchType) { - boolean matchAction = tp.getPerms().test(action); + boolean matchAction = tp.getPermsMatcher().test(action); if (isDebugEnabled) { - log.debug("action {} {} action pattern {}", action, b2s(matchAction), tp.getPerms()); + log.debug("action {} {} action pattern {}", action, b2s(matchAction), tp.getPermsMatcher()); } return matchAction; } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 8544e62c2d..6441bbd6ed 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -76,7 +76,7 @@ public class ConfigModelV7 extends ConfigModel { protected final Logger log = LogManager.getLogger(this.getClass()); private ConfigConstants.RolesMappingResolution rolesMappingResolution; private ActionGroupResolver agr = null; - private SecurityRoles securityRoles = null; + private SecurityRolesV7 securityRoles = null; private TenantHolder tenantHolder; private RoleMappingHolder roleMappingHolder; private SecurityDynamicConfiguration roles; @@ -195,17 +195,17 @@ public Set resolvedActions(final List actions) { }; } - private SecurityRoles reload(SecurityDynamicConfiguration settings) { + private SecurityRolesV7 reload(SecurityDynamicConfiguration settings) { - final Set> futures = new HashSet<>(5000); + final Set> futures = new HashSet<>(5000); final ExecutorService execs = Executors.newFixedThreadPool(10); for (Entry securityRole : settings.getCEntries().entrySet()) { - Future future = execs.submit(new Callable() { + Future future = execs.submit(new Callable() { @Override - public SecurityRoleV7 call() throws Exception { + public SecurityRole call() throws Exception { SecurityRoleV7.Builder _securityRole = new SecurityRoleV7.Builder(securityRole.getKey()); if (securityRole.getValue() == null) { @@ -235,11 +235,11 @@ public SecurityRoleV7 call() throws Exception { final List maskedFields = permittedAliasesIndex.getMasked_fields(); for (String pat : permittedAliasesIndex.getIndex_patterns()) { - IndexPatternV7 _indexPatternV7 = new IndexPatternV7(pat); - _indexPatternV7.setDlsQuery(dls); - _indexPatternV7.addFlsFields(fls); - _indexPatternV7.addMaskedFields(maskedFields); - _indexPatternV7.addPerm(agr.resolvedActions(permittedAliasesIndex.getAllowed_actions())); + IndexPattern _indexPattern = new IndexPatternV7(pat); + _indexPattern.setDlsQuery(dls); + _indexPattern.addFlsFields(fls); + _indexPattern.addMaskedFields(maskedFields); + _indexPattern.addPerm(agr.resolvedActions(permittedAliasesIndex.getAllowed_actions())); /*for(Entry> type: permittedAliasesIndex.getValue().getTypes(-).entrySet()) { TypePerm typePerm = new TypePerm(type.getKey()); @@ -248,7 +248,7 @@ public SecurityRoleV7 call() throws Exception { _indexPattern.addTypePerms(typePerm); }*/ - _securityRole.addIndexPattern(_indexPatternV7); + _securityRole.addIndexPattern(_indexPattern); } @@ -271,8 +271,8 @@ public SecurityRoleV7 call() throws Exception { } try { - SecurityRoles _securityRoles = new SecurityRoles(futures.size()); - for (Future future : futures) { + SecurityRolesV7 _securityRoles = new SecurityRolesV7(futures.size()); + for (Future future : futures) { _securityRoles.addSecurityRole(future.get()); } @@ -289,21 +289,20 @@ public SecurityRoleV7 call() throws Exception { // beans - public static class SecurityRoles implements org.opensearch.security.securityconf.SecurityRoles { + public static class SecurityRolesV7 implements SecurityRoles { protected final Logger log = LogManager.getLogger(this.getClass()); - final Set roles; + final Set roles; - private SecurityRoles(int roleCount) { + private SecurityRolesV7(int roleCount) { roles = new HashSet<>(roleCount); } - private SecurityRoles addSecurityRole(SecurityRoleV7 securityRoleV7) { - if (securityRoleV7 != null) { - this.roles.add(securityRoleV7); + public void addSecurityRole(SecurityRole securityRole) { + if (securityRole != null) { + this.roles.add(securityRole); } - return this; } @Override @@ -319,7 +318,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRoles other = (SecurityRoles) obj; + SecurityRolesV7 other = (SecurityRolesV7) obj; if (roles == null) { if (other.roles != null) return false; } else if (!roles.equals(other.roles)) return false; @@ -331,7 +330,7 @@ public String toString() { return "roles=" + roles; } - public Set getRoles() { + public Set getRoles() { return Collections.unmodifiableSet(roles); } @@ -340,8 +339,8 @@ public Set getRoleNames() { } public SecurityRoles filter(Set keep) { - final SecurityRoles retVal = new SecurityRoles(roles.size()); - for (SecurityRoleV7 sr : roles) { + final SecurityRoles retVal = new SecurityRolesV7(roles.size()); + for (SecurityRole sr : roles) { if (keep.contains(sr.getName())) { retVal.addSecurityRole(sr); } @@ -378,7 +377,7 @@ public EvaluatedDlsFlsConfig getDlsFls( Set noFlsConcreteIndices = new HashSet<>(); Set noMaskedFieldConcreteIndices = new HashSet<>(); - for (SecurityRoleV7 role : roles) { + for (SecurityRole role : roles) { for (IndexPattern ip : role.getIpatterns()) { final Set concreteIndices = ip.concreteIndexNames(user, resolver, cs); String dls = ip.getDlsQuery(user); @@ -462,7 +461,7 @@ public Set getAllPermittedIndicesForDashboards( ClusterService cs ) { Set retVal = new HashSet<>(); - for (SecurityRoleV7 sr : roles) { + for (SecurityRole sr : roles) { retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs)); retVal.addAll(resolved.getRemoteIndices()); } @@ -472,7 +471,7 @@ public Set getAllPermittedIndicesForDashboards( // dnfof only public Set reduce(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { Set retVal = new HashSet<>(); - for (SecurityRoleV7 sr : roles) { + for (SecurityRole sr : roles) { retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs)); } if (log.isDebugEnabled()) { @@ -483,7 +482,7 @@ public Set reduce(Resolved resolved, User user, String[] actions, IndexN // return true on success public boolean get(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { - for (SecurityRoleV7 sr : roles) { + for (SecurityRole sr : roles) { if (ConfigModelV7.impliesTypePerm(sr.getIpatterns(), resolved, user, actions, resolver, cs)) { return true; } @@ -499,9 +498,8 @@ public boolean impliesClusterPermissionPermission(String action) { @Override public boolean hasExplicitClusterPermissionPermission(String action) { return roles.stream() - .map(r -> r.clusterPerms == WildcardMatcher.ANY ? WildcardMatcher.NONE : r.clusterPerms) - .filter(m -> m.test(action)) - .count() > 0; + .map(r -> r.getClusterPermsMatchers() == WildcardMatcher.ANY ? WildcardMatcher.NONE : r.getClusterPermsMatchers()) + .anyMatch(m -> m.test(action)); } // rolespan @@ -512,7 +510,7 @@ public boolean impliesTypePermGlobal( IndexNameExpressionResolver resolver, ClusterService cs ) { - Set ipatterns = new HashSet(); + Set ipatterns = new HashSet<>(); roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns())); return ConfigModelV7.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs); } @@ -530,22 +528,22 @@ private boolean containsDlsFlsConfig() { } } - public static class SecurityRoleV7 implements org.opensearch.security.securityconf.SecurityRole { + public static class SecurityRoleV7 implements SecurityRole { private final String name; - private final Set ipatterns; + private final Set ipatterns; private final WildcardMatcher clusterPerms; public static final class Builder { private final String name; private final Set clusterPerms = new HashSet<>(); - private final Set ipatterns = new HashSet<>(); + private final Set ipatterns = new HashSet<>(); public Builder(String name) { this.name = Objects.requireNonNull(name); } - public Builder addIndexPattern(IndexPatternV7 indexPatternV7) { - this.ipatterns.add(indexPatternV7); + public Builder addIndexPattern(IndexPattern indexPattern) { + this.ipatterns.add(indexPattern); return this; } @@ -556,18 +554,17 @@ public Builder addClusterPerms(Collection clusterPerms) { return this; } - public SecurityRoleV7 build() { + public SecurityRole build() { return new SecurityRoleV7(name, ipatterns, WildcardMatcher.from(clusterPerms)); } } - private SecurityRoleV7(String name, Set ipatterns, WildcardMatcher clusterPerms) { + private SecurityRoleV7(String name, Set ipatterns, WildcardMatcher clusterPerms) { this.name = Objects.requireNonNull(name); this.ipatterns = ipatterns; this.clusterPerms = clusterPerms; } - @Override public boolean impliesClusterPermission(String action) { return clusterPerms.test(action); @@ -575,7 +572,6 @@ public boolean impliesClusterPermission(String action) { // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only - @Override public Set getAllResolvedPermittedIndices( Resolved resolved, User user, @@ -585,7 +581,7 @@ public Set getAllResolvedPermittedIndices( ) { final Set retVal = new HashSet<>(); - for (IndexPatternV7 p : ipatterns) { + for (IndexPattern p : ipatterns) { // what if we cannot resolve one (for create purposes) final boolean patternMatch = p.getPerms().matchAll(actions); @@ -678,20 +674,34 @@ public String toString() { // return Collections.unmodifiableSet(tenants); // } - @Override - public Set getIpatterns() { + public Set getIpatterns() { return Collections.unmodifiableSet(ipatterns); } + public Set getClusterPerms() { + // we will use clusterPerms wildcard matchers for v7 + return null; + } + @Override + public WildcardMatcher getClusterPermsMatchers() { + return clusterPerms; + } + public String getName() { return name; } + public void addClusterPerms(Collection permittedClusterActions) {} + + public void addTenant(ConfigModelV6.Tenant tenant) {} + + public void addIndexPattern(IndexPattern indexPattern) {} + } // sg roles - public static class IndexPatternV7 implements org.opensearch.security.securityconf.IndexPattern { + public static class IndexPatternV7 implements IndexPattern { private final String indexPattern; private String dlsQuery; private final Set fls = new HashSet<>(); @@ -717,20 +727,20 @@ public IndexPatternV7 addMaskedFields(List maskedFields) { return this; } - public IndexPatternV7 addPerm(Set perms) { + public void addPerm(Set perms) { if (perms != null) { this.perms.addAll(perms); } - return this; } - public IndexPatternV7 setDlsQuery(String dlsQuery) { + public void setDlsQuery(String dlsQuery) { if (dlsQuery != null) { this.dlsQuery = dlsQuery; } - return this; } + public void addTypePerms(TypePerm typePerm) {} + @Override public int hashCode() { final int prime = 31; @@ -797,12 +807,10 @@ public Set attemptResolveIndexNames(final User user, final IndexNameExpr return getResolvedIndexPattern(user, resolver, cs, true); } - @Override public Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { return null; } - @Override public Set getTypePerms() { return null; } @@ -883,8 +891,7 @@ public WildcardMatcher getPerms() { return WildcardMatcher.from(perms); } - @Override - public Set getStringPerm() { + public Set getPermsAsCollection() { return perms; } @@ -1013,12 +1020,12 @@ private static boolean impliesTypePerm( IndexMatcherAndPermissions[] indexMatcherAndPermissions; if (resolved.isLocalAll()) { indexMatcherAndPermissions = ipatterns.stream() - .filter(indexPatternV7 -> "*".equals(indexPatternV7.getUnresolvedIndexPattern(user))) - .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getStringPerm())) + .filter(indexPattern -> "*".equals(indexPattern.getUnresolvedIndexPattern(user))) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getPermsAsCollection())) .toArray(IndexMatcherAndPermissions[]::new); } else { indexMatcherAndPermissions = ipatterns.stream() - .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getStringPerm())) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getPermsAsCollection())) .toArray(IndexMatcherAndPermissions[]::new); } return resolvedRequestedIndices.stream() diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java index e0937fa4b3..6ed83d8293 100644 --- a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java +++ b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java @@ -26,7 +26,7 @@ public interface IndexPattern { WildcardMatcher getPerms(); - Set getStringPerm(); + Set getPermsAsCollection(); WildcardMatcher getNonWildCardPerms(); @@ -51,4 +51,10 @@ public interface IndexPattern { Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs); Set getTypePerms(); + + void setDlsQuery(String dls); + + void addTypePerms(TypePerm typePerm); + + void addPerm(Set strings); } diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java index 902e351449..c95637ff62 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java @@ -14,15 +14,15 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.security.resolver.IndexResolverReplacer; +import org.opensearch.security.support.WildcardMatcher; import org.opensearch.security.user.User; +import java.util.Collection; import java.util.Set; public interface SecurityRole { boolean impliesClusterPermission(String action); - // get indices which are permitted for the given types and actions - // dnfof + opensearchDashboards special only Set getAllResolvedPermittedIndices( IndexResolverReplacer.Resolved resolved, User user, @@ -31,11 +31,17 @@ Set getAllResolvedPermittedIndices( ClusterService cs ); - @Override - String toString(); - Set getIpatterns(); + Set getClusterPerms(); + + WildcardMatcher getClusterPermsMatchers(); + String getName(); + void addClusterPerms(Collection permittedClusterActions); + + void addTenant(ConfigModelV6.Tenant tenant); + + void addIndexPattern(IndexPattern indexPattern); } diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index eae1c7657f..f0cc0dda00 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -43,6 +43,8 @@ public interface SecurityRoles { Set getRoleNames(); + void addSecurityRole(SecurityRole securityRole); + Set reduce( Resolved requestedResolved, User user, @@ -86,5 +88,4 @@ Set getAllPermittedIndicesForDashboards( SecurityRoles filter(Set roles); Set getRoles(); - } diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java index 4d6a7c677d..93bc6fa96a 100644 --- a/src/main/java/org/opensearch/security/securityconf/TypePerm.java +++ b/src/main/java/org/opensearch/security/securityconf/TypePerm.java @@ -25,11 +25,18 @@ public class TypePerm { this.typeMatcher = WildcardMatcher.ANY; } - protected TypePerm addPerms(Collection perms) { + public WildcardMatcher getTypeMatcher() { + return typeMatcher; + } + + public WildcardMatcher getPermsMatcher() { + return WildcardMatcher.from(perms); + } + + protected void addPerms(Collection perms) { if (perms != null) { this.perms.addAll(perms); } - return this; } @Override @@ -58,15 +65,6 @@ public boolean equals(Object obj) { @Override public String toString() { - return System.lineSeparator() + " typePattern=" + typeMatcher + System.lineSeparator() + " perms=" + perms; + return "TypePerm{" + "typeMatcher=" + typeMatcher + ", perms=" + perms + '}'; } - - public WildcardMatcher getTypeMatcher() { - return typeMatcher; - } - - public WildcardMatcher getPerms() { - return WildcardMatcher.from(perms); - } - } diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index bc72f308d1..b1489a18c1 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -27,7 +27,7 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; -import org.opensearch.security.securityconf.ConfigModelV7; +import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.tasks.Task; @@ -64,7 +64,7 @@ public class SecurityIndexAccessEvaluatorTest { private static final String SECURITY_INDEX = ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX; @Mock - ConfigModelV7.SecurityRoles securityRoles; + SecurityRoles securityRoles; public void setup(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionsEnabled) { evaluator = new SecurityIndexAccessEvaluator( From 429d07fbe9641e6536d76f14152111bb0b6a03c0 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Sat, 2 Sep 2023 19:12:16 -0400 Subject: [PATCH 38/46] Adds more tests for index access evaluator and refactors some code Signed-off-by: Darshit Chanpura --- .../SecurityIndexAccessEvaluator.java | 63 ++---- .../security/securityconf/ConfigModelV6.java | 6 +- .../security/securityconf/ConfigModelV7.java | 14 +- .../security/securityconf/IndexPattern.java | 34 +-- .../security/securityconf/SecurityRole.java | 21 +- .../SecurityIndexAccessEvaluatorTest.java | 214 ++++++++++++++---- 6 files changed, 229 insertions(+), 123 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index ae8d2a7c22..8132895ca2 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -127,7 +127,7 @@ public PrivilegesEvaluatorResponse evaluate( ) { final boolean isDebugEnabled = log.isDebugEnabled(); - evaluateSecuredIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled, securityRoles); + evaluateSystemIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled, securityRoles); if (requestedResolved.isLocalAll() || requestedResolved.getAllIndices().contains(securityIndex) @@ -151,12 +151,13 @@ public PrivilegesEvaluatorResponse evaluate( } /** - * Checks whether user has `system:admin/system_index` as an allowed action under any of its mapped roles + * Checks whether user has `system:admin/system_index` explicitly specified + * as an allowed action under any of its mapped roles * @param securityRoles roles in which the permission needs to be checked * @return true if user does not have permission, false otherwise */ private boolean isSystemIndexAccessProhibitedForUser(SecurityRoles securityRoles) { - // Excluding `*` allowed action as it doesn't grant system index privilege + // Excluding `*` allowed action as it shouldn't grant system index privilege Set userPermissions = securityRoles.getRoles() .stream() .flatMap(role -> role.getIpatterns().stream()) @@ -220,13 +221,23 @@ private List getAllProtectedSystemIndices(final Resolved requestedResolv /** * Is the current action allowed to be performed on security index * @param action request action on security index - * @return + * @return true if action is allowed, false otherwise */ private boolean isActionAllowed(String action) { return deniedActionsMatcher.test(action); } - private void evaluateSecuredIndicesAccess( + /** + * Perform access check on requested indices and actions for those indices + * @param action action to be performed on request indices + * @param requestedResolved this object contains all indices this request is resolved to + * @param request the action request to be used for audit logging + * @param task task in which this access check will be performed + * @param presponse the pre-response object that will eventually become a response and returned to the requester + * @param isDebugEnabled flag to indicate whether debug logging is enabled + * @param securityRoles user's roles which will be used for access evaluation + */ + private void evaluateSystemIndicesAccess( String action, Resolved requestedResolved, ActionRequest request, @@ -235,11 +246,12 @@ private void evaluateSecuredIndicesAccess( Boolean isDebugEnabled, SecurityRoles securityRoles ) { - // is the request trying to access a protected system index + // Perform access check is system index permissions are enabled + boolean containsSystemIndex = requestContainsAnySystemIndices(requestedResolved); + if (isSystemIndexPermissionEnabled) { boolean containsProtectedIndex = requestContainsAnyProtectedSystemIndices(requestedResolved); - boolean containsSystemIndex = requestContainsAnySystemIndices(requestedResolved); - if (requestContainsAnyProtectedSystemIndices(requestedResolved)) { + if (containsProtectedIndex) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( @@ -252,7 +264,7 @@ private void evaluateSecuredIndicesAccess( presponse.allowed = false; presponse.markComplete(); return; - } else if (requestContainsAnySystemIndices(requestedResolved) && isSystemIndexAccessProhibitedForUser(securityRoles)) { + } else if (containsSystemIndex && isSystemIndexAccessProhibitedForUser(securityRoles)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( @@ -266,30 +278,6 @@ private void evaluateSecuredIndicesAccess( presponse.markComplete(); return; } - - if (containsProtectedIndex || (containsSystemIndex && isSystemIndexAccessProhibitedForUser(securityRoles))) { - auditLog.logSecurityIndexAttempt(request, action, task); - - if (log.isInfoEnabled()) { - String indices = String.join( - ", ", - containsProtectedIndex ? getAllProtectedSystemIndices(requestedResolved) : getAllSystemIndices(requestedResolved) - ); - - log.info( - containsProtectedIndex - ? "{} not permitted for a regular user {} on protected system indices {}" - : "No {} permission for user roles {} to System Indices {}", - action, - securityRoles, - indices - ); - } - - presponse.allowed = false; - presponse.markComplete(); - return; - } } if (isActionAllowed(action)) { @@ -307,16 +295,13 @@ private void evaluateSecuredIndicesAccess( } } else { auditLog.logSecurityIndexAttempt(request, action, task); - log.info("{} for '_all' indices is not allowed for a regular user", action); + log.warn("{} for '_all' indices is not allowed for a regular user", action); presponse.allowed = false; presponse.markComplete(); } - } else if (requestContainsAnySystemIndices(requestedResolved)) { + } else if (containsSystemIndex && !isSystemIndexPermissionEnabled) { // if system index is enabled and system index permissions are enabled we don't need to perform any further // checks as it has already been performed via isSystemIndexAccessProhibitedForUser - if (isSystemIndexPermissionEnabled) { - return; - } if (filterSecurityIndex) { Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); @@ -342,7 +327,5 @@ private void evaluateSecuredIndicesAccess( } } } - } - } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 15a903ed2b..ad48961c56 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -675,19 +675,17 @@ public IndexPatternV6(String indexPattern) { } @Override - public IndexPatternV6 addFlsFields(List flsFields) { + public void addFlsFields(List flsFields) { if (flsFields != null) { this.fls.addAll(flsFields); } - return this; } @Override - public IndexPatternV6 addMaskedFields(List maskedFields) { + public void addMaskedFields(List maskedFields) { if (maskedFields != null) { this.maskedFields.addAll(maskedFields); } - return this; } public void addTypePerms(TypePerm typePerm) { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 6441bbd6ed..0271e3f602 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -295,7 +295,7 @@ public static class SecurityRolesV7 implements SecurityRoles { final Set roles; - private SecurityRolesV7(int roleCount) { + public SecurityRolesV7(int roleCount) { roles = new HashSet<>(roleCount); } @@ -542,16 +542,14 @@ public Builder(String name) { this.name = Objects.requireNonNull(name); } - public Builder addIndexPattern(IndexPattern indexPattern) { + public void addIndexPattern(IndexPattern indexPattern) { this.ipatterns.add(indexPattern); - return this; } - public Builder addClusterPerms(Collection clusterPerms) { + public void addClusterPerms(Collection clusterPerms) { if (clusterPerms != null) { this.clusterPerms.addAll(clusterPerms); } - return this; } public SecurityRole build() { @@ -713,18 +711,16 @@ public IndexPatternV7(String indexPattern) { this.indexPattern = Objects.requireNonNull(indexPattern); } - public IndexPatternV7 addFlsFields(List flsFields) { + public void addFlsFields(List flsFields) { if (flsFields != null) { this.fls.addAll(flsFields); } - return this; } - public IndexPatternV7 addMaskedFields(List maskedFields) { + public void addMaskedFields(List maskedFields) { if (maskedFields != null) { this.maskedFields.addAll(maskedFields); } - return this; } public void addPerm(Set perms) { diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java index 6ed83d8293..2a3bc475fa 100644 --- a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java +++ b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java @@ -20,23 +20,15 @@ import java.util.Set; public interface IndexPattern { - IndexPattern addFlsFields(List flsFields); + void addFlsFields(List flsFields); - IndexPattern addMaskedFields(List maskedFields); + void addMaskedFields(List maskedFields); - WildcardMatcher getPerms(); - - Set getPermsAsCollection(); - - WildcardMatcher getNonWildCardPerms(); - - String getDlsQuery(User user); - - Set concreteIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); + void setDlsQuery(String dls); - Set getFls(); + void addTypePerms(TypePerm typePerm); - Set getMaskedFields(); + void addPerm(Set strings); boolean hasDlsQuery(); @@ -44,17 +36,25 @@ public interface IndexPattern { boolean hasMaskedFields(); + String getDlsQuery(User user); + String getUnresolvedIndexPattern(User user); + Set getPermsAsCollection(); + + Set concreteIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); + + Set getFls(); + + Set getMaskedFields(); + Set attemptResolveIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs); Set getTypePerms(); - void setDlsQuery(String dls); - - void addTypePerms(TypePerm typePerm); + WildcardMatcher getPerms(); - void addPerm(Set strings); + WildcardMatcher getNonWildCardPerms(); } diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java index c95637ff62..53d3b7033c 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java @@ -21,8 +21,17 @@ import java.util.Set; public interface SecurityRole { + + void addClusterPerms(Collection permittedClusterActions); + + void addTenant(ConfigModelV6.Tenant tenant); + + void addIndexPattern(IndexPattern indexPattern); + boolean impliesClusterPermission(String action); + String getName(); + Set getAllResolvedPermittedIndices( IndexResolverReplacer.Resolved resolved, User user, @@ -31,17 +40,9 @@ Set getAllResolvedPermittedIndices( ClusterService cs ); - Set getIpatterns(); - Set getClusterPerms(); - WildcardMatcher getClusterPermsMatchers(); - - String getName(); - - void addClusterPerms(Collection permittedClusterActions); - - void addTenant(ConfigModelV6.Tenant tenant); + Set getIpatterns(); - void addIndexPattern(IndexPattern indexPattern); + WildcardMatcher getClusterPermsMatchers(); } diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index b1489a18c1..544ffddd24 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -27,10 +27,16 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; +import org.opensearch.security.securityconf.ConfigModelV7; +import org.opensearch.security.securityconf.IndexPattern; +import org.opensearch.security.securityconf.SecurityRole; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.tasks.Task; +import java.util.List; +import java.util.Set; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; @@ -39,6 +45,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.opensearch.security.support.ConfigConstants.SYSTEM_INDEX_PERMISSION; @RunWith(MockitoJUnitRunner.class) public class SecurityIndexAccessEvaluatorTest { @@ -61,12 +68,29 @@ public class SecurityIndexAccessEvaluatorTest { private static final String TEST_SYSTEM_INDEX = ".test_system_index"; private static final String TEST_INDEX = ".test"; - private static final String SECURITY_INDEX = ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX; - @Mock + + // @Mock + // SecurityRoles securityRoles; + SecurityRoles securityRoles; - public void setup(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionsEnabled) { + public void setup( + boolean isSystemIndexEnabled, + boolean isSystemIndexPermissionsEnabled, + String index, + boolean createIndexPatternWithSystemIndexPermission + ) { + + ConfigModelV7.SecurityRoleV7.Builder _securityRole = new ConfigModelV7.SecurityRoleV7.Builder("ble"); + IndexPattern ip = new ConfigModelV7.IndexPatternV7(index); + ip.addPerm(createIndexPatternWithSystemIndexPermission ? Set.of("*", SYSTEM_INDEX_PERMISSION) : Set.of("*")); + _securityRole.addIndexPattern(ip); + _securityRole.addClusterPerms(List.of("*")); + SecurityRole secRole = _securityRole.build(); + securityRoles = new ConfigModelV7.SecurityRolesV7(1); + securityRoles.addSecurityRole(secRole); + evaluator = new SecurityIndexAccessEvaluator( Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, TEST_SYSTEM_INDEX) @@ -89,8 +113,8 @@ public void after() { } @Test - public void testUnprotectedActionAgainstNonSystemIndex_systemIndexDisabled() { - setup(false, false); + public void testUnprotectedActionOnRegularIndex_systemIndexDisabled() { + setup(false, false, TEST_INDEX, false); final Resolved resolved = createResolved(TEST_INDEX); // Action @@ -109,9 +133,9 @@ public void testUnprotectedActionAgainstNonSystemIndex_systemIndexDisabled() { } @Test - public void testUnprotectedActionAgainstSystemIndex_systemIndexDisabled() { - setup(false, false); - final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + public void testUnprotectedActionOnRegularIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); // Action final PrivilegesEvaluatorResponse response = evaluator.evaluate( @@ -129,8 +153,8 @@ public void testUnprotectedActionAgainstSystemIndex_systemIndexDisabled() { } @Test - public void testUnprotectedActionAgainstNonSystemIndex_systemIndexPermissionDisabled() { - setup(true, false); + public void testUnprotectedActionOnRegularIndex_systemIndexPermissionEnabled() { + setup(true, true, TEST_INDEX, false); final Resolved resolved = createResolved(TEST_INDEX); // Action @@ -149,8 +173,8 @@ public void testUnprotectedActionAgainstNonSystemIndex_systemIndexPermissionDisa } @Test - public void testUnprotectedActionAgainstSystemIndex_systemIndexPermissionDisabled() { - setup(true, false); + public void testUnprotectedActionOnSystemIndex_systemIndexDisabled() { + setup(false, false, TEST_SYSTEM_INDEX, false); final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action @@ -169,9 +193,9 @@ public void testUnprotectedActionAgainstSystemIndex_systemIndexPermissionDisable } @Test - public void testUnprotectedActionAgainstNonSystemIndex_systemIndexPermissionEnabled() { - setup(true, true); - final Resolved resolved = createResolved(TEST_INDEX); + public void testUnprotectedActionOnSystemIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action final PrivilegesEvaluatorResponse response = evaluator.evaluate( @@ -189,8 +213,8 @@ public void testUnprotectedActionAgainstNonSystemIndex_systemIndexPermissionEnab } @Test - public void testUnprotectedActionAgainstSystemIndex_systemIndexPermissionEnabled() { - setup(true, true); + public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_WithoutSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, false); final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action @@ -211,9 +235,30 @@ public void testUnprotectedActionAgainstSystemIndex_systemIndexPermissionEnabled verify(log).info("No {} permission for user roles {} to System Indices {}", UNPROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); } + @Test + public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_WithSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, true); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles + ); + assertThat(response, is(presponse)); + // unprotected action is not allowed on a system index + assertThat(presponse.allowed, is(false)); + + verify(log).isDebugEnabled(); + } + @Test public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexDisabled() { - setup(false, false); + setup(false, false, TEST_SYSTEM_INDEX, false); final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); @@ -229,7 +274,7 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexDisabled() { @Test public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionDisabled() { - setup(true, false); + setup(true, false, TEST_SYSTEM_INDEX, false); final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); @@ -249,8 +294,8 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionDisable } @Test - public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled() { - setup(true, true); + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, false); final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); @@ -283,9 +328,30 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled verify(log).debug("Disable realtime for this request"); } + @Test + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, true); + + final SearchRequest searchRequest = mock(SearchRequest.class); + final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + + verify(searchRequest).requestCache(Boolean.FALSE); + verify(realtimeRequest).realtime(Boolean.FALSE); + + verify(log, times(3)).isDebugEnabled(); + verify(log).debug("Disable search request cache for this request"); + verify(log).debug("Disable realtime for this request"); + } + @Test public void testProtectedActionLocalAll_systemIndexDisabled() { - setup(false, false); + setup(false, false, TEST_SYSTEM_INDEX, false); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -296,12 +362,12 @@ public void testProtectedActionLocalAll_systemIndexDisabled() { assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); verify(log).isDebugEnabled(); - verify(log).info("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); + verify(log).warn("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } @Test public void testProtectedActionLocalAll_systemIndexPermissionDisabled() { - setup(true, false); + setup(true, false, TEST_SYSTEM_INDEX, false); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -312,12 +378,12 @@ public void testProtectedActionLocalAll_systemIndexPermissionDisabled() { assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); verify(log).isDebugEnabled(); - verify(log).info("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + verify(log).warn("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); } @Test public void testProtectedActionLocalAll_systemIndexPermissionEnabled() { - setup(true, true); + setup(true, true, TEST_SYSTEM_INDEX, false); final Resolved resolved = Resolved._LOCAL_ALL; // Action @@ -328,12 +394,48 @@ public void testProtectedActionLocalAll_systemIndexPermissionEnabled() { assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); verify(log).isDebugEnabled(); - verify(log).info("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + verify(log).warn("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnRegularIndex_systemIndexDisabled() { + setup(false, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + assertThat(presponse.allowed, is(false)); + verify(log).isDebugEnabled(); + } + + @Test + public void testProtectedActionOnRegularIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + assertThat(presponse.allowed, is(false)); + verify(log).isDebugEnabled(); + } + + @Test + public void testProtectedActionOnRegularIndex_systemIndexPermissionEnabled() { + setup(true, true, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + assertThat(presponse.allowed, is(false)); + verify(log).isDebugEnabled(); } @Test public void testProtectedActionOnSystemIndex_systemIndexDisabled() { - setup(false, false); + setup(false, false, TEST_SYSTEM_INDEX, false); final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action @@ -345,7 +447,7 @@ public void testProtectedActionOnSystemIndex_systemIndexDisabled() { @Test public void testProtectedActionOnSystemIndex_systemIndexPermissionDisabled() { - setup(true, false); + setup(true, false, TEST_SYSTEM_INDEX, false); final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action @@ -359,8 +461,8 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionDisabled() { } @Test - public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled() { - setup(true, true); + public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, false); final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action @@ -374,9 +476,21 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled() { verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); } + @Test + public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, true); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + + assertThat(presponse.allowed, is(false)); + verify(log).isDebugEnabled(); + } + @Test public void testProtectedActionOnProtectedSystemIndex_systemIndexDisabled() { - setup(false, false); + setup(false, false, SECURITY_INDEX, false); final Resolved resolved = createResolved(SECURITY_INDEX); // Action @@ -392,7 +506,7 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexDisabled() { @Test public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionDisabled() { - setup(true, false); + setup(true, false, SECURITY_INDEX, false); final Resolved resolved = createResolved(SECURITY_INDEX); // Action @@ -407,25 +521,39 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionDisab } @Test - public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled() { - setup(true, true); + public void testUnProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + testSecurityIndexAccess(UNPROTECTED_ACTION); + } + + @Test + public void testUnProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + testSecurityIndexAccess(UNPROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + testSecurityIndexAccess(PROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + testSecurityIndexAccess(PROTECTED_ACTION); + } + + private void testSecurityIndexAccess(String action) { + setup(true, true, SECURITY_INDEX, true); final Resolved resolved = createResolved(SECURITY_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, action, resolved, presponse, securityRoles); - verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + verify(auditLog).logSecurityIndexAttempt(request, action, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); - verify(log).info( - "{} not permitted for a regular user {} on protected system indices {}", - PROTECTED_ACTION, - securityRoles, - ".opendistro_security" - ); + verify(log).info("{} not permitted for a regular user {} on protected system indices {}", action, securityRoles, SECURITY_INDEX); } private Resolved createResolved(final String... indexes) { From 72021f79d218825d3faef304ecf12fc919367aaf Mon Sep 17 00:00:00 2001 From: Peter Nied Date: Tue, 5 Sep 2023 07:38:56 -0500 Subject: [PATCH 39/46] Reverting all Model changes Signed-off-by: Peter Nied --- .../security/securityconf/ConfigModelV6.java | 155 +++++++++-------- .../security/securityconf/ConfigModelV7.java | 163 +++++++++++------- .../security/securityconf/IndexPattern.java | 60 ------- .../security/securityconf/SecurityRole.java | 48 ------ .../security/securityconf/SecurityRoles.java | 3 - .../security/securityconf/TypePerm.java | 70 -------- 6 files changed, 188 insertions(+), 311 deletions(-) delete mode 100644 src/main/java/org/opensearch/security/securityconf/IndexPattern.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/SecurityRole.java delete mode 100644 src/main/java/org/opensearch/security/securityconf/TypePerm.java diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index ad48961c56..d488c9b7d1 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -208,7 +208,7 @@ private SecurityRoles reload(SecurityDynamicConfiguration settings) { @Override public SecurityRole call() throws Exception { - SecurityRole _securityRole = new SecurityRoleV6(securityRole.getKey()); + SecurityRole _securityRole = new SecurityRole(securityRole.getKey()); if (securityRole.getValue() == null) { return null; @@ -247,13 +247,13 @@ public SecurityRole call() throws Exception { final List fls = permittedAliasesIndex.getValue().get_fls_(); final List maskedFields = permittedAliasesIndex.getValue().get_masked_fields_(); - IndexPattern _indexPattern = new IndexPatternV6(permittedAliasesIndex.getKey()); + IndexPattern _indexPattern = new IndexPattern(permittedAliasesIndex.getKey()); _indexPattern.setDlsQuery(dls); _indexPattern.addFlsFields(fls); _indexPattern.addMaskedFields(maskedFields); for (Entry> type : permittedAliasesIndex.getValue().getTypes().entrySet()) { - TypePerm typePerm = new TypePerm(); + TypePerm typePerm = new TypePerm(type.getKey()); final List perms = type.getValue(); typePerm.addPerms(agr.resolvedActions(perms)); _indexPattern.addTypePerms(typePerm); @@ -280,7 +280,7 @@ public SecurityRole call() throws Exception { } try { - SecurityRolesV6 _securityRoles = new SecurityRolesV6(futures.size()); + SecurityRoles _securityRoles = new SecurityRoles(futures.size()); for (Future future : futures) { _securityRoles.addSecurityRole(future.get()); } @@ -298,20 +298,21 @@ public SecurityRole call() throws Exception { // beans - public static class SecurityRolesV6 implements SecurityRoles { + public static class SecurityRoles implements org.opensearch.security.securityconf.SecurityRoles { protected final Logger log = LogManager.getLogger(this.getClass()); final Set roles; - private SecurityRolesV6(int roleCount) { + private SecurityRoles(int roleCount) { roles = new HashSet<>(roleCount); } - public void addSecurityRole(SecurityRole securityRole) { + private SecurityRoles addSecurityRole(SecurityRole securityRole) { if (securityRole != null) { this.roles.add(securityRole); } + return this; } @Override @@ -327,7 +328,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRolesV6 other = (SecurityRolesV6) obj; + SecurityRoles other = (SecurityRoles) obj; if (roles == null) { if (other.roles != null) return false; } else if (!roles.equals(other.roles)) return false; @@ -348,7 +349,7 @@ public Set getRoleNames() { } public SecurityRoles filter(Set keep) { - final SecurityRoles retVal = new SecurityRolesV6(roles.size()); + final SecurityRoles retVal = new SecurityRoles(roles.size()); for (SecurityRole sr : roles) { if (keep.contains(sr.getName())) { retVal.addSecurityRole(sr); @@ -493,9 +494,9 @@ public boolean impliesClusterPermissionPermission(String action) { @Override public boolean hasExplicitClusterPermissionPermission(String action) { return roles.stream().map(r -> { - final WildcardMatcher m = WildcardMatcher.from(r.getClusterPerms()); + final WildcardMatcher m = WildcardMatcher.from(r.clusterPerms); return m == WildcardMatcher.ANY ? WildcardMatcher.NONE : m; - }).anyMatch(m -> m.test(action)); + }).filter(m -> m.test(action)).count() > 0; } // rolespan @@ -506,31 +507,31 @@ public boolean impliesTypePermGlobal( IndexNameExpressionResolver resolver, ClusterService cs ) { - Set ipatterns = new HashSet<>(); + Set ipatterns = new HashSet(); roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns())); return ConfigModelV6.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs); } } - public static class SecurityRoleV6 implements SecurityRole { + public static class SecurityRole { private final String name; private final Set tenants = new HashSet<>(); private final Set ipatterns = new HashSet<>(); private final Set clusterPerms = new HashSet<>(); - private SecurityRoleV6(String name) { + private SecurityRole(String name) { super(); this.name = Objects.requireNonNull(name); } - public boolean impliesClusterPermission(String action) { + private boolean impliesClusterPermission(String action) { return WildcardMatcher.from(clusterPerms).test(action); } // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only - public Set getAllResolvedPermittedIndices( + private Set getAllResolvedPermittedIndices( Resolved resolved, User user, String[] actions, @@ -545,7 +546,7 @@ public Set getAllResolvedPermittedIndices( final Set tperms = p.getTypePerms(); for (TypePerm tp : tperms) { if (tp.typeMatcher.matchAny(resolved.getTypes())) { - patternMatch = tp.getPermsMatcher().matchAll(actions); + patternMatch = tp.getPerms().matchAll(actions); } } if (patternMatch) { @@ -571,22 +572,25 @@ public Set getAllResolvedPermittedIndices( return Collections.unmodifiableSet(retVal); } - public void addTenant(Tenant tenant) { + private SecurityRole addTenant(Tenant tenant) { if (tenant != null) { this.tenants.add(tenant); } + return this; } - public void addIndexPattern(IndexPattern indexPattern) { + private SecurityRole addIndexPattern(IndexPattern indexPattern) { if (indexPattern != null) { this.ipatterns.add(indexPattern); } + return this; } - public void addClusterPerms(Collection clusterPerms) { + private SecurityRole addClusterPerms(Collection clusterPerms) { if (clusterPerms != null) { this.clusterPerms.addAll(clusterPerms); } + return this; } @Override @@ -605,7 +609,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRoleV6 other = (SecurityRoleV6) obj; + SecurityRole other = (SecurityRole) obj; if (clusterPerms == null) { if (other.clusterPerms != null) return false; } else if (!clusterPerms.equals(other.clusterPerms)) return false; @@ -650,11 +654,6 @@ public Set getClusterPerms() { return Collections.unmodifiableSet(clusterPerms); } - public WildcardMatcher getClusterPermsMatchers() { - // we want to use getClusterPerms() for v6 - return null; - } - public String getName() { return name; } @@ -662,44 +661,44 @@ public String getName() { } // sg roles - public static class IndexPatternV6 implements IndexPattern { + public static class IndexPattern { private final String indexPattern; private String dlsQuery; private final Set fls = new HashSet<>(); private final Set maskedFields = new HashSet<>(); private final Set typePerms = new HashSet<>(); - public IndexPatternV6(String indexPattern) { + public IndexPattern(String indexPattern) { super(); this.indexPattern = Objects.requireNonNull(indexPattern); } - @Override - public void addFlsFields(List flsFields) { + public IndexPattern addFlsFields(List flsFields) { if (flsFields != null) { this.fls.addAll(flsFields); } + return this; } - @Override - public void addMaskedFields(List maskedFields) { + public IndexPattern addMaskedFields(List maskedFields) { if (maskedFields != null) { this.maskedFields.addAll(maskedFields); } + return this; } - public void addTypePerms(TypePerm typePerm) { + public IndexPattern addTypePerms(TypePerm typePerm) { if (typePerm != null) { this.typePerms.add(typePerm); } + return this; } - public void addPerm(Set strings) {} - - public void setDlsQuery(String dlsQuery) { + public IndexPattern setDlsQuery(String dlsQuery) { if (dlsQuery != null) { this.dlsQuery = dlsQuery; } + return this; } @Override @@ -719,7 +718,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - IndexPatternV6 other = (IndexPatternV6) obj; + IndexPattern other = (IndexPattern) obj; if (dlsQuery == null) { if (other.dlsQuery != null) return false; } else if (!dlsQuery.equals(other.dlsQuery)) return false; @@ -758,12 +757,7 @@ public String getUnresolvedIndexPattern(User user) { return replaceProperties(indexPattern, user); } - @Override - public Set attemptResolveIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs) { - return getResolvedIndexPattern(user, resolver, cs); - } - - public Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { + private Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { String unresolved = getUnresolvedIndexPattern(user); WildcardMatcher matcher = WildcardMatcher.from(unresolved); String[] resolved = null; @@ -797,11 +791,6 @@ public String getDlsQuery(User user) { return replaceProperties(dlsQuery, user); } - @Override - public Set concreteIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs) { - return getResolvedIndexPattern(user, resolver, cs); - } - public Set getFls() { return Collections.unmodifiableSet(fls); } @@ -810,39 +799,69 @@ public Set getMaskedFields() { return Collections.unmodifiableSet(maskedFields); } - @Override - public boolean hasDlsQuery() { - return false; + public Set getTypePerms() { + return Collections.unmodifiableSet(typePerms); } - @Override - public boolean hasFlsFields() { - return false; - } + } - @Override - public boolean hasMaskedFields() { - return false; + public static class TypePerm { + private final WildcardMatcher typeMatcher; + private final Set perms = new HashSet<>(); + + private TypePerm(String typePattern) { + this.typeMatcher = WildcardMatcher.ANY; } - public Set getTypePerms() { - return Collections.unmodifiableSet(typePerms); + private TypePerm addPerms(Collection perms) { + if (perms != null) { + this.perms.addAll(perms); + } + return this; } @Override - public WildcardMatcher getPerms() { - return null; + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((perms == null) ? 0 : perms.hashCode()); + result = prime * result + ((typeMatcher == null) ? 0 : typeMatcher.hashCode()); + return result; } @Override - public Set getPermsAsCollection() { - return null; + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TypePerm other = (TypePerm) obj; + if (perms == null) { + if (other.perms != null) return false; + } else if (!perms.equals(other.perms)) return false; + if (typeMatcher == null) { + if (other.typeMatcher != null) return false; + } else if (!typeMatcher.equals(other.typeMatcher)) return false; + return true; } @Override - public WildcardMatcher getNonWildCardPerms() { - return null; + public String toString() { + return System.lineSeparator() + + " typePattern=" + + typeMatcher + + System.lineSeparator() + + " perms=" + + perms; } + + public WildcardMatcher getTypeMatcher() { + return typeMatcher; + } + + public WildcardMatcher getPerms() { + return WildcardMatcher.from(perms); + } + } public static class Tenant { @@ -957,9 +976,9 @@ public boolean matches(String index, String type, String action) { log.debug("type {} {} type pattern {}", type, b2s(matchType), tp.getTypeMatcher()); } if (matchType) { - boolean matchAction = tp.getPermsMatcher().test(action); + boolean matchAction = tp.getPerms().test(action); if (isDebugEnabled) { - log.debug("action {} {} action pattern {}", action, b2s(matchAction), tp.getPermsMatcher()); + log.debug("action {} {} action pattern {}", action, b2s(matchAction), tp.getPerms()); } return matchAction; } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 0271e3f602..6f43c43d03 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -76,7 +76,7 @@ public class ConfigModelV7 extends ConfigModel { protected final Logger log = LogManager.getLogger(this.getClass()); private ConfigConstants.RolesMappingResolution rolesMappingResolution; private ActionGroupResolver agr = null; - private SecurityRolesV7 securityRoles = null; + private SecurityRoles securityRoles = null; private TenantHolder tenantHolder; private RoleMappingHolder roleMappingHolder; private SecurityDynamicConfiguration roles; @@ -195,7 +195,7 @@ public Set resolvedActions(final List actions) { }; } - private SecurityRolesV7 reload(SecurityDynamicConfiguration settings) { + private SecurityRoles reload(SecurityDynamicConfiguration settings) { final Set> futures = new HashSet<>(5000); final ExecutorService execs = Executors.newFixedThreadPool(10); @@ -206,7 +206,7 @@ private SecurityRolesV7 reload(SecurityDynamicConfiguration settings) { @Override public SecurityRole call() throws Exception { - SecurityRoleV7.Builder _securityRole = new SecurityRoleV7.Builder(securityRole.getKey()); + SecurityRole.Builder _securityRole = new SecurityRole.Builder(securityRole.getKey()); if (securityRole.getValue() == null) { return null; @@ -235,7 +235,7 @@ public SecurityRole call() throws Exception { final List maskedFields = permittedAliasesIndex.getMasked_fields(); for (String pat : permittedAliasesIndex.getIndex_patterns()) { - IndexPattern _indexPattern = new IndexPatternV7(pat); + IndexPattern _indexPattern = new IndexPattern(pat); _indexPattern.setDlsQuery(dls); _indexPattern.addFlsFields(fls); _indexPattern.addMaskedFields(maskedFields); @@ -271,7 +271,7 @@ public SecurityRole call() throws Exception { } try { - SecurityRolesV7 _securityRoles = new SecurityRolesV7(futures.size()); + SecurityRoles _securityRoles = new SecurityRoles(futures.size()); for (Future future : futures) { _securityRoles.addSecurityRole(future.get()); } @@ -289,20 +289,21 @@ public SecurityRole call() throws Exception { // beans - public static class SecurityRolesV7 implements SecurityRoles { + public static class SecurityRoles implements org.opensearch.security.securityconf.SecurityRoles { protected final Logger log = LogManager.getLogger(this.getClass()); final Set roles; - public SecurityRolesV7(int roleCount) { + private SecurityRoles(int roleCount) { roles = new HashSet<>(roleCount); } - public void addSecurityRole(SecurityRole securityRole) { + private SecurityRoles addSecurityRole(SecurityRole securityRole) { if (securityRole != null) { this.roles.add(securityRole); } + return this; } @Override @@ -318,7 +319,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRolesV7 other = (SecurityRolesV7) obj; + SecurityRoles other = (SecurityRoles) obj; if (roles == null) { if (other.roles != null) return false; } else if (!roles.equals(other.roles)) return false; @@ -339,7 +340,7 @@ public Set getRoleNames() { } public SecurityRoles filter(Set keep) { - final SecurityRoles retVal = new SecurityRolesV7(roles.size()); + final SecurityRoles retVal = new SecurityRoles(roles.size()); for (SecurityRole sr : roles) { if (keep.contains(sr.getName())) { retVal.addSecurityRole(sr); @@ -498,8 +499,9 @@ public boolean impliesClusterPermissionPermission(String action) { @Override public boolean hasExplicitClusterPermissionPermission(String action) { return roles.stream() - .map(r -> r.getClusterPermsMatchers() == WildcardMatcher.ANY ? WildcardMatcher.NONE : r.getClusterPermsMatchers()) - .anyMatch(m -> m.test(action)); + .map(r -> r.clusterPerms == WildcardMatcher.ANY ? WildcardMatcher.NONE : r.clusterPerms) + .filter(m -> m.test(action)) + .count() > 0; } // rolespan @@ -510,7 +512,7 @@ public boolean impliesTypePermGlobal( IndexNameExpressionResolver resolver, ClusterService cs ) { - Set ipatterns = new HashSet<>(); + Set ipatterns = new HashSet(); roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns())); return ConfigModelV7.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs); } @@ -528,7 +530,7 @@ private boolean containsDlsFlsConfig() { } } - public static class SecurityRoleV7 implements SecurityRole { + public static class SecurityRole { private final String name; private final Set ipatterns; private final WildcardMatcher clusterPerms; @@ -542,35 +544,36 @@ public Builder(String name) { this.name = Objects.requireNonNull(name); } - public void addIndexPattern(IndexPattern indexPattern) { + public Builder addIndexPattern(IndexPattern indexPattern) { this.ipatterns.add(indexPattern); + return this; } - public void addClusterPerms(Collection clusterPerms) { + public Builder addClusterPerms(Collection clusterPerms) { if (clusterPerms != null) { this.clusterPerms.addAll(clusterPerms); } + return this; } public SecurityRole build() { - return new SecurityRoleV7(name, ipatterns, WildcardMatcher.from(clusterPerms)); + return new SecurityRole(name, ipatterns, WildcardMatcher.from(clusterPerms)); } } - private SecurityRoleV7(String name, Set ipatterns, WildcardMatcher clusterPerms) { + private SecurityRole(String name, Set ipatterns, WildcardMatcher clusterPerms) { this.name = Objects.requireNonNull(name); this.ipatterns = ipatterns; this.clusterPerms = clusterPerms; } - public boolean impliesClusterPermission(String action) { + private boolean impliesClusterPermission(String action) { return clusterPerms.test(action); - } // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only - public Set getAllResolvedPermittedIndices( + private Set getAllResolvedPermittedIndices( Resolved resolved, User user, String[] actions, @@ -636,7 +639,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - SecurityRoleV7 other = (SecurityRoleV7) obj; + SecurityRole other = (SecurityRole) obj; if (clusterPerms == null) { if (other.clusterPerms != null) return false; } else if (!clusterPerms.equals(other.clusterPerms)) return false; @@ -676,67 +679,53 @@ public Set getIpatterns() { return Collections.unmodifiableSet(ipatterns); } - public Set getClusterPerms() { - // we will use clusterPerms wildcard matchers for v7 - return null; - } - - @Override - public WildcardMatcher getClusterPermsMatchers() { - return clusterPerms; - } - public String getName() { return name; } - public void addClusterPerms(Collection permittedClusterActions) {} - - public void addTenant(ConfigModelV6.Tenant tenant) {} - - public void addIndexPattern(IndexPattern indexPattern) {} - } // sg roles - public static class IndexPatternV7 implements IndexPattern { + public static class IndexPattern { private final String indexPattern; private String dlsQuery; private final Set fls = new HashSet<>(); private final Set maskedFields = new HashSet<>(); private final Set perms = new HashSet<>(); - public IndexPatternV7(String indexPattern) { + public IndexPattern(String indexPattern) { super(); this.indexPattern = Objects.requireNonNull(indexPattern); } - public void addFlsFields(List flsFields) { + public IndexPattern addFlsFields(List flsFields) { if (flsFields != null) { this.fls.addAll(flsFields); } + return this; } - public void addMaskedFields(List maskedFields) { + public IndexPattern addMaskedFields(List maskedFields) { if (maskedFields != null) { this.maskedFields.addAll(maskedFields); } + return this; } - public void addPerm(Set perms) { + public IndexPattern addPerm(Set perms) { if (perms != null) { this.perms.addAll(perms); } + return this; } - public void setDlsQuery(String dlsQuery) { + public IndexPattern setDlsQuery(String dlsQuery) { if (dlsQuery != null) { this.dlsQuery = dlsQuery; } + return this; } - public void addTypePerms(TypePerm typePerm) {} - @Override public int hashCode() { final int prime = 31; @@ -754,7 +743,7 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; - IndexPatternV7 other = (IndexPatternV7) obj; + IndexPattern other = (IndexPattern) obj; if (dlsQuery == null) { if (other.dlsQuery != null) return false; } else if (!dlsQuery.equals(other.dlsQuery)) return false; @@ -803,14 +792,6 @@ public Set attemptResolveIndexNames(final User user, final IndexNameExpr return getResolvedIndexPattern(user, resolver, cs, true); } - public Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) { - return null; - } - - public Set getTypePerms() { - return null; - } - public Set getResolvedIndexPattern( final User user, final IndexNameExpressionResolver resolver, @@ -887,14 +868,72 @@ public WildcardMatcher getPerms() { return WildcardMatcher.from(perms); } - public Set getPermsAsCollection() { - return perms; + } + + /*public static class TypePerm { + private final String typePattern; + private final Set perms = new HashSet<>(); + + private TypePerm(String typePattern) { + super(); + this.typePattern = Objects.requireNonNull(typePattern); + /*if(IGNORED_TYPES.contains(typePattern)) { + throw new RuntimeException("typepattern '"+typePattern+"' not allowed"); + } + } + + private TypePerm addPerms(Collection perms) { + if (perms != null) { + this.perms.addAll(perms); + } + return this; } - public WildcardMatcher getNonWildCardPerms() { - return WildcardMatcher.from(perms.stream().filter(perm -> !perm.equals("*"))); + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((perms == null) ? 0 : perms.hashCode()); + result = prime * result + ((typePattern == null) ? 0 : typePattern.hashCode()); + return result; } - } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TypePerm other = (TypePerm) obj; + if (perms == null) { + if (other.perms != null) + return false; + } else if (!perms.equals(other.perms)) + return false; + if (typePattern == null) { + if (other.typePattern != null) + return false; + } else if (!typePattern.equals(other.typePattern)) + return false; + return true; + } + + @Override + public String toString() { + return System.lineSeparator() + " typePattern=" + typePattern + System.lineSeparator() + " perms=" + perms; + } + + public String getTypePattern() { + return typePattern; + } + + public Set getPerms() { + return Collections.unmodifiableSet(perms); + } + + }*/ public static class Tenant { private final String tenant; @@ -1017,11 +1056,11 @@ private static boolean impliesTypePerm( if (resolved.isLocalAll()) { indexMatcherAndPermissions = ipatterns.stream() .filter(indexPattern -> "*".equals(indexPattern.getUnresolvedIndexPattern(user))) - .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getPermsAsCollection())) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms)) .toArray(IndexMatcherAndPermissions[]::new); } else { indexMatcherAndPermissions = ipatterns.stream() - .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.getPermsAsCollection())) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms)) .toArray(IndexMatcherAndPermissions[]::new); } return resolvedRequestedIndices.stream() diff --git a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java b/src/main/java/org/opensearch/security/securityconf/IndexPattern.java deleted file mode 100644 index 2a3bc475fa..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/IndexPattern.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.security.support.WildcardMatcher; -import org.opensearch.security.user.User; - -import java.util.List; -import java.util.Set; - -public interface IndexPattern { - void addFlsFields(List flsFields); - - void addMaskedFields(List maskedFields); - - void setDlsQuery(String dls); - - void addTypePerms(TypePerm typePerm); - - void addPerm(Set strings); - - boolean hasDlsQuery(); - - boolean hasFlsFields(); - - boolean hasMaskedFields(); - - String getDlsQuery(User user); - - String getUnresolvedIndexPattern(User user); - - Set getPermsAsCollection(); - - Set concreteIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); - - Set getFls(); - - Set getMaskedFields(); - - Set attemptResolveIndexNames(User user, IndexNameExpressionResolver resolver, ClusterService cs); - - Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs); - - Set getTypePerms(); - - WildcardMatcher getPerms(); - - WildcardMatcher getNonWildCardPerms(); -} diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java b/src/main/java/org/opensearch/security/securityconf/SecurityRole.java deleted file mode 100644 index 53d3b7033c..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRole.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.security.resolver.IndexResolverReplacer; -import org.opensearch.security.support.WildcardMatcher; -import org.opensearch.security.user.User; - -import java.util.Collection; -import java.util.Set; - -public interface SecurityRole { - - void addClusterPerms(Collection permittedClusterActions); - - void addTenant(ConfigModelV6.Tenant tenant); - - void addIndexPattern(IndexPattern indexPattern); - - boolean impliesClusterPermission(String action); - - String getName(); - - Set getAllResolvedPermittedIndices( - IndexResolverReplacer.Resolved resolved, - User user, - String[] actions, - IndexNameExpressionResolver resolver, - ClusterService cs - ); - - Set getClusterPerms(); - - Set getIpatterns(); - - WildcardMatcher getClusterPermsMatchers(); -} diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index f0cc0dda00..c52a3c1bad 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -43,8 +43,6 @@ public interface SecurityRoles { Set getRoleNames(); - void addSecurityRole(SecurityRole securityRole); - Set reduce( Resolved requestedResolved, User user, @@ -87,5 +85,4 @@ Set getAllPermittedIndicesForDashboards( SecurityRoles filter(Set roles); - Set getRoles(); } diff --git a/src/main/java/org/opensearch/security/securityconf/TypePerm.java b/src/main/java/org/opensearch/security/securityconf/TypePerm.java deleted file mode 100644 index 93bc6fa96a..0000000000 --- a/src/main/java/org/opensearch/security/securityconf/TypePerm.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf; - -import org.opensearch.security.support.WildcardMatcher; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -public class TypePerm { - protected final WildcardMatcher typeMatcher; - private final Set perms = new HashSet<>(); - - TypePerm() { - this.typeMatcher = WildcardMatcher.ANY; - } - - public WildcardMatcher getTypeMatcher() { - return typeMatcher; - } - - public WildcardMatcher getPermsMatcher() { - return WildcardMatcher.from(perms); - } - - protected void addPerms(Collection perms) { - if (perms != null) { - this.perms.addAll(perms); - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((perms == null) ? 0 : perms.hashCode()); - result = prime * result + ((typeMatcher == null) ? 0 : typeMatcher.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - TypePerm other = (TypePerm) obj; - if (perms == null) { - if (other.perms != null) return false; - } else if (!perms.equals(other.perms)) return false; - if (typeMatcher == null) { - if (other.typeMatcher != null) return false; - } else if (!typeMatcher.equals(other.typeMatcher)) return false; - return true; - } - - @Override - public String toString() { - return "TypePerm{" + "typeMatcher=" + typeMatcher + ", perms=" + perms + '}'; - } -} From 526a824d0ddb2be1468c6a23d7251a2986089765 Mon Sep 17 00:00:00 2001 From: Peter Nied Date: Tue, 5 Sep 2023 09:40:39 -0500 Subject: [PATCH 40/46] Simpler SecurityRoles interface Signed-off-by: Peter Nied --- .../privileges/PrivilegesEvaluator.java | 2 +- .../SecurityIndexAccessEvaluator.java | 63 ++---- .../security/securityconf/ConfigModelV6.java | 5 + .../security/securityconf/ConfigModelV7.java | 38 +++- .../security/securityconf/SecurityRoles.java | 7 +- .../SecurityIndexAccessEvaluatorTest.java | 202 ++++++++---------- .../SecurityRolesPermissionsTest.java | 16 -- .../impl/v7/IndexPatternV7Tests.java | 188 ---------------- 8 files changed, 163 insertions(+), 358 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 7e743116c0..e21119a764 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -314,7 +314,7 @@ public PrivilegesEvaluatorResponse evaluate( } // Security index access - if (securityIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse, securityRoles).isComplete()) { + if (securityIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse, securityRoles, user, resolver, clusterService).isComplete()) { return presponse; } diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 8132895ca2..b0e7bdfd33 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -31,14 +31,16 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.RealtimeRequest; import org.opensearch.action.search.SearchRequest; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; -import org.opensearch.security.securityconf.IndexPattern; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; +import org.opensearch.security.user.User; import org.opensearch.tasks.Task; import java.util.ArrayList; @@ -123,11 +125,12 @@ public PrivilegesEvaluatorResponse evaluate( final String action, final Resolved requestedResolved, final PrivilegesEvaluatorResponse presponse, - final SecurityRoles securityRoles + final SecurityRoles securityRoles, + final User user, + final IndexNameExpressionResolver resolver, + final ClusterService clusterService ) { - final boolean isDebugEnabled = log.isDebugEnabled(); - - evaluateSystemIndicesAccess(action, requestedResolved, request, task, presponse, isDebugEnabled, securityRoles); + evaluateSystemIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles, user, resolver, clusterService); if (requestedResolved.isLocalAll() || requestedResolved.getAllIndices().contains(securityIndex) @@ -135,14 +138,14 @@ public PrivilegesEvaluatorResponse evaluate( if (request instanceof SearchRequest) { ((SearchRequest) request).requestCache(Boolean.FALSE); - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug("Disable search request cache for this request"); } } if (request instanceof RealtimeRequest) { ((RealtimeRequest) request).realtime(Boolean.FALSE); - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug("Disable realtime for this request"); } } @@ -150,28 +153,6 @@ public PrivilegesEvaluatorResponse evaluate( return presponse; } - /** - * Checks whether user has `system:admin/system_index` explicitly specified - * as an allowed action under any of its mapped roles - * @param securityRoles roles in which the permission needs to be checked - * @return true if user does not have permission, false otherwise - */ - private boolean isSystemIndexAccessProhibitedForUser(SecurityRoles securityRoles) { - // Excluding `*` allowed action as it shouldn't grant system index privilege - Set userPermissions = securityRoles.getRoles() - .stream() - .flatMap(role -> role.getIpatterns().stream()) - .map(IndexPattern::getNonWildCardPerms) - .collect(Collectors.toSet()); - - for (WildcardMatcher userPermission : userPermissions) { - if (userPermission.matchAny(ConfigConstants.SYSTEM_INDEX_PERMISSION)) { - return false; - } - } - return true; - } - /** * Checks if request is for any system index * @param requestedResolved request which contains indices to be matched against system indices @@ -238,13 +219,15 @@ private boolean isActionAllowed(String action) { * @param securityRoles user's roles which will be used for access evaluation */ private void evaluateSystemIndicesAccess( - String action, - Resolved requestedResolved, - ActionRequest request, - Task task, - PrivilegesEvaluatorResponse presponse, - Boolean isDebugEnabled, - SecurityRoles securityRoles + final String action, + final Resolved requestedResolved, + final ActionRequest request, + final Task task, + final PrivilegesEvaluatorResponse presponse, + SecurityRoles securityRoles, + final User user, + final IndexNameExpressionResolver resolver, + final ClusterService clusterService ) { // Perform access check is system index permissions are enabled boolean containsSystemIndex = requestContainsAnySystemIndices(requestedResolved); @@ -264,7 +247,7 @@ private void evaluateSystemIndicesAccess( presponse.allowed = false; presponse.markComplete(); return; - } else if (containsSystemIndex && isSystemIndexAccessProhibitedForUser(securityRoles)) { + } else if (containsSystemIndex && !securityRoles.hasExplicitIndexPermission(requestedResolved, user, new String[] {ConfigConstants.SYSTEM_INDEX_PERMISSION}, resolver, clusterService)) { auditLog.logSecurityIndexAttempt(request, action, task); if (log.isInfoEnabled()) { log.info( @@ -284,7 +267,7 @@ private void evaluateSystemIndicesAccess( if (requestedResolved.isLocalAll()) { if (filterSecurityIndex) { irr.replace(request, false, "*", "-" + securityIndex); - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug( "Filtered '{}' from {}, resulting list with *,-{} is {}", securityIndex, @@ -307,7 +290,7 @@ private void evaluateSystemIndicesAccess( Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); allWithoutSecurity.remove(securityIndex); if (allWithoutSecurity.isEmpty()) { - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug("Filtered '{}' but resulting list is empty", securityIndex); } presponse.allowed = false; @@ -315,7 +298,7 @@ private void evaluateSystemIndicesAccess( return; } irr.replace(request, false, allWithoutSecurity.toArray(new String[0])); - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug("Filtered '{}', resulting list is {}", securityIndex, allWithoutSecurity); } } else { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index d488c9b7d1..2e0e60ad74 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -448,6 +448,11 @@ public EvaluatedDlsFlsConfig getDlsFls( return new EvaluatedDlsFlsConfig(dlsQueries, flsFields, maskedFieldsMap); } + public boolean hasExplicitIndexPermission(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { + // TODO: Handle this scenario in V6 config + return false; + } + // opensearchDashboards special only, terms eval public Set getAllPermittedIndicesForDashboards( Resolved resolved, diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 6f43c43d03..0f4c93239c 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -34,6 +34,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -463,7 +464,7 @@ public Set getAllPermittedIndicesForDashboards( ) { Set retVal = new HashSet<>(); for (SecurityRole sr : roles) { - retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs)); + retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs, Function.identity())); retVal.addAll(resolved.getRemoteIndices()); } return Collections.unmodifiableSet(retVal); @@ -473,7 +474,7 @@ public Set getAllPermittedIndicesForDashboards( public Set reduce(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { Set retVal = new HashSet<>(); for (SecurityRole sr : roles) { - retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs)); + retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, Function.identity())); } if (log.isDebugEnabled()) { log.debug("Reduced requested resolved indices {} to permitted indices {}.", resolved, retVal.toString()); @@ -499,11 +500,37 @@ public boolean impliesClusterPermissionPermission(String action) { @Override public boolean hasExplicitClusterPermissionPermission(String action) { return roles.stream() - .map(r -> r.clusterPerms == WildcardMatcher.ANY ? WildcardMatcher.NONE : r.clusterPerms) + .map(r -> matchExplicitly(r.clusterPerms)) .filter(m -> m.test(action)) .count() > 0; } + private static WildcardMatcher matchExplicitly(final WildcardMatcher matcher) { + return matcher == WildcardMatcher.ANY ? WildcardMatcher.NONE : matcher; + } + + @Override + public boolean hasExplicitIndexPermission(final Resolved resolved, final User user, final String[] actions, final IndexNameExpressionResolver resolver, final ClusterService cs) { + + final Set indicesForRequest = new HashSet<>(resolved.getAllIndicesResolved(cs, resolver)); + if (indicesForRequest.isEmpty()) { + // If no indices could be found on the request there is no way to check for the explicit permissions + return false; + } + + final Set explicitlyAllowedIndices = roles.stream() + .map(role -> role.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, SecurityRoles::matchExplicitly)) + .flatMap(s -> s.stream()) + .collect(Collectors.toSet()); + + if (log.isDebugEnabled()) { + log.debug("ExplicitIndexPermission check indices for request {}, explicitly allowed indices {}", indicesForRequest.toString(), explicitlyAllowedIndices.toString()); + } + + indicesForRequest.removeAll(explicitlyAllowedIndices); + return indicesForRequest.isEmpty(); + } + // rolespan public boolean impliesTypePermGlobal( Resolved resolved, @@ -578,13 +605,14 @@ private Set getAllResolvedPermittedIndices( User user, String[] actions, IndexNameExpressionResolver resolver, - ClusterService cs + ClusterService cs, + Function matcherModification ) { final Set retVal = new HashSet<>(); for (IndexPattern p : ipatterns) { // what if we cannot resolve one (for create purposes) - final boolean patternMatch = p.getPerms().matchAll(actions); + final boolean patternMatch = matcherModification.apply(p.getPerms()).matchAll(actions); // final Set tperms = p.getTypePerms(); // for (TypePerm tp : tperms) { diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index c52a3c1bad..a342b4653f 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -41,6 +41,12 @@ public interface SecurityRoles { boolean hasExplicitClusterPermissionPermission(String action); + /** + * Determines if the actions are explicitly granted for indices + * @return if all indices in the request have an explicit grant for all actions + */ + boolean hasExplicitIndexPermission(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs); + Set getRoleNames(); Set reduce( @@ -84,5 +90,4 @@ Set getAllPermittedIndicesForDashboards( ); SecurityRoles filter(Set roles); - } diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 544ffddd24..f76149bbbc 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -28,8 +28,6 @@ import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; import org.opensearch.security.securityconf.ConfigModelV7; -import org.opensearch.security.securityconf.IndexPattern; -import org.opensearch.security.securityconf.SecurityRole; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.tasks.Task; @@ -81,16 +79,6 @@ public void setup( String index, boolean createIndexPatternWithSystemIndexPermission ) { - - ConfigModelV7.SecurityRoleV7.Builder _securityRole = new ConfigModelV7.SecurityRoleV7.Builder("ble"); - IndexPattern ip = new ConfigModelV7.IndexPatternV7(index); - ip.addPerm(createIndexPatternWithSystemIndexPermission ? Set.of("*", SYSTEM_INDEX_PERMISSION) : Set.of("*")); - _securityRole.addIndexPattern(ip); - _securityRole.addClusterPerms(List.of("*")); - SecurityRole secRole = _securityRole.build(); - securityRoles = new ConfigModelV7.SecurityRolesV7(1); - securityRoles.addSecurityRole(secRole); - evaluator = new SecurityIndexAccessEvaluator( Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, TEST_SYSTEM_INDEX) @@ -118,16 +106,16 @@ public void testUnprotectedActionOnRegularIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - verifyNoInteractions(presponse); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // verifyNoInteractions(presponse); + // assertThat(response, is(presponse)); verify(log).isDebugEnabled(); } @@ -138,16 +126,16 @@ public void testUnprotectedActionOnRegularIndex_systemIndexPermissionDisabled() final Resolved resolved = createResolved(TEST_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - verifyNoInteractions(presponse); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // verifyNoInteractions(presponse); + // assertThat(response, is(presponse)); verify(log).isDebugEnabled(); } @@ -158,16 +146,16 @@ public void testUnprotectedActionOnRegularIndex_systemIndexPermissionEnabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - verifyNoInteractions(presponse); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // verifyNoInteractions(presponse); + // assertThat(response, is(presponse)); verify(log).isDebugEnabled(); } @@ -178,16 +166,16 @@ public void testUnprotectedActionOnSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - verifyNoInteractions(presponse); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // verifyNoInteractions(presponse); + // assertThat(response, is(presponse)); verify(log).isDebugEnabled(); } @@ -198,16 +186,16 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - verifyNoInteractions(presponse); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // verifyNoInteractions(presponse); + // assertThat(response, is(presponse)); verify(log).isDebugEnabled(); } @@ -218,16 +206,16 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_With final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - verify(presponse).markComplete(); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // verify(presponse).markComplete(); + // assertThat(response, is(presponse)); verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); verify(log).isDebugEnabled(); @@ -241,17 +229,17 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_With final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate( - request, - null, - UNPROTECTED_ACTION, - resolved, - presponse, - securityRoles - ); - assertThat(response, is(presponse)); + // final PrivilegesEvaluatorResponse response = evaluator.evaluate( + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles + // ); + // assertThat(response, is(presponse)); // unprotected action is not allowed on a system index - assertThat(presponse.allowed, is(false)); + // assertThat(presponse.allowed, is(false)); verify(log).isDebugEnabled(); } @@ -265,9 +253,9 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); verify(log, times(3)).isDebugEnabled(); } @@ -281,9 +269,9 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionDisable final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); @@ -302,9 +290,9 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); @@ -337,9 +325,9 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); @@ -355,7 +343,7 @@ public void testProtectedActionLocalAll_systemIndexDisabled() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(log).isDebugEnabled(); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); @@ -371,7 +359,7 @@ public void testProtectedActionLocalAll_systemIndexPermissionDisabled() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(log).isDebugEnabled(); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); @@ -387,7 +375,7 @@ public void testProtectedActionLocalAll_systemIndexPermissionEnabled() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(log).isDebugEnabled(); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); @@ -403,7 +391,7 @@ public void testProtectedActionOnRegularIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); assertThat(presponse.allowed, is(false)); verify(log).isDebugEnabled(); @@ -415,7 +403,7 @@ public void testProtectedActionOnRegularIndex_systemIndexPermissionDisabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); assertThat(presponse.allowed, is(false)); verify(log).isDebugEnabled(); @@ -427,7 +415,7 @@ public void testProtectedActionOnRegularIndex_systemIndexPermissionEnabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); assertThat(presponse.allowed, is(false)); verify(log).isDebugEnabled(); @@ -439,7 +427,7 @@ public void testProtectedActionOnSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); assertThat(presponse.allowed, is(false)); verify(log).isDebugEnabled(); @@ -451,7 +439,7 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); @@ -466,7 +454,7 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withou final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); @@ -482,7 +470,7 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withSy final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); assertThat(presponse.allowed, is(false)); verify(log).isDebugEnabled(); @@ -494,7 +482,7 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(SECURITY_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); @@ -510,7 +498,7 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionDisab final Resolved resolved = createResolved(SECURITY_INDEX); // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); @@ -545,7 +533,7 @@ private void testSecurityIndexAccess(String action) { final Resolved resolved = createResolved(SECURITY_INDEX); // Action - evaluator.evaluate(request, task, action, resolved, presponse, securityRoles); + // evaluator.evaluate(request, task, action, resolved, presponse, securityRoles); verify(auditLog).logSecurityIndexAttempt(request, action, task); assertThat(presponse.allowed, is(false)); diff --git a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java index 808bf1659b..81a6ff890a 100644 --- a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java +++ b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java @@ -193,22 +193,6 @@ public void hasExplicitClusterPermissionPermissionForRestAdmin() { } } - @Test - public void testTypePerms() { - TypePerm typePerm = new TypePerm(); - TypePerm typePerm2 = new TypePerm(); - TypePerm emptyTypePerm = new TypePerm(); - - List perms = Arrays.asList("be", "asas"); - typePerm.addPerms(perms); - typePerm2.addPerms(perms); - - Assert.assertEquals(typePerm, typePerm2); - Assert.assertEquals(typePerm.hashCode(), typePerm2.hashCode()); - Assert.assertNotEquals(typePerm, emptyTypePerm); - - } - void assertHasNoPermissionsForRestApiAdminOnePermissionRole(final Endpoint allowEndpoint, final SecurityRoles allowOnlyRoleForRole) { final Collection noPermissionEndpoints = ENDPOINTS_WITH_PERMISSIONS.keySet() .stream() diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java index 9af39a8869..2ddf69c2f7 100644 --- a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java +++ b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java @@ -31,7 +31,6 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.security.securityconf.ConfigModelV7.IndexPatternV7; import org.opensearch.security.user.User; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,191 +50,4 @@ @RunWith(MockitoJUnitRunner.class) public class IndexPatternV7Tests { - @Mock - private User user; - @Mock - private IndexNameExpressionResolver resolver; - @Mock - private ClusterService clusterService; - - private IndexPatternV7 ip; - - @Before - public void before() { - ip = spy(new IndexPatternV7("defaultPattern")); - } - - @After - public void after() { - verifyNoMoreInteractions(user, resolver, clusterService); - } - - @Test - public void testCtor() { - assertThrows(NullPointerException.class, () -> new IndexPatternV7(null)); - } - - /** Ensure that concreteIndexNames sends correct parameters are sent to getResolvedIndexPattern */ - @Test - public void testConcreteIndexNamesOverload() { - doReturn(ImmutableSet.of("darn")).when(ip).getResolvedIndexPattern(user, resolver, clusterService, false); - - final Set results = ip.concreteIndexNames(user, resolver, clusterService); - - assertThat(results, contains("darn")); - - verify(ip).getResolvedIndexPattern(user, resolver, clusterService, false); - verify(ip).concreteIndexNames(user, resolver, clusterService); - verifyNoMoreInteractions(ip); - } - - /** Ensure that attemptResolveIndexNames sends correct parameters are sent to getResolvedIndexPattern */ - @Test - public void testAttemptResolveIndexNamesOverload() { - doReturn(ImmutableSet.of("yarn")).when(ip).getResolvedIndexPattern(user, resolver, clusterService, true); - - final Set results = ip.attemptResolveIndexNames(user, resolver, clusterService); - - assertThat(results, contains("yarn")); - - verify(ip).getResolvedIndexPattern(user, resolver, clusterService, true); - verify(ip).attemptResolveIndexNames(user, resolver, clusterService); - verifyNoMoreInteractions(ip); - } - - /** Verify concreteIndexNames when there are no matches */ - @Test - public void testExactNameWithNoMatches() { - doReturn("index-17").when(ip).getUnresolvedIndexPattern(user); - when(clusterService.state()).thenReturn(mock(ClusterState.class)); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17"))).thenReturn( - new String[] {} - ); - - final Set results = ip.concreteIndexNames(user, resolver, clusterService); - - assertThat(results, contains("index-17")); - - verify(clusterService).state(); - verify(ip).getUnresolvedIndexPattern(user); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17")); - } - - /** Verify concreteIndexNames on exact name matches */ - @Test - public void testExactName() { - doReturn("index-17").when(ip).getUnresolvedIndexPattern(user); - when(clusterService.state()).thenReturn(mock(ClusterState.class)); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17"))).thenReturn( - new String[] { "resolved-index-17" } - ); - - final Set results = ip.concreteIndexNames(user, resolver, clusterService); - - assertThat(results, contains("resolved-index-17")); - - verify(clusterService).state(); - verify(ip).getUnresolvedIndexPattern(user); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17")); - } - - /** Verify concreteIndexNames on multiple matches */ - @Test - public void testMultipleConcreteIndices() { - doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); - doReturn(createClusterState()).when(clusterService).state(); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*"))).thenReturn( - new String[] { "resolved-index-17", "resolved-index-18" } - ); - - final Set results = ip.concreteIndexNames(user, resolver, clusterService); - - assertThat(results, contains("resolved-index-17", "resolved-index-18")); - - verify(clusterService, times(2)).state(); - verify(ip).getUnresolvedIndexPattern(user); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*")); - } - - /** Verify concreteIndexNames when there is an alias */ - @Test - public void testMultipleConcreteIndicesWithOneAlias() { - doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); - - doReturn( - createClusterState( - new IndexShorthand("index-100", Type.ALIAS), // Name and type match - new IndexShorthand("19", Type.ALIAS) // Type matches/wrong name - ) - ).when(clusterService).state(); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100"))).thenReturn( - new String[] { "resolved-index-100" } - ); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*"))).thenReturn( - new String[] { "resolved-index-17", "resolved-index-18" } - ); - - final Set results = ip.concreteIndexNames(user, resolver, clusterService); - - assertThat(results, contains("resolved-index-100", "resolved-index-17", "resolved-index-18")); - - verify(clusterService, times(3)).state(); - verify(ip).getUnresolvedIndexPattern(user); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100")); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*")); - } - - /** Verify attemptResolveIndexNames with multiple aliases */ - @Test - public void testMultipleConcreteAliasedAndUnresolved() { - doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); - doReturn( - createClusterState( - new IndexShorthand("index-100", Type.ALIAS), // Name and type match - new IndexShorthand("index-101", Type.ALIAS), // Name and type match - new IndexShorthand("19", Type.ALIAS) // Type matches/wrong name - ) - ).when(clusterService).state(); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100"), eq("index-101"))) - .thenReturn(new String[] { "resolved-index-100", "resolved-index-101" }); - when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*"))).thenReturn( - new String[] { "resolved-index-17", "resolved-index-18" } - ); - - final Set results = ip.attemptResolveIndexNames(user, resolver, clusterService); - - assertThat(results, contains("resolved-index-100", "resolved-index-101", "resolved-index-17", "resolved-index-18", "index-1*")); - - verify(clusterService, times(3)).state(); - verify(ip).getUnresolvedIndexPattern(user); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100"), eq("index-101")); - verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*")); - } - - private ClusterState createClusterState(final IndexShorthand... indices) { - final TreeMap indexMap = new TreeMap(); - Arrays.stream(indices).forEach(indexShorthand -> { - final IndexAbstraction indexAbstraction = mock(IndexAbstraction.class); - when(indexAbstraction.getType()).thenReturn(indexShorthand.type); - indexMap.put(indexShorthand.name, indexAbstraction); - }); - - final Metadata mockMetadata = mock(Metadata.class, withSettings().strictness(Strictness.LENIENT)); - when(mockMetadata.getIndicesLookup()).thenReturn(indexMap); - - final ClusterState mockClusterState = mock(ClusterState.class, withSettings().strictness(Strictness.LENIENT)); - when(mockClusterState.getMetadata()).thenReturn(mockMetadata); - - return mockClusterState; - } - - private class IndexShorthand { - public final String name; - public final Type type; - - public IndexShorthand(final String name, final Type type) { - this.name = name; - this.type = type; - } - } } From e595953ca53aa74fcec5609a2d3a73e067f8fd65 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Tue, 5 Sep 2023 16:51:26 -0400 Subject: [PATCH 41/46] Adds more tests around system index permissions and cleans up code Signed-off-by: Darshit Chanpura --- .../privileges/PrivilegesEvaluator.java | 12 +- .../SecurityIndexAccessEvaluator.java | 33 ++- .../security/securityconf/ConfigModelV6.java | 8 +- .../security/securityconf/ConfigModelV7.java | 21 +- .../security/securityconf/SecurityRoles.java | 8 +- .../SecurityIndexAccessEvaluatorTest.java | 90 +++---- .../SecurityRolesPermissionsTest.java | 1 - .../impl/v7/IndexPatternTests.java | 241 ++++++++++++++++++ .../impl/v7/IndexPatternV7Tests.java | 53 ---- .../AbstractSystemIndicesTests.java | 16 +- .../SystemIndexDisabledTests.java | 47 ++-- .../SystemIndexPermissionDisabledTests.java | 10 +- .../SystemIndexPermissionEnabledTests.java | 35 +-- 13 files changed, 402 insertions(+), 173 deletions(-) create mode 100644 src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java delete mode 100644 src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index e21119a764..48f4db38e9 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -314,7 +314,17 @@ public PrivilegesEvaluatorResponse evaluate( } // Security index access - if (securityIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse, securityRoles, user, resolver, clusterService).isComplete()) { + if (securityIndexAccessEvaluator.evaluate( + request, + task, + action0, + requestedResolved, + presponse, + securityRoles, + user, + resolver, + clusterService + ).isComplete()) { return presponse; } diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index b0e7bdfd33..9e2e9f70f1 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -247,20 +247,27 @@ private void evaluateSystemIndicesAccess( presponse.allowed = false; presponse.markComplete(); return; - } else if (containsSystemIndex && !securityRoles.hasExplicitIndexPermission(requestedResolved, user, new String[] {ConfigConstants.SYSTEM_INDEX_PERMISSION}, resolver, clusterService)) { - auditLog.logSecurityIndexAttempt(request, action, task); - if (log.isInfoEnabled()) { - log.info( - "No {} permission for user roles {} to System Indices {}", - action, - securityRoles, - String.join(", ", getAllSystemIndices(requestedResolved)) - ); + } else if (containsSystemIndex + && !securityRoles.hasExplicitIndexPermission( + requestedResolved, + user, + new String[] { ConfigConstants.SYSTEM_INDEX_PERMISSION }, + resolver, + clusterService + )) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "No {} permission for user roles {} to System Indices {}", + action, + securityRoles, + String.join(", ", getAllSystemIndices(requestedResolved)) + ); + } + presponse.allowed = false; + presponse.markComplete(); + return; } - presponse.allowed = false; - presponse.markComplete(); - return; - } } if (isActionAllowed(action)) { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 2e0e60ad74..3423c96fd9 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -448,7 +448,13 @@ public EvaluatedDlsFlsConfig getDlsFls( return new EvaluatedDlsFlsConfig(dlsQueries, flsFields, maskedFieldsMap); } - public boolean hasExplicitIndexPermission(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { + public boolean hasExplicitIndexPermission( + Resolved resolved, + User user, + String[] actions, + IndexNameExpressionResolver resolver, + ClusterService cs + ) { // TODO: Handle this scenario in V6 config return false; } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 0f4c93239c..928981efd6 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -499,10 +499,7 @@ public boolean impliesClusterPermissionPermission(String action) { @Override public boolean hasExplicitClusterPermissionPermission(String action) { - return roles.stream() - .map(r -> matchExplicitly(r.clusterPerms)) - .filter(m -> m.test(action)) - .count() > 0; + return roles.stream().map(r -> matchExplicitly(r.clusterPerms)).filter(m -> m.test(action)).count() > 0; } private static WildcardMatcher matchExplicitly(final WildcardMatcher matcher) { @@ -510,7 +507,13 @@ private static WildcardMatcher matchExplicitly(final WildcardMatcher matcher) { } @Override - public boolean hasExplicitIndexPermission(final Resolved resolved, final User user, final String[] actions, final IndexNameExpressionResolver resolver, final ClusterService cs) { + public boolean hasExplicitIndexPermission( + final Resolved resolved, + final User user, + final String[] actions, + final IndexNameExpressionResolver resolver, + final ClusterService cs + ) { final Set indicesForRequest = new HashSet<>(resolved.getAllIndicesResolved(cs, resolver)); if (indicesForRequest.isEmpty()) { @@ -520,11 +523,15 @@ public boolean hasExplicitIndexPermission(final Resolved resolved, final User us final Set explicitlyAllowedIndices = roles.stream() .map(role -> role.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, SecurityRoles::matchExplicitly)) - .flatMap(s -> s.stream()) + .flatMap(Collection::stream) .collect(Collectors.toSet()); if (log.isDebugEnabled()) { - log.debug("ExplicitIndexPermission check indices for request {}, explicitly allowed indices {}", indicesForRequest.toString(), explicitlyAllowedIndices.toString()); + log.debug( + "ExplicitIndexPermission check indices for request {}, explicitly allowed indices {}", + indicesForRequest.toString(), + explicitlyAllowedIndices.toString() + ); } indicesForRequest.removeAll(explicitlyAllowedIndices); diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index a342b4653f..079853d581 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -45,7 +45,13 @@ public interface SecurityRoles { * Determines if the actions are explicitly granted for indices * @return if all indices in the request have an explicit grant for all actions */ - boolean hasExplicitIndexPermission(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs); + boolean hasExplicitIndexPermission( + Resolved resolved, + User user, + String[] actions, + IndexNameExpressionResolver resolver, + ClusterService cs + ); Set getRoleNames(); diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index f76149bbbc..9040b508fb 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -27,23 +27,17 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; -import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.tasks.Task; -import java.util.List; -import java.util.Set; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.opensearch.security.support.ConfigConstants.SYSTEM_INDEX_PERMISSION; @RunWith(MockitoJUnitRunner.class) public class SecurityIndexAccessEvaluatorTest { @@ -107,12 +101,12 @@ public void testUnprotectedActionOnRegularIndex_systemIndexDisabled() { // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // verifyNoInteractions(presponse); // assertThat(response, is(presponse)); @@ -127,12 +121,12 @@ public void testUnprotectedActionOnRegularIndex_systemIndexPermissionDisabled() // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // verifyNoInteractions(presponse); // assertThat(response, is(presponse)); @@ -147,12 +141,12 @@ public void testUnprotectedActionOnRegularIndex_systemIndexPermissionEnabled() { // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // verifyNoInteractions(presponse); // assertThat(response, is(presponse)); @@ -167,12 +161,12 @@ public void testUnprotectedActionOnSystemIndex_systemIndexDisabled() { // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // verifyNoInteractions(presponse); // assertThat(response, is(presponse)); @@ -187,12 +181,12 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionDisabled() { // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // verifyNoInteractions(presponse); // assertThat(response, is(presponse)); @@ -207,12 +201,12 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_With // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // verify(presponse).markComplete(); // assertThat(response, is(presponse)); @@ -230,12 +224,12 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_With // Action // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles + // request, + // null, + // UNPROTECTED_ACTION, + // resolved, + // presponse, + // securityRoles // ); // assertThat(response, is(presponse)); // unprotected action is not allowed on a system index diff --git a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java index 81a6ff890a..49a9be8a91 100644 --- a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java +++ b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java @@ -30,7 +30,6 @@ import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java new file mode 100644 index 0000000000..2b95a6e84c --- /dev/null +++ b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java @@ -0,0 +1,241 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.securityconf.impl.v7; + +import java.util.Arrays; +import java.util.Set; +import java.util.TreeMap; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.quality.Strictness; + +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexAbstraction; +import org.opensearch.cluster.metadata.IndexAbstraction.Type; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.security.securityconf.ConfigModelV7.IndexPattern; +import org.opensearch.security.user.User; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +@RunWith(MockitoJUnitRunner.class) +public class IndexPatternTests { + + @Mock + private User user; + @Mock + private IndexNameExpressionResolver resolver; + @Mock + private ClusterService clusterService; + + private IndexPattern ip; + + @Before + public void before() { + ip = spy(new IndexPattern("defaultPattern")); + } + + @After + public void after() { + verifyNoMoreInteractions(user, resolver, clusterService); + } + + @Test + public void testCtor() { + assertThrows(NullPointerException.class, () -> new IndexPattern(null)); + } + + /** Ensure that concreteIndexNames sends correct parameters are sent to getResolvedIndexPattern */ + @Test + public void testConcreteIndexNamesOverload() { + doReturn(ImmutableSet.of("darn")).when(ip).getResolvedIndexPattern(user, resolver, clusterService, false); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("darn")); + + verify(ip).getResolvedIndexPattern(user, resolver, clusterService, false); + verify(ip).concreteIndexNames(user, resolver, clusterService); + verifyNoMoreInteractions(ip); + } + + /** Ensure that attemptResolveIndexNames sends correct parameters are sent to getResolvedIndexPattern */ + @Test + public void testAttemptResolveIndexNamesOverload() { + doReturn(ImmutableSet.of("yarn")).when(ip).getResolvedIndexPattern(user, resolver, clusterService, true); + + final Set results = ip.attemptResolveIndexNames(user, resolver, clusterService); + + assertThat(results, contains("yarn")); + + verify(ip).getResolvedIndexPattern(user, resolver, clusterService, true); + verify(ip).attemptResolveIndexNames(user, resolver, clusterService); + verifyNoMoreInteractions(ip); + } + + /** Verify concreteIndexNames when there are no matches */ + @Test + public void testExactNameWithNoMatches() { + doReturn("index-17").when(ip).getUnresolvedIndexPattern(user); + when(clusterService.state()).thenReturn(mock(ClusterState.class)); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17"))).thenReturn( + new String[] {} + ); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("index-17")); + + verify(clusterService).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17")); + } + + /** Verify concreteIndexNames on exact name matches */ + @Test + public void testExactName() { + doReturn("index-17").when(ip).getUnresolvedIndexPattern(user); + when(clusterService.state()).thenReturn(mock(ClusterState.class)); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17"))).thenReturn( + new String[] { "resolved-index-17" } + ); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-17")); + + verify(clusterService).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-17")); + } + + /** Verify concreteIndexNames on multiple matches */ + @Test + public void testMultipleConcreteIndices() { + doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); + doReturn(createClusterState()).when(clusterService).state(); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*"))).thenReturn( + new String[] { "resolved-index-17", "resolved-index-18" } + ); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-17", "resolved-index-18")); + + verify(clusterService, times(2)).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*")); + } + + /** Verify concreteIndexNames when there is an alias */ + @Test + public void testMultipleConcreteIndicesWithOneAlias() { + doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); + + doReturn( + createClusterState( + new IndexShorthand("index-100", Type.ALIAS), // Name and type match + new IndexShorthand("19", Type.ALIAS) // Type matches/wrong name + ) + ).when(clusterService).state(); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100"))).thenReturn( + new String[] { "resolved-index-100" } + ); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*"))).thenReturn( + new String[] { "resolved-index-17", "resolved-index-18" } + ); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-100", "resolved-index-17", "resolved-index-18")); + + verify(clusterService, times(3)).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100")); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*")); + } + + /** Verify attemptResolveIndexNames with multiple aliases */ + @Test + public void testMultipleConcreteAliasedAndUnresolved() { + doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); + doReturn( + createClusterState( + new IndexShorthand("index-100", Type.ALIAS), // Name and type match + new IndexShorthand("index-101", Type.ALIAS), // Name and type match + new IndexShorthand("19", Type.ALIAS) // Type matches/wrong name + ) + ).when(clusterService).state(); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100"), eq("index-101"))) + .thenReturn(new String[] { "resolved-index-100", "resolved-index-101" }); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*"))).thenReturn( + new String[] { "resolved-index-17", "resolved-index-18" } + ); + + final Set results = ip.attemptResolveIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-100", "resolved-index-101", "resolved-index-17", "resolved-index-18", "index-1*")); + + verify(clusterService, times(3)).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-100"), eq("index-101")); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("index-1*")); + } + + private ClusterState createClusterState(final IndexShorthand... indices) { + final TreeMap indexMap = new TreeMap(); + Arrays.stream(indices).forEach(indexShorthand -> { + final IndexAbstraction indexAbstraction = mock(IndexAbstraction.class); + when(indexAbstraction.getType()).thenReturn(indexShorthand.type); + indexMap.put(indexShorthand.name, indexAbstraction); + }); + + final Metadata mockMetadata = mock(Metadata.class, withSettings().strictness(Strictness.LENIENT)); + when(mockMetadata.getIndicesLookup()).thenReturn(indexMap); + + final ClusterState mockClusterState = mock(ClusterState.class, withSettings().strictness(Strictness.LENIENT)); + when(mockClusterState.getMetadata()).thenReturn(mockMetadata); + + return mockClusterState; + } + + private class IndexShorthand { + public final String name; + public final Type type; + + public IndexShorthand(final String name, final Type type) { + this.name = name; + this.type = type; + } + } +} diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java deleted file mode 100644 index 2ddf69c2f7..0000000000 --- a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternV7Tests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.security.securityconf.impl.v7; - -import java.util.Arrays; -import java.util.Set; -import java.util.TreeMap; - -import com.google.common.collect.ImmutableSet; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.quality.Strictness; - -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.metadata.IndexAbstraction; -import org.opensearch.cluster.metadata.IndexAbstraction.Type; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.metadata.Metadata; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.security.user.User; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; - -@RunWith(MockitoJUnitRunner.class) -public class IndexPatternV7Tests { - -} diff --git a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java index bcc624fd22..5dcc050a37 100644 --- a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java @@ -47,7 +47,12 @@ public abstract class AbstractSystemIndicesTests extends SingleClusterTest { static final String ACCESSIBLE_ONLY_BY_SUPER_ADMIN = ".opendistro_security"; - static final List SYSTEM_INDICES = List.of(".system_index_1", ACCESSIBLE_ONLY_BY_SUPER_ADMIN); + static final String SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS = "random_system_index"; + static final List SYSTEM_INDICES = List.of( + ".system_index_1", + SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS, + ACCESSIBLE_ONLY_BY_SUPER_ADMIN + ); static final List INDICES_FOR_CREATE_REQUEST = List.of(".system_index_2"); static final String matchAllQuery = "{\n\"query\": {\"match_all\": {}}}"; @@ -174,9 +179,12 @@ void validateForbiddenResponse(RestHelper.HttpResponse response, String action, MatcherAssert.assertThat(response.getBody(), Matchers.containsStringIgnoringCase(permissionExceptionMessage(action, user))); } - void allowedExceptSecurityIndex(String index, RestHelper.HttpResponse response, String action, String user) { - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(response, action, user); + void shouldBeAllowedOnlyForAuthorizedIndices(String index, RestHelper.HttpResponse response, String action, String user) { + boolean isSecurityIndexRequest = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN); + boolean isRequestingAccessToNonAuthorizedSystemIndex = (!user.equals(allAccessUser) + && index.equals(SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS)); + if (isSecurityIndexRequest || isRequestingAccessToNonAuthorizedSystemIndex) { + validateForbiddenResponse(response, isSecurityIndexRequest ? "" : action, user); } else { assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java index 24bed0a872..e85bddecb4 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -90,7 +90,7 @@ private void testSearchWithUser(String user, Header header) throws IOException { for (String index : SYSTEM_INDICES) { // security index is only accessible by super-admin RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", header); - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) || index.startsWith(SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS)) { validateForbiddenResponse(response, "indices:data/read/search", user); } else { validateSearchResponse(response, 1); @@ -121,28 +121,33 @@ public void testDeleteAsSuperAdmin() { @Test public void testDeleteAsAdmin() { - testDeleteWithUser(allAccessUser, allAccessUserHeader); + testDeleteWithUser(allAccessUser, allAccessUserHeader, "", ""); } @Test public void testDeleteAsNormalUser() { - testDeleteWithUser(normalUser, normalUserHeader); + testDeleteWithUser(normalUser, normalUserHeader, "indices:admin/delete", "indices:data/write/delete"); } @Test public void testDeleteAsNormalUserWithoutSystemIndexAccess() { - testDeleteWithUser(normalUserWithoutSystemIndex, normalUserWithoutSystemIndexHeader); + testDeleteWithUser( + normalUserWithoutSystemIndex, + normalUserWithoutSystemIndexHeader, + "indices:admin/delete", + "indices:data/write/delete" + ); } - private void testDeleteWithUser(String user, Header header) { + private void testDeleteWithUser(String user, Header header, String indexAction, String documentAction) { RestHelper restHelper = sslRestHelper(); for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", header); - allowedExceptSecurityIndex(index, response, "", user); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, documentAction, user); response = restHelper.executeDeleteRequest(index, header); - allowedExceptSecurityIndex(index, response, "", user); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, indexAction, user); } } @@ -168,7 +173,7 @@ public void testCloseOpenAsAdmin() { for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", allAccessUserHeader); - allowedExceptSecurityIndex(index, response, "", allAccessUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", allAccessUser); // User can open the index but cannot close it response = restHelper.executePostRequest(index + "/_open", "", allAccessUserHeader); @@ -191,11 +196,15 @@ private void testCloseOpenWithUser(String user, Header header) { for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", header); - allowedExceptSecurityIndex(index, response, "", user); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "indices:admin/close", user); // User can open the index but cannot close it response = restHelper.executePostRequest(index + "/_open", "", header); - allowedExceptSecurityIndex(index, response, "indices:admin/open", user); + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) || index.equals(SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS)) { + validateForbiddenResponse(response, "indices:admin/open", user); + } else { + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } } } @@ -278,10 +287,10 @@ private void testUpdateWithUser(String user, Header header) { for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_mapping", newMappings, header); - allowedExceptSecurityIndex(index, response, "", user); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "indices:admin/mapping/put", user); response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, header); - allowedExceptSecurityIndex(index, response, "", user); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "indices:admin/settings/update", user); } } @@ -337,14 +346,14 @@ public void testSnapshotSystemIndicesAsAdmin() { assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); res = restHelper.executePostRequest(snapshotRequest + "/_restore?wait_for_completion=true", "", allAccessUserHeader); - allowedExceptSecurityIndex(index, res, "", allAccessUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, res, "", allAccessUser); res = restHelper.executePostRequest( snapshotRequest + "/_restore?wait_for_completion=true", "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", allAccessUserHeader ); - allowedExceptSecurityIndex(index, res, "", allAccessUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, res, "", allAccessUser); } } @@ -373,19 +382,17 @@ private void testSnapshotWithUser(String user, Header header) { RestHelper.HttpResponse res = restHelper.executeGetRequest(snapshotRequest); assertEquals(HttpStatus.SC_UNAUTHORIZED, res.getStatusCode()); + String action = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? "" : "indices:data/write/index, indices:admin/create"; + res = restHelper.executePostRequest(snapshotRequest + "/_restore?wait_for_completion=true", "", header); - allowedExceptSecurityIndex(index, res, "", user); + shouldBeAllowedOnlyForAuthorizedIndices(index, res, action, user); res = restHelper.executePostRequest( snapshotRequest + "/_restore?wait_for_completion=true", "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", header ); - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(res, "", user); - } else { - validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", user); - } + validateForbiddenResponse(res, action, user); } } } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java index c973c7ea4a..c3feb70b98 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java @@ -88,7 +88,7 @@ private void testSearchWithUser(String user, Header header) throws IOException { for (String index : SYSTEM_INDICES) { // security index is only accessible by super-admin RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", header); - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) || index.equals(SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS)) { validateForbiddenResponse(response, "indices:data/read/search", user); } else { // got 0 hits because system index permissions are not enabled @@ -194,7 +194,11 @@ private void testCloseOpenWithUser(String user, Header header) { // normal user cannot open or close security index response = restHelper.executePostRequest(index + "/_open", "", header); - allowedExceptSecurityIndex(index, response, "indices:admin/open", user); + if (index.startsWith(".system")) { + assertEquals(RestStatus.OK.getStatus(), response.getStatusCode()); + } else { + validateForbiddenResponse(response, "indices:admin/open", user); + } } } @@ -347,7 +351,7 @@ public void testSnapshotSystemIndicesAsAdmin() { "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", allAccessUserHeader ); - allowedExceptSecurityIndex(index, res, "", allAccessUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, res, "", allAccessUser); } } diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java index d52e7e3ccf..b9630011cc 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java @@ -75,7 +75,7 @@ public void testSearchAsNormalUser() throws Exception { for (String index : SYSTEM_INDICES) { // security index is only accessible by super-admin RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserHeader); - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { + if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) || index.equals(SYSTEM_INDEX_WITH_NO_ASSOCIATED_ROLE_PERMISSIONS)) { validateForbiddenResponse(response, "", normalUser); } else { validateSearchResponse(response, 0); @@ -94,10 +94,8 @@ public void testSearchAsNormalUserWithoutSystemIndexAccess() { // search system indices for (String index : SYSTEM_INDICES) { - // security index is only accessible by super-admin RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_search", "", normalUserWithoutSystemIndexHeader); validateForbiddenResponse(response, "", normalUserWithoutSystemIndex); - } // search all indices @@ -143,10 +141,10 @@ public void testDeleteAsNormalUser() { // permission for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executeDeleteRequest(index + "/_doc/document1", normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", normalUser); response = restHelper.executeDeleteRequest(index, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", normalUser); } } @@ -194,11 +192,11 @@ public void testCloseOpenAsNormalUser() { for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executePostRequest(index + "/_close", "", normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", normalUser); // normal user cannot open or close security index response = restHelper.executePostRequest(index + "/_open", "", normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", normalUser); } } @@ -291,10 +289,10 @@ public void testUpdateAsNormalUser() { for (String index : SYSTEM_INDICES) { RestHelper.HttpResponse response = restHelper.executePutRequest(index + "/_settings", updateIndexSettings, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", normalUser); response = restHelper.executePutRequest(index + "/_mapping", newMappings, normalUserHeader); - allowedExceptSecurityIndex(index, response, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, response, "", normalUser); } } @@ -377,7 +375,7 @@ public void testSnapshotSystemIndicesAsAdmin() { "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", allAccessUserHeader ); - allowedExceptSecurityIndex(index, res, "", allAccessUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, res, "", allAccessUser); } } @@ -401,18 +399,16 @@ public void testSnapshotSystemIndicesAsNormalUser() { "", normalUserHeader ); - allowedExceptSecurityIndex(index, res, "", normalUser); + shouldBeAllowedOnlyForAuthorizedIndices(index, res, "", normalUser); res = restHelper.executePostRequest( "_snapshot/" + index + "/" + index + "_1/_restore?wait_for_completion=true", "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", normalUserHeader ); - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(res, "", normalUser); - } else { - validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUser); - } + + String action = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? "" : "indices:data/write/index, indices:admin/create"; + validateForbiddenResponse(res, action, normalUser); } } @@ -443,11 +439,8 @@ public void testSnapshotSystemIndicesAsNormalUserWithoutSystemIndexAccess() { "{ \"rename_pattern\": \"(.+)\", \"rename_replacement\": \"restored_index_with_global_state_$1\" }", normalUserWithoutSystemIndexHeader ); - if (index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN)) { - validateForbiddenResponse(res, "", normalUserWithoutSystemIndex); - } else { - validateForbiddenResponse(res, "indices:data/write/index, indices:admin/create", normalUserWithoutSystemIndex); - } + String action = index.equals(ACCESSIBLE_ONLY_BY_SUPER_ADMIN) ? "" : "indices:data/write/index, indices:admin/create"; + validateForbiddenResponse(res, action, normalUserWithoutSystemIndex); } } } From 7d00991a0f8e8b42e8aa0405a6311c728e7acc06 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Tue, 5 Sep 2023 20:04:58 -0400 Subject: [PATCH 42/46] Adds tests to SecurityIndexAccessEvaluator Signed-off-by: Darshit Chanpura --- .../SecurityIndexAccessEvaluatorTest.java | 405 ++++++++++++------ 1 file changed, 272 insertions(+), 133 deletions(-) diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 9040b508fb..dc95a0dbe0 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -23,21 +23,36 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; +import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; import org.opensearch.tasks.Task; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.opensearch.security.support.ConfigConstants.SYSTEM_INDEX_PERMISSION; @RunWith(MockitoJUnitRunner.class) public class SecurityIndexAccessEvaluatorTest { @@ -54,6 +69,9 @@ public class SecurityIndexAccessEvaluatorTest { private PrivilegesEvaluatorResponse presponse; @Mock private Logger log; + @Mock + ClusterService cs; + private SecurityIndexAccessEvaluator evaluator; private static final String UNPROTECTED_ACTION = "indices:data/read"; private static final String PROTECTED_ACTION = "indices:data/write"; @@ -62,17 +80,62 @@ public class SecurityIndexAccessEvaluatorTest { private static final String TEST_INDEX = ".test"; private static final String SECURITY_INDEX = ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX; - // @Mock - // SecurityRoles securityRoles; - + @Mock SecurityRoles securityRoles; + User user; + + IndexNameExpressionResolver indexNameExpressionResolver; + + private ThreadContext createThreadContext() { + return new ThreadContext(Settings.EMPTY); + } + + protected IndexNameExpressionResolver createIndexNameExpressionResolver(ThreadContext threadContext) { + return new IndexNameExpressionResolver(threadContext); + } + public void setup( boolean isSystemIndexEnabled, boolean isSystemIndexPermissionsEnabled, String index, boolean createIndexPatternWithSystemIndexPermission ) { + ThreadContext threadContext = createThreadContext(); + indexNameExpressionResolver = createIndexNameExpressionResolver(threadContext); + + // create a security role + ConfigModelV7.IndexPattern ip = spy(new ConfigModelV7.IndexPattern(index)); + ConfigModelV7.SecurityRole.Builder _securityRole = new ConfigModelV7.SecurityRole.Builder("role_a"); + ip.addPerm(createIndexPatternWithSystemIndexPermission ? Set.of("*", SYSTEM_INDEX_PERMISSION) : Set.of("*")); + _securityRole.addIndexPattern(ip); + _securityRole.addClusterPerms(List.of("*")); + ConfigModelV7.SecurityRole secRole = _securityRole.build(); + + try { + // create an instance of Security Role + Constructor constructor = ConfigModelV7.SecurityRoles.class.getDeclaredConstructor(int.class); + constructor.setAccessible(true); + securityRoles = constructor.newInstance(1); + + // add security role to Security Roles + Method addSecurityRoleMethod = ConfigModelV7.SecurityRoles.class.getDeclaredMethod( + "addSecurityRole", + ConfigModelV7.SecurityRole.class + ); + addSecurityRoleMethod.setAccessible(true); + addSecurityRoleMethod.invoke(securityRoles, secRole); + + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + // create a user and associate them with the role + user = new User("user_a"); + user.addSecurityRoles(List.of("role_a")); + + // when trying to resolve Index Names + evaluator = new SecurityIndexAccessEvaluator( Settings.builder() .put(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, TEST_SYSTEM_INDEX) @@ -87,6 +150,7 @@ public void setup( when(log.isDebugEnabled()).thenReturn(true); when(log.isInfoEnabled()).thenReturn(true); + doReturn(ImmutableSet.of(index)).when(ip).getResolvedIndexPattern(user, indexNameExpressionResolver, cs, true); } @After @@ -100,18 +164,20 @@ public void testUnprotectedActionOnRegularIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // verifyNoInteractions(presponse); - // assertThat(response, is(presponse)); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); - verify(log).isDebugEnabled(); } @Test @@ -120,18 +186,19 @@ public void testUnprotectedActionOnRegularIndex_systemIndexPermissionDisabled() final Resolved resolved = createResolved(TEST_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // verifyNoInteractions(presponse); - // assertThat(response, is(presponse)); - - verify(log).isDebugEnabled(); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); } @Test @@ -140,18 +207,19 @@ public void testUnprotectedActionOnRegularIndex_systemIndexPermissionEnabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // verifyNoInteractions(presponse); - // assertThat(response, is(presponse)); - - verify(log).isDebugEnabled(); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); } @Test @@ -160,18 +228,19 @@ public void testUnprotectedActionOnSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // verifyNoInteractions(presponse); - // assertThat(response, is(presponse)); - - verify(log).isDebugEnabled(); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); } @Test @@ -180,18 +249,19 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // verifyNoInteractions(presponse); - // assertThat(response, is(presponse)); - - verify(log).isDebugEnabled(); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); } @Test @@ -200,19 +270,21 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_With final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // verify(presponse).markComplete(); - // assertThat(response, is(presponse)); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verify(presponse).markComplete(); + assertThat(response, is(presponse)); verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); - verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); verify(log).info("No {} permission for user roles {} to System Indices {}", UNPROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); } @@ -223,19 +295,20 @@ public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_With final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // final PrivilegesEvaluatorResponse response = evaluator.evaluate( - // request, - // null, - // UNPROTECTED_ACTION, - // resolved, - // presponse, - // securityRoles - // ); - // assertThat(response, is(presponse)); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + assertThat(response, is(presponse)); // unprotected action is not allowed on a system index - // assertThat(presponse.allowed, is(false)); - - verify(log).isDebugEnabled(); + assertThat(presponse.allowed, is(false)); } @Test @@ -247,11 +320,31 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); - verify(log, times(3)).isDebugEnabled(); + verifyNoInteractions(presponse); } @Test @@ -263,14 +356,34 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionDisable final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); - verify(log, times(3)).isDebugEnabled(); + verify(log, times(2)).isDebugEnabled(); verify(log).debug("Disable search request cache for this request"); verify(log).debug("Disable realtime for this request"); } @@ -284,21 +397,41 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); - verify(log, times(3)).isDebugEnabled(); + verify(log, times(2)).isDebugEnabled(); verify(log).debug("Disable search request cache for this request"); verify(log).debug("Disable realtime for this request"); verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); verify(auditLog).logSecurityIndexAttempt(searchRequest, UNPROTECTED_ACTION, null); verify(auditLog).logSecurityIndexAttempt(realtimeRequest, UNPROTECTED_ACTION, null); verify(presponse, times(3)).markComplete(); - verify(log, times(3)).isDebugEnabled(); + verify(log, times(2)).isDebugEnabled(); verify(log, times(3)).isInfoEnabled(); verify(log, times(3)).info( "No {} permission for user roles {} to System Indices {}", @@ -319,14 +452,34 @@ public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); - // evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); - verify(log, times(3)).isDebugEnabled(); + verify(log, times(2)).isDebugEnabled(); verify(log).debug("Disable search request cache for this request"); verify(log).debug("Disable realtime for this request"); } @@ -337,13 +490,11 @@ public void testProtectedActionLocalAll_systemIndexDisabled() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); - verify(log).isDebugEnabled(); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).warn("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } @@ -353,13 +504,11 @@ public void testProtectedActionLocalAll_systemIndexPermissionDisabled() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); - verify(log).isDebugEnabled(); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).warn("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); } @@ -369,13 +518,11 @@ public void testProtectedActionLocalAll_systemIndexPermissionEnabled() { final Resolved resolved = Resolved._LOCAL_ALL; // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); - verify(log).isDebugEnabled(); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).warn("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); } @@ -385,10 +532,9 @@ public void testProtectedActionOnRegularIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); assertThat(presponse.allowed, is(false)); - verify(log).isDebugEnabled(); } @Test @@ -397,10 +543,9 @@ public void testProtectedActionOnRegularIndex_systemIndexPermissionDisabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); assertThat(presponse.allowed, is(false)); - verify(log).isDebugEnabled(); } @Test @@ -409,10 +554,9 @@ public void testProtectedActionOnRegularIndex_systemIndexPermissionEnabled() { final Resolved resolved = createResolved(TEST_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); assertThat(presponse.allowed, is(false)); - verify(log).isDebugEnabled(); } @Test @@ -421,10 +565,9 @@ public void testProtectedActionOnSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); assertThat(presponse.allowed, is(false)); - verify(log).isDebugEnabled(); } @Test @@ -433,12 +576,11 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionDisabled() { final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, TEST_SYSTEM_INDEX); } @@ -448,26 +590,25 @@ public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withou final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); } @Test public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, true); final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); assertThat(presponse.allowed, is(false)); - verify(log).isDebugEnabled(); } @Test @@ -476,13 +617,12 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexDisabled() { final Resolved resolved = createResolved(SECURITY_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, SECURITY_INDEX); } @@ -492,23 +632,22 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionDisab final Resolved resolved = createResolved(SECURITY_INDEX); // Action - // evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, SECURITY_INDEX); } @Test - public void testUnProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + public void testUnprotectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { testSecurityIndexAccess(UNPROTECTED_ACTION); } @Test - public void testUnProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + public void testUnprotectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { testSecurityIndexAccess(UNPROTECTED_ACTION); } @@ -524,16 +663,16 @@ public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabl private void testSecurityIndexAccess(String action) { setup(true, true, SECURITY_INDEX, true); + final Resolved resolved = createResolved(SECURITY_INDEX); // Action - // evaluator.evaluate(request, task, action, resolved, presponse, securityRoles); + evaluator.evaluate(request, task, action, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, action, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - verify(log).isDebugEnabled(); verify(log).isInfoEnabled(); verify(log).info("{} not permitted for a regular user {} on protected system indices {}", action, securityRoles, SECURITY_INDEX); } From c2ab7e6050d5b3c3aa7ae8cbe665d3b85490d777 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 6 Sep 2023 09:02:01 -0400 Subject: [PATCH 43/46] Addresses most of the PR feedback Signed-off-by: Darshit Chanpura --- .../privileges/SecurityIndexAccessEvaluator.java | 14 ++++++++------ .../org/opensearch/security/IntegrationTests.java | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 9e2e9f70f1..efe311aa3d 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -82,7 +82,7 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, this.systemIndexMatcher = WildcardMatcher.from( settings.getAsList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT) ); - this.protectedSystemIndexMatcher = WildcardMatcher.from(ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); + this.protectedSystemIndexMatcher = WildcardMatcher.from(this.securityIndex); this.isSystemIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT @@ -215,8 +215,10 @@ private boolean isActionAllowed(String action) { * @param request the action request to be used for audit logging * @param task task in which this access check will be performed * @param presponse the pre-response object that will eventually become a response and returned to the requester - * @param isDebugEnabled flag to indicate whether debug logging is enabled * @param securityRoles user's roles which will be used for access evaluation + * @param user this user's permissions will be looked up + * @param resolver the index expression resolver + * @param clusterService required to fetch cluster state metadata */ private void evaluateSystemIndicesAccess( final String action, @@ -289,10 +291,10 @@ private void evaluateSystemIndicesAccess( presponse.allowed = false; presponse.markComplete(); } - } else if (containsSystemIndex && !isSystemIndexPermissionEnabled) { - // if system index is enabled and system index permissions are enabled we don't need to perform any further - // checks as it has already been performed via isSystemIndexAccessProhibitedForUser - + } + // if system index is enabled and system index permissions are enabled we don't need to perform any further + // checks as it has already been performed via hasExplicitIndexPermission + else if (containsSystemIndex && !isSystemIndexPermissionEnabled) { if (filterSecurityIndex) { Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); allWithoutSecurity.remove(securityIndex); diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index 9a23e4f98e..63ae25316b 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -1047,7 +1047,7 @@ public void testSecurityIndexSecurity() throws Exception { ); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); res = rh.executePostRequest(".opendistro_security/_freeze", "", encodeBasicHeader("nagilum", "nagilum")); - Assert.assertTrue(res.getStatusCode() >= 400); + Assert.assertEquals(400, res.getStatusCode()); String bulkBody = "{ \"index\" : { \"_index\" : \".opendistro_security\", \"_id\" : \"1\" } }\n" + "{ \"field1\" : \"value1\" }\n" From 8f48a100f6431e96e6fbecc5a56746da10527a8d Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 6 Sep 2023 10:10:34 -0400 Subject: [PATCH 44/46] Fills the hasExplicitIndexPermission method in v6 config Signed-off-by: Darshit Chanpura --- .../security/securityconf/ConfigModelV6.java | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 3423c96fd9..99d73fd333 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -455,8 +455,27 @@ public boolean hasExplicitIndexPermission( IndexNameExpressionResolver resolver, ClusterService cs ) { - // TODO: Handle this scenario in V6 config - return false; + final Set indicesForRequest = new HashSet<>(resolved.getAllIndicesResolved(cs, resolver)); + if (indicesForRequest.isEmpty()) { + // If no indices could be found on the request there is no way to check for the explicit permissions + return false; + } + + final Set explicitlyAllowedIndices = roles.stream() + .map(role -> role.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, true)) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + if (log.isDebugEnabled()) { + log.debug( + "ExplicitIndexPermission check indices for request {}, explicitly allowed indices {}", + indicesForRequest.toString(), + explicitlyAllowedIndices.toString() + ); + } + + indicesForRequest.removeAll(explicitlyAllowedIndices); + return indicesForRequest.isEmpty(); } // opensearchDashboards special only, terms eval @@ -469,7 +488,7 @@ public Set getAllPermittedIndicesForDashboards( ) { Set retVal = new HashSet<>(); for (SecurityRole sr : roles) { - retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs)); + retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs, false)); retVal.addAll(resolved.getRemoteIndices()); } return Collections.unmodifiableSet(retVal); @@ -479,7 +498,7 @@ public Set getAllPermittedIndicesForDashboards( public Set reduce(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { Set retVal = new HashSet<>(); for (SecurityRole sr : roles) { - retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs)); + retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, false)); } if (log.isDebugEnabled()) { log.debug("Reduced requested resolved indices {} to permitted indices {}.", resolved, retVal.toString()); @@ -547,7 +566,8 @@ private Set getAllResolvedPermittedIndices( User user, String[] actions, IndexNameExpressionResolver resolver, - ClusterService cs + ClusterService cs, + boolean matchExplicitly ) { final Set retVal = new HashSet<>(); @@ -556,7 +576,11 @@ private Set getAllResolvedPermittedIndices( boolean patternMatch = false; final Set tperms = p.getTypePerms(); for (TypePerm tp : tperms) { - if (tp.typeMatcher.matchAny(resolved.getTypes())) { + // if matchExplicitly is true we don't want to match against `*` pattern + WildcardMatcher matcher = matchExplicitly && (tp.getTypeMatcher() == WildcardMatcher.ANY) + ? WildcardMatcher.NONE + : tp.getTypeMatcher(); + if (matcher.matchAny(resolved.getTypes())) { patternMatch = tp.getPerms().matchAll(actions); } } From 4fde43ececa773d7f403926eaa1330617c5b945e Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 6 Sep 2023 15:41:14 -0400 Subject: [PATCH 45/46] Fixes ConfigModelV6 hasExplicitPermission and adds test Signed-off-by: Darshit Chanpura --- .../security/securityconf/ConfigModelV6.java | 2 +- .../SecurityRolesPermissionsV6Test.java | 180 ++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 99d73fd333..84109c845e 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -577,7 +577,7 @@ private Set getAllResolvedPermittedIndices( final Set tperms = p.getTypePerms(); for (TypePerm tp : tperms) { // if matchExplicitly is true we don't want to match against `*` pattern - WildcardMatcher matcher = matchExplicitly && (tp.getTypeMatcher() == WildcardMatcher.ANY) + WildcardMatcher matcher = matchExplicitly && (tp.getPerms() == WildcardMatcher.ANY) ? WildcardMatcher.NONE : tp.getTypeMatcher(); if (matcher.matchAny(resolved.getTypes())) { diff --git a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java new file mode 100644 index 0000000000..edf5a7533b --- /dev/null +++ b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsV6Test.java @@ -0,0 +1,180 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.securityconf; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.quality.Strictness; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexAbstraction; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; +import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.resolver.IndexResolverReplacer; +import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +public class SecurityRolesPermissionsV6Test { + static final String TEST_INDEX = ".test"; + + // a role with * permission but no system:admin/system_index permission + static final Map NO_EXPLICIT_SYSTEM_INDEX_PERMISSION = ImmutableMap.builder() + .put("all_access_without_system_index_permission", role(new String[] { "*" }, new String[] { TEST_INDEX }, new String[] { "*" })) + .build(); + + static final Map HAS_SYSTEM_INDEX_PERMISSION = ImmutableMap.builder() + .put( + "has_system_index_permission", + role(new String[] { "*" }, new String[] { TEST_INDEX }, new String[] { ConfigConstants.SYSTEM_INDEX_PERMISSION }) + ) + .build(); + + static ObjectNode role(final String[] clusterPermissions, final String[] indexPatterns, final String[] allowedActions) { + ObjectMapper objectMapper = DefaultObjectMapper.objectMapper; + // form cluster permissions + final ArrayNode clusterPermissionsArrayNode = objectMapper.createArrayNode(); + Arrays.stream(clusterPermissions).forEach(clusterPermissionsArrayNode::add); + + // form index_permissions + ArrayNode permissions = objectMapper.createArrayNode(); + Arrays.stream(allowedActions).forEach(permissions::add); // permission in v6 format + + ObjectNode permissionNode = objectMapper.createObjectNode(); + permissionNode.set("*", permissions); // type : "*" + + ObjectNode indexPermission = objectMapper.createObjectNode(); + indexPermission.set("*", permissionNode); // '*' -> all indices + + // add both to the role + ObjectNode role = objectMapper.createObjectNode(); + role.put("readonly", true); + role.set("cluster", clusterPermissionsArrayNode); + role.set("indices", indexPermission); + + return role; + } + + final ConfigModel configModel; + + public SecurityRolesPermissionsV6Test() throws IOException { + this.configModel = new ConfigModelV6( + createRolesConfig(), + createRoleMappingsConfig(), + createActionGroupsConfig(), + mock(DynamicConfigModel.class), + Settings.EMPTY + ); + } + + @Test + public void hasExplicitIndexPermission() { + IndexNameExpressionResolver resolver = mock(IndexNameExpressionResolver.class); + User user = new User("test"); + ClusterService cs = mock(ClusterService.class); + doReturn(createClusterState(new IndexShorthand(TEST_INDEX, IndexAbstraction.Type.ALIAS))).when(cs).state(); + IndexResolverReplacer.Resolved resolved = createResolved(TEST_INDEX); + + // test hasExplicitIndexPermission + final SecurityRoles securityRoleWithStarAccess = configModel.getSecurityRoles() + .filter(ImmutableSet.of("all_access_without_system_index_permission")); + user.addSecurityRoles(List.of("all_access_without_system_index_permission")); + + Assert.assertFalse( + "Should not allow system index access with * only", + securityRoleWithStarAccess.hasExplicitIndexPermission(resolved, user, new String[] {}, resolver, cs) + ); + + final SecurityRoles securityRoleWithExplicitAccess = configModel.getSecurityRoles() + .filter(ImmutableSet.of("has_system_index_permission")); + user.addSecurityRoles(List.of("has_system_index_permission")); + + Assert.assertTrue( + "Should allow system index access with explicit only", + securityRoleWithExplicitAccess.hasExplicitIndexPermission(resolved, user, new String[] {}, resolver, cs) + ); + } + + static SecurityDynamicConfiguration createRolesConfig() throws IOException { + final ObjectNode rolesNode = DefaultObjectMapper.objectMapper.createObjectNode(); + NO_EXPLICIT_SYSTEM_INDEX_PERMISSION.forEach(rolesNode::set); + HAS_SYSTEM_INDEX_PERMISSION.forEach(rolesNode::set); + return SecurityDynamicConfiguration.fromNode(rolesNode, CType.ROLES, 1, 0, 0); + } + + static SecurityDynamicConfiguration createRoleMappingsConfig() throws IOException { + final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); + return SecurityDynamicConfiguration.fromNode(metaNode, CType.ROLESMAPPING, 1, 0, 0); + } + + static SecurityDynamicConfiguration createActionGroupsConfig() throws IOException { + final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); + return SecurityDynamicConfiguration.fromNode(metaNode, CType.ACTIONGROUPS, 1, 0, 0); + } + + private IndexResolverReplacer.Resolved createResolved(final String... indexes) { + return new IndexResolverReplacer.Resolved( + ImmutableSet.of(), + ImmutableSet.copyOf(indexes), + ImmutableSet.copyOf(indexes), + ImmutableSet.of(), + IndicesOptions.STRICT_EXPAND_OPEN + ); + } + + private ClusterState createClusterState(final IndexShorthand... indices) { + final TreeMap indexMap = new TreeMap(); + Arrays.stream(indices).forEach(indexShorthand -> { + final IndexAbstraction indexAbstraction = mock(IndexAbstraction.class); + when(indexAbstraction.getType()).thenReturn(indexShorthand.type); + indexMap.put(indexShorthand.name, indexAbstraction); + }); + + final Metadata mockMetadata = mock(Metadata.class, withSettings().strictness(Strictness.LENIENT)); + when(mockMetadata.getIndicesLookup()).thenReturn(indexMap); + + final ClusterState mockClusterState = mock(ClusterState.class, withSettings().strictness(Strictness.LENIENT)); + when(mockClusterState.getMetadata()).thenReturn(mockMetadata); + + return mockClusterState; + } + + private class IndexShorthand { + public final String name; + public final IndexAbstraction.Type type; + + public IndexShorthand(final String name, final IndexAbstraction.Type type) { + this.name = name; + this.type = type; + } + } +} From 98832d98b10be302a7dd7cbdd4aaa694d85593f9 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 6 Sep 2023 15:44:42 -0400 Subject: [PATCH 46/46] Refactors some variables in SecurityIndexAccessEvaluator Signed-off-by: Darshit Chanpura --- .../privileges/SecurityIndexAccessEvaluator.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index efe311aa3d..3743b27383 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -65,7 +65,7 @@ public class SecurityIndexAccessEvaluator { private final boolean filterSecurityIndex; // for system-indices configuration private final WildcardMatcher systemIndexMatcher; - private final WildcardMatcher protectedSystemIndexMatcher; + private final WildcardMatcher superAdminAccessOnlyIndexMatcher; private final WildcardMatcher deniedActionsMatcher; private final boolean isSystemIndexEnabled; @@ -82,7 +82,7 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, this.systemIndexMatcher = WildcardMatcher.from( settings.getAsList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT) ); - this.protectedSystemIndexMatcher = WildcardMatcher.from(this.securityIndex); + this.superAdminAccessOnlyIndexMatcher = WildcardMatcher.from(this.securityIndex); this.isSystemIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT @@ -92,14 +92,14 @@ public SecurityIndexAccessEvaluator(final Settings settings, AuditLog auditLog, false ); - final List securityIndexDeniedActionPatternsList = deniedActionPatterns(); + final List deniedActionPatternsList = deniedActionPatterns(); - final List securityIndexDeniedActionPatternsListNoSnapshot = new ArrayList<>(securityIndexDeniedActionPatternsList); - securityIndexDeniedActionPatternsListNoSnapshot.add("indices:admin/close*"); - securityIndexDeniedActionPatternsListNoSnapshot.add("cluster:admin/snapshot/restore*"); + final List deniedActionPatternsListNoSnapshot = new ArrayList<>(deniedActionPatternsList); + deniedActionPatternsListNoSnapshot.add("indices:admin/close*"); + deniedActionPatternsListNoSnapshot.add("cluster:admin/snapshot/restore*"); deniedActionsMatcher = WildcardMatcher.from( - restoreSecurityIndexEnabled ? securityIndexDeniedActionPatternsList : securityIndexDeniedActionPatternsListNoSnapshot + restoreSecurityIndexEnabled ? deniedActionPatternsList : deniedActionPatternsListNoSnapshot ); isSystemIndexPermissionEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, @@ -196,7 +196,7 @@ private boolean requestContainsAnyProtectedSystemIndices(final Resolved requeste * @return the list of protected system indices present in the request */ private List getAllProtectedSystemIndices(final Resolved requestedResolved) { - return new ArrayList<>(protectedSystemIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + return new ArrayList<>(superAdminAccessOnlyIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); } /**