From 543f5dca68fcb465197f17719ae64e025647c9d9 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 17 Jul 2023 17:38:11 -0400 Subject: [PATCH 1/6] Update imports for files refactored in core PR #8157 (#3003) * Update imports for files refactored in core PR #8157 Signed-off-by: Craig Perkins * Update references to old packages in test files Signed-off-by: Craig Perkins * Get remaining bad imports in integrationTest Signed-off-by: Craig Perkins * Update log4j in bwc build.gradle Signed-off-by: Craig Perkins * Use versions.log4j Signed-off-by: Craig Perkins * Also reference guava version Signed-off-by: Craig Perkins * Update integtest.sh Signed-off-by: Craig Perkins * Update tests that expect certain amount of headers in a response Signed-off-by: Craig Perkins * Empty commit Signed-off-by: Craig Perkins --------- Signed-off-by: Craig Perkins (cherry picked from commit 37aacdc0ceab4462498c9bdf339e6e0b5d30ef80) --- bwc-test/build.gradle | 4 +- scripts/integtest.sh | 4 +- .../security/CrossClusterSearchTests.java | 455 +++ .../security/DoNotFailOnForbiddenTests.java | 411 +++ .../security/PointInTimeOperationTest.java | 426 +++ .../security/SearchOperationTest.java | 2719 +++++++++++++++++ .../security/http/JwtAuthenticationTests.java | 270 ++ .../http/LdapTlsAuthenticationTest.java | 414 +++ .../test/framework/TestSecurityConfig.java | 717 +++++ .../matcher/OpenSearchExceptionMatchers.java | 37 + .../OpenSearchStatusExceptionMatcher.java | 52 + .../matcher/SearchResponseMatchers.java | 87 + .../SearchResponseWithStatusCodeMatcher.java | 39 + .../matcher/SuccessBulkResponseMatcher.java | 47 + ...ssfulClearIndicesCacheResponseMatcher.java | 37 + .../SuccessfulCreatePitResponseMatcher.java | 37 + .../SuccessfulDeletePitResponseMatcher.java | 42 + .../SuccessfulDeleteResponseMatcher.java | 39 + .../SuccessfulSearchResponseMatcher.java | 37 + .../SuccessfulUpdateResponseMatcher.java | 39 + .../jwt/AbstractHTTPJwtAuthenticator.java | 2 +- .../auth/http/jwt/HTTPJwtAuthenticator.java | 2 +- .../kerberos/HTTPSpnegoAuthenticator.java | 2 +- .../http/saml/AuthTokenProcessorHandler.java | 4 +- .../auth/http/saml/HTTPSamlAuthenticator.java | 2 +- .../security/OpenSearchSecurityPlugin.java | 6 +- .../ConfigUpdateNodeResponse.java | 4 +- .../configupdate/ConfigUpdateRequest.java | 4 +- .../configupdate/ConfigUpdateResponse.java | 4 +- .../TransportConfigUpdateAction.java | 4 +- .../security/action/whoami/WhoAmIRequest.java | 2 +- .../action/whoami/WhoAmIResponse.java | 4 +- .../security/auditlog/AuditLog.java | 2 +- .../security/auditlog/NullAuditLog.java | 2 +- .../auditlog/impl/AbstractAuditLog.java | 4 +- .../security/auditlog/impl/AuditLogImpl.java | 2 +- .../security/auditlog/impl/AuditMessage.java | 4 +- .../auditlog/impl/RequestResolver.java | 4 +- .../security/auth/BackendRegistry.java | 2 +- ...mplianceIndexingOperationListenerImpl.java | 2 +- .../compliance/FieldReadCallback.java | 4 +- ...onfigUpdateAlreadyInProgressException.java | 2 +- .../ConfigurationLoaderSecurity7.java | 2 +- .../ConfigurationRepository.java | 2 +- .../configuration/DlsFlsFilterLeafReader.java | 6 +- .../configuration/DlsFlsValveImpl.java | 2 +- .../SecurityFlsDlsIndexSearcherWrapper.java | 2 +- .../SecurityIndexSearcherWrapper.java | 2 +- .../StaticResourceException.java | 2 +- .../dlic/rest/api/AbstractApiAction.java | 4 +- .../dlic/rest/api/AccountApiAction.java | 4 +- .../dlic/rest/api/ActionGroupsApiAction.java | 2 +- .../dlic/rest/api/AllowlistApiAction.java | 2 +- .../dlic/rest/api/AuditApiAction.java | 2 +- .../rest/api/AuthTokenProcessorAction.java | 2 +- .../dlic/rest/api/FlushCacheApiAction.java | 2 +- .../dlic/rest/api/InternalUsersApiAction.java | 2 +- .../dlic/rest/api/MigrateApiAction.java | 2 +- .../rest/api/MultiTenancyConfigApiAction.java | 4 +- .../dlic/rest/api/NodesDnApiAction.java | 2 +- .../rest/api/PatchableResourceApiAction.java | 4 +- .../dlic/rest/api/PermissionsInfoAction.java | 2 +- .../dlic/rest/api/RolesApiAction.java | 2 +- .../dlic/rest/api/RolesMappingApiAction.java | 2 +- .../dlic/rest/api/SecurityConfigAction.java | 2 +- .../dlic/rest/api/SecuritySSLCertsAction.java | 4 +- .../dlic/rest/api/TenantsApiAction.java | 2 +- .../dlic/rest/api/ValidateApiAction.java | 2 +- .../security/dlic/rest/support/Utils.java | 2 +- .../AbstractConfigurationValidator.java | 2 +- .../rest/validation/AccountValidator.java | 2 +- .../rest/validation/ActionGroupValidator.java | 2 +- .../rest/validation/AllowlistValidator.java | 2 +- .../dlic/rest/validation/AuditValidator.java | 2 +- .../rest/validation/CredentialsValidator.java | 4 +- .../validation/InternalUsersValidator.java | 2 +- .../MultiTenancyConfigValidator.java | 2 +- .../dlic/rest/validation/NoOpValidator.java | 2 +- .../rest/validation/NodesDnValidator.java | 2 +- .../validation/RolesMappingValidator.java | 2 +- .../dlic/rest/validation/RolesValidator.java | 2 +- .../validation/SecurityConfigValidator.java | 2 +- .../dlic/rest/validation/TenantValidator.java | 2 +- .../rest/validation/WhitelistValidator.java | 2 +- .../security/filter/SecurityFilter.java | 2 +- .../security/filter/SecurityRestFilter.java | 2 +- .../security/http/HTTPBasicAuthenticator.java | 2 +- .../resolver/IndexResolverReplacer.java | 2 +- .../security/rest/DashboardsInfoAction.java | 2 +- .../rest/SecurityConfigUpdateAction.java | 2 +- .../security/rest/SecurityHealthAction.java | 2 +- .../security/rest/SecurityInfoAction.java | 2 +- .../security/rest/SecurityWhoAmIAction.java | 2 +- .../security/rest/TenantInfoAction.java | 2 +- .../impl/AllowlistingSettings.java | 2 +- .../impl/WhitelistingSettings.java | 2 +- .../ssl/OpenSearchSecuritySSLPlugin.java | 2 +- .../security/ssl/SecureSSLSettings.java | 130 + .../ssl/http/netty/ValidatingDispatcher.java | 2 +- .../ssl/rest/SecuritySSLInfoAction.java | 2 +- .../transport/SecuritySSLNettyTransport.java | 2 +- .../security/support/ConfigHelper.java | 2 +- .../security/support/ModuleInfo.java | 8 +- .../security/tools/SecurityAdmin.java | 4 +- .../transport/SecurityInterceptor.java | 2 +- .../org/opensearch/security/user/User.java | 6 +- .../http/saml/HTTPSamlAuthenticatorTest.java | 6 +- .../security/RolesInjectorIntegTest.java | 2 +- .../security/RolesValidationIntegTest.java | 2 +- .../TransportUserInjectorIntegTest.java | 2 +- .../auditlog/helper/MockRestRequest.java | 2 +- .../auditlog/impl/AuditMessageTest.java | 2 +- .../dlic/dlsfls/CCReplicationTest.java | 8 +- .../dlsfls/DlsFlsCrossClusterSearchTest.java | 6 +- .../security/dlic/dlsfls/DlsTest.java | 2 +- .../HTTPExtendedProxyAuthenticatorTest.java | 4 +- .../ProtectedIndicesTests.java | 2 +- .../system_indices/SystemIndicesTests.java | 2 +- .../security/test/helper/file/FileHelper.java | 2 +- .../security/util/FakeRestRequest.java | 2 +- 120 files changed, 6172 insertions(+), 137 deletions(-) create mode 100644 src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java create mode 100644 src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java create mode 100644 src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java create mode 100644 src/integrationTest/java/org/opensearch/security/SearchOperationTest.java create mode 100644 src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java create mode 100644 src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java create mode 100644 src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java diff --git a/bwc-test/build.gradle b/bwc-test/build.gradle index 01594b31ad..f04914342f 100644 --- a/bwc-test/build.gradle +++ b/bwc-test/build.gradle @@ -67,9 +67,9 @@ repositories { } dependencies { - testImplementation 'com.google.guava:guava:30.0-jre' + testImplementation "com.google.guava:guava:${versions.guava}" testImplementation "org.opensearch.test:framework:${opensearch_version}" - testImplementation 'org.apache.logging.log4j:log4j-core:2.17.1' + testImplementation "org.apache.logging.log4j:log4j-core:${versions.log4j}" } loggerUsageCheck.enabled = false diff --git a/scripts/integtest.sh b/scripts/integtest.sh index 6d4c1dbedd..0401d00fa0 100755 --- a/scripts/integtest.sh +++ b/scripts/integtest.sh @@ -20,7 +20,7 @@ function usage() { echo -e "-v OPENSEARCH_VERSION\t, no defaults" echo -e "-n SNAPSHOT\t, defaults to false" echo -e "-m CLUSTER_NAME\t, defaults to docker-cluster" - echo -e "-u COMMON_UTILS_VERSION\t, defaults to 2.9.0.0-SNAPSHOT" + echo -e "-u COMMON_UTILS_VERSION\t, defaults to 3.0.0.0-SNAPSHOT" echo "--------------------------------------------------------------------------" } @@ -101,7 +101,7 @@ then fi if [ -z "$COMMON_UTILS_VERSION" ] then - COMMON_UTILS_VERSION="2.9.0.0-SNAPSHOT" + COMMON_UTILS_VERSION="3.0.0.0-SNAPSHOT" fi USERNAME=`echo $CREDENTIAL | awk -F ':' '{print $1}'` diff --git a/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java b/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java new file mode 100644 index 0000000000..86d27efa87 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java @@ -0,0 +1,455 @@ +/* +* 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; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.client.Client; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.test.framework.TestSecurityConfig.Role; +import org.opensearch.test.framework.TestSecurityConfig.User; +import org.opensearch.test.framework.certificate.TestCertificates; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.SearchRequestFactory; +import org.opensearch.test.framework.cluster.TestRestClient; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.client.RequestOptions.DEFAULT; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.security.Song.ARTIST_FIRST; +import static org.opensearch.security.Song.FIELD_ARTIST; +import static org.opensearch.security.Song.FIELD_GENRE; +import static org.opensearch.security.Song.FIELD_LYRICS; +import static org.opensearch.security.Song.FIELD_STARS; +import static org.opensearch.security.Song.FIELD_TITLE; +import static org.opensearch.security.Song.GENRE_JAZZ; +import static org.opensearch.security.Song.GENRE_ROCK; +import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; +import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitDoesNotContainField; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentsInAnyOrder; + +/** +* This is a parameterized test so that one test class is used to test security plugin behaviour when ccsMinimizeRoundtrips +* option is enabled or disabled. Method {@link #parameters()} is a source of parameters values. +*/ +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class CrossClusterSearchTests { + + private static final String SONG_INDEX_NAME = "song_lyrics"; + + private static final String PROHIBITED_SONG_INDEX_NAME = "prohibited_song_lyrics"; + + public static final String REMOTE_CLUSTER_NAME = "ccsRemote"; + public static final String REMOTE_SONG_INDEX = REMOTE_CLUSTER_NAME + ":" + SONG_INDEX_NAME; + + public static final String SONG_ID_1R = "remote-00001"; + public static final String SONG_ID_2L = "local-00002"; + public static final String SONG_ID_3R = "remote-00003"; + public static final String SONG_ID_4L = "local-00004"; + public static final String SONG_ID_5R = "remote-00005"; + public static final String SONG_ID_6R = "remote-00006"; + + private static final Role LIMITED_ROLE = new Role("limited_role").indexPermissions( + "indices:data/read/search", + "indices:admin/shards/search_shards" + ).on(SONG_INDEX_NAME, "user-${user.name}-${attr.internal.type}"); + + private static final Role DLS_ROLE_ROCK = new Role("dls_role_rock").indexPermissions( + "indices:data/read/search", + "indices:data/read/get", + "indices:admin/shards/search_shards" + ).dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_GENRE, GENRE_ROCK)).on(SONG_INDEX_NAME); + + private static final Role DLS_ROLE_JAZZ = new Role("dls_role_jazz").indexPermissions( + "indices:data/read/search", + "indices:data/read/get", + "indices:admin/shards/search_shards" + ).dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_GENRE, GENRE_JAZZ)).on(SONG_INDEX_NAME); + + private static final Role FLS_EXCLUDE_LYRICS_ROLE = new Role("fls_exclude_lyrics_role").indexPermissions( + "indices:data/read/search", + "indices:data/read/get", + "indices:admin/shards/search_shards" + ).fls("~" + FIELD_LYRICS).on(SONG_INDEX_NAME); + + private static final Role FLS_INCLUDE_TITLE_ROLE = new Role("fls_include_title_role").indexPermissions( + "indices:data/read/search", + "indices:data/read/get", + "indices:admin/shards/search_shards" + ).fls(FIELD_TITLE).on(SONG_INDEX_NAME); + + public static final String TYPE_ATTRIBUTE = "type"; + + private static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS).attr(TYPE_ATTRIBUTE, "administrative"); + private static final User LIMITED_USER = new User("limited_user").attr(TYPE_ATTRIBUTE, "personal"); + + private static final User FLS_INCLUDE_TITLE_USER = new User("fls_include_title_user"); + + private static final User FLS_EXCLUDE_LYRICS_USER = new User("fls_exclude_lyrics_user"); + + private static final User DLS_USER_ROCK = new User("dls-user-rock"); + + private static final User DLS_USER_JAZZ = new User("dls-user-jazz"); + + public static final String LIMITED_USER_INDEX_NAME = "user-" + LIMITED_USER.getName() + "-" + LIMITED_USER.getAttribute(TYPE_ATTRIBUTE); + public static final String ADMIN_USER_INDEX_NAME = "user-" + ADMIN_USER.getName() + "-" + ADMIN_USER.getAttribute(TYPE_ATTRIBUTE); + + private static final TestCertificates TEST_CERTIFICATES = new TestCertificates(); + + private final boolean ccsMinimizeRoundtrips; + + public static final String PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED = "plugins.security.restapi.roles_enabled"; + @ClassRule + public static final LocalCluster remoteCluster = new LocalCluster.Builder().certificates(TEST_CERTIFICATES) + .clusterManager(ClusterManager.SINGLENODE) + .anonymousAuth(false) + .clusterName(REMOTE_CLUSTER_NAME) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .roles(LIMITED_ROLE, DLS_ROLE_ROCK, DLS_ROLE_JAZZ, FLS_EXCLUDE_LYRICS_ROLE, FLS_INCLUDE_TITLE_ROLE) + .users(ADMIN_USER) + .build(); + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder().certificates(TEST_CERTIFICATES) + .clusterManager(ClusterManager.SINGLE_REMOTE_CLIENT) + .anonymousAuth(false) + .clusterName("ccsLocal") + .nodeSettings(Map.of(PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_" + ADMIN_USER.getName() + "__" + ALL_ACCESS.getName()))) + .remote(REMOTE_CLUSTER_NAME, remoteCluster) + .roles(LIMITED_ROLE, DLS_ROLE_ROCK, DLS_ROLE_JAZZ, FLS_EXCLUDE_LYRICS_ROLE, FLS_INCLUDE_TITLE_ROLE) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(ADMIN_USER, LIMITED_USER, DLS_USER_ROCK, DLS_USER_JAZZ, FLS_INCLUDE_TITLE_USER, FLS_EXCLUDE_LYRICS_USER) + .build(); + + @ParametersFactory(shuffle = false) + public static Iterable parameters() { + return List.of(new Object[] { true }, new Object[] { false }); + } + + public CrossClusterSearchTests(Boolean ccsMinimizeRoundtrips) { + this.ccsMinimizeRoundtrips = ccsMinimizeRoundtrips; + } + + @BeforeClass + public static void createTestData() { + try (Client client = remoteCluster.getInternalNodeClient()) { + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_6R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[5].asMap()).get(); + client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_3R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); + client.prepareIndex(LIMITED_USER_INDEX_NAME).setId(SONG_ID_5R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[4].asMap()).get(); + } + try (Client client = cluster.getInternalNodeClient()) { + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_2L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2].asMap()).get(); + client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_4L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[3].asMap()).get(); + } + try (TestRestClient client = cluster.getRestClient(ADMIN_USER)) { + client.assignRoleToUser(LIMITED_USER.getName(), LIMITED_ROLE.getName()).assertStatusCode(200); + client.assignRoleToUser(DLS_USER_ROCK.getName(), DLS_ROLE_ROCK.getName()).assertStatusCode(200); + client.assignRoleToUser(DLS_USER_JAZZ.getName(), DLS_ROLE_JAZZ.getName()).assertStatusCode(200); + client.assignRoleToUser(FLS_INCLUDE_TITLE_USER.getName(), FLS_INCLUDE_TITLE_ROLE.getName()).assertStatusCode(200); + client.assignRoleToUser(FLS_EXCLUDE_LYRICS_USER.getName(), FLS_EXCLUDE_LYRICS_ROLE.getName()).assertStatusCode(200); + } + } + + @Test + public void shouldFindDocumentOnRemoteCluster_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_SONG_INDEX); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(2)); + assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1R)); + assertThat(response, searchHitsContainDocumentWithId(1, SONG_INDEX_NAME, SONG_ID_6R)); + } + } + + private SearchRequest searchAll(String indexName) { + SearchRequest searchRequest = SearchRequestFactory.searchAll(indexName); + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); + return searchRequest; + } + + @Test + public void shouldFindDocumentOnRemoteCluster_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":" + PROHIBITED_SONG_INDEX_NAME); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchForDocumentOnRemoteClustersWhenStarIsUsedAsClusterName_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll("*" + ":" + SONG_INDEX_NAME); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + // only remote documents are found + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(2)); + assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1R)); + assertThat(response, searchHitsContainDocumentWithId(1, SONG_INDEX_NAME, SONG_ID_6R)); + } + } + + @Test + public void shouldSearchForDocumentOnRemoteClustersWhenStarIsUsedAsClusterName_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll("*" + ":" + PROHIBITED_SONG_INDEX_NAME); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchForDocumentOnBothClustersWhenIndexOnBothClusterArePointedOut_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = SearchRequestFactory.searchAll(REMOTE_SONG_INDEX, SONG_INDEX_NAME); + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(3)); + assertThat( + response, + searchHitsContainDocumentsInAnyOrder( + Pair.of(SONG_INDEX_NAME, SONG_ID_1R), + Pair.of(SONG_INDEX_NAME, SONG_ID_2L), + Pair.of(SONG_INDEX_NAME, SONG_ID_6R) + ) + ); + } + } + + @Test + public void shouldSearchForDocumentOnBothClustersWhenIndexOnBothClusterArePointedOut_negativeLackOfLocalAccess() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + var searchRequest = SearchRequestFactory.searchAll(REMOTE_SONG_INDEX, PROHIBITED_SONG_INDEX_NAME); + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchForDocumentOnBothClustersWhenIndexOnBothClusterArePointedOut_negativeLackOfRemoteAccess() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + String remoteIndex = REMOTE_CLUSTER_NAME + ":" + PROHIBITED_SONG_INDEX_NAME; + SearchRequest searchRequest = SearchRequestFactory.searchAll(remoteIndex, SONG_INDEX_NAME); + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchViaAllAliasOnRemoteCluster_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":_all"); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(4)); + assertThat( + response, + searchHitsContainDocumentsInAnyOrder( + Pair.of(SONG_INDEX_NAME, SONG_ID_1R), + Pair.of(SONG_INDEX_NAME, SONG_ID_6R), + Pair.of(PROHIBITED_SONG_INDEX_NAME, SONG_ID_3R), + Pair.of(LIMITED_USER_INDEX_NAME, SONG_ID_5R) + ) + ); + } + } + + @Test + public void shouldSearchViaAllAliasOnRemoteCluster_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":_all"); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchAllIndexOnRemoteClusterWhenStarIsUsedAsIndexName_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":*"); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(4)); + assertThat( + response, + searchHitsContainDocumentsInAnyOrder( + Pair.of(SONG_INDEX_NAME, SONG_ID_1R), + Pair.of(SONG_INDEX_NAME, SONG_ID_6R), + Pair.of(PROHIBITED_SONG_INDEX_NAME, SONG_ID_3R), + Pair.of(LIMITED_USER_INDEX_NAME, SONG_ID_5R) + ) + ); + } + } + + @Test + public void shouldSearchAllIndexOnRemoteClusterWhenStarIsUsedAsIndexName_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":*"); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldResolveUserNameExpressionInRoleIndexPattern_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":" + LIMITED_USER_INDEX_NAME); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, numberOfTotalHitsIsEqualTo(1)); + } + } + + @Test + public void shouldResolveUserNameExpressionInRoleIndexPattern_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":" + ADMIN_USER_INDEX_NAME); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchInIndexWithPrefix_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":song*"); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(2)); + assertThat( + response, + searchHitsContainDocumentsInAnyOrder(Pair.of(SONG_INDEX_NAME, SONG_ID_1R), Pair.of(SONG_INDEX_NAME, SONG_ID_6R)) + ); + } + } + + @Test + public void shouldSearchInIndexWithPrefix_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":prohibited*"); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldEvaluateDocumentLevelSecurityRulesOnRemoteClusterOnSearchRequest_caseRock() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DLS_USER_ROCK)) { + SearchRequest searchRequest = searchAll(REMOTE_SONG_INDEX); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + // searching for all documents, so is it important that result contain only one document with id SONG_ID_1 + // and document with SONG_ID_6 is excluded from result set by DLS + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(1)); + assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1R)); + } + } + + @Test + public void shouldEvaluateDocumentLevelSecurityRulesOnRemoteClusterOnSearchRequest_caseJazz() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DLS_USER_JAZZ)) { + SearchRequest searchRequest = searchAll(REMOTE_SONG_INDEX); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + // searching for all documents, so is it important that result contain only one document with id SONG_ID_6 + // and document with SONG_ID_1 is excluded from result set by DLS + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(1)); + assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_6R)); + } + } + + @Test + public void shouldHaveAccessOnlyToSpecificField() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(FLS_INCLUDE_TITLE_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(REMOTE_SONG_INDEX, QUERY_TITLE_MAGNUM_OPUS); + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(1)); + // document should contain only title field + assertThat(response, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + assertThat(response, searchHitDoesNotContainField(0, FIELD_ARTIST)); + assertThat(response, searchHitDoesNotContainField(0, FIELD_LYRICS)); + assertThat(response, searchHitDoesNotContainField(0, FIELD_STARS)); + assertThat(response, searchHitDoesNotContainField(0, FIELD_GENRE)); + } + } + + @Test + public void shouldLackAccessToSpecificField() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(FLS_EXCLUDE_LYRICS_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(REMOTE_SONG_INDEX, QUERY_TITLE_MAGNUM_OPUS); + searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); + + SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(1)); + // document should not contain lyrics field + assertThat(response, searchHitDoesNotContainField(0, FIELD_LYRICS)); + + assertThat(response, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + assertThat(response, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_FIRST)); + assertThat(response, searchHitContainsFieldWithValue(0, FIELD_STARS, 1)); + assertThat(response, searchHitContainsFieldWithValue(0, FIELD_GENRE, GENRE_ROCK)); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java b/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java new file mode 100644 index 0000000000..fedcf0bbcb --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java @@ -0,0 +1,411 @@ +/* +* 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; + +import java.io.IOException; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.hamcrest.Matchers; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; +import org.opensearch.action.fieldcaps.FieldCapabilitiesRequest; +import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; +import org.opensearch.action.get.MultiGetItemResponse; +import org.opensearch.action.get.MultiGetRequest; +import org.opensearch.action.get.MultiGetResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.search.MultiSearchRequest; +import org.opensearch.action.search.MultiSearchResponse; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchScrollRequest; +import org.opensearch.client.Client; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.TestSecurityConfig.User; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.arrayContainingInAnyOrder; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.nullValue; +import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.client.RequestOptions.DEFAULT; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.security.Song.FIELD_STARS; +import static org.opensearch.security.Song.FIELD_TITLE; +import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; +import static org.opensearch.security.Song.QUERY_TITLE_NEXT_SONG; +import static org.opensearch.security.Song.QUERY_TITLE_POISON; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; +import static org.opensearch.security.Song.TITLE_NEXT_SONG; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.averageAggregationRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.getSearchScrollRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.searchRequestWithScroll; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.statsAggregationRequest; +import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; +import static org.opensearch.test.framework.matcher.GetResponseMatchers.containDocument; +import static org.opensearch.test.framework.matcher.GetResponseMatchers.containOnlyDocumentId; +import static org.opensearch.test.framework.matcher.GetResponseMatchers.documentContainField; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containAggregationWithNameAndType; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containNotEmptyScrollingId; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfHitsInPageIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class DoNotFailOnForbiddenTests { + + /** + * Songs accessible for {@link #LIMITED_USER} + */ + private static final String MARVELOUS_SONGS = "marvelous_songs"; + + /** + * Songs inaccessible for {@link #LIMITED_USER} + */ + private static final String HORRIBLE_SONGS = "horrible_songs"; + + private static final String BOTH_INDEX_PATTERN = "*songs"; + + private static final String ID_1 = "1"; + private static final String ID_2 = "2"; + private static final String ID_3 = "3"; + private static final String ID_4 = "4"; + + private static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS); + private static final User LIMITED_USER = new User("limited_user").roles( + new TestSecurityConfig.Role("limited-role").clusterPermissions( + "indices:data/read/mget", + "indices:data/read/msearch", + "indices:data/read/scroll" + ) + .indexPermissions( + "indices:data/read/search", + "indices:data/read/mget*", + "indices:data/read/field_caps", + "indices:data/read/field_caps*", + "indices:data/read/msearch", + "indices:data/read/scroll" + ) + .on(MARVELOUS_SONGS) + ); + + private static final String BOTH_INDEX_ALIAS = "both-indices"; + private static final String FORBIDDEN_INDEX_ALIAS = "forbidden-index"; + + @ClassRule + public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(ADMIN_USER, LIMITED_USER) + .anonymousAuth(false) + .doNotFailOnForbidden(true) + .build(); + + @BeforeClass + public static void createTestData() { + try (Client client = cluster.getInternalNodeClient()) { + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_1).source(SONGS[0].asMap())) + .actionGet(); + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_2).source(SONGS[1].asMap())) + .actionGet(); + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_3).source(SONGS[2].asMap())) + .actionGet(); + + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(HORRIBLE_SONGS).id(ID_4).source(SONGS[3].asMap())) + .actionGet(); + + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction( + new IndicesAliasesRequest.AliasActions(ADD).indices(MARVELOUS_SONGS, HORRIBLE_SONGS).alias(BOTH_INDEX_ALIAS) + ) + ) + .actionGet(); + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction( + new IndicesAliasesRequest.AliasActions(ADD).indices(HORRIBLE_SONGS).alias(FORBIDDEN_INDEX_ALIAS) + ) + ) + .actionGet(); + + } + } + + @Test + public void shouldPerformSimpleSearch_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest( + new String[] { MARVELOUS_SONGS, HORRIBLE_SONGS }, + QUERY_TITLE_MAGNUM_OPUS + ); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); + } + } + + private static void assertThatContainOneSong(SearchResponse searchResponse, String documentId, String title) { + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, MARVELOUS_SONGS, documentId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, title)); + } + + @Test + public void shouldPerformSimpleSearch_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(HORRIBLE_SONGS, QUERY_TITLE_POISON); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchForDocumentsViaIndexPattern_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(BOTH_INDEX_PATTERN, QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); + } + } + + @Test + public void shouldSearchForDocumentsViaIndexPattern_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(HORRIBLE_SONGS, QUERY_TITLE_POISON); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchForDocumentsViaAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(BOTH_INDEX_ALIAS, QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); + } + } + + @Test + public void shouldSearchForDocumentsViaAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(FORBIDDEN_INDEX_ALIAS, QUERY_TITLE_POISON); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldSearchForDocumentsViaAll_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); + } + } + + @Test + public void shouldSearchForDocumentsViaAll_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_POISON); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(0)); + } + } + + @Test + public void shouldMGetDocument_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + MultiGetRequest request = new MultiGetRequest().add(BOTH_INDEX_PATTERN, ID_1).add(BOTH_INDEX_PATTERN, ID_4); + + MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); + + MultiGetItemResponse[] responses = response.getResponses(); + assertThat(responses, arrayWithSize(2)); + MultiGetItemResponse firstResult = responses[0]; + MultiGetItemResponse secondResult = responses[1]; + assertThat(firstResult.getFailure(), nullValue()); + assertThat(secondResult.getFailure(), nullValue()); + assertThat( + firstResult.getResponse(), + allOf(containDocument(MARVELOUS_SONGS, ID_1), documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)) + ); + assertThat(secondResult.getResponse(), containOnlyDocumentId(MARVELOUS_SONGS, ID_4)); + } + } + + @Test + public void shouldMGetDocument_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + MultiGetRequest request = new MultiGetRequest().add(HORRIBLE_SONGS, ID_4); + + assertThatThrownBy(() -> restHighLevelClient.mget(request, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldMSearchDocument_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + MultiSearchRequest request = new MultiSearchRequest(); + request.add(queryStringQueryRequest(BOTH_INDEX_PATTERN, QUERY_TITLE_MAGNUM_OPUS)); + request.add(queryStringQueryRequest(BOTH_INDEX_PATTERN, QUERY_TITLE_NEXT_SONG)); + + MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); + + MultiSearchResponse.Item[] responses = response.getResponses(); + assertThat(responses, Matchers.arrayWithSize(2)); + assertThat(responses[0].getFailure(), nullValue()); + assertThat(responses[1].getFailure(), nullValue()); + + assertThat(responses[0].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + assertThat(responses[0].getResponse(), searchHitsContainDocumentWithId(0, MARVELOUS_SONGS, ID_1)); + assertThat(responses[1].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); + assertThat(responses[1].getResponse(), searchHitsContainDocumentWithId(0, MARVELOUS_SONGS, ID_3)); + } + } + + @Test + public void shouldMSearchDocument_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + MultiSearchRequest request = new MultiSearchRequest(); + request.add(queryStringQueryRequest(FORBIDDEN_INDEX_ALIAS, QUERY_TITLE_POISON)); + + assertThatThrownBy(() -> restHighLevelClient.msearch(request, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldGetFieldCapabilities_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(MARVELOUS_SONGS, HORRIBLE_SONGS).fields(FIELD_TITLE); + + FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); + + assertThat(response.get(), aMapWithSize(1)); + assertThat(response.getIndices(), arrayWithSize(1)); + assertThat(response.getField(FIELD_TITLE), hasKey("text")); + assertThat(response.getIndices(), arrayContainingInAnyOrder(MARVELOUS_SONGS)); + } + } + + @Test + public void shouldGetFieldCapabilities_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(HORRIBLE_SONGS).fields(FIELD_TITLE); + + assertThatThrownBy(() -> restHighLevelClient.fieldCaps(request, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldScrollOverSearchResults_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchRequestWithScroll(BOTH_INDEX_PATTERN, 2); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containNotEmptyScrollingId()); + + SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); + + SearchResponse scrollResponse = restHighLevelClient.scroll(scrollRequest, DEFAULT); + assertThat(scrollResponse, isSuccessfulSearchResponse()); + assertThat(scrollResponse, containNotEmptyScrollingId()); + assertThat(scrollResponse, numberOfTotalHitsIsEqualTo(3)); + assertThat(scrollResponse, numberOfHitsInPageIsEqualTo(1)); + } + } + + @Test + public void shouldScrollOverSearchResults_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + SearchRequest searchRequest = searchRequestWithScroll(HORRIBLE_SONGS, 2); + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldPerformAggregation_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + final String aggregationName = "averageStars"; + SearchRequest searchRequest = averageAggregationRequest(BOTH_INDEX_PATTERN, aggregationName, FIELD_STARS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); + } + } + + @Test + public void shouldPerformAggregation_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + final String aggregationName = "averageStars"; + SearchRequest searchRequest = averageAggregationRequest(HORRIBLE_SONGS, aggregationName, FIELD_STARS); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldPerformStatAggregation_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + final String aggregationName = "statsStars"; + SearchRequest searchRequest = statsAggregationRequest(BOTH_INDEX_ALIAS, aggregationName, FIELD_STARS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "stats")); + } + } + + @Test + public void shouldPerformStatAggregation_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + final String aggregationName = "statsStars"; + SearchRequest searchRequest = statsAggregationRequest(HORRIBLE_SONGS, aggregationName, FIELD_STARS); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + +} diff --git a/src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java b/src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java new file mode 100644 index 0000000000..3b01ca69bf --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java @@ -0,0 +1,426 @@ +/* +* 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; + +import java.io.IOException; + +import com.carrotsearch.randomizedtesting.RandomizedRunner; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.OpenSearchStatusException; +import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.GetAllPitNodesResponse; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.client.Client; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.search.builder.PointInTimeBuilder; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.client.RequestOptions.DEFAULT; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.core.rest.RestStatus.OK; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; +import static org.opensearch.test.framework.matcher.PitResponseMatchers.deleteResponseContainsExactlyPitWithIds; +import static org.opensearch.test.framework.matcher.PitResponseMatchers.getAllResponseContainsExactlyPitWithIds; +import static org.opensearch.test.framework.matcher.PitResponseMatchers.isSuccessfulCreatePitResponse; +import static org.opensearch.test.framework.matcher.PitResponseMatchers.isSuccessfulDeletePitResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentsInAnyOrder; + +@RunWith(RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class PointInTimeOperationTest { + + private static final String FIRST_SONG_INDEX = "song-index-1"; + private static final String FIRST_INDEX_ALIAS = "song-index-1-alias"; + private static final String SECOND_SONG_INDEX = "song-index-2"; + private static final String SECOND_INDEX_ALIAS = "song-index-2-alias"; + + private static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); + + /** + * User who is allowed to perform PIT operations only on the {@link #FIRST_SONG_INDEX} + */ + private static final TestSecurityConfig.User LIMITED_POINT_IN_TIME_USER = new TestSecurityConfig.User("limited_point_in_time_user") + .roles( + new TestSecurityConfig.Role("limited_point_in_time_user").indexPermissions( + "indices:data/read/point_in_time/create", + "indices:data/read/point_in_time/delete", + "indices:data/read/search", + "indices:data/read/point_in_time/readall", // anyway user needs the all indexes permission (*) to find all pits + "indices:monitor/point_in_time/segments" // anyway user needs the all indexes permission (*) to list all pits segments + ).on(FIRST_SONG_INDEX) + ); + /** + * User who is allowed to perform PIT operations on all indices + */ + private static final TestSecurityConfig.User POINT_IN_TIME_USER = new TestSecurityConfig.User("point_in_time_user").roles( + new TestSecurityConfig.Role("point_in_time_user").indexPermissions( + "indices:data/read/point_in_time/create", + "indices:data/read/point_in_time/delete", + "indices:data/read/search", + "indices:data/read/point_in_time/readall", + "indices:monitor/point_in_time/segments" + ).on("*") + ); + + private static final String ID_1 = "1"; + private static final String ID_2 = "2"; + private static final String ID_3 = "3"; + private static final String ID_4 = "4"; + + @BeforeClass + public static void createTestData() { + try (Client client = cluster.getInternalNodeClient()) { + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(FIRST_SONG_INDEX).id(ID_1).source(SONGS[0].asMap())) + .actionGet(); + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(FIRST_SONG_INDEX).id(ID_2).source(SONGS[1].asMap())) + .actionGet(); + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(FIRST_SONG_INDEX).id(ID_3).source(SONGS[2].asMap())) + .actionGet(); + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction( + new IndicesAliasesRequest.AliasActions(ADD).indices(FIRST_SONG_INDEX).alias(FIRST_INDEX_ALIAS) + ) + ) + .actionGet(); + + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SECOND_SONG_INDEX).id(ID_4).source(SONGS[3].asMap())) + .actionGet(); + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction( + new IndicesAliasesRequest.AliasActions(ADD).indices(SECOND_SONG_INDEX).alias(SECOND_INDEX_ALIAS) + ) + ) + .actionGet(); + } + } + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .anonymousAuth(false) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(ADMIN_USER, LIMITED_POINT_IN_TIME_USER, POINT_IN_TIME_USER) + .build(); + + @Test + public void createPit_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, FIRST_SONG_INDEX); + + CreatePitResponse createPitResponse = restHighLevelClient.createPit(createPitRequest, DEFAULT); + + assertThat(createPitResponse, isSuccessfulCreatePitResponse()); + } + } + + @Test + public void createPitWithIndexAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, FIRST_INDEX_ALIAS); + + CreatePitResponse createPitResponse = restHighLevelClient.createPit(createPitRequest, DEFAULT); + + assertThat(createPitResponse, isSuccessfulCreatePitResponse()); + } + } + + @Test + public void createPit_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, SECOND_SONG_INDEX); + + assertThatThrownBy(() -> restHighLevelClient.createPit(createPitRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void createPitWithIndexAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, SECOND_INDEX_ALIAS); + + assertThatThrownBy(() -> restHighLevelClient.createPit(createPitRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void listAllPits_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(POINT_IN_TIME_USER)) { + cleanUpPits(); + String firstIndexPit = createPitForIndices(FIRST_SONG_INDEX); + String secondIndexPit = createPitForIndices(SECOND_SONG_INDEX); + + GetAllPitNodesResponse getAllPitsResponse = restHighLevelClient.getAllPits(DEFAULT); + + assertThat(getAllPitsResponse, getAllResponseContainsExactlyPitWithIds(firstIndexPit, secondIndexPit)); + } + } + + @Test + public void listAllPits_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + assertThatThrownBy(() -> restHighLevelClient.getAllPits(DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void deletePit_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(FIRST_SONG_INDEX); + + DeletePitResponse deletePitResponse = restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT); + assertThat(deletePitResponse, isSuccessfulDeletePitResponse()); + assertThat(deletePitResponse, deleteResponseContainsExactlyPitWithIds(existingPitId)); + } + } + + @Test + public void deletePitCreatedWithIndexAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(FIRST_INDEX_ALIAS); + + DeletePitResponse deletePitResponse = restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT); + assertThat(deletePitResponse, isSuccessfulDeletePitResponse()); + assertThat(deletePitResponse, deleteResponseContainsExactlyPitWithIds(existingPitId)); + } + } + + @Test + public void deletePit_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(SECOND_SONG_INDEX); + + assertThatThrownBy( + () -> restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + public void deletePitCreatedWithIndexAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(SECOND_INDEX_ALIAS); + + assertThatThrownBy( + () -> restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + public void deleteAllPits_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(POINT_IN_TIME_USER)) { + cleanUpPits(); + String firstIndexPit = createPitForIndices(FIRST_SONG_INDEX); + String secondIndexPit = createPitForIndices(SECOND_SONG_INDEX); + + DeletePitResponse deletePitResponse = restHighLevelClient.deleteAllPits(DEFAULT); + assertThat(deletePitResponse, isSuccessfulDeletePitResponse()); + assertThat(deletePitResponse, deleteResponseContainsExactlyPitWithIds(firstIndexPit, secondIndexPit)); + } + } + + @Test + public void deleteAllPits_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + assertThatThrownBy(() -> restHighLevelClient.deleteAllPits(DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void searchWithPit_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(FIRST_SONG_INDEX); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + Pair.of(FIRST_SONG_INDEX, ID_1), + Pair.of(FIRST_SONG_INDEX, ID_2), + Pair.of(FIRST_SONG_INDEX, ID_3) + ) + ); + } + } + + @Test + public void searchWithPitCreatedWithIndexAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(FIRST_INDEX_ALIAS); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat( + searchResponse, + searchHitsContainDocumentsInAnyOrder( + Pair.of(FIRST_SONG_INDEX, ID_1), + Pair.of(FIRST_SONG_INDEX, ID_2), + Pair.of(FIRST_SONG_INDEX, ID_3) + ) + ); + } + } + + @Test + public void searchWithPit_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(SECOND_SONG_INDEX); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void searchWithPitCreatedWithIndexAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(SECOND_INDEX_ALIAS); + + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void listPitSegments_positive() throws IOException { + try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(FIRST_SONG_INDEX); + String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); + HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); + + response.assertStatusCode(OK.getStatus()); + } + } + + @Test + public void listPitSegmentsCreatedWithIndexAlias_positive() throws IOException { + try (TestRestClient restClient = cluster.getRestClient(POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(FIRST_INDEX_ALIAS); + String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); + HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); + + response.assertStatusCode(OK.getStatus()); + } + } + + @Test + public void listPitSegments_negative() throws IOException { + try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(SECOND_SONG_INDEX); + String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); + HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); + + response.assertStatusCode(FORBIDDEN.getStatus()); + } + } + + @Test + public void listPitSegmentsCreatedWithIndexAlias_negative() throws IOException { + try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { + String existingPitId = createPitForIndices(SECOND_INDEX_ALIAS); + String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); + HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); + + response.assertStatusCode(FORBIDDEN.getStatus()); + } + } + + @Test + public void listAllPitSegments_positive() { + try (TestRestClient restClient = cluster.getRestClient(POINT_IN_TIME_USER)) { + HttpResponse response = restClient.get("/_cat/pit_segments/_all"); + + response.assertStatusCode(OK.getStatus()); + } + } + + @Test + public void listAllPitSegments_negative() { + try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { + HttpResponse response = restClient.get("/_cat/pit_segments/_all"); + + response.assertStatusCode(FORBIDDEN.getStatus()); + } + } + + /** + * Creates PIT for given indices. Returns PIT id. + */ + private String createPitForIndices(String... indices) throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, indices); + + CreatePitResponse createPitResponse = restHighLevelClient.createPit(createPitRequest, DEFAULT); + + assertThat(createPitResponse, isSuccessfulCreatePitResponse()); + return createPitResponse.getId(); + } + } + + /** + * Deletes all PITs. + */ + public void cleanUpPits() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + try { + restHighLevelClient.deleteAllPits(DEFAULT); + } catch (OpenSearchStatusException ex) { + if (ex.status() != RestStatus.NOT_FOUND) { + throw ex; + } + // tried to remove pits but no pit exists + } + } + } + +} diff --git a/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java b/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java new file mode 100644 index 0000000000..f16d40e905 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java @@ -0,0 +1,2719 @@ +/* +* 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; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import com.google.common.base.Stopwatch; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.awaitility.Awaitility; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; +import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.opensearch.action.admin.indices.alias.Alias; +import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; +import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; +import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest; +import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheResponse; +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequest; +import org.opensearch.action.admin.indices.open.OpenIndexRequest; +import org.opensearch.action.admin.indices.open.OpenIndexResponse; +import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; +import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.opensearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest; +import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesRequest; +import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesResponse; +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkResponse; +import org.opensearch.action.delete.DeleteRequest; +import org.opensearch.action.delete.DeleteResponse; +import org.opensearch.action.fieldcaps.FieldCapabilitiesRequest; +import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; +import org.opensearch.action.get.GetRequest; +import org.opensearch.action.get.GetResponse; +import org.opensearch.action.get.MultiGetItemResponse; +import org.opensearch.action.get.MultiGetRequest; +import org.opensearch.action.get.MultiGetRequest.Item; +import org.opensearch.action.get.MultiGetResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.search.MultiSearchRequest; +import org.opensearch.action.search.MultiSearchResponse; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchScrollRequest; +import org.opensearch.action.update.UpdateRequest; +import org.opensearch.action.update.UpdateResponse; +import org.opensearch.client.Client; +import org.opensearch.client.ClusterAdminClient; +import org.opensearch.client.IndicesAdminClient; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.client.core.CountRequest; +import org.opensearch.client.indices.CloseIndexRequest; +import org.opensearch.client.indices.CloseIndexResponse; +import org.opensearch.client.indices.CreateIndexRequest; +import org.opensearch.client.indices.CreateIndexResponse; +import org.opensearch.client.indices.GetIndexRequest; +import org.opensearch.client.indices.GetIndexResponse; +import org.opensearch.client.indices.GetMappingsRequest; +import org.opensearch.client.indices.GetMappingsResponse; +import org.opensearch.client.indices.PutIndexTemplateRequest; +import org.opensearch.client.indices.PutMappingRequest; +import org.opensearch.client.indices.ResizeRequest; +import org.opensearch.client.indices.ResizeResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.IndexTemplateMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.query.BoolQueryBuilder; +import org.opensearch.index.query.MatchQueryBuilder; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.index.reindex.BulkByScrollResponse; +import org.opensearch.index.reindex.ReindexRequest; +import org.opensearch.repositories.RepositoryMissingException; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.test.framework.AuditCompliance; +import org.opensearch.test.framework.AuditConfiguration; +import org.opensearch.test.framework.AuditFilters; +import org.opensearch.test.framework.TestSecurityConfig.Role; +import org.opensearch.test.framework.TestSecurityConfig.User; +import org.opensearch.test.framework.audit.AuditLogsRule; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; +import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.REMOVE; +import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.REMOVE_INDEX; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.client.RequestOptions.DEFAULT; +import static org.opensearch.rest.RestRequest.Method.DELETE; +import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.RestRequest.Method.POST; +import static org.opensearch.rest.RestRequest.Method.PUT; +import static org.opensearch.core.rest.RestStatus.ACCEPTED; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.core.rest.RestStatus.INTERNAL_SERVER_ERROR; +import static org.opensearch.security.Song.FIELD_ARTIST; +import static org.opensearch.security.Song.FIELD_STARS; +import static org.opensearch.security.Song.FIELD_TITLE; +import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; +import static org.opensearch.security.Song.QUERY_TITLE_NEXT_SONG; +import static org.opensearch.security.Song.QUERY_TITLE_POISON; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; +import static org.opensearch.security.Song.TITLE_NEXT_SONG; +import static org.opensearch.security.Song.TITLE_POISON; +import static org.opensearch.security.Song.TITLE_SONG_1_PLUS_1; +import static org.opensearch.security.auditlog.impl.AuditCategory.INDEX_EVENT; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.auditPredicate; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.grantedPrivilege; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.missingPrivilege; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.userAuthenticated; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.averageAggregationRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.getSearchScrollRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.searchRequestWithScroll; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.statsAggregationRequest; +import static org.opensearch.test.framework.matcher.BulkResponseMatchers.bulkResponseContainExceptions; +import static org.opensearch.test.framework.matcher.BulkResponseMatchers.failureBulkResponse; +import static org.opensearch.test.framework.matcher.BulkResponseMatchers.successBulkResponse; +import static org.opensearch.test.framework.matcher.ClusterMatchers.aliasExists; +import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainSuccessSnapshot; +import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainTemplate; +import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainTemplateWithAlias; +import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainsDocument; +import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainsDocumentWithFieldValue; +import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainsSnapshotRepository; +import static org.opensearch.test.framework.matcher.ClusterMatchers.indexExists; +import static org.opensearch.test.framework.matcher.ClusterMatchers.indexMappingIsEqualTo; +import static org.opensearch.test.framework.matcher.ClusterMatchers.indexSettingsContainValues; +import static org.opensearch.test.framework.matcher.ClusterMatchers.indexStateIsEqualTo; +import static org.opensearch.test.framework.matcher.ClusterMatchers.snapshotInClusterDoesNotExists; +import static org.opensearch.test.framework.matcher.DeleteResponseMatchers.isSuccessfulDeleteResponse; +import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; +import static org.opensearch.test.framework.matcher.FieldCapabilitiesResponseMatchers.containsExactlyIndices; +import static org.opensearch.test.framework.matcher.FieldCapabilitiesResponseMatchers.containsFieldWithNameAndType; +import static org.opensearch.test.framework.matcher.FieldCapabilitiesResponseMatchers.numberOfFieldsIsEqualTo; +import static org.opensearch.test.framework.matcher.GetResponseMatchers.containDocument; +import static org.opensearch.test.framework.matcher.GetResponseMatchers.documentContainField; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.getIndexResponseContainsIndices; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.getMappingsResponseContainsIndices; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.getSettingsResponseContainsIndices; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulClearIndicesCacheResponse; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulCloseIndexResponse; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulCreateIndexResponse; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulOpenIndexResponse; +import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulResizeResponse; +import static org.opensearch.test.framework.matcher.MultiGetResponseMatchers.isSuccessfulMultiGetResponse; +import static org.opensearch.test.framework.matcher.MultiGetResponseMatchers.numberOfGetItemResponsesIsEqualTo; +import static org.opensearch.test.framework.matcher.MultiSearchResponseMatchers.isSuccessfulMultiSearchResponse; +import static org.opensearch.test.framework.matcher.MultiSearchResponseMatchers.numberOfSearchItemResponsesIsEqualTo; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.errorMessageContain; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containAggregationWithNameAndType; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containNotEmptyScrollingId; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfHitsInPageIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; +import static org.opensearch.test.framework.matcher.UpdateResponseMatchers.isSuccessfulUpdateResponse; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class SearchOperationTest { + + private static final Logger log = LogManager.getLogger(SearchOperationTest.class); + + public static final String SONG_INDEX_NAME = "song_lyrics"; + public static final String PROHIBITED_SONG_INDEX_NAME = "prohibited_song_lyrics"; + public static final String WRITE_SONG_INDEX_NAME = "write_song_index"; + + public static final String SONG_LYRICS_ALIAS = "song_lyrics_index_alias"; + public static final String PROHIBITED_SONG_ALIAS = "prohibited_song_lyrics_index_alias"; + private static final String COLLECTIVE_INDEX_ALIAS = "collective-index-alias"; + private static final String TEMPLATE_INDEX_PREFIX = "song-transcription*"; + public static final String TEMPORARY_ALIAS_NAME = "temporary-alias"; + public static final String ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001 = "alias-used-in-musical-index-template-0001"; + public static final String ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002 = "alias-used-in-musical-index-template-0002"; + public static final String ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003 = "alias-used-in-musical-index-template-0003"; + public static final String INDEX_NAME_SONG_TRANSCRIPTION_JAZZ = "song-transcription-jazz"; + + public static final String MUSICAL_INDEX_TEMPLATE = "musical-index-template"; + public static final String ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE = "alias_create_index_with_alias_positive"; + public static final String ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE = "alias_create_index_with_alias_negative"; + + public static final String UNDELETABLE_TEMPLATE_NAME = "undeletable-template-name"; + + public static final String ALIAS_FROM_UNDELETABLE_TEMPLATE = "alias-from-undeletable-template"; + + public static final String TEST_SNAPSHOT_REPOSITORY_NAME = "test-snapshot-repository"; + + public static final String UNUSED_SNAPSHOT_REPOSITORY_NAME = "unused-snapshot-repository"; + + public static final String RESTORED_SONG_INDEX_NAME = "restored_" + WRITE_SONG_INDEX_NAME; + + public static final String UPDATE_DELETE_OPERATION_INDEX_NAME = "update_delete_index"; + + public static final String DOCUMENT_TO_UPDATE_ID = "doc_to_update"; + + private static final String ID_P4 = "4"; + private static final String ID_S3 = "3"; + private static final String ID_S2 = "2"; + private static final String ID_S1 = "1"; + + static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS); + + /** + * All user read permissions are related to {@link #SONG_INDEX_NAME} index + */ + static final User LIMITED_READ_USER = new User("limited_read_user").roles( + new Role("limited-song-reader").clusterPermissions( + "indices:data/read/mget", + "indices:data/read/msearch", + "indices:data/read/scroll" + ) + .indexPermissions( + "indices:data/read/search", + "indices:data/read/get", + "indices:data/read/mget*", + "indices:admin/aliases", + "indices:data/read/field_caps", + "indices:data/read/field_caps*" + ) + .on(SONG_INDEX_NAME) + ); + + static final User LIMITED_WRITE_USER = new User("limited_write_user").roles( + new Role("limited-write-role").clusterPermissions( + "indices:data/write/bulk", + "indices:admin/template/put", + "indices:admin/template/delete", + "cluster:admin/repository/put", + "cluster:admin/repository/delete", + "cluster:admin/snapshot/create", + "cluster:admin/snapshot/status", + "cluster:admin/snapshot/status[nodes]", + "cluster:admin/snapshot/delete", + "cluster:admin/snapshot/get", + "cluster:admin/snapshot/restore" + ) + .indexPermissions( + "indices:data/write/index", + "indices:data/write/bulk[s]", + "indices:admin/create", + "indices:admin/mapping/put", + "indices:data/write/update", + "indices:data/write/bulk[s]", + "indices:data/write/delete", + "indices:data/write/bulk[s]" + ) + .on(WRITE_SONG_INDEX_NAME), + new Role("transcription-role").indexPermissions( + "indices:data/write/index", + "indices:admin/create", + "indices:data/write/bulk[s]", + "indices:admin/mapping/put" + ).on(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ), + new Role("limited-write-index-restore-role").indexPermissions( + "indices:data/write/index", + "indices:admin/create", + "indices:data/read/search" + ).on(RESTORED_SONG_INDEX_NAME) + ); + + /** + * User who is allowed read both index {@link #SONG_INDEX_NAME} and {@link #PROHIBITED_SONG_INDEX_NAME} + */ + static final User DOUBLE_READER_USER = new User("double_read_user").roles( + new Role("full-song-reader").indexPermissions("indices:data/read/search").on(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME) + ); + + static final User REINDEXING_USER = new User("reindexing_user").roles( + new Role("song-reindexing-target-write").clusterPermissions("indices:data/write/reindex", "indices:data/write/bulk") + .indexPermissions("indices:admin/create", "indices:data/write/index", "indices:data/write/bulk[s]", "indices:admin/mapping/put") + .on(WRITE_SONG_INDEX_NAME), + new Role("song-reindexing-source-read").clusterPermissions("indices:data/read/scroll") + .indexPermissions("indices:data/read/search") + .on(SONG_INDEX_NAME) + ); + + private Client internalClient; + /** + * User who is allowed to update and delete documents on index {@link #UPDATE_DELETE_OPERATION_INDEX_NAME} + */ + static final User UPDATE_DELETE_USER = new User("update_delete_user").roles( + new Role("document-updater").clusterPermissions("indices:data/write/bulk") + .indexPermissions( + "indices:data/write/update", + "indices:data/write/index", + "indices:data/write/bulk[s]", + "indices:admin/mapping/put" + ) + .on(UPDATE_DELETE_OPERATION_INDEX_NAME), + new Role("document-remover").indexPermissions("indices:data/write/delete").on(UPDATE_DELETE_OPERATION_INDEX_NAME) + ); + + static final String INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX = "index_operations_"; + + /** + * User who is allowed to perform index-related operations on + * indices with names prefixed by the {@link #INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX} + */ + static final User USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES = new User("index-operation-tester").roles( + new Role("index-manager").indexPermissions( + "indices:admin/create", + "indices:admin/get", + "indices:admin/delete", + "indices:admin/close", + "indices:admin/close*", + "indices:admin/open", + "indices:admin/resize", + "indices:monitor/stats", + "indices:monitor/settings/get", + "indices:admin/settings/update", + "indices:admin/mapping/put", + "indices:admin/mappings/get", + "indices:admin/cache/clear", + "indices:admin/aliases" + ).on(INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("*")) + ); + + private static User USER_ALLOWED_TO_CREATE_INDEX = new User("user-allowed-to-create-index").roles( + new Role("create-index-role").indexPermissions("indices:admin/create").on("*") + ); + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .anonymousAuth(false) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users( + ADMIN_USER, + LIMITED_READ_USER, + LIMITED_WRITE_USER, + DOUBLE_READER_USER, + REINDEXING_USER, + UPDATE_DELETE_USER, + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, + USER_ALLOWED_TO_CREATE_INDEX + ) + .audit( + new AuditConfiguration(true).compliance(new AuditCompliance().enabled(true)) + .filters(new AuditFilters().enabledRest(true).enabledTransport(true)) + ) + .build(); + + @Rule + public AuditLogsRule auditLogsRule = new AuditLogsRule(); + + @BeforeClass + public static void createTestData() { + try (Client client = cluster.getInternalNodeClient()) { + client.prepareIndex(SONG_INDEX_NAME).setId(ID_S1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + client.prepareIndex(UPDATE_DELETE_OPERATION_INDEX_NAME) + .setId(DOCUMENT_TO_UPDATE_ID) + .setRefreshPolicy(IMMEDIATE) + .setSource("field", "value") + .get(); + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction(new AliasActions(ADD).indices(SONG_INDEX_NAME).alias(SONG_LYRICS_ALIAS)) + ) + .actionGet(); + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SONG_INDEX_NAME).id(ID_S2).source(SONGS[1].asMap())) + .actionGet(); + client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SONG_INDEX_NAME).id(ID_S3).source(SONGS[2].asMap())) + .actionGet(); + + client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(ID_P4).setSource(SONGS[3].asMap()).setRefreshPolicy(IMMEDIATE).get(); + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction( + new AliasActions(ADD).indices(PROHIBITED_SONG_INDEX_NAME).alias(PROHIBITED_SONG_ALIAS) + ) + ) + .actionGet(); + + client.admin() + .indices() + .aliases( + new IndicesAliasesRequest().addAliasAction( + new AliasActions(ADD).indices(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME).alias(COLLECTIVE_INDEX_ALIAS) + ) + ) + .actionGet(); + var createTemplateRequest = new org.opensearch.action.admin.indices.template.put.PutIndexTemplateRequest( + UNDELETABLE_TEMPLATE_NAME + ); + createTemplateRequest.patterns(List.of("pattern-does-not-match-to-any-index")); + createTemplateRequest.alias(new Alias(ALIAS_FROM_UNDELETABLE_TEMPLATE)); + client.admin().indices().putTemplate(createTemplateRequest).actionGet(); + + client.admin() + .cluster() + .putRepository( + new PutRepositoryRequest(UNUSED_SNAPSHOT_REPOSITORY_NAME).type("fs") + .settings(Map.of("location", cluster.getSnapshotDirPath())) + ) + .actionGet(); + } + } + + @Before + public void retrieveClusterClient() { + this.internalClient = cluster.getInternalNodeClient(); + } + + @After + public void cleanData() throws ExecutionException, InterruptedException { + Stopwatch stopwatch = Stopwatch.createStarted(); + IndicesAdminClient indices = internalClient.admin().indices(); + List indicesToBeDeleted = List.of( + WRITE_SONG_INDEX_NAME, + INDEX_NAME_SONG_TRANSCRIPTION_JAZZ, + RESTORED_SONG_INDEX_NAME, + INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("*") + ); + for (String indexToBeDeleted : indicesToBeDeleted) { + IndicesExistsRequest indicesExistsRequest = new IndicesExistsRequest(indexToBeDeleted); + var indicesExistsResponse = indices.exists(indicesExistsRequest).get(); + if (indicesExistsResponse.isExists()) { + DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexToBeDeleted); + indices.delete(deleteIndexRequest).actionGet(); + Awaitility.await().ignoreExceptions().until(() -> indices.exists(indicesExistsRequest).get().isExists() == false); + } + } + + List aliasesToBeDeleted = List.of( + TEMPORARY_ALIAS_NAME, + ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001, + ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002, + ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE, + ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE + ); + for (String aliasToBeDeleted : aliasesToBeDeleted) { + if (indices.exists(new IndicesExistsRequest(aliasToBeDeleted)).get().isExists()) { + AliasActions aliasAction = new AliasActions(AliasActions.Type.REMOVE).indices(SONG_INDEX_NAME).alias(aliasToBeDeleted); + internalClient.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(aliasAction)).get(); + } + } + + GetIndexTemplatesResponse response = indices.getTemplates(new GetIndexTemplatesRequest(MUSICAL_INDEX_TEMPLATE)).get(); + for (IndexTemplateMetadata metadata : response.getIndexTemplates()) { + indices.deleteTemplate(new DeleteIndexTemplateRequest(metadata.getName())).get(); + } + + ClusterAdminClient clusterClient = internalClient.admin().cluster(); + try { + clusterClient.deleteRepository(new DeleteRepositoryRequest(TEST_SNAPSHOT_REPOSITORY_NAME)).actionGet(); + } catch (RepositoryMissingException e) { + log.debug("Repository '{}' does not exist. This is expected in most of test cases", TEST_SNAPSHOT_REPOSITORY_NAME, e); + } + internalClient.close(); + log.debug("Cleaning data after test took {}", stopwatch.stop()); + } + + @Test + public void shouldSearchForDocuments_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldSearchForDocuments_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(PROHIBITED_SONG_INDEX_NAME, QUERY_TITLE_POISON); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldSearchForDocumentsViaAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(SONG_LYRICS_ALIAS, QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics_index_alias/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldSearchForDocumentsViaAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(PROHIBITED_SONG_ALIAS, QUERY_TITLE_POISON); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics_index_alias/_search") + ); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchSongViaMultiIndexAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(COLLECTIVE_INDEX_ALIAS, QUERY_TITLE_NEXT_SONG); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S3)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/collective-index-alias/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchSongViaMultiIndexAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(COLLECTIVE_INDEX_ALIAS, QUERY_TITLE_POISON); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/collective-index-alias/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchAllIndexes_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(ADMIN_USER).withRestRequest(POST, "/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(ADMIN_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchAllIndexes_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest(QUERY_TITLE_MAGNUM_OPUS); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchSongIndexesWithAsterisk_prohibitedSongIndex_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("*" + SONG_INDEX_NAME, QUERY_TITLE_POISON); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, PROHIBITED_SONG_INDEX_NAME, ID_P4)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_POISON)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/*song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchSongIndexesWithAsterisk_singIndex_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("*" + SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S3)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/*song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); + } + + @Test + public void shouldBeAbleToSearchSongIndexesWithAsterisk_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("*" + SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/*song_lyrics/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldFindSongUsingDslQuery_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = new SearchRequest(SONG_INDEX_NAME); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); + boolQueryBuilder.filter(QueryBuilders.regexpQuery(FIELD_ARTIST, "f.+")); + boolQueryBuilder.filter(new MatchQueryBuilder(FIELD_TITLE, TITLE_MAGNUM_OPUS)); + searchSourceBuilder.query(boolQueryBuilder); + searchRequest.source(searchSourceBuilder); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldFindSongUsingDslQuery_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = new SearchRequest(PROHIBITED_SONG_INDEX_NAME); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); + boolQueryBuilder.filter(QueryBuilders.regexpQuery(FIELD_ARTIST, "n.+")); + boolQueryBuilder.filter(new MatchQueryBuilder(FIELD_TITLE, TITLE_POISON)); + searchSourceBuilder.query(boolQueryBuilder); + searchRequest.source(searchSourceBuilder); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldPerformSearchWithAllIndexAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(ADMIN_USER).withRestRequest(POST, "/_all/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(ADMIN_USER, "SearchRequest")); + } + + @Test + public void shouldPerformSearchWithAllIndexAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_MAGNUM_OPUS); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_all/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldScrollOverSearchResults_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = searchRequestWithScroll(SONG_INDEX_NAME, 2); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containNotEmptyScrollingId()); + + SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); + + SearchResponse scrollResponse = restHighLevelClient.scroll(scrollRequest, DEFAULT); + assertThat(scrollResponse, isSuccessfulSearchResponse()); + assertThat(scrollResponse, containNotEmptyScrollingId()); + assertThat(scrollResponse, numberOfTotalHitsIsEqualTo(3)); + assertThat(scrollResponse, numberOfHitsInPageIsEqualTo(1)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_search/scroll")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchScrollRequest")); + } + + @Test + public void shouldScrollOverSearchResults_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { + SearchRequest searchRequest = searchRequestWithScroll(SONG_INDEX_NAME, 2); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containNotEmptyScrollingId()); + + SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); + + assertThatThrownBy(() -> restHighLevelClient.scroll(scrollRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/_search/scroll")); + auditLogsRule.assertExactlyOne(missingPrivilege(DOUBLE_READER_USER, "SearchScrollRequest")); + } + + @Test + public void shouldGetDocument_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + GetResponse response = restHighLevelClient.get(new GetRequest(SONG_INDEX_NAME, ID_S1), DEFAULT); + + assertThat(response, containDocument(SONG_INDEX_NAME, ID_S1)); + assertThat(response, documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/song_lyrics/_doc/1")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "GetRequest")); + } + + @Test + public void shouldGetDocument_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + GetRequest getRequest = new GetRequest(PROHIBITED_SONG_INDEX_NAME, ID_P4); + assertThatThrownBy(() -> restHighLevelClient.get(getRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/prohibited_song_lyrics/_doc/4")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "GetRequest")); + } + + @Test + public void shouldPerformMultiGetDocuments_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + MultiGetRequest request = new MultiGetRequest(); + request.add(new Item(SONG_INDEX_NAME, ID_S1)); + request.add(new Item(SONG_INDEX_NAME, ID_S2)); + + MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); + + assertThat(response, is(notNullValue())); + assertThat(response, isSuccessfulMultiGetResponse()); + assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); + + MultiGetItemResponse[] responses = response.getResponses(); + assertThat( + responses[0].getResponse(), + allOf(containDocument(SONG_INDEX_NAME, ID_S1), documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)) + ); + assertThat( + responses[1].getResponse(), + allOf(containDocument(SONG_INDEX_NAME, ID_S2), documentContainField(FIELD_TITLE, TITLE_SONG_1_PLUS_1)) + ); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_mget")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetShardRequest")); + } + + @Test + public void shouldPerformMultiGetDocuments_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { + MultiGetRequest request = new MultiGetRequest(); + request.add(new Item(SONG_INDEX_NAME, ID_S1)); + + assertThatThrownBy(() -> restHighLevelClient.mget(request, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/_mget")); + auditLogsRule.assertExactlyOne(missingPrivilege(DOUBLE_READER_USER, "MultiGetRequest")); + } + + @Test + public void shouldPerformMultiGetDocuments_partiallyPositive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + MultiGetRequest request = new MultiGetRequest(); + request.add(new Item(SONG_INDEX_NAME, ID_S1)); + request.add(new Item(PROHIBITED_SONG_INDEX_NAME, ID_P4)); + + MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); + + assertThat(request, notNullValue()); + assertThat(response, not(isSuccessfulMultiGetResponse())); + assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); + + MultiGetItemResponse[] responses = response.getResponses(); + assertThat(responses, arrayContaining(hasProperty("failure", nullValue()), hasProperty("failure", notNullValue()))); + assertThat(responses[1].getFailure().getFailure(), statusException(INTERNAL_SERVER_ERROR)); + assertThat(responses[1].getFailure().getFailure(), errorMessageContain("security_exception")); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_mget")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetShardRequest").withIndex(SONG_INDEX_NAME)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "MultiGetShardRequest").withIndex(PROHIBITED_SONG_INDEX_NAME)); + } + + @Test + public void shouldBeAllowedToPerformMulitSearch_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + MultiSearchRequest request = new MultiSearchRequest(); + request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS)); + request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG)); + + MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response, isSuccessfulMultiSearchResponse()); + assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); + + MultiSearchResponse.Item[] responses = response.getResponses(); + + assertThat(responses[0].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + assertThat(responses[0].getResponse(), searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); + assertThat(responses[1].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); + assertThat(responses[1].getResponse(), searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S3)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_msearch")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiSearchRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldBeAllowedToPerformMulitSearch_partiallyPositive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + MultiSearchRequest request = new MultiSearchRequest(); + request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS)); + request.add(queryStringQueryRequest(PROHIBITED_SONG_INDEX_NAME, QUERY_TITLE_POISON)); + + MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response, not(isSuccessfulMultiSearchResponse())); + assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); + + MultiSearchResponse.Item[] responses = response.getResponses(); + assertThat(responses[0].getFailure(), nullValue()); + assertThat(responses[1].getFailure(), statusException(INTERNAL_SERVER_ERROR)); + assertThat(responses[1].getFailure(), errorMessageContain("security_exception")); + assertThat(responses[1].getResponse(), nullValue()); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_msearch")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiSearchRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(SONG_INDEX_NAME)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(PROHIBITED_SONG_INDEX_NAME)); + } + + @Test + public void shouldBeAllowedToPerformMulitSearch_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { + MultiSearchRequest request = new MultiSearchRequest(); + request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS)); + request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG)); + + assertThatThrownBy(() -> restHighLevelClient.msearch(request, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/_msearch")); + auditLogsRule.assertExactlyOne(missingPrivilege(DOUBLE_READER_USER, "MultiSearchRequest")); + } + + @Test + public void shouldAggregateDataAndComputeAverage_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + final String aggregationName = "averageStars"; + SearchRequest searchRequest = averageAggregationRequest(SONG_INDEX_NAME, aggregationName, FIELD_STARS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(SONG_INDEX_NAME)); + } + + @Test + public void shouldAggregateDataAndComputeAverage_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = averageAggregationRequest(PROHIBITED_SONG_INDEX_NAME, "averageStars", FIELD_STARS); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(PROHIBITED_SONG_INDEX_NAME)); + } + + @Test + public void shouldPerformStatAggregation_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + final String aggregationName = "statsStars"; + SearchRequest searchRequest = statsAggregationRequest(SONG_INDEX_NAME, aggregationName, FIELD_STARS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "stats")); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldPerformStatAggregation_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SearchRequest searchRequest = statsAggregationRequest(PROHIBITED_SONG_INDEX_NAME, "statsStars", FIELD_STARS); + + assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); + } + + @Test + public void shouldIndexDocumentInBulkRequest_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); + bulkRequest.setRefreshPolicy(IMMEDIATE); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat(response, successBulkResponse()); + assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, "one")); + assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, "two")); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "one", FIELD_TITLE, TITLE_MAGNUM_OPUS)); + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) + ); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertAtLeast(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER));// sometimes 4 or 6 + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest"));// sometimes 2 or 4 + } + + @Test + public void shouldIndexDocumentInBulkRequest_partiallyPositive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); + bulkRequest.setRefreshPolicy(IMMEDIATE); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat( + response, + bulkResponseContainExceptions(0, allOf(statusException(INTERNAL_SERVER_ERROR), errorMessageContain("security_exception"))) + ); + assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, "two")); + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) + ); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); + } + + @Test + public void shouldIndexDocumentInBulkRequest_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); + bulkRequest.setRefreshPolicy(IMMEDIATE); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat( + response, + allOf( + failureBulkResponse(), + bulkResponseContainExceptions(statusException(INTERNAL_SERVER_ERROR)), + bulkResponseContainExceptions(errorMessageContain("security_exception")) + ) + ); + assertThat(internalClient, not(clusterContainsDocument(SONG_INDEX_NAME, "one"))); + assertThat(internalClient, not(clusterContainsDocument(SONG_INDEX_NAME, "two"))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); + } + + @Test + public void shouldUpdateDocumentsInBulk_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + final String titleOne = "shape of my mind"; + final String titleTwo = "forgiven"; + BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); + restHighLevelClient.bulk(bulkRequest, DEFAULT); + bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new UpdateRequest(WRITE_SONG_INDEX_NAME, "one").doc(Map.of(FIELD_TITLE, titleOne))); + bulkRequest.add(new UpdateRequest(WRITE_SONG_INDEX_NAME, "two").doc(Map.of(FIELD_TITLE, titleTwo))); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat(response, successBulkResponse()); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "one", FIELD_TITLE, titleOne)); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, titleTwo)); + } + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + + } + + @Test + public void shouldUpdateDocumentsInBulk_partiallyPositive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + final String titleOne = "shape of my mind"; + BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + restHighLevelClient.bulk(bulkRequest, DEFAULT); + bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new UpdateRequest(WRITE_SONG_INDEX_NAME, "one").doc(Map.of(FIELD_TITLE, titleOne))); + bulkRequest.add(new UpdateRequest(SONG_INDEX_NAME, ID_S2).doc(Map.of(FIELD_TITLE, "forgiven"))); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat( + response, + bulkResponseContainExceptions(1, allOf(statusException(INTERNAL_SERVER_ERROR), errorMessageContain("security_exception"))) + ); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "one", FIELD_TITLE, titleOne)); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S2, FIELD_TITLE, TITLE_SONG_1_PLUS_1)); + } + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); + } + + @Test + public void shouldUpdateDocumentsInBulk_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new UpdateRequest(SONG_INDEX_NAME, ID_S1).doc(Map.of(FIELD_TITLE, "shape of my mind"))); + bulkRequest.add(new UpdateRequest(SONG_INDEX_NAME, ID_S2).doc(Map.of(FIELD_TITLE, "forgiven"))); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat( + response, + allOf( + failureBulkResponse(), + bulkResponseContainExceptions(statusException(INTERNAL_SERVER_ERROR)), + bulkResponseContainExceptions(errorMessageContain("security_exception")) + ) + ); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S1, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S2, FIELD_TITLE, TITLE_SONG_1_PLUS_1)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); + } + + @Test + public void shouldDeleteDocumentInBulk_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("three").source(SONGS[2].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("four").source(SONGS[3].asMap())); + assertThat(restHighLevelClient.bulk(bulkRequest, DEFAULT), successBulkResponse()); + bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "one")); + bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "three")); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat(response, successBulkResponse()); + assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, "one"))); + assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, "three"))); + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) + ); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "four", FIELD_TITLE, TITLE_POISON)); + } + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + public void shouldDeleteDocumentInBulk_partiallyPositive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); + assertThat(restHighLevelClient.bulk(bulkRequest, DEFAULT), successBulkResponse()); + bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "one")); + bulkRequest.add(new DeleteRequest(SONG_INDEX_NAME, ID_S3)); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, "one"))); + + assertThat( + response, + bulkResponseContainExceptions(1, allOf(statusException(INTERNAL_SERVER_ERROR), errorMessageContain("security_exception"))) + ); + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) + ); + assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S3, FIELD_TITLE, TITLE_NEXT_SONG)); + } + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); + } + + @Test + public void shouldDeleteDocumentInBulk_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); + bulkRequest.add(new DeleteRequest(SONG_INDEX_NAME, ID_S1)); + bulkRequest.add(new DeleteRequest(SONG_INDEX_NAME, ID_S3)); + + BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); + + assertThat( + response, + allOf( + failureBulkResponse(), + bulkResponseContainExceptions(statusException(INTERNAL_SERVER_ERROR)), + bulkResponseContainExceptions(errorMessageContain("security_exception")) + ) + ); + assertThat(internalClient, clusterContainsDocument(SONG_INDEX_NAME, ID_S1)); + assertThat(internalClient, clusterContainsDocument(SONG_INDEX_NAME, ID_S3)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); + + } + + @Test + public void shouldReindexDocuments_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { + ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(SONG_INDEX_NAME).setDestIndex(WRITE_SONG_INDEX_NAME); + + BulkByScrollResponse response = restHighLevelClient.reindex(reindexRequest, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response.getBulkFailures(), empty()); + assertThat(response.getSearchFailures(), empty()); + assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_S1)); + assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_S2)); + assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_S3)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(REINDEXING_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(REINDEXING_USER, "PutMappingRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchScrollRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(REINDEXING_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "ClearScrollRequest")); + } + + @Test + public void shouldReindexDocuments_negativeSource() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { + ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(PROHIBITED_SONG_INDEX_NAME) + .setDestIndex(WRITE_SONG_INDEX_NAME); + + assertThatThrownBy(() -> restHighLevelClient.reindex(reindexRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_P4))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "SearchRequest")); + } + + @Test + public void shouldReindexDocuments_negativeDestination() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { + ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(SONG_INDEX_NAME).setDestIndex(PROHIBITED_SONG_INDEX_NAME); + + assertThatThrownBy(() -> restHighLevelClient.reindex(reindexRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(internalClient, not(clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_S1))); + assertThat(internalClient, not(clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_S2))); + assertThat(internalClient, not(clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_S3))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "BulkShardRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "ClearScrollRequest")); + } + + @Test + public void shouldReindexDocuments_negativeSourceAndDestination() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { + ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(PROHIBITED_SONG_INDEX_NAME).setDestIndex(SONG_INDEX_NAME); + + assertThatThrownBy(() -> restHighLevelClient.reindex(reindexRequest, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "SearchRequest")); + } + + @Test + public void shouldUpdateDocument_positive() throws IOException { + String newField = "newField"; + String newValue = "newValue"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { + UpdateRequest updateRequest = new UpdateRequest(UPDATE_DELETE_OPERATION_INDEX_NAME, DOCUMENT_TO_UPDATE_ID).doc( + newField, + newValue + ).setRefreshPolicy(IMMEDIATE); + + UpdateResponse response = restHighLevelClient.update(updateRequest, DEFAULT); + + assertThat(response, isSuccessfulUpdateResponse()); + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(UPDATE_DELETE_OPERATION_INDEX_NAME, DOCUMENT_TO_UPDATE_ID, newField, newValue) + ); + } + } + + @Test + public void shouldUpdateDocument_negative() throws IOException { + String newField = "newField"; + String newValue = "newValue"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { + UpdateRequest updateRequest = new UpdateRequest(PROHIBITED_SONG_INDEX_NAME, DOCUMENT_TO_UPDATE_ID).doc(newField, newValue) + .setRefreshPolicy(IMMEDIATE); + + assertThatThrownBy(() -> restHighLevelClient.update(updateRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldDeleteDocument_positive() throws IOException { + String docId = "shouldDeleteDocument_positive"; + try (Client client = cluster.getInternalNodeClient()) { + client.index( + new IndexRequest(UPDATE_DELETE_OPERATION_INDEX_NAME).id(docId).source("field", "value").setRefreshPolicy(IMMEDIATE) + ).actionGet(); + assertThat(internalClient, clusterContainsDocument(UPDATE_DELETE_OPERATION_INDEX_NAME, docId)); + } + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { + DeleteRequest deleteRequest = new DeleteRequest(UPDATE_DELETE_OPERATION_INDEX_NAME, docId).setRefreshPolicy(IMMEDIATE); + + DeleteResponse response = restHighLevelClient.delete(deleteRequest, DEFAULT); + + assertThat(response, isSuccessfulDeleteResponse()); + assertThat(internalClient, not(clusterContainsDocument(UPDATE_DELETE_OPERATION_INDEX_NAME, docId))); + } + } + + @Test + public void shouldDeleteDocument_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { + DeleteRequest deleteRequest = new DeleteRequest(PROHIBITED_SONG_INDEX_NAME, ID_S1).setRefreshPolicy(IMMEDIATE); + + assertThatThrownBy(() -> restHighLevelClient.delete(deleteRequest, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldCreateAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + AliasActions aliasAction = new AliasActions(ADD).indices(SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); + IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); + + var response = restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, clusterContainsDocument(TEMPORARY_ALIAS_NAME, ID_S1)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); + auditLogsRule.assertExactly(2, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_READ_USER)); + } + + @Test + public void shouldCreateAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + AliasActions aliasAction = new AliasActions(ADD).indices(PROHIBITED_SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); + IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); + + assertThatThrownBy( + () -> restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT), + statusException(FORBIDDEN) + ); + + assertThat(internalClient, not(clusterContainsDocument(TEMPORARY_ALIAS_NAME, ID_P4))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); + } + + @Test + public void shouldDeleteAlias_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + AliasActions aliasAction = new AliasActions(ADD).indices(SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); + IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); + restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT); + aliasAction = new AliasActions(REMOVE).indices(SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); + indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); + + var response = restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, not(clusterContainsDocument(TEMPORARY_ALIAS_NAME, ID_S1))); + } + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); + auditLogsRule.assertExactly(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_READ_USER)); + } + + @Test + public void shouldDeleteAlias_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + AliasActions aliasAction = new AliasActions(REMOVE).indices(PROHIBITED_SONG_INDEX_NAME).alias(PROHIBITED_SONG_ALIAS); + IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); + + assertThatThrownBy( + () -> restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT), + statusException(FORBIDDEN) + ); + + assertThat(internalClient, clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_P4)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); + } + + @Test + public void shouldCreateIndexTemplate_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002)); + + var response = restHighLevelClient.indices().putTemplate(request, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); + String documentId = "0001"; + IndexRequest indexRequest = new IndexRequest(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ).id(documentId) + .source(SONGS[0].asMap()) + .setRefreshPolicy(IMMEDIATE); + restHighLevelClient.index(indexRequest, DEFAULT); + assertThat(internalClient, clusterContainsDocument(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ, documentId)); + assertThat(internalClient, clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001, documentId)); + assertThat(internalClient, clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002, documentId)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_template/musical-index-template")); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/song-transcription-jazz/_doc/0001")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "IndexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(8, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + public void shouldCreateIndexTemplate_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002)); + + assertThatThrownBy(() -> restHighLevelClient.indices().putTemplate(request, DEFAULT), statusException(FORBIDDEN)); + assertThat(internalClient, not(clusterContainTemplate(MUSICAL_INDEX_TEMPLATE))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_template/musical-index-template")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "PutIndexTemplateRequest")); + } + + @Test + public void shouldDeleteTemplate_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)); + restHighLevelClient.indices().putTemplate(request, DEFAULT); + assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); + DeleteIndexTemplateRequest deleteRequest = new DeleteIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE); + + var response = restHighLevelClient.indices().deleteTemplate(deleteRequest, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, not(clusterContainTemplate(MUSICAL_INDEX_TEMPLATE))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_template/musical-index-template")); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(DELETE, "/_template/musical-index-template")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "DeleteIndexTemplateRequest")); + auditLogsRule.assertExactly(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + public void shouldDeleteTemplate_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + DeleteIndexTemplateRequest deleteRequest = new DeleteIndexTemplateRequest(UNDELETABLE_TEMPLATE_NAME); + + assertThatThrownBy(() -> restHighLevelClient.indices().deleteTemplate(deleteRequest, DEFAULT), statusException(FORBIDDEN)); + + assertThat(internalClient, clusterContainTemplate(UNDELETABLE_TEMPLATE_NAME)); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_READ_USER).withRestRequest(DELETE, "/_template/undeletable-template-name") + ); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "DeleteIndexTemplateRequest")); + } + + @Test + public void shouldUpdateTemplate_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002)); + restHighLevelClient.indices().putTemplate(request, DEFAULT); + assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); + request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) + .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003)); + + var response = restHighLevelClient.indices().putTemplate(request, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + String documentId = "000one"; + IndexRequest indexRequest = new IndexRequest(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ).id(documentId) + .source(SONGS[0].asMap()) + .setRefreshPolicy(IMMEDIATE); + restHighLevelClient.index(indexRequest, DEFAULT); + assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); + assertThat(internalClient, clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003, documentId)); + assertThat(internalClient, not(clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001, documentId))); + assertThat(internalClient, not(clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002, documentId))); + } + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_template/musical-index-template")); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/song-transcription-jazz/_doc/000one")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "IndexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(10, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + public void shouldUpdateTemplate_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + PutIndexTemplateRequest request = new PutIndexTemplateRequest(UNDELETABLE_TEMPLATE_NAME).patterns( + List.of(TEMPLATE_INDEX_PREFIX) + ).alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003)); + + assertThatThrownBy(() -> restHighLevelClient.indices().putTemplate(request, DEFAULT), statusException(FORBIDDEN)); + assertThat(internalClient, clusterContainTemplateWithAlias(UNDELETABLE_TEMPLATE_NAME, ALIAS_FROM_UNDELETABLE_TEMPLATE)); + assertThat( + internalClient, + not(clusterContainTemplateWithAlias(UNDELETABLE_TEMPLATE_NAME, ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003)) + ); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_template/undeletable-template-name")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "PutIndexTemplateRequest")); + } + + @Test + public void shouldGetFieldCapabilitiesForAllIndexes_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().fields(FIELD_TITLE); + + FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response, containsExactlyIndices(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME, UPDATE_DELETE_OPERATION_INDEX_NAME)); + assertThat(response, numberOfFieldsIsEqualTo(1)); + assertThat(response, containsFieldWithNameAndType(FIELD_TITLE, "text")); + } + auditLogsRule.assertExactlyOne(userAuthenticated(ADMIN_USER).withRestRequest(GET, "/_field_caps")); + auditLogsRule.assertExactlyOne(grantedPrivilege(ADMIN_USER, "FieldCapabilitiesRequest")); + auditLogsRule.assertExactly(3, grantedPrivilege(ADMIN_USER, "FieldCapabilitiesIndexRequest")); + } + + @Test + public void shouldGetFieldCapabilitiesForAllIndexes_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().fields(FIELD_TITLE); + + assertThatThrownBy(() -> restHighLevelClient.fieldCaps(request, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/_field_caps")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "FieldCapabilitiesRequest")); + } + + @Test + public void shouldGetFieldCapabilitiesForParticularIndex_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(SONG_INDEX_NAME).fields(FIELD_TITLE); + + FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); + + assertThat(response, notNullValue()); + assertThat(response, containsExactlyIndices(SONG_INDEX_NAME)); + assertThat(response, numberOfFieldsIsEqualTo(1)); + assertThat(response, containsFieldWithNameAndType(FIELD_TITLE, "text")); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/song_lyrics/_field_caps")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "FieldCapabilitiesRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "FieldCapabilitiesIndexRequest")); + } + + @Test + public void shouldGetFieldCapabilitiesForParticularIndex_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(PROHIBITED_SONG_INDEX_NAME).fields(FIELD_TITLE); + + assertThatThrownBy(() -> restHighLevelClient.fieldCaps(request, DEFAULT), statusException(FORBIDDEN)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/prohibited_song_lyrics/_field_caps")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "FieldCapabilitiesRequest")); + } + + @Test + public void shouldCreateSnapshotRepository_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + String snapshotDirPath = cluster.getSnapshotDirPath(); + + var response = steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotDirPath, "fs"); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + } + + @Test + public void shouldCreateSnapshotRepository_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + String snapshotDirPath = cluster.getSnapshotDirPath(); + + assertThatThrownBy( + () -> steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotDirPath, "fs"), + statusException(FORBIDDEN) + ); + assertThat(internalClient, not(clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "PutRepositoryRequest")); + } + + @Test + public void shouldDeleteSnapshotRepository_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + assertThat(internalClient, clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME)); + + var response = steps.deleteSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME); + + assertThat(response, notNullValue()); + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, not(clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(DELETE, "/_snapshot/test-snapshot-repository") + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "DeleteRepositoryRequest")); + } + + @Test + public void shouldDeleteSnapshotRepository_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + + assertThatThrownBy(() -> steps.deleteSnapshotRepository(UNUSED_SNAPSHOT_REPOSITORY_NAME), statusException(FORBIDDEN)); + assertThat(internalClient, clusterContainsSnapshotRepository(UNUSED_SNAPSHOT_REPOSITORY_NAME)); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_READ_USER).withRestRequest(DELETE, "/_snapshot/unused-snapshot-repository") + ); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "DeleteRepositoryRequest")); + } + + @Test // Bug which can be reproduced with the below test: https://github.com/opensearch-project/security/issues/2169 + public void shouldCreateSnapshot_positive() throws IOException { + final String snapshotName = "snapshot-positive-test"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + + CreateSnapshotResponse response = steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME); + + assertThat(response, notNullValue()); + assertThat(response.status(), equalTo(RestStatus.ACCEPTED)); + steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + assertThat(internalClient, clusterContainSuccessSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/snapshot-positive-test") + ); + auditLogsRule.assertAtLeast( + 1, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/snapshot-positive-test") + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + } + + @Test + public void shouldCreateSnapshot_negative() throws IOException { + final String snapshotName = "snapshot-negative-test"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + + assertThatThrownBy( + () -> steps.createSnapshot(UNUSED_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME), + statusException(FORBIDDEN) + ); + + assertThat(internalClient, snapshotInClusterDoesNotExists(UNUSED_SNAPSHOT_REPOSITORY_NAME, snapshotName)); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_snapshot/unused-snapshot-repository/snapshot-negative-test") + ); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "CreateSnapshotRequest")); + } + + @Test + public void shouldDeleteSnapshot_positive() throws IOException { + String snapshotName = "delete-snapshot-positive"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + restHighLevelClient.snapshot(); + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME); + steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + + var response = steps.deleteSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(internalClient, snapshotInClusterDoesNotExists(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") + ); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(DELETE, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") + ); + auditLogsRule.assertAtLeast( + 1, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "DeleteSnapshotRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + } + + @Test + public void shouldDeleteSnapshot_negative() throws IOException { + String snapshotName = "delete-snapshot-negative"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME); + steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + } + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + assertThatThrownBy(() -> steps.deleteSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName), statusException(FORBIDDEN)); + + assertThat(internalClient, clusterContainSuccessSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName)); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") + ); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_READ_USER).withRestRequest(DELETE, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") + ); + auditLogsRule.assertAtLeast( + 1, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactly(1, missingPrivilege(LIMITED_READ_USER, "DeleteSnapshotRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + } + + @Test + public void shouldRestoreSnapshot_positive() throws IOException { + final String snapshotName = "restore-snapshot-positive"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + // 1. create some documents + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Eins").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1].asMap())); + bulkRequest.setRefreshPolicy(IMMEDIATE); + restHighLevelClient.bulk(bulkRequest, DEFAULT); + + // 2. create snapshot repository + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + + // 3. create snapshot + steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, WRITE_SONG_INDEX_NAME); + + // 4. wait till snapshot is ready + steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + + // 5. introduce some changes + bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Drei").source(SONGS[2].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Vier").source(SONGS[3].asMap())); + bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "Eins")); + bulkRequest.setRefreshPolicy(IMMEDIATE); + restHighLevelClient.bulk(bulkRequest, DEFAULT); + + // 6. restore the snapshot + var response = steps.restoreSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, "(.+)", "restored_$1"); + + assertThat(response, notNullValue()); + assertThat(response.status(), equalTo(ACCEPTED)); + + // 7. wait until snapshot is restored + CountRequest countRequest = new CountRequest(RESTORED_SONG_INDEX_NAME); + Awaitility.await() + .ignoreExceptions() + .alias("Index contains proper number of documents restored from snapshot.") + .until(() -> restHighLevelClient.count(countRequest, DEFAULT).getCount() == 2); + + // 8. verify that document are present in restored index + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(RESTORED_SONG_INDEX_NAME, "Eins", FIELD_TITLE, TITLE_MAGNUM_OPUS) + ); + assertThat( + internalClient, + clusterContainsDocumentWithFieldValue(RESTORED_SONG_INDEX_NAME, "Zwei", FIELD_TITLE, TITLE_SONG_1_PLUS_1) + ); + assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Drei"))); + assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Vier"))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/restore-snapshot-positive") + ); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + POST, + "/_snapshot/test-snapshot-repository/restore-snapshot-positive/_restore" + ) + ); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/restored_write_song_index/_count")); + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertAtLeast( + 1, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/restore-snapshot-positive") + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "SearchRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + public void shouldRestoreSnapshot_failureForbiddenIndex() throws IOException { + final String snapshotName = "restore-snapshot-negative-forbidden-index"; + String restoreToIndex = "forbidden_index"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + // 1. create some documents + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Eins").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1].asMap())); + bulkRequest.setRefreshPolicy(IMMEDIATE); + restHighLevelClient.bulk(bulkRequest, DEFAULT); + + // 2. create snapshot repository + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + + // 3. create snapshot + steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, WRITE_SONG_INDEX_NAME); + + // 4. wait till snapshot is ready + steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + + // 5. restore the snapshot + assertThatThrownBy( + () -> steps.restoreSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, "(.+)", restoreToIndex), + statusException(FORBIDDEN) + ); + + // 6. verify that document are not present in restored index + assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Eins"))); + assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Zwei"))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + PUT, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index" + ) + ); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + POST, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index/_restore" + ) + ); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertAtLeast( + 1, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + GET, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index" + ) + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); + } + + @Test + public void shouldRestoreSnapshot_failureOperationForbidden() throws IOException { + String snapshotName = "restore-snapshot-negative-forbidden-operation"; + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + // 1. create some documents + BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Eins").source(SONGS[0].asMap())); + bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1].asMap())); + bulkRequest.setRefreshPolicy(IMMEDIATE); + restHighLevelClient.bulk(bulkRequest, DEFAULT); + + // 2. create snapshot repository + steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); + + // 3. create snapshot + steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, WRITE_SONG_INDEX_NAME); + + // 4. wait till snapshot is ready + steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); + } + // 5. restore the snapshot + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + assertThatThrownBy( + () -> steps.restoreSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, "(.+)", "restored_$1"), + statusException(FORBIDDEN) + ); + + // 6. verify that documents does not exist + assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Eins"))); + assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Zwei"))); + } + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + PUT, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation" + ) + ); + auditLogsRule.assertExactlyOne( + userAuthenticated(LIMITED_READ_USER).withRestRequest( + POST, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation/_restore" + ) + ); + auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertAtLeast( + 1, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + GET, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation" + ) + ); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactly(1, missingPrivilege(LIMITED_READ_USER, "RestoreSnapshotRequest")); + auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + // required permissions: "indices:admin/create" + public void createIndex_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_positive"); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); + CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, DEFAULT); + + assertThat(createIndexResponse, isSuccessfulCreateIndexResponse(indexName)); + assertThat(cluster, indexExists(indexName)); + } + } + + @Test + public void createIndex_negative() throws IOException { + String indexName = "create_index_negative"; + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); + + assertThatThrownBy(() -> restHighLevelClient.indices().create(createIndexRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(indexName))); + } + } + + @Test + // required permissions: "indices:admin/get" + public void checkIfIndexExists_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("index_exists_positive"); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + boolean exists = restHighLevelClient.indices().exists(new GetIndexRequest(indexName), DEFAULT); + + assertThat(exists, is(false)); + } + } + + @Test + public void checkIfIndexExists_negative() throws IOException { + String indexThatUserHasNoAccessTo = "index_exists_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + assertThatThrownBy( + () -> restHighLevelClient.indices().exists(new GetIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .exists(new GetIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy(() -> restHighLevelClient.indices().exists(new GetIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + // required permissions: "indices:admin/delete" + public void deleteIndex_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("delete_index_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName); + var response = restHighLevelClient.indices().delete(deleteIndexRequest, DEFAULT); + + assertThat(response.isAcknowledged(), is(true)); + assertThat(cluster, not(indexExists(indexName))); + } + } + + @Test + public void deleteIndex_negative() throws IOException { + String indexThatUserHasNoAccessTo = "delete_index_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices().delete(new DeleteIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .delete(new DeleteIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().delete(new DeleteIndexRequest("*"), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + // required permissions: indices:admin/aliases, indices:admin/delete + public void shouldDeleteIndexByAliasRequest_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("delete_index_by_alias_request_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + IndicesAliasesRequest request = new IndicesAliasesRequest().addAliasAction(new AliasActions(REMOVE_INDEX).indices(indexName)); + + var response = restHighLevelClient.indices().updateAliases(request, DEFAULT); + + assertThat(response.isAcknowledged(), is(true)); + assertThat(cluster, not(indexExists(indexName))); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES).withRestRequest(POST, "/_aliases") + ); + auditLogsRule.assertExactly( + 2, + grantedPrivilege(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, "IndicesAliasesRequest") + ); + auditLogsRule.assertExactly( + 2, + auditPredicate(INDEX_EVENT).withEffectiveUser(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES) + ); + } + + @Test + public void shouldDeleteIndexByAliasRequest_negative() throws IOException { + String indexName = "delete_index_by_alias_request_negative"; + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + IndicesAliasesRequest request = new IndicesAliasesRequest().addAliasAction(new AliasActions(REMOVE_INDEX).indices(indexName)); + + assertThatThrownBy(() -> restHighLevelClient.indices().updateAliases(request, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + // required permissions: "indices:admin/get" + public void getIndex_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("get_index_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + GetIndexRequest getIndexRequest = new GetIndexRequest(indexName); + GetIndexResponse response = restHighLevelClient.indices().get(getIndexRequest, DEFAULT); + + assertThat(response, getIndexResponseContainsIndices(indexName)); + } + } + + @Test + public void getIndex_negative() throws IOException { + String indexThatUserHasNoAccessTo = "get_index_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices().get(new GetIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().get(new GetIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy(() -> restHighLevelClient.indices().get(new GetIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + // required permissions: "indices:admin/close", "indices:admin/close*" + public void closeIndex_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("close_index_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + CloseIndexRequest closeIndexRequest = new CloseIndexRequest(indexName); + CloseIndexResponse response = restHighLevelClient.indices().close(closeIndexRequest, DEFAULT); + + assertThat(response, isSuccessfulCloseIndexResponse()); + assertThat(cluster, indexStateIsEqualTo(indexName, IndexMetadata.State.CLOSE)); + } + } + + @Test + public void closeIndex_negative() throws IOException { + String indexThatUserHasNoAccessTo = "close_index_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices().close(new CloseIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .close(new CloseIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy(() -> restHighLevelClient.indices().close(new CloseIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + // required permissions: "indices:admin/open" + public void openIndex_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("open_index_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + IndexOperationsHelper.closeIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + OpenIndexRequest closeIndexRequest = new OpenIndexRequest(indexName); + OpenIndexResponse response = restHighLevelClient.indices().open(closeIndexRequest, DEFAULT); + + assertThat(response, isSuccessfulOpenIndexResponse()); + assertThat(cluster, indexStateIsEqualTo(indexName, IndexMetadata.State.OPEN)); + } + } + + @Test + public void openIndex_negative() throws IOException { + String indexThatUserHasNoAccessTo = "open_index_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices().open(new OpenIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .open(new OpenIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy(() -> restHighLevelClient.indices().open(new OpenIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + @Ignore + // required permissions: "indices:admin/resize", "indices:monitor/stats + // todo even when I assign the `indices:admin/resize` and `indices:monitor/stats` permissions to test user, this test fails. + // Issue: https://github.com/opensearch-project/security/issues/2141 + public void shrinkIndex_positive() throws IOException { + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_positive_source"); + Settings sourceIndexSettings = Settings.builder().put("index.blocks.write", true).put("index.number_of_shards", 2).build(); + String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_positive_target"); + IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + ResizeResponse response = restHighLevelClient.indices().shrink(resizeRequest, DEFAULT); + + assertThat(response, isSuccessfulResizeResponse(targetIndexName)); + assertThat(cluster, indexExists(targetIndexName)); + } + } + + @Test + public void shrinkIndex_negative() throws IOException { + // user cannot access target index + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_negative_source"); + String targetIndexName = "shrink_index_negative_target"; + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + + assertThatThrownBy(() -> restHighLevelClient.indices().shrink(resizeRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(targetIndexName))); + } + + // user cannot access source index + sourceIndexName = "shrink_index_negative_source"; + targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_negative_target"); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + + assertThatThrownBy(() -> restHighLevelClient.indices().shrink(resizeRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(targetIndexName))); + } + } + + @Test + @Ignore + // required permissions: "indices:admin/resize", "indices:monitor/stats + // todo even when I assign the `indices:admin/resize` and `indices:monitor/stats` permissions to test user, this test fails. + // Issue: https://github.com/opensearch-project/security/issues/2141 + public void cloneIndex_positive() throws IOException { + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_positive_source"); + Settings sourceIndexSettings = Settings.builder().put("index.blocks.write", true).build(); + String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_positive_target"); + IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + ResizeResponse response = restHighLevelClient.indices().clone(resizeRequest, DEFAULT); + + assertThat(response, isSuccessfulResizeResponse(targetIndexName)); + assertThat(cluster, indexExists(targetIndexName)); + } + } + + @Test + public void cloneIndex_negative() throws IOException { + // user cannot access target index + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_negative_source"); + String targetIndexName = "clone_index_negative_target"; + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + + assertThatThrownBy(() -> restHighLevelClient.indices().clone(resizeRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(targetIndexName))); + } + + // user cannot access source index + sourceIndexName = "clone_index_negative_source"; + targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_negative_target"); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + + assertThatThrownBy(() -> restHighLevelClient.indices().clone(resizeRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(targetIndexName))); + } + } + + @Test + @Ignore + // required permissions: "indices:admin/resize", "indices:monitor/stats + // todo even when I assign the `indices:admin/resize` and `indices:monitor/stats` permissions to test user, this test fails. + // Issue: https://github.com/opensearch-project/security/issues/2141 + public void splitIndex_positive() throws IOException { + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_positive_source"); + Settings sourceIndexSettings = Settings.builder().put("index.blocks.write", true).build(); + String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_positive_target"); + IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build()); + ResizeResponse response = restHighLevelClient.indices().split(resizeRequest, DEFAULT); + + assertThat(response, isSuccessfulResizeResponse(targetIndexName)); + assertThat(cluster, indexExists(targetIndexName)); + } + } + + @Test + public void splitIndex_negative() throws IOException { + // user cannot access target index + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_negative_source"); + String targetIndexName = "split_index_negative_target"; + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build()); + + assertThatThrownBy(() -> restHighLevelClient.indices().split(resizeRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(targetIndexName))); + } + + // user cannot access source index + sourceIndexName = "split_index_negative_source"; + targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_negative_target"); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build()); + + assertThatThrownBy(() -> restHighLevelClient.indices().split(resizeRequest, DEFAULT), statusException(FORBIDDEN)); + assertThat(cluster, not(indexExists(targetIndexName))); + } + } + + @Test + // required permissions: "indices:monitor/settings/get" + public void getIndexSettings_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("get_index_settings_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + GetSettingsRequest getSettingsRequest = new GetSettingsRequest().indices(indexName); + GetSettingsResponse response = restHighLevelClient.indices().getSettings(getSettingsRequest, DEFAULT); + + assertThat(response, getSettingsResponseContainsIndices(indexName)); + } + } + + @Test + public void getIndexSettings_negative() throws IOException { + String indexThatUserHasNoAccessTo = "get_index_settings_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + assertThatThrownBy( + () -> restHighLevelClient.indices().getSettings(new GetSettingsRequest().indices(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .getSettings(new GetSettingsRequest().indices(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().getSettings(new GetSettingsRequest().indices("*"), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + // required permissions: "indices:admin/settings/update" + public void updateIndexSettings_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("update_index_settings_positive"); + Settings initialSettings = Settings.builder().put("index.number_of_replicas", "2").build(); + Settings updatedSettings = Settings.builder().put("index.number_of_replicas", "4").build(); + IndexOperationsHelper.createIndex(cluster, indexName, initialSettings); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + UpdateSettingsRequest updateSettingsRequest = new UpdateSettingsRequest(indexName).settings(updatedSettings); + var response = restHighLevelClient.indices().putSettings(updateSettingsRequest, DEFAULT); + + assertThat(response.isAcknowledged(), is(true)); + assertThat(cluster, indexSettingsContainValues(indexName, updatedSettings)); + } + } + + @Test + public void updateIndexSettings_negative() throws IOException { + String indexThatUserHasNoAccessTo = "update_index_settings_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + Settings settingsToUpdate = Settings.builder().put("index.number_of_replicas", 2).build(); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices() + .putSettings(new UpdateSettingsRequest(indexThatUserHasNoAccessTo).settings(settingsToUpdate), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .putSettings( + new UpdateSettingsRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo).settings(settingsToUpdate), + DEFAULT + ), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().putSettings(new UpdateSettingsRequest("*").settings(settingsToUpdate), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + // required permissions: indices:admin/mapping/put + public void createIndexMappings_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_mappings_positive"); + Map indexMapping = Map.of("properties", Map.of("message", Map.of("type", "text"))); + IndexOperationsHelper.createIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + PutMappingRequest putMappingRequest = new PutMappingRequest(indexName).source(indexMapping); + var response = restHighLevelClient.indices().putMapping(putMappingRequest, DEFAULT); + + assertThat(response.isAcknowledged(), is(true)); + assertThat(cluster, indexMappingIsEqualTo(indexName, indexMapping)); + } + } + + @Test + public void createIndexMappings_negative() throws IOException { + String indexThatUserHasNoAccessTo = "create_index_mappings_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + Map indexMapping = Map.of("properties", Map.of("message", Map.of("type", "text"))); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices() + .putMapping(new PutMappingRequest(indexThatUserHasNoAccessTo).source(indexMapping), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .putMapping(new PutMappingRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo).source(indexMapping), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().putMapping(new PutMappingRequest("*").source(indexMapping), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + // required permissions: indices:admin/mappings/get + public void getIndexMappings_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("get_index_mappings_positive"); + Map indexMapping = Map.of("properties", Map.of("message", Map.of("type", "text"))); + IndexOperationsHelper.createIndex(cluster, indexName); + IndexOperationsHelper.createMapping(cluster, indexName, indexMapping); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + GetMappingsRequest getMappingsRequest = new GetMappingsRequest().indices(indexName); + GetMappingsResponse response = restHighLevelClient.indices().getMapping(getMappingsRequest, DEFAULT); + + assertThat(response, getMappingsResponseContainsIndices(indexName)); + } + } + + @Test + public void getIndexMappings_negative() throws IOException { + String indexThatUserHasNoAccessTo = "get_index_mappings_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices().getMapping(new GetMappingsRequest().indices(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .getMapping(new GetMappingsRequest().indices(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().getMapping(new GetMappingsRequest().indices("*"), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + // required permissions: "indices:admin/cache/clear" + public void clearIndexCache_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clear_index_cache_positive"); + IndexOperationsHelper.createIndex(cluster, indexName); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ClearIndicesCacheRequest clearIndicesCacheRequest = new ClearIndicesCacheRequest(indexName); + ClearIndicesCacheResponse response = restHighLevelClient.indices().clearCache(clearIndicesCacheRequest, DEFAULT); + + assertThat(response, isSuccessfulClearIndicesCacheResponse()); + } + } + + @Test + public void clearIndexCache_negative() throws IOException { + String indexThatUserHasNoAccessTo = "clear_index_cache_negative"; + String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + + assertThatThrownBy( + () -> restHighLevelClient.indices().clearCache(new ClearIndicesCacheRequest(indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices() + .clearCache(new ClearIndicesCacheRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), + statusException(FORBIDDEN) + ); + assertThatThrownBy( + () -> restHighLevelClient.indices().clearCache(new ClearIndicesCacheRequest("*"), DEFAULT), + statusException(FORBIDDEN) + ); + } + } + + @Test + // required permissions: "indices:admin/create", "indices:admin/aliases" + public void shouldCreateIndexWithAlias_positive() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_with_alias_positive"); + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName).alias( + new Alias(ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE) + ); + + CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, DEFAULT); + + assertThat(createIndexResponse, isSuccessfulCreateIndexResponse(indexName)); + assertThat(cluster, indexExists(indexName)); + assertThat(internalClient, aliasExists(ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE)); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES).withRestRequest( + PUT, + "/index_operations_create_index_with_alias_positive" + ) + ); + auditLogsRule.assertExactly( + 2, + grantedPrivilege(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, "CreateIndexRequest") + ); + auditLogsRule.assertExactly( + 2, + auditPredicate(INDEX_EVENT).withEffectiveUser(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES) + ); + } + + @Test + public void shouldCreateIndexWithAlias_negative() throws IOException { + String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_with_alias_negative"); + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(USER_ALLOWED_TO_CREATE_INDEX)) { + CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName).alias( + new Alias(ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE) + ); + + assertThatThrownBy(() -> restHighLevelClient.indices().create(createIndexRequest, DEFAULT), statusException(FORBIDDEN)); + + assertThat(internalClient, not(aliasExists(ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE))); + } + auditLogsRule.assertExactlyOne( + userAuthenticated(USER_ALLOWED_TO_CREATE_INDEX).withRestRequest(PUT, "/index_operations_create_index_with_alias_negative") + ); + auditLogsRule.assertExactlyOne(missingPrivilege(USER_ALLOWED_TO_CREATE_INDEX, "CreateIndexRequest")); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java b/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java new file mode 100644 index 0000000000..9df611e207 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java @@ -0,0 +1,270 @@ +/* +* 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.http; + +import java.io.IOException; +import java.security.KeyPair; +import java.util.Base64; +import java.util.List; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.message.BasicHeader; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.client.Client; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.test.framework.JwtConfigBuilder; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.TestSecurityConfig.Role; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; +import org.opensearch.test.framework.log.LogsRule; + +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.apache.http.HttpHeaders.AUTHORIZATION; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.client.RequestOptions.DEFAULT; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.security.Song.FIELD_TITLE; +import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.BASIC_AUTH_DOMAIN_ORDER; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; +import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class JwtAuthenticationTests { + + public static final String CLAIM_USERNAME = "preferred-username"; + public static final String CLAIM_ROLES = "backend-user-roles"; + + public static final String USER_SUPERHERO = "superhero"; + public static final String USERNAME_ROOT = "root"; + public static final String ROLE_ADMIN = "role_admin"; + public static final String ROLE_DEVELOPER = "role_developer"; + public static final String ROLE_QA = "role_qa"; + public static final String ROLE_CTO = "role_cto"; + public static final String ROLE_CEO = "role_ceo"; + public static final String ROLE_VP = "role_vp"; + public static final String POINTER_BACKEND_ROLES = "/backend_roles"; + public static final String POINTER_USERNAME = "/user_name"; + + public static final String QA_DEPARTMENT = "qa-department"; + + public static final String CLAIM_DEPARTMENT = "department"; + + public static final String DEPARTMENT_SONG_INDEX_PATTERN = String.format("song_lyrics_${attr.jwt.%s}", CLAIM_DEPARTMENT); + + public static final String QA_SONG_INDEX_NAME = String.format("song_lyrics_%s", QA_DEPARTMENT); + + private static final KeyPair KEY_PAIR = Keys.keyPairFor(SignatureAlgorithm.RS256); + private static final String PUBLIC_KEY = new String(Base64.getEncoder().encode(KEY_PAIR.getPublic().getEncoded()), US_ASCII); + + static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); + + private static final String JWT_AUTH_HEADER = "jwt-auth"; + + private static final JwtAuthorizationHeaderFactory tokenFactory = new JwtAuthorizationHeaderFactory( + KEY_PAIR.getPrivate(), + CLAIM_USERNAME, + CLAIM_ROLES, + JWT_AUTH_HEADER + ); + + public static final TestSecurityConfig.AuthcDomain JWT_AUTH_DOMAIN = new TestSecurityConfig.AuthcDomain( + "jwt", + BASIC_AUTH_DOMAIN_ORDER - 1 + ).jwtHttpAuthenticator( + new JwtConfigBuilder().jwtHeader(JWT_AUTH_HEADER).signingKey(PUBLIC_KEY).subjectKey(CLAIM_USERNAME).rolesKey(CLAIM_ROLES) + ).backend("noop"); + public static final String SONG_ID_1 = "song-id-01"; + + public static final Role DEPARTMENT_SONG_LISTENER_ROLE = new Role("department-song-listener-role").indexPermissions( + "indices:data/read/search" + ).on(DEPARTMENT_SONG_INDEX_PATTERN); + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE) + .anonymousAuth(false) + .nodeSettings( + Map.of("plugins.security.restapi.roles_enabled", List.of("user_" + ADMIN_USER.getName() + "__" + ALL_ACCESS.getName())) + ) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(ADMIN_USER) + .roles(DEPARTMENT_SONG_LISTENER_ROLE) + .authc(JWT_AUTH_DOMAIN) + .build(); + + @Rule + public LogsRule logsRule = new LogsRule("com.amazon.dlic.auth.http.jwt.HTTPJwtAuthenticator"); + + @BeforeClass + public static void createTestData() { + try (Client client = cluster.getInternalNodeClient()) { + client.prepareIndex(QA_SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + } + try (TestRestClient client = cluster.getRestClient(ADMIN_USER)) { + client.createRoleMapping(ROLE_VP, DEPARTMENT_SONG_LISTENER_ROLE.getName()); + } + } + + @Test + public void shouldAuthenticateWithJwtToken_positive() { + try (TestRestClient client = cluster.getRestClient(tokenFactory.generateValidToken(USER_SUPERHERO))) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USER_SUPERHERO)); + } + } + + @Test + public void shouldAuthenticateWithJwtToken_positiveWithAnotherUsername() { + try (TestRestClient client = cluster.getRestClient(tokenFactory.generateValidToken(USERNAME_ROOT))) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USERNAME_ROOT)); + } + } + + @Test + public void shouldAuthenticateWithJwtToken_failureLackingUserName() { + try (TestRestClient client = cluster.getRestClient(tokenFactory.generateTokenWithoutPreferredUsername(USER_SUPERHERO))) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + logsRule.assertThatContainExactly("No subject found in JWT token"); + } + } + + @Test + public void shouldAuthenticateWithJwtToken_failureExpiredToken() { + try (TestRestClient client = cluster.getRestClient(tokenFactory.generateExpiredToken(USER_SUPERHERO))) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + logsRule.assertThatContainExactly("Invalid or expired JWT token."); + } + } + + @Test + public void shouldAuthenticateWithJwtToken_failureIncorrectFormatOfToken() { + Header header = new BasicHeader(AUTHORIZATION, "not.a.token"); + try (TestRestClient client = cluster.getRestClient(header)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + logsRule.assertThatContainExactly(String.format("No JWT token found in '%s' header header", JWT_AUTH_HEADER)); + } + } + + @Test + public void shouldAuthenticateWithJwtToken_failureIncorrectSignature() { + KeyPair incorrectKeyPair = Keys.keyPairFor(SignatureAlgorithm.RS256); + Header header = tokenFactory.generateTokenSignedWithKey(incorrectKeyPair.getPrivate(), USER_SUPERHERO); + try (TestRestClient client = cluster.getRestClient(header)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + logsRule.assertThatContainExactly("Invalid or expired JWT token."); + } + } + + @Test + public void shouldReadRolesFromToken_positiveFirstRoleSet() { + Header header = tokenFactory.generateValidToken(USER_SUPERHERO, ROLE_ADMIN, ROLE_DEVELOPER, ROLE_QA); + try (TestRestClient client = cluster.getRestClient(header)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List roles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(roles, hasSize(3)); + assertThat(roles, containsInAnyOrder(ROLE_ADMIN, ROLE_DEVELOPER, ROLE_QA)); + } + } + + @Test + public void shouldReadRolesFromToken_positiveSecondRoleSet() { + Header header = tokenFactory.generateValidToken(USER_SUPERHERO, ROLE_CTO, ROLE_CEO, ROLE_VP); + try (TestRestClient client = cluster.getRestClient(header)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List roles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(roles, hasSize(3)); + assertThat(roles, containsInAnyOrder(ROLE_CTO, ROLE_CEO, ROLE_VP)); + } + } + + @Test + public void shouldExposeTokenClaimsAsUserAttributes_positive() throws IOException { + String[] roles = { ROLE_VP }; + Map additionalClaims = Map.of(CLAIM_DEPARTMENT, QA_DEPARTMENT); + Header header = tokenFactory.generateValidTokenWithCustomClaims(USER_SUPERHERO, roles, additionalClaims); + try (RestHighLevelClient client = cluster.getRestHighLevelClient(List.of(header))) { + SearchRequest searchRequest = queryStringQueryRequest(QA_SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS); + + SearchResponse response = client.search(searchRequest, DEFAULT); + + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response, numberOfTotalHitsIsEqualTo(1)); + assertThat(response, searchHitsContainDocumentWithId(0, QA_SONG_INDEX_NAME, SONG_ID_1)); + assertThat(response, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); + } + } + + @Test + public void shouldExposeTokenClaimsAsUserAttributes_negative() throws IOException { + String[] roles = { ROLE_VP }; + Map additionalClaims = Map.of(CLAIM_DEPARTMENT, "department-without-access-to-qa-song-index"); + Header header = tokenFactory.generateValidTokenWithCustomClaims(USER_SUPERHERO, roles, additionalClaims); + try (RestHighLevelClient client = cluster.getRestHighLevelClient(List.of(header))) { + SearchRequest searchRequest = queryStringQueryRequest(QA_SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS); + + assertThatThrownBy(() -> client.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java new file mode 100644 index 0000000000..bac79ffd12 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java @@ -0,0 +1,414 @@ +/* +* 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.http; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.hc.core5.http.message.BasicHeader; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.runner.RunWith; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.client.Client; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.test.framework.AuthorizationBackend; +import org.opensearch.test.framework.AuthzDomain; +import org.opensearch.test.framework.LdapAuthenticationConfigBuilder; +import org.opensearch.test.framework.LdapAuthorizationConfigBuilder; +import org.opensearch.test.framework.RolesMapping; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AuthenticationBackend; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.HttpAuthenticator; +import org.opensearch.test.framework.TestSecurityConfig.Role; +import org.opensearch.test.framework.TestSecurityConfig.User; +import org.opensearch.test.framework.certificate.TestCertificates; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; +import org.opensearch.test.framework.ldap.EmbeddedLDAPServer; +import org.opensearch.test.framework.log.LogsRule; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.client.RequestOptions.DEFAULT; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.security.http.DirectoryInformationTrees.CN_GROUP_ADMIN; +import static org.opensearch.security.http.DirectoryInformationTrees.CN_GROUP_BRIDGE; +import static org.opensearch.security.http.DirectoryInformationTrees.CN_GROUP_CREW; +import static org.opensearch.security.http.DirectoryInformationTrees.DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG; +import static org.opensearch.security.http.DirectoryInformationTrees.DN_GROUPS_TEST_ORG; +import static org.opensearch.security.http.DirectoryInformationTrees.DN_OPEN_SEARCH_PEOPLE_TEST_ORG; +import static org.opensearch.security.http.DirectoryInformationTrees.DN_PEOPLE_TEST_ORG; +import static org.opensearch.security.http.DirectoryInformationTrees.LDIF_DATA; +import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_JEAN; +import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_KIRK; +import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_LEONARD; +import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_OPEN_SEARCH; +import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_SPOCK; +import static org.opensearch.security.http.DirectoryInformationTrees.USERNAME_ATTRIBUTE; +import static org.opensearch.security.http.DirectoryInformationTrees.USER_JEAN; +import static org.opensearch.security.http.DirectoryInformationTrees.USER_KIRK; +import static org.opensearch.security.http.DirectoryInformationTrees.USER_LEONARD; +import static org.opensearch.security.http.DirectoryInformationTrees.USER_SEARCH; +import static org.opensearch.security.http.DirectoryInformationTrees.USER_SPOCK; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.BASIC_AUTH_DOMAIN_ORDER; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; +import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; +import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; + +/** +* Test uses plain TLS connection between OpenSearch and LDAP server. +*/ +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class LdapTlsAuthenticationTest { + + private static final String SONG_INDEX_NAME = "song_lyrics"; + + private static final String HEADER_NAME_IMPERSONATE = "opendistro_security_impersonate_as"; + + private static final String PERSONAL_INDEX_NAME_SPOCK = "personal-" + USER_SPOCK; + private static final String PERSONAL_INDEX_NAME_KIRK = "personal-" + USER_KIRK; + + private static final String POINTER_BACKEND_ROLES = "/backend_roles"; + private static final String POINTER_ROLES = "/roles"; + private static final String POINTER_USERNAME = "/user_name"; + private static final String POINTER_ERROR_REASON = "/error/reason"; + + private static final String SONG_ID_1 = "l0001"; + private static final String SONG_ID_2 = "l0002"; + private static final String SONG_ID_3 = "l0003"; + + private static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS); + + private static final TestCertificates TEST_CERTIFICATES = new TestCertificates(); + + private static final Role ROLE_INDEX_ADMINISTRATOR = new Role("index_administrator").indexPermissions("*").on("*"); + private static final Role ROLE_PERSONAL_INDEX_ACCESS = new Role("personal_index_access").indexPermissions("*") + .on("personal-${attr.ldap.uid}"); + + private static final EmbeddedLDAPServer embeddedLDAPServer = new EmbeddedLDAPServer( + TEST_CERTIFICATES.getRootCertificateData(), + TEST_CERTIFICATES.getLdapCertificateData(), + LDIF_DATA + ); + + private static final Map USER_IMPERSONATION_CONFIGURATION = Map.of( + "plugins.security.authcz.rest_impersonation_user." + USER_KIRK, + List.of(USER_SPOCK) + ); + + private static final LocalCluster cluster = new LocalCluster.Builder().testCertificates(TEST_CERTIFICATES) + .clusterManager(ClusterManager.SINGLENODE) + .anonymousAuth(false) + .nodeSettings(USER_IMPERSONATION_CONFIGURATION) + .authc( + new AuthcDomain("ldap", BASIC_AUTH_DOMAIN_ORDER + 1, true).httpAuthenticator(new HttpAuthenticator("basic").challenge(false)) + .backend( + new AuthenticationBackend("ldap").config( + () -> LdapAuthenticationConfigBuilder.config() + // this port is available when embeddedLDAPServer is already started, therefore Supplier interface is used + .hosts(List.of("localhost:" + embeddedLDAPServer.getLdapTlsPort())) + .enableSsl(true) + .bindDn(DN_OPEN_SEARCH_PEOPLE_TEST_ORG) + .password(PASSWORD_OPEN_SEARCH) + .userBase(DN_PEOPLE_TEST_ORG) + .userSearch(USER_SEARCH) + .usernameAttribute(USERNAME_ATTRIBUTE) + .penTrustedCasFilePath(TEST_CERTIFICATES.getRootCertificate().getAbsolutePath()) + .build() + ) + ) + ) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(ADMIN_USER) + .roles(ROLE_INDEX_ADMINISTRATOR, ROLE_PERSONAL_INDEX_ACCESS) + .rolesMapping( + new RolesMapping(ROLE_INDEX_ADMINISTRATOR).backendRoles(CN_GROUP_ADMIN), + new RolesMapping(ROLE_PERSONAL_INDEX_ACCESS).backendRoles(CN_GROUP_CREW) + ) + .authz( + new AuthzDomain("ldap_roles").httpEnabled(true) + .transportEnabled(true) + .authorizationBackend( + new AuthorizationBackend("ldap").config( + () -> new LdapAuthorizationConfigBuilder().hosts(List.of("localhost:" + embeddedLDAPServer.getLdapTlsPort())) + .enableSsl(true) + .bindDn(DN_OPEN_SEARCH_PEOPLE_TEST_ORG) + .password(PASSWORD_OPEN_SEARCH) + .userBase(DN_PEOPLE_TEST_ORG) + .userSearch(USER_SEARCH) + .usernameAttribute(USERNAME_ATTRIBUTE) + .penTrustedCasFilePath(TEST_CERTIFICATES.getRootCertificate().getAbsolutePath()) + .roleBase(DN_GROUPS_TEST_ORG) + .roleSearch("(uniqueMember={0})") + .userRoleAttribute(null) + .userRoleName("disabled") + .roleName("cn") + .resolveNestedRoles(true) + .build() + ) + ) + ) + .build(); + + @ClassRule + public static final RuleChain ruleChain = RuleChain.outerRule(embeddedLDAPServer).around(cluster); + + @Rule + public LogsRule logsRule = new LogsRule("com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend"); + + @BeforeClass + public static void createTestData() { + try (Client client = cluster.getInternalNodeClient()) { + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(SONG_ID_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(SONG_ID_3).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2].asMap()).get(); + } + } + + @Test + public void shouldAuthenticateUserWithLdap_positiveSpockUser() { + try (TestRestClient client = cluster.getRestClient(USER_SPOCK, PASSWORD_SPOCK)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USER_SPOCK)); + } + } + + @Test + public void shouldAuthenticateUserWithLdap_positiveKirkUser() { + try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USER_KIRK)); + } + } + + @Test + public void shouldAuthenticateUserWithLdap_negativeWhenIncorrectPassword() { + try (TestRestClient client = cluster.getRestClient(USER_SPOCK, "incorrect password")) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + String expectedStackTraceFragment = "Unable to bind as user '".concat(DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG) + .concat("' because the provided password was incorrect."); + logsRule.assertThatStackTraceContain(expectedStackTraceFragment); + } + } + + @Test + public void shouldAuthenticateUserWithLdap_negativeWhenIncorrectUsername() { + final String username = "invalid-user-name"; + try (TestRestClient client = cluster.getRestClient(username, PASSWORD_SPOCK)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + logsRule.assertThatStackTraceContain(String.format("No user %s found", username)); + } + } + + @Test + public void shouldAuthenticateUserWithLdap_negativeWhenUserDoesNotExist() { + final String username = "doesNotExist"; + try (TestRestClient client = cluster.getRestClient(username, "password")) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + logsRule.assertThatStackTraceContain(String.format("No user %s found", username)); + } + } + + @Test + public void shouldResolveUserRolesAgainstLdapBackend_positiveSpockUser() { + try (TestRestClient client = cluster.getRestClient(USER_SPOCK, PASSWORD_SPOCK)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, contains(CN_GROUP_CREW)); + assertThat(response.getTextArrayFromJsonBody(POINTER_ROLES), contains(ROLE_PERSONAL_INDEX_ACCESS.getName())); + } + } + + @Test + public void shouldResolveUserRolesAgainstLdapBackend_positiveKirkUser() { + try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + assertThat(response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES), contains(CN_GROUP_ADMIN)); + assertThat(response.getTextArrayFromJsonBody(POINTER_ROLES), contains(ROLE_INDEX_ADMINISTRATOR.getName())); + } + } + + @Test + public void shouldPerformAuthorizationAgainstLdapToAccessIndex_positive() throws IOException { + try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_KIRK, PASSWORD_KIRK)) { + SearchRequest request = queryStringQueryRequest(SONG_INDEX_NAME, "*"); + + SearchResponse searchResponse = client.search(request, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1)); + } + } + + @Test + public void shouldPerformAuthorizationAgainstLdapToAccessIndex_negative() throws IOException { + try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_LEONARD, PASSWORD_LEONARD)) { + SearchRequest request = queryStringQueryRequest(SONG_INDEX_NAME, "*"); + + assertThatThrownBy(() -> client.search(request, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldResolveUserAttributesLoadedFromLdap_positive() throws IOException { + try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_SPOCK, PASSWORD_SPOCK)) { + SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_SPOCK, "*"); + + SearchResponse searchResponse = client.search(request, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, PERSONAL_INDEX_NAME_SPOCK, SONG_ID_2)); + } + } + + @Test + public void shouldResolveUserAttributesLoadedFromLdap_negative() throws IOException { + try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_SPOCK, PASSWORD_SPOCK)) { + SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_KIRK, "*"); + + assertThatThrownBy(() -> client.search(request, DEFAULT), statusException(FORBIDDEN)); + } + } + + @Test + public void shouldResolveNestedGroups_positive() { + try (TestRestClient client = cluster.getRestClient(USER_JEAN, PASSWORD_JEAN)) { + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, hasSize(2)); + // CN_GROUP_CREW is retrieved recursively: cn=Jean,ou=people,o=test.org -> cn=bridge,ou=groups,o=test.org -> + // cn=crew,ou=groups,o=test.org + assertThat(backendRoles, containsInAnyOrder(CN_GROUP_CREW, CN_GROUP_BRIDGE)); + assertThat(response.getTextArrayFromJsonBody(POINTER_ROLES), contains(ROLE_PERSONAL_INDEX_ACCESS.getName())); + } + } + + @Test + public void shouldResolveNestedGroups_negative() { + try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, not(containsInAnyOrder(CN_GROUP_CREW))); + } + } + + @Test + public void shouldImpersonateUser_positive() { + try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { + + HttpResponse response = client.getAuthInfo(new BasicHeader(HEADER_NAME_IMPERSONATE, USER_SPOCK)); + + response.assertStatusCode(200); + assertThat(response.getTextFromJsonBody(POINTER_USERNAME), equalTo(USER_SPOCK)); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, hasSize(1)); + assertThat(backendRoles, contains(CN_GROUP_CREW)); + } + } + + @Test + public void shouldImpersonateUser_negativeJean() { + try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { + + HttpResponse response = client.getAuthInfo(new BasicHeader(HEADER_NAME_IMPERSONATE, USER_JEAN)); + + response.assertStatusCode(403); + String expectedMessage = String.format("'%s' is not allowed to impersonate as '%s'", USER_KIRK, USER_JEAN); + assertThat(response.getTextFromJsonBody(POINTER_ERROR_REASON), equalTo(expectedMessage)); + } + } + + @Test + public void shouldImpersonateUser_negativeKirk() { + try (TestRestClient client = cluster.getRestClient(USER_JEAN, PASSWORD_JEAN)) { + + HttpResponse response = client.getAuthInfo(new BasicHeader(HEADER_NAME_IMPERSONATE, USER_KIRK)); + + response.assertStatusCode(403); + String expectedMessage = String.format("'%s' is not allowed to impersonate as '%s'", USER_JEAN, USER_KIRK); + assertThat(response.getTextFromJsonBody(POINTER_ERROR_REASON), equalTo(expectedMessage)); + } + } + + @Test + public void shouldAccessImpersonatedUserPersonalIndex_positive() throws IOException { + BasicHeader impersonateHeader = new BasicHeader(HEADER_NAME_IMPERSONATE, USER_SPOCK); + try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_KIRK, PASSWORD_KIRK, impersonateHeader)) { + SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_SPOCK, "*"); + + SearchResponse searchResponse = client.search(request, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, PERSONAL_INDEX_NAME_SPOCK, SONG_ID_2)); + } + } + + @Test + public void shouldAccessImpersonatedUserPersonalIndex_negative() throws IOException { + BasicHeader impersonateHeader = new BasicHeader(HEADER_NAME_IMPERSONATE, USER_SPOCK); + try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_KIRK, PASSWORD_KIRK, impersonateHeader)) { + SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_KIRK, "*"); + + assertThatThrownBy(() -> client.search(request, DEFAULT), statusException(FORBIDDEN)); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java new file mode 100644 index 0000000000..d44f10eba0 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java @@ -0,0 +1,717 @@ +/* +* Copyright 2021 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.test.framework; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.crypto.generators.OpenBSDBCrypt; + +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.update.UpdateRequest; +import org.opensearch.client.Client; +import org.opensearch.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.test.framework.cluster.OpenSearchClientProvider.UserCredentialsHolder; + +import static org.apache.http.HttpHeaders.AUTHORIZATION; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; + +/** +* This class allows the declarative specification of the security configuration; in particular: +* +* - config.yml +* - internal_users.yml +* - roles.yml +* - roles_mapping.yml +* +* The class does the whole round-trip, i.e., the configuration is serialized to YAML/JSON and then written to +* the configuration index of the security plugin. +*/ +public class TestSecurityConfig { + + private static final Logger log = LogManager.getLogger(TestSecurityConfig.class); + + private Config config = new Config(); + private Map internalUsers = new LinkedHashMap<>(); + private Map roles = new LinkedHashMap<>(); + private AuditConfiguration auditConfiguration; + private Map rolesMapping = new LinkedHashMap<>(); + + private String indexName = ".opendistro_security"; + + public TestSecurityConfig() { + + } + + public TestSecurityConfig configIndexName(String configIndexName) { + this.indexName = configIndexName; + return this; + } + + public TestSecurityConfig authFailureListeners(AuthFailureListeners listener) { + config.authFailureListeners(listener); + return this; + } + + public TestSecurityConfig anonymousAuth(boolean anonymousAuthEnabled) { + config.anonymousAuth(anonymousAuthEnabled); + return this; + } + + public TestSecurityConfig doNotFailOnForbidden(boolean doNotFailOnForbidden) { + config.doNotFailOnForbidden(doNotFailOnForbidden); + return this; + } + + public TestSecurityConfig xff(XffConfig xffConfig) { + config.xffConfig(xffConfig); + return this; + } + + public TestSecurityConfig authc(AuthcDomain authcDomain) { + config.authc(authcDomain); + return this; + } + + public TestSecurityConfig authz(AuthzDomain authzDomain) { + config.authz(authzDomain); + return this; + } + + public TestSecurityConfig user(User user) { + this.internalUsers.put(user.name, user); + + for (Role role : user.roles) { + this.roles.put(role.name, role); + } + + return this; + } + + public List getUsers() { + return new ArrayList<>(internalUsers.values()); + } + + public TestSecurityConfig roles(Role... roles) { + for (Role role : roles) { + if (this.roles.containsKey(role.name)) { + throw new IllegalStateException("Role with name " + role.name + " is already defined"); + } + this.roles.put(role.name, role); + } + + return this; + } + + public TestSecurityConfig audit(AuditConfiguration auditConfiguration) { + this.auditConfiguration = auditConfiguration; + return this; + } + + public TestSecurityConfig rolesMapping(RolesMapping... mappings) { + for (RolesMapping mapping : mappings) { + String roleName = mapping.getRoleName(); + if (rolesMapping.containsKey(roleName)) { + throw new IllegalArgumentException("Role mapping " + roleName + " already exists"); + } + this.rolesMapping.put(roleName, mapping); + } + return this; + } + + public static class Config implements ToXContentObject { + private boolean anonymousAuth; + + private Boolean doNotFailOnForbidden; + private XffConfig xffConfig; + private Map authcDomainMap = new LinkedHashMap<>(); + + private AuthFailureListeners authFailureListeners; + private Map authzDomainMap = new LinkedHashMap<>(); + + public Config anonymousAuth(boolean anonymousAuth) { + this.anonymousAuth = anonymousAuth; + return this; + } + + public Config doNotFailOnForbidden(Boolean doNotFailOnForbidden) { + this.doNotFailOnForbidden = doNotFailOnForbidden; + return this; + } + + public Config xffConfig(XffConfig xffConfig) { + this.xffConfig = xffConfig; + return this; + } + + public Config authc(AuthcDomain authcDomain) { + authcDomainMap.put(authcDomain.id, authcDomain); + return this; + } + + public Config authFailureListeners(AuthFailureListeners authFailureListeners) { + this.authFailureListeners = authFailureListeners; + return this; + } + + public Config authz(AuthzDomain authzDomain) { + authzDomainMap.put(authzDomain.getId(), authzDomain); + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + xContentBuilder.startObject("dynamic"); + + if (anonymousAuth || (xffConfig != null)) { + xContentBuilder.startObject("http"); + xContentBuilder.field("anonymous_auth_enabled", anonymousAuth); + if (xffConfig != null) { + xContentBuilder.field("xff", xffConfig); + } + xContentBuilder.endObject(); + } + if (doNotFailOnForbidden != null) { + xContentBuilder.field("do_not_fail_on_forbidden", doNotFailOnForbidden); + } + + xContentBuilder.field("authc", authcDomainMap); + if (authzDomainMap.isEmpty() == false) { + xContentBuilder.field("authz", authzDomainMap); + } + + if (authFailureListeners != null) { + xContentBuilder.field("auth_failure_listeners", authFailureListeners); + } + + xContentBuilder.endObject(); + xContentBuilder.endObject(); + return xContentBuilder; + } + } + + public static class User implements UserCredentialsHolder, ToXContentObject { + + public final static TestSecurityConfig.User USER_ADMIN = new TestSecurityConfig.User("admin").roles( + new Role("allaccess").indexPermissions("*").on("*").clusterPermissions("*") + ); + + String name; + private String password; + List roles = new ArrayList<>(); + private Map attributes = new HashMap<>(); + + public User(String name) { + this.name = name; + this.password = "secret"; + } + + public User password(String password) { + this.password = password; + return this; + } + + public User roles(Role... roles) { + // We scope the role names by user to keep tests free of potential side effects + String roleNamePrefix = "user_" + this.getName() + "__"; + this.roles.addAll( + Arrays.asList(roles).stream().map((r) -> r.clone().name(roleNamePrefix + r.getName())).collect(Collectors.toSet()) + ); + return this; + } + + public User attr(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public String getName() { + return name; + } + + public String getPassword() { + return password; + } + + public Set getRoleNames() { + return roles.stream().map(Role::getName).collect(Collectors.toSet()); + } + + public Object getAttribute(String attributeName) { + return attributes.get(attributeName); + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + + xContentBuilder.field("hash", hash(password.toCharArray())); + + Set roleNames = getRoleNames(); + + if (!roleNames.isEmpty()) { + xContentBuilder.field("opendistro_security_roles", roleNames); + } + + if (attributes != null && attributes.size() != 0) { + xContentBuilder.field("attributes", attributes); + } + + xContentBuilder.endObject(); + return xContentBuilder; + } + } + + public static class Role implements ToXContentObject { + public static Role ALL_ACCESS = new Role("all_access").clusterPermissions("*").indexPermissions("*").on("*"); + + private String name; + private List clusterPermissions = new ArrayList<>(); + + private List indexPermissions = new ArrayList<>(); + + public Role(String name) { + this.name = name; + } + + public Role clusterPermissions(String... clusterPermissions) { + this.clusterPermissions.addAll(Arrays.asList(clusterPermissions)); + return this; + } + + public IndexPermission indexPermissions(String... indexPermissions) { + return new IndexPermission(this, indexPermissions); + } + + public Role name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public Role clone() { + Role role = new Role(this.name); + role.clusterPermissions.addAll(this.clusterPermissions); + role.indexPermissions.addAll(this.indexPermissions); + return role; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + + if (!clusterPermissions.isEmpty()) { + xContentBuilder.field("cluster_permissions", clusterPermissions); + } + + if (!indexPermissions.isEmpty()) { + xContentBuilder.field("index_permissions", indexPermissions); + } + + xContentBuilder.endObject(); + return xContentBuilder; + } + } + + public static class IndexPermission implements ToXContentObject { + private List allowedActions; + private List indexPatterns; + private Role role; + private String dlsQuery; + private List fls; + private List maskedFields; + + IndexPermission(Role role, String... allowedActions) { + this.allowedActions = Arrays.asList(allowedActions); + this.role = role; + } + + public IndexPermission dls(String dlsQuery) { + this.dlsQuery = dlsQuery; + return this; + } + + public IndexPermission fls(String... fls) { + this.fls = Arrays.asList(fls); + return this; + } + + public IndexPermission maskedFields(String... maskedFields) { + this.maskedFields = Arrays.asList(maskedFields); + return this; + } + + public Role on(String... indexPatterns) { + this.indexPatterns = Arrays.asList(indexPatterns); + this.role.indexPermissions.add(this); + return this.role; + } + + public Role on(TestIndex... testindices) { + this.indexPatterns = Arrays.asList(testindices).stream().map(TestIndex::getName).collect(Collectors.toList()); + this.role.indexPermissions.add(this); + return this.role; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + + xContentBuilder.field("index_patterns", indexPatterns); + xContentBuilder.field("allowed_actions", allowedActions); + + if (dlsQuery != null) { + xContentBuilder.field("dls", dlsQuery); + } + + if (fls != null) { + xContentBuilder.field("fls", fls); + } + + if (maskedFields != null) { + xContentBuilder.field("masked_fields", maskedFields); + } + + xContentBuilder.endObject(); + return xContentBuilder; + } + } + + public static class AuthcDomain implements ToXContentObject { + + private static String PUBLIC_KEY = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoqZbjLUAWc+DZTkinQAdvy1GFjPHPnxheU89hSiWoDD3NOW76H3u3T7cCDdOah2msdxSlBmCBH6wik8qLYkcV8owWukQg3PQmbEhrdPaKo0QCgomWs4nLgtmEYqcZ+QQldd82MdTlQ1QmoQmI9Uxqs1SuaKZASp3Gy19y8su5CV+FZ6BruUw9HELK055sAwl3X7j5ouabXGbcib2goBF3P52LkvbJLuWr5HDZEOeSkwIeqSeMojASM96K5SdotD+HwEyjaTjzRPL2Aa1BEQFWOQ6CFJLyLH7ZStDuPM1mJU1VxIVfMbZrhsUBjAnIhRynmWxML7YlNqkP9j6jyOIYQIDAQAB"; + + public static final int BASIC_AUTH_DOMAIN_ORDER = 0; + public final static AuthcDomain AUTHC_HTTPBASIC_INTERNAL = new TestSecurityConfig.AuthcDomain("basic", BASIC_AUTH_DOMAIN_ORDER) + .httpAuthenticatorWithChallenge("basic") + .backend("internal"); + + public final static AuthcDomain AUTHC_HTTPBASIC_INTERNAL_WITHOUT_CHALLENGE = new TestSecurityConfig.AuthcDomain( + "basic", + BASIC_AUTH_DOMAIN_ORDER + ).httpAuthenticator("basic").backend("internal"); + + public final static AuthcDomain DISABLED_AUTHC_HTTPBASIC_INTERNAL = new TestSecurityConfig.AuthcDomain( + "basic", + BASIC_AUTH_DOMAIN_ORDER, + false + ).httpAuthenticator("basic").backend("internal"); + + public final static AuthcDomain JWT_AUTH_DOMAIN = new TestSecurityConfig.AuthcDomain("jwt", 1).jwtHttpAuthenticator( + new JwtConfigBuilder().jwtHeader(AUTHORIZATION).signingKey(PUBLIC_KEY) + ).backend("noop"); + + private final String id; + private boolean enabled = true; + private int order; + private List skipUsers = new ArrayList<>(); + private HttpAuthenticator httpAuthenticator; + private AuthenticationBackend authenticationBackend; + + public AuthcDomain(String id, int order, boolean enabled) { + this.id = id; + this.order = order; + this.enabled = enabled; + } + + public AuthcDomain(String id, int order) { + this(id, order, true); + } + + public AuthcDomain httpAuthenticator(String type) { + this.httpAuthenticator = new HttpAuthenticator(type); + return this; + } + + public AuthcDomain jwtHttpAuthenticator(JwtConfigBuilder builder) { + this.httpAuthenticator = new HttpAuthenticator("jwt").challenge(false).config(builder.build()); + return this; + } + + public AuthcDomain httpAuthenticatorWithChallenge(String type) { + this.httpAuthenticator = new HttpAuthenticator(type).challenge(true); + return this; + } + + public AuthcDomain httpAuthenticator(HttpAuthenticator httpAuthenticator) { + this.httpAuthenticator = httpAuthenticator; + return this; + } + + public AuthcDomain backend(String type) { + this.authenticationBackend = new AuthenticationBackend(type); + return this; + } + + public AuthcDomain backend(AuthenticationBackend authenticationBackend) { + this.authenticationBackend = authenticationBackend; + return this; + } + + public AuthcDomain skipUsers(String... users) { + this.skipUsers.addAll(Arrays.asList(users)); + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + + xContentBuilder.field("http_enabled", enabled); + xContentBuilder.field("order", order); + + if (httpAuthenticator != null) { + xContentBuilder.field("http_authenticator", httpAuthenticator); + } + + if (authenticationBackend != null) { + xContentBuilder.field("authentication_backend", authenticationBackend); + } + + if (skipUsers != null && skipUsers.size() > 0) { + xContentBuilder.field("skip_users", skipUsers); + } + + xContentBuilder.endObject(); + return xContentBuilder; + } + + public static class HttpAuthenticator implements ToXContentObject { + private final String type; + private boolean challenge; + private Map config = new HashMap(); + + public HttpAuthenticator(String type) { + this.type = type; + } + + public HttpAuthenticator challenge(boolean challenge) { + this.challenge = challenge; + return this; + } + + public HttpAuthenticator config(Map config) { + this.config.putAll(config); + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + + xContentBuilder.field("type", type); + xContentBuilder.field("challenge", challenge); + xContentBuilder.field("config", config); + + xContentBuilder.endObject(); + return xContentBuilder; + } + } + + public static class AuthenticationBackend implements ToXContentObject { + private final String type; + private Supplier> config = () -> new HashMap(); + + public AuthenticationBackend(String type) { + this.type = type; + } + + public AuthenticationBackend config(Map config) { + Map configCopy = new HashMap<>(config); + this.config = () -> configCopy; + return this; + } + + public AuthenticationBackend config(Supplier> configSupplier) { + this.config = configSupplier; + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.startObject(); + + xContentBuilder.field("type", type); + xContentBuilder.field("config", config.get()); + + xContentBuilder.endObject(); + return xContentBuilder; + } + } + } + + public void initIndex(Client client) { + Map settings = new HashMap<>(); + if (indexName.startsWith(".")) { + settings.put("index.hidden", true); + } + client.admin().indices().create(new CreateIndexRequest(indexName).settings(settings)).actionGet(); + + writeSingleEntryConfigToIndex(client, CType.CONFIG, config); + if (auditConfiguration != null) { + writeSingleEntryConfigToIndex(client, CType.AUDIT, "config", auditConfiguration); + } + writeConfigToIndex(client, CType.ROLES, roles); + writeConfigToIndex(client, CType.INTERNALUSERS, internalUsers); + writeConfigToIndex(client, CType.ROLESMAPPING, rolesMapping); + writeEmptyConfigToIndex(client, CType.ACTIONGROUPS); + writeEmptyConfigToIndex(client, CType.TENANTS); + } + + public void updateInternalUsersConfiguration(Client client, List users) { + Map userMap = new HashMap<>(); + for (User user : users) { + userMap.put(user.getName(), user); + } + updateConfigInIndex(client, CType.INTERNALUSERS, userMap); + } + + static String hash(final char[] clearTextPassword) { + final byte[] salt = new byte[16]; + new SecureRandom().nextBytes(salt); + final String hash = OpenBSDBCrypt.generate((Objects.requireNonNull(clearTextPassword)), salt, 12); + Arrays.fill(salt, (byte) 0); + Arrays.fill(clearTextPassword, '\0'); + return hash; + } + + private void writeEmptyConfigToIndex(Client client, CType configType) { + writeConfigToIndex(client, configType, Collections.emptyMap()); + } + + private void writeConfigToIndex(Client client, CType configType, Map config) { + try { + String json = configToJson(configType, config); + + log.info("Writing security configuration into index " + configType + ":\n" + json); + + BytesReference bytesReference = toByteReference(json); + client.index( + new IndexRequest(indexName).id(configType.toLCString()) + .setRefreshPolicy(IMMEDIATE) + .source(configType.toLCString(), bytesReference) + ).actionGet(); + } catch (Exception e) { + throw new RuntimeException("Error while initializing config for " + indexName, e); + } + } + + private static BytesReference toByteReference(String string) throws UnsupportedEncodingException { + return BytesReference.fromByteBuffer(ByteBuffer.wrap(string.getBytes("utf-8"))); + } + + private void updateConfigInIndex(Client client, CType configType, Map config) { + try { + String json = configToJson(configType, config); + BytesReference bytesReference = toByteReference(json); + log.info("Update configuration of type '{}' in index '{}', new value '{}'.", configType, indexName, json); + UpdateRequest upsert = new UpdateRequest(indexName, configType.toLCString()).doc(configType.toLCString(), bytesReference) + .setRefreshPolicy(IMMEDIATE); + client.update(upsert).actionGet(); + } catch (Exception e) { + throw new RuntimeException("Error while updating config for " + indexName, e); + } + } + + private static String configToJson(CType configType, Map config) throws IOException { + XContentBuilder builder = XContentFactory.jsonBuilder(); + + builder.startObject(); + builder.startObject("_meta"); + builder.field("type", configType.toLCString()); + builder.field("config_version", 2); + builder.endObject(); + + for (Map.Entry entry : config.entrySet()) { + builder.field(entry.getKey(), entry.getValue()); + } + + builder.endObject(); + + return Strings.toString(builder); + } + + private void writeSingleEntryConfigToIndex(Client client, CType configType, ToXContentObject config) { + writeSingleEntryConfigToIndex(client, configType, configType.toLCString(), config); + } + + private void writeSingleEntryConfigToIndex(Client client, CType configType, String configurationRoot, ToXContentObject config) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + + builder.startObject(); + builder.startObject("_meta"); + builder.field("type", configType.toLCString()); + builder.field("config_version", 2); + builder.endObject(); + + builder.field(configurationRoot, config); + + builder.endObject(); + + String json = Strings.toString(builder); + + log.info("Writing security plugin configuration into index " + configType + ":\n" + json); + + client.index( + new IndexRequest(indexName).id(configType.toLCString()) + .setRefreshPolicy(IMMEDIATE) + .source(configType.toLCString(), toByteReference(json)) + ).actionGet(); + } catch (Exception e) { + throw new RuntimeException("Error while initializing config for " + indexName, e); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java new file mode 100644 index 0000000000..6e8519c230 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java @@ -0,0 +1,37 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Matcher; + +import org.opensearch.core.rest.RestStatus; + +import static org.hamcrest.Matchers.containsString; + +public class OpenSearchExceptionMatchers { + + private OpenSearchExceptionMatchers() {} + + public static Matcher statusException(RestStatus expectedRestStatus) { + return new OpenSearchStatusExceptionMatcher(expectedRestStatus); + } + + public static Matcher errorMessage(Matcher errorMessageMatcher) { + return new ExceptionErrorMessageMatcher(errorMessageMatcher); + } + + public static Matcher errorMessageContain(String errorMessage) { + return errorMessage(containsString(errorMessage)); + } + + public static Matcher hasCause(Class clazz) { + return new ExceptionHasCauseMatcher(clazz); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java new file mode 100644 index 0000000000..e8efcf151f --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java @@ -0,0 +1,52 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.OpenSearchException; +import org.opensearch.core.rest.RestStatus; + +import static java.util.Objects.requireNonNull; + +class OpenSearchStatusExceptionMatcher extends TypeSafeDiagnosingMatcher { + + private final RestStatus expectedRestStatus; + + public OpenSearchStatusExceptionMatcher(RestStatus expectedRestStatus) { + this.expectedRestStatus = requireNonNull(expectedRestStatus, "Expected rest status is required."); + } + + @Override + protected boolean matchesSafely(Throwable throwable, Description mismatchDescription) { + if ((throwable instanceof OpenSearchException) == false) { + mismatchDescription.appendText("actual exception type is ") + .appendValue(throwable.getClass().getCanonicalName()) + .appendText(", error message ") + .appendValue(throwable.getMessage()); + return false; + } + OpenSearchException openSearchException = (OpenSearchException) throwable; + if (expectedRestStatus.equals(openSearchException.status()) == false) { + mismatchDescription.appendText("actual status code is ") + .appendValue(openSearchException.status()) + .appendText(", error message ") + .appendValue(throwable.getMessage()); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("OpenSearchException with status code ").appendValue(expectedRestStatus); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java new file mode 100644 index 0000000000..cf3a6d9e57 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java @@ -0,0 +1,87 @@ +/* +* 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.test.framework.matcher; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang3.tuple.Pair; +import org.hamcrest.Matcher; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.search.SearchHits; + +public class SearchResponseMatchers { + + private SearchResponseMatchers() {} + + public static Matcher isSuccessfulSearchResponse() { + return new SuccessfulSearchResponseMatcher(); + } + + public static Matcher numberOfTotalHitsIsEqualTo(int expectedNumberOfHits) { + return new NumberOfTotalHitsIsEqualToMatcher(expectedNumberOfHits); + } + + public static Matcher numberOfHitsInPageIsEqualTo(int expectedNumberOfHits) { + return new NumberOfHitsInPageIsEqualToMatcher(expectedNumberOfHits); + } + + public static Matcher searchHitContainsFieldWithValue(int hitIndex, String fieldName, T expectedValue) { + return new SearchHitContainsFieldWithValueMatcher<>(hitIndex, fieldName, expectedValue); + } + + public static Matcher searchHitDoesNotContainField(int hitIndex, String fieldName) { + return new SearchHitDoesNotContainFieldMatcher(hitIndex, fieldName); + } + + public static Matcher searchHitsContainDocumentWithId(int hitIndex, String indexName, String documentId) { + return new SearchHitsContainDocumentWithIdMatcher(hitIndex, indexName, documentId); + } + + public static Matcher restStatusIs(RestStatus expectedRestStatus) { + return new SearchResponseWithStatusCodeMatcher(expectedRestStatus); + } + + public static Matcher containNotEmptyScrollingId() { + return new ContainNotEmptyScrollingIdMatcher(); + } + + public static Matcher containAggregationWithNameAndType( + String expectedAggregationName, + String expectedAggregationType + ) { + return new ContainsAggregationWithNameAndTypeMatcher(expectedAggregationName, expectedAggregationType); + } + + /** + * Matcher checks if search result contains all expected documents + * + * @param documentIds Pair contain index name and document id + * @return matcher + */ + public static Matcher searchHitsContainDocumentsInAnyOrder(List> documentIds) { + return new SearchHitsContainDocumentsInAnyOrderMatcher(documentIds); + } + + public static Matcher searchHitsContainDocumentsInAnyOrder(Pair... documentIds) { + return new SearchHitsContainDocumentsInAnyOrderMatcher(Arrays.asList(documentIds)); + } + + static Long readTotalHits(SearchResponse searchResponse) { + return Optional.ofNullable(searchResponse) + .map(SearchResponse::getHits) + .map(SearchHits::getTotalHits) + .map(totalHits -> totalHits.value) + .orElse(null); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java new file mode 100644 index 0000000000..5b0f99e11b --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java @@ -0,0 +1,39 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.core.rest.RestStatus; + +class SearchResponseWithStatusCodeMatcher extends TypeSafeDiagnosingMatcher { + + private final RestStatus expectedRestStatus; + + public SearchResponseWithStatusCodeMatcher(RestStatus expectedRestStatus) { + this.expectedRestStatus = expectedRestStatus; + } + + @Override + protected boolean matchesSafely(SearchResponse searchResponse, Description mismatchDescription) { + if (expectedRestStatus.equals(searchResponse.status()) == false) { + mismatchDescription.appendText("actual response status is ").appendValue(searchResponse.status()); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Expected response status is ").appendValue(expectedRestStatus); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java new file mode 100644 index 0000000000..ca4b94d148 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java @@ -0,0 +1,47 @@ +/* +* 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.test.framework.matcher; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.bulk.BulkItemResponse; +import org.opensearch.action.bulk.BulkResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessBulkResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(BulkResponse response, Description mismatchDescription) { + RestStatus status = response.status(); + if (RestStatus.OK.equals(status) == false) { + mismatchDescription.appendText("incorrect response status ").appendValue(status); + return false; + } + if (response.hasFailures()) { + String failureDescription = Arrays.stream(response.getItems()) + .filter(BulkItemResponse::isFailed) + .map(BulkItemResponse::getFailure) + .map(Object::toString) + .collect(Collectors.joining(",\n")); + mismatchDescription.appendText("bulk response contains failures ").appendValue(failureDescription); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("success bulk response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java new file mode 100644 index 0000000000..b70b2c2f9e --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java @@ -0,0 +1,37 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessfulClearIndicesCacheResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(ClearIndicesCacheResponse response, Description mismatchDescription) { + if (!RestStatus.OK.equals(response.getStatus())) { + mismatchDescription.appendText("Status is equal to ").appendValue(response.getStatus()); + return false; + } + if (response.getShardFailures().length != 0) { + mismatchDescription.appendText("Contains ").appendValue(response.getShardFailures().length).appendText(" shard failures"); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful clear index cache response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java new file mode 100644 index 0000000000..66b59b1526 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java @@ -0,0 +1,37 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessfulCreatePitResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(CreatePitResponse response, Description mismatchDescription) { + if (!RestStatus.OK.equals(response.status())) { + mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); + return false; + } + if (response.getShardFailures().length != 0) { + mismatchDescription.appendText("contains ").appendValue(response.getShardFailures().length).appendText(" shard failures"); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful create pit response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java new file mode 100644 index 0000000000..20906946f1 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java @@ -0,0 +1,42 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.search.DeletePitInfo; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessfulDeletePitResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(DeletePitResponse response, Description mismatchDescription) { + if (!RestStatus.OK.equals(response.status())) { + mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); + return false; + } + for (DeletePitInfo deletePitInfo : response.getDeletePitResults()) { + if (!deletePitInfo.isSuccessful()) { + mismatchDescription.appendValue("Pit: ") + .appendValue(deletePitInfo.getPitId()) + .appendText(" - delete result was not successful"); + return false; + } + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful delete pit response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java new file mode 100644 index 0000000000..6c10b2b6f8 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java @@ -0,0 +1,39 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.delete.DeleteResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessfulDeleteResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(DeleteResponse response, Description mismatchDescription) { + if (!RestStatus.OK.equals(response.status())) { + mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); + return false; + } + if (response.getShardInfo().getFailures().length != 0) { + mismatchDescription.appendText("contains ") + .appendValue(response.getShardInfo().getFailures().length) + .appendText(" shard failures"); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful delete response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java new file mode 100644 index 0000000000..21017a9014 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java @@ -0,0 +1,37 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessfulSearchResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(SearchResponse searchResponse, Description mismatchDescription) { + if (RestStatus.OK.equals(searchResponse.status()) == false) { + mismatchDescription.appendText("has status ").appendValue(searchResponse.status()).appendText(" which denotes failure."); + return false; + } + if (searchResponse.getShardFailures().length != 0) { + mismatchDescription.appendText("contains ").appendValue(searchResponse.getShardFailures().length).appendText(" shard failures"); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful search response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java new file mode 100644 index 0000000000..14faab0c4c --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java @@ -0,0 +1,39 @@ +/* +* 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.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.update.UpdateResponse; +import org.opensearch.core.rest.RestStatus; + +class SuccessfulUpdateResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(UpdateResponse response, Description mismatchDescription) { + if (!RestStatus.OK.equals(response.status())) { + mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); + return false; + } + if (response.getShardInfo().getFailures().length != 0) { + mismatchDescription.appendText("contains ") + .appendValue(response.getShardInfo().getFailures().length) + .appendText(" shard failures"); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful update response"); + } +} diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java index 9df525fa50..0c38245586 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java @@ -38,7 +38,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.user.AuthCredentials; diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java index 1d690dd603..3a7d31d54a 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java @@ -40,7 +40,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.user.AuthCredentials; diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java index 4b73ab9491..9d1cb8ab04 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java @@ -51,7 +51,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.user.AuthCredentials; diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java index 1c49d10b2e..27dc5c3ec8 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java @@ -53,14 +53,14 @@ import org.opensearch.OpenSearchSecurityException; import org.opensearch.SpecialPermission; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.dlic.rest.api.AuthTokenProcessorAction; diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java index d3068b852a..cd6209952f 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java @@ -58,7 +58,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.Destroyable; import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.support.ConfigConstants; diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 54402544d0..439b22af94 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -79,7 +79,7 @@ import org.opensearch.common.component.LifecycleComponent; import org.opensearch.common.component.LifecycleListener; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.network.NetworkModule; import org.opensearch.common.network.NetworkService; @@ -98,7 +98,7 @@ import org.opensearch.extensions.ExtensionsManager; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.HttpServerTransport.Dispatcher; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexModule; import org.opensearch.index.cache.query.QueryCache; import org.opensearch.indices.IndicesService; @@ -109,7 +109,7 @@ import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.script.ScriptService; import org.opensearch.search.internal.InternalScrollSearchRequest; import org.opensearch.search.internal.ReaderContext; diff --git a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java index 0ac4459102..99ff946b05 100644 --- a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java +++ b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java @@ -31,8 +31,8 @@ import org.opensearch.action.support.nodes.BaseNodeResponse; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; diff --git a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateRequest.java b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateRequest.java index 5310c497a2..d4e860569d 100644 --- a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateRequest.java +++ b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateRequest.java @@ -30,8 +30,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; public class ConfigUpdateRequest extends BaseNodesRequest { diff --git a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java index fd3176c016..9dd483248b 100644 --- a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java +++ b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java @@ -32,8 +32,8 @@ import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.core.xcontent.XContentBuilder; diff --git a/src/main/java/org/opensearch/security/action/configupdate/TransportConfigUpdateAction.java b/src/main/java/org/opensearch/security/action/configupdate/TransportConfigUpdateAction.java index 2fcd55f307..5c3b5b9e3f 100644 --- a/src/main/java/org/opensearch/security/action/configupdate/TransportConfigUpdateAction.java +++ b/src/main/java/org/opensearch/security/action/configupdate/TransportConfigUpdateAction.java @@ -39,8 +39,8 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.common.inject.Provider; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; import org.opensearch.security.auth.BackendRegistry; import org.opensearch.security.configuration.ConfigurationRepository; diff --git a/src/main/java/org/opensearch/security/action/whoami/WhoAmIRequest.java b/src/main/java/org/opensearch/security/action/whoami/WhoAmIRequest.java index 8226cc0a09..c393205adf 100644 --- a/src/main/java/org/opensearch/security/action/whoami/WhoAmIRequest.java +++ b/src/main/java/org/opensearch/security/action/whoami/WhoAmIRequest.java @@ -29,7 +29,7 @@ import java.io.IOException; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; public class WhoAmIRequest extends BaseNodesRequest { diff --git a/src/main/java/org/opensearch/security/action/whoami/WhoAmIResponse.java b/src/main/java/org/opensearch/security/action/whoami/WhoAmIResponse.java index 3e9d74fe25..0b30aeb94b 100644 --- a/src/main/java/org/opensearch/security/action/whoami/WhoAmIResponse.java +++ b/src/main/java/org/opensearch/security/action/whoami/WhoAmIResponse.java @@ -30,8 +30,8 @@ import org.opensearch.action.ActionResponse; import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.ToXContent; import org.opensearch.core.xcontent.XContentBuilder; diff --git a/src/main/java/org/opensearch/security/auditlog/AuditLog.java b/src/main/java/org/opensearch/security/auditlog/AuditLog.java index 3ac7e095aa..612b790686 100644 --- a/src/main/java/org/opensearch/security/auditlog/AuditLog.java +++ b/src/main/java/org/opensearch/security/auditlog/AuditLog.java @@ -34,7 +34,7 @@ import org.opensearch.index.engine.Engine.Index; import org.opensearch.index.engine.Engine.IndexResult; import org.opensearch.index.get.GetResult; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.rest.RestRequest; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.compliance.ComplianceConfig; diff --git a/src/main/java/org/opensearch/security/auditlog/NullAuditLog.java b/src/main/java/org/opensearch/security/auditlog/NullAuditLog.java index 7fe3324d2e..809951d48a 100644 --- a/src/main/java/org/opensearch/security/auditlog/NullAuditLog.java +++ b/src/main/java/org/opensearch/security/auditlog/NullAuditLog.java @@ -34,7 +34,7 @@ import org.opensearch.index.engine.Engine.Index; import org.opensearch.index.engine.Engine.IndexResult; import org.opensearch.index.get.GetResult; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.rest.RestRequest; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.compliance.ComplianceConfig; 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 37b98ed67e..db5e211c79 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java @@ -43,7 +43,7 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; +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; @@ -59,7 +59,7 @@ import org.opensearch.index.engine.Engine.Index; import org.opensearch.index.engine.Engine.IndexResult; import org.opensearch.index.get.GetResult; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.rest.RestRequest; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auditlog.AuditLog; diff --git a/src/main/java/org/opensearch/security/auditlog/impl/AuditLogImpl.java b/src/main/java/org/opensearch/security/auditlog/impl/AuditLogImpl.java index a09c6a694e..c88f1fca3f 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/AuditLogImpl.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/AuditLogImpl.java @@ -30,7 +30,7 @@ import org.opensearch.index.engine.Engine.Index; import org.opensearch.index.engine.Engine.IndexResult; import org.opensearch.index.get.GetResult; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.rest.RestRequest; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.auditlog.routing.AuditMessageRouter; 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 7afe7eb5a6..11aec86f45 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java @@ -34,14 +34,14 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.core.common.Strings; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.rest.RestRequest; import org.opensearch.security.auditlog.AuditLog.Operation; import org.opensearch.security.auditlog.AuditLog.Origin; 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 ecf7a2bd36..c37247e7db 100644 --- a/src/main/java/org/opensearch/security/auditlog/impl/RequestResolver.java +++ b/src/main/java/org/opensearch/security/auditlog/impl/RequestResolver.java @@ -42,14 +42,14 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; +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.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.reindex.DeleteByQueryRequest; import org.opensearch.index.reindex.ReindexRequest; import org.opensearch.index.reindex.UpdateByQueryRequest; diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 0a287d19f5..b2873f9625 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -54,7 +54,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.auth.blocking.ClientBlockRegistry; import org.opensearch.security.auth.internal.NoOpAuthenticationBackend; diff --git a/src/main/java/org/opensearch/security/compliance/ComplianceIndexingOperationListenerImpl.java b/src/main/java/org/opensearch/security/compliance/ComplianceIndexingOperationListenerImpl.java index cf369bb0cb..c1ac1271bb 100644 --- a/src/main/java/org/opensearch/security/compliance/ComplianceIndexingOperationListenerImpl.java +++ b/src/main/java/org/opensearch/security/compliance/ComplianceIndexingOperationListenerImpl.java @@ -24,7 +24,7 @@ import org.opensearch.index.engine.Engine.IndexResult; import org.opensearch.index.get.GetResult; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.security.auditlog.AuditLog; public final class ComplianceIndexingOperationListenerImpl extends ComplianceIndexingOperationListener { diff --git a/src/main/java/org/opensearch/security/compliance/FieldReadCallback.java b/src/main/java/org/opensearch/security/compliance/FieldReadCallback.java index 73f536c2f8..56385b15f3 100644 --- a/src/main/java/org/opensearch/security/compliance/FieldReadCallback.java +++ b/src/main/java/org/opensearch/security/compliance/FieldReadCallback.java @@ -27,10 +27,10 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.xcontent.support.XContentMapValues; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.Uid; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.dlic.rest.support.Utils; import org.opensearch.security.support.HeaderHelper; diff --git a/src/main/java/org/opensearch/security/configuration/ConfigUpdateAlreadyInProgressException.java b/src/main/java/org/opensearch/security/configuration/ConfigUpdateAlreadyInProgressException.java index c628a3156e..6387c17103 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigUpdateAlreadyInProgressException.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigUpdateAlreadyInProgressException.java @@ -20,7 +20,7 @@ import java.io.IOException; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; public class ConfigUpdateAlreadyInProgressException extends OpenSearchException { diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java index 662289459a..f9e61fe7c1 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java @@ -49,7 +49,7 @@ import org.opensearch.action.get.MultiGetResponse.Failure; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java b/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java index 46bd593e7a..04b9a1d6d5 100644 --- a/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java +++ b/src/main/java/org/opensearch/security/configuration/ConfigurationRepository.java @@ -65,7 +65,7 @@ import org.opensearch.common.util.concurrent.ThreadContext.StoredContext; import org.opensearch.common.xcontent.XContentType; import org.opensearch.env.Environment; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.securityconf.DynamicConfigFactory; diff --git a/src/main/java/org/opensearch/security/configuration/DlsFlsFilterLeafReader.java b/src/main/java/org/opensearch/security/configuration/DlsFlsFilterLeafReader.java index 7100be35e5..508c3dd9b4 100644 --- a/src/main/java/org/opensearch/security/configuration/DlsFlsFilterLeafReader.java +++ b/src/main/java/org/opensearch/security/configuration/DlsFlsFilterLeafReader.java @@ -64,8 +64,8 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.lucene.index.SequentialStoredFieldsLeafReader; import org.opensearch.common.util.concurrent.ThreadContext; @@ -74,7 +74,7 @@ import org.opensearch.common.xcontent.support.XContentMapValues; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexService; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.compliance.ComplianceConfig; import org.opensearch.security.compliance.FieldReadCallback; diff --git a/src/main/java/org/opensearch/security/configuration/DlsFlsValveImpl.java b/src/main/java/org/opensearch/security/configuration/DlsFlsValveImpl.java index 81fe9e255f..de7ffe1fc2 100644 --- a/src/main/java/org/opensearch/security/configuration/DlsFlsValveImpl.java +++ b/src/main/java/org/opensearch/security/configuration/DlsFlsValveImpl.java @@ -54,7 +54,7 @@ import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.index.query.ParsedQuery; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.search.DocValueFormat; import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; diff --git a/src/main/java/org/opensearch/security/configuration/SecurityFlsDlsIndexSearcherWrapper.java b/src/main/java/org/opensearch/security/configuration/SecurityFlsDlsIndexSearcherWrapper.java index 2e58424c63..b6df13c9d9 100644 --- a/src/main/java/org/opensearch/security/configuration/SecurityFlsDlsIndexSearcherWrapper.java +++ b/src/main/java/org/opensearch/security/configuration/SecurityFlsDlsIndexSearcherWrapper.java @@ -27,7 +27,7 @@ import org.opensearch.index.IndexService; import org.opensearch.index.mapper.IgnoredFieldMapper; import org.opensearch.index.query.QueryShardContext; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.shard.ShardUtils; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.compliance.ComplianceIndexingOperationListener; diff --git a/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java b/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java index 9c7d451fa3..1619b3da32 100644 --- a/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java +++ b/src/main/java/org/opensearch/security/configuration/SecurityIndexSearcherWrapper.java @@ -38,7 +38,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexService; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.securityconf.ConfigModel; diff --git a/src/main/java/org/opensearch/security/configuration/StaticResourceException.java b/src/main/java/org/opensearch/security/configuration/StaticResourceException.java index 8574a170bb..1191c3a1ab 100644 --- a/src/main/java/org/opensearch/security/configuration/StaticResourceException.java +++ b/src/main/java/org/opensearch/security/configuration/StaticResourceException.java @@ -20,7 +20,7 @@ import java.io.IOException; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; public class StaticResourceException extends OpenSearchException { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java index 288a8eac0e..727624e4e4 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java @@ -30,7 +30,7 @@ import org.opensearch.client.Client; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext.StoredContext; import org.opensearch.common.xcontent.XContentHelper; @@ -44,7 +44,7 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateNodeResponse; 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 68446366bc..a93929f57b 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 @@ -23,7 +23,7 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; @@ -34,7 +34,7 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.configuration.ConfigurationRepository; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java index 94af3ad3af..3b3e772eda 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java @@ -21,7 +21,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java index 0c5b2775aa..054ac1ea46 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java @@ -21,7 +21,7 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java index a61f66c6e3..078bfcc62a 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java @@ -23,7 +23,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java index fa6d967624..87c82cf77c 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java @@ -20,7 +20,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java index 2f8a60aa65..4898897df7 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java @@ -21,7 +21,7 @@ import org.opensearch.action.ActionListener; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java index 532032891d..358693d9a1 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java @@ -23,7 +23,7 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.core.xcontent.XContentBuilder; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java index 87858d37b0..398039015b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java @@ -32,7 +32,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java index ef5be4634f..bef77ecb2b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/MultiTenancyConfigApiAction.java @@ -26,14 +26,14 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.configuration.ConfigurationRepository; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java index 6d44e4073c..c7392e4441 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java @@ -21,7 +21,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java index e6d1f1744a..6050b9785e 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java @@ -28,8 +28,8 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.common.Strings; 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 f57c4eb59c..ad8a536bfc 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 @@ -33,7 +33,7 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.configuration.ConfigurationRepository; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java index 65dbaac05e..ea1ca791ba 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java @@ -19,7 +19,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestController; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java index 627dabfaa2..e2909edd6c 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java @@ -21,7 +21,7 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java index 9584a34c06..c4abc45cee 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java @@ -21,7 +21,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestChannel; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsAction.java index 4949dedad9..0b576bfd68 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecuritySSLCertsAction.java @@ -27,7 +27,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.BytesRestResponse; @@ -35,7 +35,7 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.configuration.ConfigurationRepository; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java index 5fbb907ecf..b011325c86 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java @@ -34,7 +34,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestController; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java index de79b131b3..f9612d3b40 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java @@ -20,7 +20,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; 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 74908dbf60..315121a977 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 @@ -35,7 +35,7 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchParseException; import org.opensearch.SpecialPermission; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.xcontent.XContentHelper; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/AbstractConfigurationValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/AbstractConfigurationValidator.java index 51d58d75f6..543ad4b4a7 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/AbstractConfigurationValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/AbstractConfigurationValidator.java @@ -28,7 +28,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/AccountValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/AccountValidator.java index 32cf06078b..a2f085f05b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/AccountValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/AccountValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/ActionGroupValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/ActionGroupValidator.java index a9f298fb15..7c65ff4567 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/ActionGroupValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/ActionGroupValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/AllowlistValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/AllowlistValidator.java index 5b53a1fb49..9fc7465642 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/AllowlistValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/AllowlistValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/AuditValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/AuditValidator.java index 1bff373c0d..b57cd1e715 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/AuditValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/AuditValidator.java @@ -15,7 +15,7 @@ import com.google.common.collect.ImmutableSet; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; import org.opensearch.security.DefaultObjectMapper; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/CredentialsValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/CredentialsValidator.java index a0f67c97ce..283af8dd00 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/CredentialsValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/CredentialsValidator.java @@ -13,8 +13,8 @@ import java.util.Map; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.compress.NotXContentException; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.compress.NotXContentException; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/InternalUsersValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/InternalUsersValidator.java index 9681c47232..87423e3912 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/InternalUsersValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/InternalUsersValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/MultiTenancyConfigValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/MultiTenancyConfigValidator.java index 42f86dbee5..dd07e9ac2e 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/MultiTenancyConfigValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/MultiTenancyConfigValidator.java @@ -10,7 +10,7 @@ */ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/NoOpValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/NoOpValidator.java index 7c64102091..d29ec91561 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/NoOpValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/NoOpValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/NodesDnValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/NodesDnValidator.java index c98df1de0a..a2abb06f7b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/NodesDnValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/NodesDnValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/RolesMappingValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/RolesMappingValidator.java index 728c2e0ca0..10c630c771 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/RolesMappingValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/RolesMappingValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/RolesValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/RolesValidator.java index 2e57730e41..019a21c73b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/RolesValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/RolesValidator.java @@ -16,7 +16,7 @@ import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.ReadContext; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; import org.opensearch.security.configuration.MaskedField; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/SecurityConfigValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/SecurityConfigValidator.java index cd2ee56b4a..30f3c91965 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/SecurityConfigValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/SecurityConfigValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/TenantValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/TenantValidator.java index 51e0e97264..07a0ec9cf3 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/TenantValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/TenantValidator.java @@ -27,7 +27,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/dlic/rest/validation/WhitelistValidator.java b/src/main/java/org/opensearch/security/dlic/rest/validation/WhitelistValidator.java index cf85b248d4..91a283b4c5 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/validation/WhitelistValidator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/validation/WhitelistValidator.java @@ -11,7 +11,7 @@ package org.opensearch.security.dlic.rest.validation; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; diff --git a/src/main/java/org/opensearch/security/filter/SecurityFilter.java b/src/main/java/org/opensearch/security/filter/SecurityFilter.java index 38675d97c5..e540d48512 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityFilter.java @@ -71,7 +71,7 @@ import org.opensearch.core.common.logging.LoggerMessageFormat; import org.opensearch.index.reindex.DeleteByQueryRequest; import org.opensearch.index.reindex.UpdateByQueryRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.action.whoami.WhoAmIAction; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.auditlog.AuditLog.Origin; diff --git a/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java b/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java index 80bba54ea2..1009ff5e96 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java @@ -45,7 +45,7 @@ import org.opensearch.rest.RestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.auditlog.AuditLog.Origin; import org.opensearch.security.auth.BackendRegistry; diff --git a/src/main/java/org/opensearch/security/http/HTTPBasicAuthenticator.java b/src/main/java/org/opensearch/security/http/HTTPBasicAuthenticator.java index 88ac128828..4be83bc2e2 100644 --- a/src/main/java/org/opensearch/security/http/HTTPBasicAuthenticator.java +++ b/src/main/java/org/opensearch/security/http/HTTPBasicAuthenticator.java @@ -36,7 +36,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.support.HTTPHelper; import org.opensearch.security.user.AuthCredentials; diff --git a/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java b/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java index a907bdbe13..ea8985ee69 100644 --- a/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java +++ b/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java @@ -82,7 +82,7 @@ import org.opensearch.cluster.metadata.IndexAbstraction; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.reindex.ReindexRequest; import org.opensearch.security.OpenSearchSecurityPlugin; diff --git a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java index ed5b965be2..96221985fd 100644 --- a/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/DashboardsInfoAction.java @@ -42,7 +42,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; diff --git a/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java b/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java index 379a3b6b13..c582c9f51b 100644 --- a/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java +++ b/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java @@ -24,7 +24,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.rest.action.RestActions.NodesResponseRestListener; import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; diff --git a/src/main/java/org/opensearch/security/rest/SecurityHealthAction.java b/src/main/java/org/opensearch/security/rest/SecurityHealthAction.java index 0631e3044a..579d42e7a6 100644 --- a/src/main/java/org/opensearch/security/rest/SecurityHealthAction.java +++ b/src/main/java/org/opensearch/security/rest/SecurityHealthAction.java @@ -39,7 +39,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.BackendRegistry; import static org.opensearch.rest.RestRequest.Method.GET; diff --git a/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java b/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java index 6159f555c7..9ed1427122 100644 --- a/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/SecurityInfoAction.java @@ -48,7 +48,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.Base64Helper; import org.opensearch.security.support.ConfigConstants; diff --git a/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java b/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java index 8f20a0b9a2..f57d26e7b6 100644 --- a/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java +++ b/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java @@ -28,7 +28,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.security.ssl.util.SSLRequestHelper; diff --git a/src/main/java/org/opensearch/security/rest/TenantInfoAction.java b/src/main/java/org/opensearch/security/rest/TenantInfoAction.java index c6f09efd98..f3afc0f006 100644 --- a/src/main/java/org/opensearch/security/rest/TenantInfoAction.java +++ b/src/main/java/org/opensearch/security/rest/TenantInfoAction.java @@ -47,7 +47,7 @@ import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.configuration.ConfigurationRepository; import org.opensearch.security.privileges.PrivilegesEvaluator; diff --git a/src/main/java/org/opensearch/security/securityconf/impl/AllowlistingSettings.java b/src/main/java/org/opensearch/security/securityconf/impl/AllowlistingSettings.java index e2c86009d1..98fc7a266a 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/AllowlistingSettings.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/AllowlistingSettings.java @@ -20,7 +20,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; public class AllowlistingSettings { private boolean enabled; diff --git a/src/main/java/org/opensearch/security/securityconf/impl/WhitelistingSettings.java b/src/main/java/org/opensearch/security/securityconf/impl/WhitelistingSettings.java index 57405b24fe..4462bae90f 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/WhitelistingSettings.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/WhitelistingSettings.java @@ -20,7 +20,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; public class WhitelistingSettings extends AllowlistingSettings { private boolean enabled; diff --git a/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java b/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java index 12c3b92e41..a23f6b9660 100644 --- a/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java +++ b/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java @@ -44,7 +44,7 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Booleans; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.network.NetworkModule; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; diff --git a/src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java b/src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java new file mode 100644 index 0000000000..1581deba20 --- /dev/null +++ b/src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java @@ -0,0 +1,130 @@ +/* + * 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. + * + */ +package org.opensearch.security.ssl; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.common.settings.SecureSetting; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; + +import static org.opensearch.security.ssl.util.SSLConfigConstants.DEFAULT_STORE_PASSWORD; + +/** + * Container for secured settings (passwords for certs, keystores) and the now deprecated original settings + */ +public final class SecureSSLSettings { + private static final Logger LOG = LogManager.getLogger(SecureSSLSettings.class); + + private static final String SECURE_SUFFIX = "_secure"; + private static final String PREFIX = "plugins.security.ssl"; + private static final String HTTP_PREFIX = PREFIX + ".http"; + private static final String TRANSPORT_PREFIX = PREFIX + ".transport"; + + public enum SSLSetting { + // http settings + SECURITY_SSL_HTTP_PEMKEY_PASSWORD(HTTP_PREFIX + ".pemkey_password"), + SECURITY_SSL_HTTP_KEYSTORE_PASSWORD(HTTP_PREFIX + ".keystore_password"), + SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD(HTTP_PREFIX + ".keystore_keypassword"), + SECURITY_SSL_HTTP_TRUSTSTORE_PASSWORD(HTTP_PREFIX + ".truststore_password", DEFAULT_STORE_PASSWORD), + + // transport settings + SECURITY_SSL_TRANSPORT_PEMKEY_PASSWORD(TRANSPORT_PREFIX + ".pemkey_password"), + SECURITY_SSL_TRANSPORT_SERVER_PEMKEY_PASSWORD(TRANSPORT_PREFIX + ".server.pemkey_password"), + SECURITY_SSL_TRANSPORT_CLIENT_PEMKEY_PASSWORD(TRANSPORT_PREFIX + ".client.pemkey_password"), + SECURITY_SSL_TRANSPORT_KEYSTORE_PASSWORD(TRANSPORT_PREFIX + ".keystore_password"), + SECURITY_SSL_TRANSPORT_KEYSTORE_KEYPASSWORD(TRANSPORT_PREFIX + ".keystore_keypassword"), + SECURITY_SSL_TRANSPORT_SERVER_KEYSTORE_KEYPASSWORD(TRANSPORT_PREFIX + ".server.keystore_keypassword"), + SECURITY_SSL_TRANSPORT_CLIENT_KEYSTORE_KEYPASSWORD(TRANSPORT_PREFIX + ".client.keystore_keypassword"), + SECURITY_SSL_TRANSPORT_TRUSTSTORE_PASSWORD(TRANSPORT_PREFIX + ".truststore_password", DEFAULT_STORE_PASSWORD); + + SSLSetting(String insecurePropertyName) { + this(insecurePropertyName, null); + } + + SSLSetting(String insecurePropertyName, String defaultValue) { + this.insecurePropertyName = insecurePropertyName; + this.propertyName = String.format("%s%s", this.insecurePropertyName, SECURE_SUFFIX); + this.defaultValue = defaultValue; + } + + public final String insecurePropertyName; + + public final String propertyName; + + public final String defaultValue; + + public Setting asSetting() { + return SecureSetting.secureString(this.propertyName, new InsecureFallbackStringSetting(this.insecurePropertyName)); + } + + public Setting asInsecureSetting() { + return new InsecureFallbackStringSetting(this.insecurePropertyName); + } + + public String getSetting(Settings settings) { + return this.getSetting(settings, this.defaultValue); + } + + public String getSetting(Settings settings, String defaultValue) { + return Optional.of(this.asSetting().get(settings)) + .filter(ss -> ss.length() > 0) + .map(SecureString::toString) + .orElse(defaultValue); + } + } + + private SecureSSLSettings() {} + + public static List> getSecureSettings() { + return Arrays.stream(SSLSetting.values()) + .flatMap(setting -> Stream.of(setting.asSetting(), setting.asInsecureSetting())) + .collect(Collectors.toList()); + } + + /** + * Alternative to InsecureStringSetting, which doesn't raise an exception if allow_insecure_settings is false, but + * instead log.WARNs the violation. This is to appease a potential cyclic dependency between commons-utils + */ + private static class InsecureFallbackStringSetting extends Setting { + private final String name; + + private InsecureFallbackStringSetting(String name) { + super(name, "", s -> new SecureString(s.toCharArray()), Property.Deprecated, Property.Filtered, Property.NodeScope); + this.name = name; + } + + public SecureString get(Settings settings) { + if (this.exists(settings)) { + LOG.warn( + "Setting [{}] has a secure counterpart [{}{}] which should be used instead - allowing for legacy SSL setups", + this.name, + this.name, + SECURE_SUFFIX + ); + } + + return super.get(settings); + } + } +} diff --git a/src/main/java/org/opensearch/security/ssl/http/netty/ValidatingDispatcher.java b/src/main/java/org/opensearch/security/ssl/http/netty/ValidatingDispatcher.java index d9c901cabc..e053da7787 100644 --- a/src/main/java/org/opensearch/security/ssl/http/netty/ValidatingDispatcher.java +++ b/src/main/java/org/opensearch/security/ssl/http/netty/ValidatingDispatcher.java @@ -32,7 +32,7 @@ import org.opensearch.http.HttpServerTransport.Dispatcher; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.ssl.SslExceptionHandler; import org.opensearch.security.ssl.util.ExceptionUtils; import org.opensearch.security.ssl.util.SSLRequestHelper; diff --git a/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLInfoAction.java b/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLInfoAction.java index d9d35d0289..54d109f497 100644 --- a/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLInfoAction.java +++ b/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLInfoAction.java @@ -38,7 +38,7 @@ import org.opensearch.rest.RestController; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.ssl.SecurityKeyStore; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.security.ssl.util.SSLRequestHelper; diff --git a/src/main/java/org/opensearch/security/ssl/transport/SecuritySSLNettyTransport.java b/src/main/java/org/opensearch/security/ssl/transport/SecuritySSLNettyTransport.java index cbd130abe2..3c3d23022a 100644 --- a/src/main/java/org/opensearch/security/ssl/transport/SecuritySSLNettyTransport.java +++ b/src/main/java/org/opensearch/security/ssl/transport/SecuritySSLNettyTransport.java @@ -49,7 +49,7 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.Version; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.PageCacheRecycler; diff --git a/src/main/java/org/opensearch/security/support/ConfigHelper.java b/src/main/java/org/opensearch/security/support/ConfigHelper.java index 4b53d4fc62..92bf069028 100644 --- a/src/main/java/org/opensearch/security/support/ConfigHelper.java +++ b/src/main/java/org/opensearch/security/support/ConfigHelper.java @@ -42,7 +42,7 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.WriteRequest.RefreshPolicy; import org.opensearch.client.Client; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.MediaType; diff --git a/src/main/java/org/opensearch/security/support/ModuleInfo.java b/src/main/java/org/opensearch/security/support/ModuleInfo.java index b511be14e2..a225aee3be 100644 --- a/src/main/java/org/opensearch/security/support/ModuleInfo.java +++ b/src/main/java/org/opensearch/security/support/ModuleInfo.java @@ -26,15 +26,15 @@ package org.opensearch.security.support; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; + import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.Map; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; - public class ModuleInfo implements Serializable, Writeable { private static final long serialVersionUID = -1077651823194285138L; diff --git a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java index e309ed1d6c..3cf1428d05 100644 --- a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java +++ b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java @@ -104,7 +104,7 @@ import org.opensearch.client.transport.NoNodeAvailableException; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; @@ -116,7 +116,7 @@ import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexNotFoundException; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.NonValidatingObjectMapper; import org.opensearch.security.auditlog.config.AuditConfig; diff --git a/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java b/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java index ae1be44159..b3e942a1f5 100644 --- a/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java +++ b/src/main/java/org/opensearch/security/transport/SecurityInterceptor.java @@ -45,7 +45,7 @@ import org.opensearch.action.search.SearchRequest; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; diff --git a/src/main/java/org/opensearch/security/user/User.java b/src/main/java/org/opensearch/security/user/User.java index 43ca5d0e57..2642b368d7 100644 --- a/src/main/java/org/opensearch/security/user/User.java +++ b/src/main/java/org/opensearch/security/user/User.java @@ -38,9 +38,9 @@ import com.google.common.collect.Lists; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; /** * A authenticated user and attributes associated to them (like roles, tenant, custom attributes) diff --git a/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java b/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java index b8ce0da6d2..eec2ff76b7 100644 --- a/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java @@ -41,8 +41,8 @@ import org.junit.Test; import org.opensaml.saml.saml2.core.NameIDType; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; @@ -51,7 +51,7 @@ import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; import org.opensearch.rest.RestResponse; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.test.helper.file.FileHelper; import org.opensearch.security.user.AuthCredentials; diff --git a/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java b/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java index eb4ebde951..ec39e11345 100644 --- a/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java +++ b/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java @@ -33,7 +33,7 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; diff --git a/src/test/java/org/opensearch/security/RolesValidationIntegTest.java b/src/test/java/org/opensearch/security/RolesValidationIntegTest.java index 5e0360b16f..0aa24b1f59 100644 --- a/src/test/java/org/opensearch/security/RolesValidationIntegTest.java +++ b/src/test/java/org/opensearch/security/RolesValidationIntegTest.java @@ -27,7 +27,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; diff --git a/src/test/java/org/opensearch/security/TransportUserInjectorIntegTest.java b/src/test/java/org/opensearch/security/TransportUserInjectorIntegTest.java index d3812e4f37..4bfb0a675c 100644 --- a/src/test/java/org/opensearch/security/TransportUserInjectorIntegTest.java +++ b/src/test/java/org/opensearch/security/TransportUserInjectorIntegTest.java @@ -25,7 +25,7 @@ import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; diff --git a/src/test/java/org/opensearch/security/auditlog/helper/MockRestRequest.java b/src/test/java/org/opensearch/security/auditlog/helper/MockRestRequest.java index d2b15750ff..458e240596 100644 --- a/src/test/java/org/opensearch/security/auditlog/helper/MockRestRequest.java +++ b/src/test/java/org/opensearch/security/auditlog/helper/MockRestRequest.java @@ -13,7 +13,7 @@ import java.util.Collections; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.rest.RestRequest; diff --git a/src/test/java/org/opensearch/security/auditlog/impl/AuditMessageTest.java b/src/test/java/org/opensearch/security/auditlog/impl/AuditMessageTest.java index 6bfaf32816..f53872bb3a 100644 --- a/src/test/java/org/opensearch/security/auditlog/impl/AuditMessageTest.java +++ b/src/test/java/org/opensearch/security/auditlog/impl/AuditMessageTest.java @@ -24,7 +24,7 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.xcontent.XContentType; import org.opensearch.security.auditlog.AuditLog; diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/CCReplicationTest.java b/src/test/java/org/opensearch/security/dlic/dlsfls/CCReplicationTest.java index 8996baa7df..3183172ed7 100644 --- a/src/test/java/org/opensearch/security/dlic/dlsfls/CCReplicationTest.java +++ b/src/test/java/org/opensearch/security/dlic/dlsfls/CCReplicationTest.java @@ -43,9 +43,9 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.NamedXContentRegistry; @@ -56,7 +56,7 @@ import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.script.ScriptService; import org.opensearch.security.OpenSearchSecurityPlugin; import org.opensearch.security.support.ConfigConstants; diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java index 6950432501..4c7c87fe6a 100644 --- a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java +++ b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java @@ -164,7 +164,7 @@ public void testCcs() throws Exception { Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); Assert.assertFalse(ccs.getBody().contains("xxx1")); - Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); + Assert.assertEquals(ccs.getHeaders().toString(), 3, ccs.getHeaders().size()); } @Test @@ -251,7 +251,7 @@ public void testCcsDifferentConfig() throws Exception { Assert.assertTrue(ccs.getBody().contains("__fn__crl2")); Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); - Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); + Assert.assertEquals(ccs.getHeaders().toString(), 3, ccs.getHeaders().size()); } @Test @@ -382,6 +382,6 @@ public void testCcsDifferentConfigBoth() throws Exception { Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); Assert.assertTrue(ccs.getBody().contains("someoneelse")); - Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); + Assert.assertEquals(ccs.getHeaders().toString(), 3, ccs.getHeaders().size()); } } diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java index d7ce522f51..cd278f2f2c 100644 --- a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java +++ b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java @@ -111,7 +111,7 @@ public void testDls() throws Exception { ); Assert.assertTrue(res.getBody().contains("\"value\" : 1,\n \"relation")); Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); - Assert.assertEquals(res.getHeaders().toString(), 1, res.getHeaders().size()); + Assert.assertEquals(res.getHeaders().toString(), 3, res.getHeaders().size()); Assert.assertEquals( HttpStatus.SC_OK, diff --git a/src/test/java/org/opensearch/security/http/proxy/HTTPExtendedProxyAuthenticatorTest.java b/src/test/java/org/opensearch/security/http/proxy/HTTPExtendedProxyAuthenticatorTest.java index 47d779cd12..085ea61668 100644 --- a/src/test/java/org/opensearch/security/http/proxy/HTTPExtendedProxyAuthenticatorTest.java +++ b/src/test/java/org/opensearch/security/http/proxy/HTTPExtendedProxyAuthenticatorTest.java @@ -37,7 +37,7 @@ import org.opensearch.OpenSearchSecurityException; import org.opensearch.action.ActionListener; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.xcontent.NamedXContentRegistry; @@ -46,7 +46,7 @@ import org.opensearch.http.HttpResponse; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.AuthCredentials; diff --git a/src/test/java/org/opensearch/security/protected_indices/ProtectedIndicesTests.java b/src/test/java/org/opensearch/security/protected_indices/ProtectedIndicesTests.java index dded42e8a4..8398b4b30a 100644 --- a/src/test/java/org/opensearch/security/protected_indices/ProtectedIndicesTests.java +++ b/src/test/java/org/opensearch/security/protected_indices/ProtectedIndicesTests.java @@ -48,7 +48,7 @@ import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; 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 23bfaefa7d..764470a5eb 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndicesTests.java @@ -32,7 +32,7 @@ import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; diff --git a/src/test/java/org/opensearch/security/test/helper/file/FileHelper.java b/src/test/java/org/opensearch/security/test/helper/file/FileHelper.java index acb4a7d217..2bf66a265b 100644 --- a/src/test/java/org/opensearch/security/test/helper/file/FileHelper.java +++ b/src/test/java/org/opensearch/security/test/helper/file/FileHelper.java @@ -46,7 +46,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.xcontent.NamedXContentRegistry; diff --git a/src/test/java/org/opensearch/security/util/FakeRestRequest.java b/src/test/java/org/opensearch/security/util/FakeRestRequest.java index 05e69ef614..ceb86d6579 100644 --- a/src/test/java/org/opensearch/security/util/FakeRestRequest.java +++ b/src/test/java/org/opensearch/security/util/FakeRestRequest.java @@ -16,7 +16,7 @@ import java.util.List; import java.util.Map; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.rest.RestRequest; public class FakeRestRequest extends RestRequest { From 28982a66f41bfe5f0ab32c4a551cf8994255a8d6 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 19 Jul 2023 15:57:33 -0400 Subject: [PATCH 2/6] Fixes Index import Signed-off-by: Darshit Chanpura --- .../opensearch/security/configuration/ClusterInfoHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java b/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java index 2cbc876434..78448fb732 100644 --- a/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java +++ b/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java @@ -39,7 +39,7 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; public class ClusterInfoHolder implements ClusterStateListener { From 5fdad239cec063714f23ac8405a4b6bd7d619639 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 19 Jul 2023 17:01:34 -0400 Subject: [PATCH 3/6] Addresses dlicDlsFlsTest failures Signed-off-by: Darshit Chanpura --- .../security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java | 6 +++--- .../java/org/opensearch/security/dlic/dlsfls/DlsTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java index 4c7c87fe6a..6950432501 100644 --- a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java +++ b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java @@ -164,7 +164,7 @@ public void testCcs() throws Exception { Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); Assert.assertFalse(ccs.getBody().contains("xxx1")); - Assert.assertEquals(ccs.getHeaders().toString(), 3, ccs.getHeaders().size()); + Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); } @Test @@ -251,7 +251,7 @@ public void testCcsDifferentConfig() throws Exception { Assert.assertTrue(ccs.getBody().contains("__fn__crl2")); Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); - Assert.assertEquals(ccs.getHeaders().toString(), 3, ccs.getHeaders().size()); + Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); } @Test @@ -382,6 +382,6 @@ public void testCcsDifferentConfigBoth() throws Exception { Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); Assert.assertTrue(ccs.getBody().contains("someoneelse")); - Assert.assertEquals(ccs.getHeaders().toString(), 3, ccs.getHeaders().size()); + Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); } } diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java index cd278f2f2c..d7ce522f51 100644 --- a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java +++ b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java @@ -111,7 +111,7 @@ public void testDls() throws Exception { ); Assert.assertTrue(res.getBody().contains("\"value\" : 1,\n \"relation")); Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); - Assert.assertEquals(res.getHeaders().toString(), 3, res.getHeaders().size()); + Assert.assertEquals(res.getHeaders().toString(), 1, res.getHeaders().size()); Assert.assertEquals( HttpStatus.SC_OK, From 3fd19c8a07d39e9179eac046b11512252a3a2ba1 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 20 Jul 2023 10:09:53 -0400 Subject: [PATCH 4/6] Addresses PR feedback Signed-off-by: Darshit Chanpura --- scripts/integtest.sh | 4 +- .../security/ssl/SecureSSLSettings.java | 130 ------------------ 2 files changed, 2 insertions(+), 132 deletions(-) delete mode 100644 src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java diff --git a/scripts/integtest.sh b/scripts/integtest.sh index 0401d00fa0..0983d160b4 100755 --- a/scripts/integtest.sh +++ b/scripts/integtest.sh @@ -20,7 +20,7 @@ function usage() { echo -e "-v OPENSEARCH_VERSION\t, no defaults" echo -e "-n SNAPSHOT\t, defaults to false" echo -e "-m CLUSTER_NAME\t, defaults to docker-cluster" - echo -e "-u COMMON_UTILS_VERSION\t, defaults to 3.0.0.0-SNAPSHOT" + echo -e "-u COMMON_UTILS_VERSION\t, defaults to 2.9.0.0-SNAPSHOT" echo "--------------------------------------------------------------------------" } @@ -101,7 +101,7 @@ then fi if [ -z "$COMMON_UTILS_VERSION" ] then - COMMON_UTILS_VERSION="3.0.0.0-SNAPSHOT" + COMMON_UTILS_VERSION="2.9x.0.0-SNAPSHOT" fi USERNAME=`echo $CREDENTIAL | awk -F ':' '{print $1}'` diff --git a/src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java b/src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java deleted file mode 100644 index 1581deba20..0000000000 --- a/src/main/java/org/opensearch/security/ssl/SecureSSLSettings.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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. - * - */ -package org.opensearch.security.ssl; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.common.settings.SecureSetting; -import org.opensearch.core.common.settings.SecureString; -import org.opensearch.common.settings.Setting; -import org.opensearch.common.settings.Settings; - -import static org.opensearch.security.ssl.util.SSLConfigConstants.DEFAULT_STORE_PASSWORD; - -/** - * Container for secured settings (passwords for certs, keystores) and the now deprecated original settings - */ -public final class SecureSSLSettings { - private static final Logger LOG = LogManager.getLogger(SecureSSLSettings.class); - - private static final String SECURE_SUFFIX = "_secure"; - private static final String PREFIX = "plugins.security.ssl"; - private static final String HTTP_PREFIX = PREFIX + ".http"; - private static final String TRANSPORT_PREFIX = PREFIX + ".transport"; - - public enum SSLSetting { - // http settings - SECURITY_SSL_HTTP_PEMKEY_PASSWORD(HTTP_PREFIX + ".pemkey_password"), - SECURITY_SSL_HTTP_KEYSTORE_PASSWORD(HTTP_PREFIX + ".keystore_password"), - SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD(HTTP_PREFIX + ".keystore_keypassword"), - SECURITY_SSL_HTTP_TRUSTSTORE_PASSWORD(HTTP_PREFIX + ".truststore_password", DEFAULT_STORE_PASSWORD), - - // transport settings - SECURITY_SSL_TRANSPORT_PEMKEY_PASSWORD(TRANSPORT_PREFIX + ".pemkey_password"), - SECURITY_SSL_TRANSPORT_SERVER_PEMKEY_PASSWORD(TRANSPORT_PREFIX + ".server.pemkey_password"), - SECURITY_SSL_TRANSPORT_CLIENT_PEMKEY_PASSWORD(TRANSPORT_PREFIX + ".client.pemkey_password"), - SECURITY_SSL_TRANSPORT_KEYSTORE_PASSWORD(TRANSPORT_PREFIX + ".keystore_password"), - SECURITY_SSL_TRANSPORT_KEYSTORE_KEYPASSWORD(TRANSPORT_PREFIX + ".keystore_keypassword"), - SECURITY_SSL_TRANSPORT_SERVER_KEYSTORE_KEYPASSWORD(TRANSPORT_PREFIX + ".server.keystore_keypassword"), - SECURITY_SSL_TRANSPORT_CLIENT_KEYSTORE_KEYPASSWORD(TRANSPORT_PREFIX + ".client.keystore_keypassword"), - SECURITY_SSL_TRANSPORT_TRUSTSTORE_PASSWORD(TRANSPORT_PREFIX + ".truststore_password", DEFAULT_STORE_PASSWORD); - - SSLSetting(String insecurePropertyName) { - this(insecurePropertyName, null); - } - - SSLSetting(String insecurePropertyName, String defaultValue) { - this.insecurePropertyName = insecurePropertyName; - this.propertyName = String.format("%s%s", this.insecurePropertyName, SECURE_SUFFIX); - this.defaultValue = defaultValue; - } - - public final String insecurePropertyName; - - public final String propertyName; - - public final String defaultValue; - - public Setting asSetting() { - return SecureSetting.secureString(this.propertyName, new InsecureFallbackStringSetting(this.insecurePropertyName)); - } - - public Setting asInsecureSetting() { - return new InsecureFallbackStringSetting(this.insecurePropertyName); - } - - public String getSetting(Settings settings) { - return this.getSetting(settings, this.defaultValue); - } - - public String getSetting(Settings settings, String defaultValue) { - return Optional.of(this.asSetting().get(settings)) - .filter(ss -> ss.length() > 0) - .map(SecureString::toString) - .orElse(defaultValue); - } - } - - private SecureSSLSettings() {} - - public static List> getSecureSettings() { - return Arrays.stream(SSLSetting.values()) - .flatMap(setting -> Stream.of(setting.asSetting(), setting.asInsecureSetting())) - .collect(Collectors.toList()); - } - - /** - * Alternative to InsecureStringSetting, which doesn't raise an exception if allow_insecure_settings is false, but - * instead log.WARNs the violation. This is to appease a potential cyclic dependency between commons-utils - */ - private static class InsecureFallbackStringSetting extends Setting { - private final String name; - - private InsecureFallbackStringSetting(String name) { - super(name, "", s -> new SecureString(s.toCharArray()), Property.Deprecated, Property.Filtered, Property.NodeScope); - this.name = name; - } - - public SecureString get(Settings settings) { - if (this.exists(settings)) { - LOG.warn( - "Setting [{}] has a secure counterpart [{}{}] which should be used instead - allowing for legacy SSL setups", - this.name, - this.name, - SECURE_SUFFIX - ); - } - - return super.get(settings); - } - } -} From aea96d6051bde3098916139d1957d3c49526b326 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 20 Jul 2023 10:49:11 -0400 Subject: [PATCH 5/6] Removed Integration Tests Signed-off-by: Darshit Chanpura --- scripts/integtest.sh | 2 +- .../security/CrossClusterSearchTests.java | 455 --- .../security/DoNotFailOnForbiddenTests.java | 411 --- .../security/PointInTimeOperationTest.java | 426 --- .../security/SearchOperationTest.java | 2719 ----------------- .../security/http/JwtAuthenticationTests.java | 270 -- .../http/LdapTlsAuthenticationTest.java | 414 --- .../test/framework/TestSecurityConfig.java | 717 ----- .../matcher/OpenSearchExceptionMatchers.java | 37 - .../OpenSearchStatusExceptionMatcher.java | 52 - .../matcher/SearchResponseMatchers.java | 87 - .../SearchResponseWithStatusCodeMatcher.java | 39 - .../matcher/SuccessBulkResponseMatcher.java | 47 - ...ssfulClearIndicesCacheResponseMatcher.java | 37 - .../SuccessfulCreatePitResponseMatcher.java | 37 - .../SuccessfulDeletePitResponseMatcher.java | 42 - .../SuccessfulDeleteResponseMatcher.java | 39 - .../SuccessfulSearchResponseMatcher.java | 37 - .../SuccessfulUpdateResponseMatcher.java | 39 - 19 files changed, 1 insertion(+), 5906 deletions(-) delete mode 100644 src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java delete mode 100644 src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java delete mode 100644 src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java delete mode 100644 src/integrationTest/java/org/opensearch/security/SearchOperationTest.java delete mode 100644 src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java delete mode 100644 src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java delete mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java diff --git a/scripts/integtest.sh b/scripts/integtest.sh index 0983d160b4..6d4c1dbedd 100755 --- a/scripts/integtest.sh +++ b/scripts/integtest.sh @@ -101,7 +101,7 @@ then fi if [ -z "$COMMON_UTILS_VERSION" ] then - COMMON_UTILS_VERSION="2.9x.0.0-SNAPSHOT" + COMMON_UTILS_VERSION="2.9.0.0-SNAPSHOT" fi USERNAME=`echo $CREDENTIAL | awk -F ':' '{print $1}'` diff --git a/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java b/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java deleted file mode 100644 index 86d27efa87..0000000000 --- a/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java +++ /dev/null @@ -1,455 +0,0 @@ -/* -* 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; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import org.apache.commons.lang3.tuple.Pair; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.opensearch.action.search.SearchRequest; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.client.Client; -import org.opensearch.client.RestHighLevelClient; -import org.opensearch.test.framework.TestSecurityConfig.Role; -import org.opensearch.test.framework.TestSecurityConfig.User; -import org.opensearch.test.framework.certificate.TestCertificates; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; -import org.opensearch.test.framework.cluster.SearchRequestFactory; -import org.opensearch.test.framework.cluster.TestRestClient; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.opensearch.client.RequestOptions.DEFAULT; -import static org.opensearch.core.rest.RestStatus.FORBIDDEN; -import static org.opensearch.security.Song.ARTIST_FIRST; -import static org.opensearch.security.Song.FIELD_ARTIST; -import static org.opensearch.security.Song.FIELD_GENRE; -import static org.opensearch.security.Song.FIELD_LYRICS; -import static org.opensearch.security.Song.FIELD_STARS; -import static org.opensearch.security.Song.FIELD_TITLE; -import static org.opensearch.security.Song.GENRE_JAZZ; -import static org.opensearch.security.Song.GENRE_ROCK; -import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; -import static org.opensearch.security.Song.SONGS; -import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; -import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitDoesNotContainField; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentsInAnyOrder; - -/** -* This is a parameterized test so that one test class is used to test security plugin behaviour when ccsMinimizeRoundtrips -* option is enabled or disabled. Method {@link #parameters()} is a source of parameters values. -*/ -@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class CrossClusterSearchTests { - - private static final String SONG_INDEX_NAME = "song_lyrics"; - - private static final String PROHIBITED_SONG_INDEX_NAME = "prohibited_song_lyrics"; - - public static final String REMOTE_CLUSTER_NAME = "ccsRemote"; - public static final String REMOTE_SONG_INDEX = REMOTE_CLUSTER_NAME + ":" + SONG_INDEX_NAME; - - public static final String SONG_ID_1R = "remote-00001"; - public static final String SONG_ID_2L = "local-00002"; - public static final String SONG_ID_3R = "remote-00003"; - public static final String SONG_ID_4L = "local-00004"; - public static final String SONG_ID_5R = "remote-00005"; - public static final String SONG_ID_6R = "remote-00006"; - - private static final Role LIMITED_ROLE = new Role("limited_role").indexPermissions( - "indices:data/read/search", - "indices:admin/shards/search_shards" - ).on(SONG_INDEX_NAME, "user-${user.name}-${attr.internal.type}"); - - private static final Role DLS_ROLE_ROCK = new Role("dls_role_rock").indexPermissions( - "indices:data/read/search", - "indices:data/read/get", - "indices:admin/shards/search_shards" - ).dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_GENRE, GENRE_ROCK)).on(SONG_INDEX_NAME); - - private static final Role DLS_ROLE_JAZZ = new Role("dls_role_jazz").indexPermissions( - "indices:data/read/search", - "indices:data/read/get", - "indices:admin/shards/search_shards" - ).dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_GENRE, GENRE_JAZZ)).on(SONG_INDEX_NAME); - - private static final Role FLS_EXCLUDE_LYRICS_ROLE = new Role("fls_exclude_lyrics_role").indexPermissions( - "indices:data/read/search", - "indices:data/read/get", - "indices:admin/shards/search_shards" - ).fls("~" + FIELD_LYRICS).on(SONG_INDEX_NAME); - - private static final Role FLS_INCLUDE_TITLE_ROLE = new Role("fls_include_title_role").indexPermissions( - "indices:data/read/search", - "indices:data/read/get", - "indices:admin/shards/search_shards" - ).fls(FIELD_TITLE).on(SONG_INDEX_NAME); - - public static final String TYPE_ATTRIBUTE = "type"; - - private static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS).attr(TYPE_ATTRIBUTE, "administrative"); - private static final User LIMITED_USER = new User("limited_user").attr(TYPE_ATTRIBUTE, "personal"); - - private static final User FLS_INCLUDE_TITLE_USER = new User("fls_include_title_user"); - - private static final User FLS_EXCLUDE_LYRICS_USER = new User("fls_exclude_lyrics_user"); - - private static final User DLS_USER_ROCK = new User("dls-user-rock"); - - private static final User DLS_USER_JAZZ = new User("dls-user-jazz"); - - public static final String LIMITED_USER_INDEX_NAME = "user-" + LIMITED_USER.getName() + "-" + LIMITED_USER.getAttribute(TYPE_ATTRIBUTE); - public static final String ADMIN_USER_INDEX_NAME = "user-" + ADMIN_USER.getName() + "-" + ADMIN_USER.getAttribute(TYPE_ATTRIBUTE); - - private static final TestCertificates TEST_CERTIFICATES = new TestCertificates(); - - private final boolean ccsMinimizeRoundtrips; - - public static final String PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED = "plugins.security.restapi.roles_enabled"; - @ClassRule - public static final LocalCluster remoteCluster = new LocalCluster.Builder().certificates(TEST_CERTIFICATES) - .clusterManager(ClusterManager.SINGLENODE) - .anonymousAuth(false) - .clusterName(REMOTE_CLUSTER_NAME) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .roles(LIMITED_ROLE, DLS_ROLE_ROCK, DLS_ROLE_JAZZ, FLS_EXCLUDE_LYRICS_ROLE, FLS_INCLUDE_TITLE_ROLE) - .users(ADMIN_USER) - .build(); - - @ClassRule - public static final LocalCluster cluster = new LocalCluster.Builder().certificates(TEST_CERTIFICATES) - .clusterManager(ClusterManager.SINGLE_REMOTE_CLIENT) - .anonymousAuth(false) - .clusterName("ccsLocal") - .nodeSettings(Map.of(PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_" + ADMIN_USER.getName() + "__" + ALL_ACCESS.getName()))) - .remote(REMOTE_CLUSTER_NAME, remoteCluster) - .roles(LIMITED_ROLE, DLS_ROLE_ROCK, DLS_ROLE_JAZZ, FLS_EXCLUDE_LYRICS_ROLE, FLS_INCLUDE_TITLE_ROLE) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .users(ADMIN_USER, LIMITED_USER, DLS_USER_ROCK, DLS_USER_JAZZ, FLS_INCLUDE_TITLE_USER, FLS_EXCLUDE_LYRICS_USER) - .build(); - - @ParametersFactory(shuffle = false) - public static Iterable parameters() { - return List.of(new Object[] { true }, new Object[] { false }); - } - - public CrossClusterSearchTests(Boolean ccsMinimizeRoundtrips) { - this.ccsMinimizeRoundtrips = ccsMinimizeRoundtrips; - } - - @BeforeClass - public static void createTestData() { - try (Client client = remoteCluster.getInternalNodeClient()) { - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_6R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[5].asMap()).get(); - client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_3R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); - client.prepareIndex(LIMITED_USER_INDEX_NAME).setId(SONG_ID_5R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[4].asMap()).get(); - } - try (Client client = cluster.getInternalNodeClient()) { - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_2L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2].asMap()).get(); - client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_4L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[3].asMap()).get(); - } - try (TestRestClient client = cluster.getRestClient(ADMIN_USER)) { - client.assignRoleToUser(LIMITED_USER.getName(), LIMITED_ROLE.getName()).assertStatusCode(200); - client.assignRoleToUser(DLS_USER_ROCK.getName(), DLS_ROLE_ROCK.getName()).assertStatusCode(200); - client.assignRoleToUser(DLS_USER_JAZZ.getName(), DLS_ROLE_JAZZ.getName()).assertStatusCode(200); - client.assignRoleToUser(FLS_INCLUDE_TITLE_USER.getName(), FLS_INCLUDE_TITLE_ROLE.getName()).assertStatusCode(200); - client.assignRoleToUser(FLS_EXCLUDE_LYRICS_USER.getName(), FLS_EXCLUDE_LYRICS_ROLE.getName()).assertStatusCode(200); - } - } - - @Test - public void shouldFindDocumentOnRemoteCluster_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_SONG_INDEX); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(2)); - assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1R)); - assertThat(response, searchHitsContainDocumentWithId(1, SONG_INDEX_NAME, SONG_ID_6R)); - } - } - - private SearchRequest searchAll(String indexName) { - SearchRequest searchRequest = SearchRequestFactory.searchAll(indexName); - searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); - return searchRequest; - } - - @Test - public void shouldFindDocumentOnRemoteCluster_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":" + PROHIBITED_SONG_INDEX_NAME); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchForDocumentOnRemoteClustersWhenStarIsUsedAsClusterName_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll("*" + ":" + SONG_INDEX_NAME); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - // only remote documents are found - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(2)); - assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1R)); - assertThat(response, searchHitsContainDocumentWithId(1, SONG_INDEX_NAME, SONG_ID_6R)); - } - } - - @Test - public void shouldSearchForDocumentOnRemoteClustersWhenStarIsUsedAsClusterName_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll("*" + ":" + PROHIBITED_SONG_INDEX_NAME); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchForDocumentOnBothClustersWhenIndexOnBothClusterArePointedOut_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = SearchRequestFactory.searchAll(REMOTE_SONG_INDEX, SONG_INDEX_NAME); - searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(3)); - assertThat( - response, - searchHitsContainDocumentsInAnyOrder( - Pair.of(SONG_INDEX_NAME, SONG_ID_1R), - Pair.of(SONG_INDEX_NAME, SONG_ID_2L), - Pair.of(SONG_INDEX_NAME, SONG_ID_6R) - ) - ); - } - } - - @Test - public void shouldSearchForDocumentOnBothClustersWhenIndexOnBothClusterArePointedOut_negativeLackOfLocalAccess() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - var searchRequest = SearchRequestFactory.searchAll(REMOTE_SONG_INDEX, PROHIBITED_SONG_INDEX_NAME); - searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchForDocumentOnBothClustersWhenIndexOnBothClusterArePointedOut_negativeLackOfRemoteAccess() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - String remoteIndex = REMOTE_CLUSTER_NAME + ":" + PROHIBITED_SONG_INDEX_NAME; - SearchRequest searchRequest = SearchRequestFactory.searchAll(remoteIndex, SONG_INDEX_NAME); - searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchViaAllAliasOnRemoteCluster_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":_all"); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(4)); - assertThat( - response, - searchHitsContainDocumentsInAnyOrder( - Pair.of(SONG_INDEX_NAME, SONG_ID_1R), - Pair.of(SONG_INDEX_NAME, SONG_ID_6R), - Pair.of(PROHIBITED_SONG_INDEX_NAME, SONG_ID_3R), - Pair.of(LIMITED_USER_INDEX_NAME, SONG_ID_5R) - ) - ); - } - } - - @Test - public void shouldSearchViaAllAliasOnRemoteCluster_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":_all"); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchAllIndexOnRemoteClusterWhenStarIsUsedAsIndexName_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":*"); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(4)); - assertThat( - response, - searchHitsContainDocumentsInAnyOrder( - Pair.of(SONG_INDEX_NAME, SONG_ID_1R), - Pair.of(SONG_INDEX_NAME, SONG_ID_6R), - Pair.of(PROHIBITED_SONG_INDEX_NAME, SONG_ID_3R), - Pair.of(LIMITED_USER_INDEX_NAME, SONG_ID_5R) - ) - ); - } - } - - @Test - public void shouldSearchAllIndexOnRemoteClusterWhenStarIsUsedAsIndexName_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":*"); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldResolveUserNameExpressionInRoleIndexPattern_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":" + LIMITED_USER_INDEX_NAME); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, numberOfTotalHitsIsEqualTo(1)); - } - } - - @Test - public void shouldResolveUserNameExpressionInRoleIndexPattern_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":" + ADMIN_USER_INDEX_NAME); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchInIndexWithPrefix_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":song*"); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(2)); - assertThat( - response, - searchHitsContainDocumentsInAnyOrder(Pair.of(SONG_INDEX_NAME, SONG_ID_1R), Pair.of(SONG_INDEX_NAME, SONG_ID_6R)) - ); - } - } - - @Test - public void shouldSearchInIndexWithPrefix_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchAll(REMOTE_CLUSTER_NAME + ":prohibited*"); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldEvaluateDocumentLevelSecurityRulesOnRemoteClusterOnSearchRequest_caseRock() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DLS_USER_ROCK)) { - SearchRequest searchRequest = searchAll(REMOTE_SONG_INDEX); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - // searching for all documents, so is it important that result contain only one document with id SONG_ID_1 - // and document with SONG_ID_6 is excluded from result set by DLS - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(1)); - assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1R)); - } - } - - @Test - public void shouldEvaluateDocumentLevelSecurityRulesOnRemoteClusterOnSearchRequest_caseJazz() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DLS_USER_JAZZ)) { - SearchRequest searchRequest = searchAll(REMOTE_SONG_INDEX); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - // searching for all documents, so is it important that result contain only one document with id SONG_ID_6 - // and document with SONG_ID_1 is excluded from result set by DLS - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(1)); - assertThat(response, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_6R)); - } - } - - @Test - public void shouldHaveAccessOnlyToSpecificField() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(FLS_INCLUDE_TITLE_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(REMOTE_SONG_INDEX, QUERY_TITLE_MAGNUM_OPUS); - searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(1)); - // document should contain only title field - assertThat(response, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - assertThat(response, searchHitDoesNotContainField(0, FIELD_ARTIST)); - assertThat(response, searchHitDoesNotContainField(0, FIELD_LYRICS)); - assertThat(response, searchHitDoesNotContainField(0, FIELD_STARS)); - assertThat(response, searchHitDoesNotContainField(0, FIELD_GENRE)); - } - } - - @Test - public void shouldLackAccessToSpecificField() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(FLS_EXCLUDE_LYRICS_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(REMOTE_SONG_INDEX, QUERY_TITLE_MAGNUM_OPUS); - searchRequest.setCcsMinimizeRoundtrips(ccsMinimizeRoundtrips); - - SearchResponse response = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(1)); - // document should not contain lyrics field - assertThat(response, searchHitDoesNotContainField(0, FIELD_LYRICS)); - - assertThat(response, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - assertThat(response, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_FIRST)); - assertThat(response, searchHitContainsFieldWithValue(0, FIELD_STARS, 1)); - assertThat(response, searchHitContainsFieldWithValue(0, FIELD_GENRE, GENRE_ROCK)); - } - } -} diff --git a/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java b/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java deleted file mode 100644 index fedcf0bbcb..0000000000 --- a/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java +++ /dev/null @@ -1,411 +0,0 @@ -/* -* 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; - -import java.io.IOException; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import org.hamcrest.Matchers; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; -import org.opensearch.action.fieldcaps.FieldCapabilitiesRequest; -import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; -import org.opensearch.action.get.MultiGetItemResponse; -import org.opensearch.action.get.MultiGetRequest; -import org.opensearch.action.get.MultiGetResponse; -import org.opensearch.action.index.IndexRequest; -import org.opensearch.action.search.MultiSearchRequest; -import org.opensearch.action.search.MultiSearchResponse; -import org.opensearch.action.search.SearchRequest; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.action.search.SearchScrollRequest; -import org.opensearch.client.Client; -import org.opensearch.client.RestHighLevelClient; -import org.opensearch.test.framework.TestSecurityConfig; -import org.opensearch.test.framework.TestSecurityConfig.User; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.aMapWithSize; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; -import static org.hamcrest.Matchers.arrayWithSize; -import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.nullValue; -import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.opensearch.client.RequestOptions.DEFAULT; -import static org.opensearch.core.rest.RestStatus.FORBIDDEN; -import static org.opensearch.security.Song.FIELD_STARS; -import static org.opensearch.security.Song.FIELD_TITLE; -import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; -import static org.opensearch.security.Song.QUERY_TITLE_NEXT_SONG; -import static org.opensearch.security.Song.QUERY_TITLE_POISON; -import static org.opensearch.security.Song.SONGS; -import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; -import static org.opensearch.security.Song.TITLE_NEXT_SONG; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.averageAggregationRequest; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.getSearchScrollRequest; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.searchRequestWithScroll; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.statsAggregationRequest; -import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; -import static org.opensearch.test.framework.matcher.GetResponseMatchers.containDocument; -import static org.opensearch.test.framework.matcher.GetResponseMatchers.containOnlyDocumentId; -import static org.opensearch.test.framework.matcher.GetResponseMatchers.documentContainField; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containAggregationWithNameAndType; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containNotEmptyScrollingId; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfHitsInPageIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; - -@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class DoNotFailOnForbiddenTests { - - /** - * Songs accessible for {@link #LIMITED_USER} - */ - private static final String MARVELOUS_SONGS = "marvelous_songs"; - - /** - * Songs inaccessible for {@link #LIMITED_USER} - */ - private static final String HORRIBLE_SONGS = "horrible_songs"; - - private static final String BOTH_INDEX_PATTERN = "*songs"; - - private static final String ID_1 = "1"; - private static final String ID_2 = "2"; - private static final String ID_3 = "3"; - private static final String ID_4 = "4"; - - private static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS); - private static final User LIMITED_USER = new User("limited_user").roles( - new TestSecurityConfig.Role("limited-role").clusterPermissions( - "indices:data/read/mget", - "indices:data/read/msearch", - "indices:data/read/scroll" - ) - .indexPermissions( - "indices:data/read/search", - "indices:data/read/mget*", - "indices:data/read/field_caps", - "indices:data/read/field_caps*", - "indices:data/read/msearch", - "indices:data/read/scroll" - ) - .on(MARVELOUS_SONGS) - ); - - private static final String BOTH_INDEX_ALIAS = "both-indices"; - private static final String FORBIDDEN_INDEX_ALIAS = "forbidden-index"; - - @ClassRule - public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .users(ADMIN_USER, LIMITED_USER) - .anonymousAuth(false) - .doNotFailOnForbidden(true) - .build(); - - @BeforeClass - public static void createTestData() { - try (Client client = cluster.getInternalNodeClient()) { - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_1).source(SONGS[0].asMap())) - .actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_2).source(SONGS[1].asMap())) - .actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_3).source(SONGS[2].asMap())) - .actionGet(); - - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(HORRIBLE_SONGS).id(ID_4).source(SONGS[3].asMap())) - .actionGet(); - - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction( - new IndicesAliasesRequest.AliasActions(ADD).indices(MARVELOUS_SONGS, HORRIBLE_SONGS).alias(BOTH_INDEX_ALIAS) - ) - ) - .actionGet(); - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction( - new IndicesAliasesRequest.AliasActions(ADD).indices(HORRIBLE_SONGS).alias(FORBIDDEN_INDEX_ALIAS) - ) - ) - .actionGet(); - - } - } - - @Test - public void shouldPerformSimpleSearch_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest( - new String[] { MARVELOUS_SONGS, HORRIBLE_SONGS }, - QUERY_TITLE_MAGNUM_OPUS - ); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); - } - } - - private static void assertThatContainOneSong(SearchResponse searchResponse, String documentId, String title) { - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, MARVELOUS_SONGS, documentId)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, title)); - } - - @Test - public void shouldPerformSimpleSearch_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(HORRIBLE_SONGS, QUERY_TITLE_POISON); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchForDocumentsViaIndexPattern_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(BOTH_INDEX_PATTERN, QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); - } - } - - @Test - public void shouldSearchForDocumentsViaIndexPattern_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(HORRIBLE_SONGS, QUERY_TITLE_POISON); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchForDocumentsViaAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(BOTH_INDEX_ALIAS, QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); - } - } - - @Test - public void shouldSearchForDocumentsViaAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(FORBIDDEN_INDEX_ALIAS, QUERY_TITLE_POISON); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldSearchForDocumentsViaAll_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThatContainOneSong(searchResponse, ID_1, TITLE_MAGNUM_OPUS); - } - } - - @Test - public void shouldSearchForDocumentsViaAll_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_POISON); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(0)); - } - } - - @Test - public void shouldMGetDocument_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - MultiGetRequest request = new MultiGetRequest().add(BOTH_INDEX_PATTERN, ID_1).add(BOTH_INDEX_PATTERN, ID_4); - - MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); - - MultiGetItemResponse[] responses = response.getResponses(); - assertThat(responses, arrayWithSize(2)); - MultiGetItemResponse firstResult = responses[0]; - MultiGetItemResponse secondResult = responses[1]; - assertThat(firstResult.getFailure(), nullValue()); - assertThat(secondResult.getFailure(), nullValue()); - assertThat( - firstResult.getResponse(), - allOf(containDocument(MARVELOUS_SONGS, ID_1), documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)) - ); - assertThat(secondResult.getResponse(), containOnlyDocumentId(MARVELOUS_SONGS, ID_4)); - } - } - - @Test - public void shouldMGetDocument_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - MultiGetRequest request = new MultiGetRequest().add(HORRIBLE_SONGS, ID_4); - - assertThatThrownBy(() -> restHighLevelClient.mget(request, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldMSearchDocument_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - MultiSearchRequest request = new MultiSearchRequest(); - request.add(queryStringQueryRequest(BOTH_INDEX_PATTERN, QUERY_TITLE_MAGNUM_OPUS)); - request.add(queryStringQueryRequest(BOTH_INDEX_PATTERN, QUERY_TITLE_NEXT_SONG)); - - MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); - - MultiSearchResponse.Item[] responses = response.getResponses(); - assertThat(responses, Matchers.arrayWithSize(2)); - assertThat(responses[0].getFailure(), nullValue()); - assertThat(responses[1].getFailure(), nullValue()); - - assertThat(responses[0].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - assertThat(responses[0].getResponse(), searchHitsContainDocumentWithId(0, MARVELOUS_SONGS, ID_1)); - assertThat(responses[1].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); - assertThat(responses[1].getResponse(), searchHitsContainDocumentWithId(0, MARVELOUS_SONGS, ID_3)); - } - } - - @Test - public void shouldMSearchDocument_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - MultiSearchRequest request = new MultiSearchRequest(); - request.add(queryStringQueryRequest(FORBIDDEN_INDEX_ALIAS, QUERY_TITLE_POISON)); - - assertThatThrownBy(() -> restHighLevelClient.msearch(request, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldGetFieldCapabilities_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(MARVELOUS_SONGS, HORRIBLE_SONGS).fields(FIELD_TITLE); - - FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); - - assertThat(response.get(), aMapWithSize(1)); - assertThat(response.getIndices(), arrayWithSize(1)); - assertThat(response.getField(FIELD_TITLE), hasKey("text")); - assertThat(response.getIndices(), arrayContainingInAnyOrder(MARVELOUS_SONGS)); - } - } - - @Test - public void shouldGetFieldCapabilities_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(HORRIBLE_SONGS).fields(FIELD_TITLE); - - assertThatThrownBy(() -> restHighLevelClient.fieldCaps(request, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldScrollOverSearchResults_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchRequestWithScroll(BOTH_INDEX_PATTERN, 2); - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containNotEmptyScrollingId()); - - SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); - - SearchResponse scrollResponse = restHighLevelClient.scroll(scrollRequest, DEFAULT); - assertThat(scrollResponse, isSuccessfulSearchResponse()); - assertThat(scrollResponse, containNotEmptyScrollingId()); - assertThat(scrollResponse, numberOfTotalHitsIsEqualTo(3)); - assertThat(scrollResponse, numberOfHitsInPageIsEqualTo(1)); - } - } - - @Test - public void shouldScrollOverSearchResults_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - SearchRequest searchRequest = searchRequestWithScroll(HORRIBLE_SONGS, 2); - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldPerformAggregation_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - final String aggregationName = "averageStars"; - SearchRequest searchRequest = averageAggregationRequest(BOTH_INDEX_PATTERN, aggregationName, FIELD_STARS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); - } - } - - @Test - public void shouldPerformAggregation_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - final String aggregationName = "averageStars"; - SearchRequest searchRequest = averageAggregationRequest(HORRIBLE_SONGS, aggregationName, FIELD_STARS); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldPerformStatAggregation_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - final String aggregationName = "statsStars"; - SearchRequest searchRequest = statsAggregationRequest(BOTH_INDEX_ALIAS, aggregationName, FIELD_STARS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "stats")); - } - } - - @Test - public void shouldPerformStatAggregation_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { - final String aggregationName = "statsStars"; - SearchRequest searchRequest = statsAggregationRequest(HORRIBLE_SONGS, aggregationName, FIELD_STARS); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - -} diff --git a/src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java b/src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java deleted file mode 100644 index 3b01ca69bf..0000000000 --- a/src/integrationTest/java/org/opensearch/security/PointInTimeOperationTest.java +++ /dev/null @@ -1,426 +0,0 @@ -/* -* 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; - -import java.io.IOException; - -import com.carrotsearch.randomizedtesting.RandomizedRunner; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import org.apache.commons.lang3.tuple.Pair; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.opensearch.OpenSearchStatusException; -import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; -import org.opensearch.action.index.IndexRequest; -import org.opensearch.action.search.CreatePitRequest; -import org.opensearch.action.search.CreatePitResponse; -import org.opensearch.action.search.DeletePitRequest; -import org.opensearch.action.search.DeletePitResponse; -import org.opensearch.action.search.GetAllPitNodesResponse; -import org.opensearch.action.search.SearchRequest; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.client.Client; -import org.opensearch.client.RestHighLevelClient; -import org.opensearch.common.unit.TimeValue; -import org.opensearch.core.rest.RestStatus; -import org.opensearch.search.builder.PointInTimeBuilder; -import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.framework.TestSecurityConfig; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; -import org.opensearch.test.framework.cluster.TestRestClient; -import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.opensearch.client.RequestOptions.DEFAULT; -import static org.opensearch.core.rest.RestStatus.FORBIDDEN; -import static org.opensearch.core.rest.RestStatus.OK; -import static org.opensearch.security.Song.SONGS; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; -import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; -import static org.opensearch.test.framework.matcher.PitResponseMatchers.deleteResponseContainsExactlyPitWithIds; -import static org.opensearch.test.framework.matcher.PitResponseMatchers.getAllResponseContainsExactlyPitWithIds; -import static org.opensearch.test.framework.matcher.PitResponseMatchers.isSuccessfulCreatePitResponse; -import static org.opensearch.test.framework.matcher.PitResponseMatchers.isSuccessfulDeletePitResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentsInAnyOrder; - -@RunWith(RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class PointInTimeOperationTest { - - private static final String FIRST_SONG_INDEX = "song-index-1"; - private static final String FIRST_INDEX_ALIAS = "song-index-1-alias"; - private static final String SECOND_SONG_INDEX = "song-index-2"; - private static final String SECOND_INDEX_ALIAS = "song-index-2-alias"; - - private static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); - - /** - * User who is allowed to perform PIT operations only on the {@link #FIRST_SONG_INDEX} - */ - private static final TestSecurityConfig.User LIMITED_POINT_IN_TIME_USER = new TestSecurityConfig.User("limited_point_in_time_user") - .roles( - new TestSecurityConfig.Role("limited_point_in_time_user").indexPermissions( - "indices:data/read/point_in_time/create", - "indices:data/read/point_in_time/delete", - "indices:data/read/search", - "indices:data/read/point_in_time/readall", // anyway user needs the all indexes permission (*) to find all pits - "indices:monitor/point_in_time/segments" // anyway user needs the all indexes permission (*) to list all pits segments - ).on(FIRST_SONG_INDEX) - ); - /** - * User who is allowed to perform PIT operations on all indices - */ - private static final TestSecurityConfig.User POINT_IN_TIME_USER = new TestSecurityConfig.User("point_in_time_user").roles( - new TestSecurityConfig.Role("point_in_time_user").indexPermissions( - "indices:data/read/point_in_time/create", - "indices:data/read/point_in_time/delete", - "indices:data/read/search", - "indices:data/read/point_in_time/readall", - "indices:monitor/point_in_time/segments" - ).on("*") - ); - - private static final String ID_1 = "1"; - private static final String ID_2 = "2"; - private static final String ID_3 = "3"; - private static final String ID_4 = "4"; - - @BeforeClass - public static void createTestData() { - try (Client client = cluster.getInternalNodeClient()) { - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(FIRST_SONG_INDEX).id(ID_1).source(SONGS[0].asMap())) - .actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(FIRST_SONG_INDEX).id(ID_2).source(SONGS[1].asMap())) - .actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(FIRST_SONG_INDEX).id(ID_3).source(SONGS[2].asMap())) - .actionGet(); - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction( - new IndicesAliasesRequest.AliasActions(ADD).indices(FIRST_SONG_INDEX).alias(FIRST_INDEX_ALIAS) - ) - ) - .actionGet(); - - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SECOND_SONG_INDEX).id(ID_4).source(SONGS[3].asMap())) - .actionGet(); - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction( - new IndicesAliasesRequest.AliasActions(ADD).indices(SECOND_SONG_INDEX).alias(SECOND_INDEX_ALIAS) - ) - ) - .actionGet(); - } - } - - @ClassRule - public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) - .anonymousAuth(false) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .users(ADMIN_USER, LIMITED_POINT_IN_TIME_USER, POINT_IN_TIME_USER) - .build(); - - @Test - public void createPit_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, FIRST_SONG_INDEX); - - CreatePitResponse createPitResponse = restHighLevelClient.createPit(createPitRequest, DEFAULT); - - assertThat(createPitResponse, isSuccessfulCreatePitResponse()); - } - } - - @Test - public void createPitWithIndexAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, FIRST_INDEX_ALIAS); - - CreatePitResponse createPitResponse = restHighLevelClient.createPit(createPitRequest, DEFAULT); - - assertThat(createPitResponse, isSuccessfulCreatePitResponse()); - } - } - - @Test - public void createPit_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, SECOND_SONG_INDEX); - - assertThatThrownBy(() -> restHighLevelClient.createPit(createPitRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void createPitWithIndexAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, SECOND_INDEX_ALIAS); - - assertThatThrownBy(() -> restHighLevelClient.createPit(createPitRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void listAllPits_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(POINT_IN_TIME_USER)) { - cleanUpPits(); - String firstIndexPit = createPitForIndices(FIRST_SONG_INDEX); - String secondIndexPit = createPitForIndices(SECOND_SONG_INDEX); - - GetAllPitNodesResponse getAllPitsResponse = restHighLevelClient.getAllPits(DEFAULT); - - assertThat(getAllPitsResponse, getAllResponseContainsExactlyPitWithIds(firstIndexPit, secondIndexPit)); - } - } - - @Test - public void listAllPits_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - assertThatThrownBy(() -> restHighLevelClient.getAllPits(DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void deletePit_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(FIRST_SONG_INDEX); - - DeletePitResponse deletePitResponse = restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT); - assertThat(deletePitResponse, isSuccessfulDeletePitResponse()); - assertThat(deletePitResponse, deleteResponseContainsExactlyPitWithIds(existingPitId)); - } - } - - @Test - public void deletePitCreatedWithIndexAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(FIRST_INDEX_ALIAS); - - DeletePitResponse deletePitResponse = restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT); - assertThat(deletePitResponse, isSuccessfulDeletePitResponse()); - assertThat(deletePitResponse, deleteResponseContainsExactlyPitWithIds(existingPitId)); - } - } - - @Test - public void deletePit_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(SECOND_SONG_INDEX); - - assertThatThrownBy( - () -> restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - public void deletePitCreatedWithIndexAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(SECOND_INDEX_ALIAS); - - assertThatThrownBy( - () -> restHighLevelClient.deletePit(new DeletePitRequest(existingPitId), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - public void deleteAllPits_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(POINT_IN_TIME_USER)) { - cleanUpPits(); - String firstIndexPit = createPitForIndices(FIRST_SONG_INDEX); - String secondIndexPit = createPitForIndices(SECOND_SONG_INDEX); - - DeletePitResponse deletePitResponse = restHighLevelClient.deleteAllPits(DEFAULT); - assertThat(deletePitResponse, isSuccessfulDeletePitResponse()); - assertThat(deletePitResponse, deleteResponseContainsExactlyPitWithIds(firstIndexPit, secondIndexPit)); - } - } - - @Test - public void deleteAllPits_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - assertThatThrownBy(() -> restHighLevelClient.deleteAllPits(DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void searchWithPit_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(FIRST_SONG_INDEX); - - SearchRequest searchRequest = new SearchRequest(); - searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat( - searchResponse, - searchHitsContainDocumentsInAnyOrder( - Pair.of(FIRST_SONG_INDEX, ID_1), - Pair.of(FIRST_SONG_INDEX, ID_2), - Pair.of(FIRST_SONG_INDEX, ID_3) - ) - ); - } - } - - @Test - public void searchWithPitCreatedWithIndexAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(FIRST_INDEX_ALIAS); - - SearchRequest searchRequest = new SearchRequest(); - searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat( - searchResponse, - searchHitsContainDocumentsInAnyOrder( - Pair.of(FIRST_SONG_INDEX, ID_1), - Pair.of(FIRST_SONG_INDEX, ID_2), - Pair.of(FIRST_SONG_INDEX, ID_3) - ) - ); - } - } - - @Test - public void searchWithPit_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(SECOND_SONG_INDEX); - - SearchRequest searchRequest = new SearchRequest(); - searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void searchWithPitCreatedWithIndexAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(SECOND_INDEX_ALIAS); - - SearchRequest searchRequest = new SearchRequest(); - searchRequest.source(new SearchSourceBuilder().pointInTimeBuilder(new PointInTimeBuilder(existingPitId))); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void listPitSegments_positive() throws IOException { - try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(FIRST_SONG_INDEX); - String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); - HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); - - response.assertStatusCode(OK.getStatus()); - } - } - - @Test - public void listPitSegmentsCreatedWithIndexAlias_positive() throws IOException { - try (TestRestClient restClient = cluster.getRestClient(POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(FIRST_INDEX_ALIAS); - String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); - HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); - - response.assertStatusCode(OK.getStatus()); - } - } - - @Test - public void listPitSegments_negative() throws IOException { - try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(SECOND_SONG_INDEX); - String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); - HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); - - response.assertStatusCode(FORBIDDEN.getStatus()); - } - } - - @Test - public void listPitSegmentsCreatedWithIndexAlias_negative() throws IOException { - try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { - String existingPitId = createPitForIndices(SECOND_INDEX_ALIAS); - String body = String.format("{\"pit_id\":[\"%s\"]}", existingPitId); - HttpResponse response = restClient.getWithJsonBody("/_cat/pit_segments", body); - - response.assertStatusCode(FORBIDDEN.getStatus()); - } - } - - @Test - public void listAllPitSegments_positive() { - try (TestRestClient restClient = cluster.getRestClient(POINT_IN_TIME_USER)) { - HttpResponse response = restClient.get("/_cat/pit_segments/_all"); - - response.assertStatusCode(OK.getStatus()); - } - } - - @Test - public void listAllPitSegments_negative() { - try (TestRestClient restClient = cluster.getRestClient(LIMITED_POINT_IN_TIME_USER)) { - HttpResponse response = restClient.get("/_cat/pit_segments/_all"); - - response.assertStatusCode(FORBIDDEN.getStatus()); - } - } - - /** - * Creates PIT for given indices. Returns PIT id. - */ - private String createPitForIndices(String... indices) throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueMinutes(30), false, indices); - - CreatePitResponse createPitResponse = restHighLevelClient.createPit(createPitRequest, DEFAULT); - - assertThat(createPitResponse, isSuccessfulCreatePitResponse()); - return createPitResponse.getId(); - } - } - - /** - * Deletes all PITs. - */ - public void cleanUpPits() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - try { - restHighLevelClient.deleteAllPits(DEFAULT); - } catch (OpenSearchStatusException ex) { - if (ex.status() != RestStatus.NOT_FOUND) { - throw ex; - } - // tried to remove pits but no pit exists - } - } - } - -} diff --git a/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java b/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java deleted file mode 100644 index f16d40e905..0000000000 --- a/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java +++ /dev/null @@ -1,2719 +0,0 @@ -/* -* 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; - -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import com.google.common.base.Stopwatch; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.awaitility.Awaitility; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.opensearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; -import org.opensearch.action.admin.cluster.repositories.put.PutRepositoryRequest; -import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; -import org.opensearch.action.admin.indices.alias.Alias; -import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; -import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; -import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest; -import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheResponse; -import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; -import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequest; -import org.opensearch.action.admin.indices.open.OpenIndexRequest; -import org.opensearch.action.admin.indices.open.OpenIndexResponse; -import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; -import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; -import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; -import org.opensearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest; -import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesRequest; -import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesResponse; -import org.opensearch.action.bulk.BulkRequest; -import org.opensearch.action.bulk.BulkResponse; -import org.opensearch.action.delete.DeleteRequest; -import org.opensearch.action.delete.DeleteResponse; -import org.opensearch.action.fieldcaps.FieldCapabilitiesRequest; -import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; -import org.opensearch.action.get.GetRequest; -import org.opensearch.action.get.GetResponse; -import org.opensearch.action.get.MultiGetItemResponse; -import org.opensearch.action.get.MultiGetRequest; -import org.opensearch.action.get.MultiGetRequest.Item; -import org.opensearch.action.get.MultiGetResponse; -import org.opensearch.action.index.IndexRequest; -import org.opensearch.action.search.MultiSearchRequest; -import org.opensearch.action.search.MultiSearchResponse; -import org.opensearch.action.search.SearchRequest; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.action.search.SearchScrollRequest; -import org.opensearch.action.update.UpdateRequest; -import org.opensearch.action.update.UpdateResponse; -import org.opensearch.client.Client; -import org.opensearch.client.ClusterAdminClient; -import org.opensearch.client.IndicesAdminClient; -import org.opensearch.client.RestHighLevelClient; -import org.opensearch.client.core.CountRequest; -import org.opensearch.client.indices.CloseIndexRequest; -import org.opensearch.client.indices.CloseIndexResponse; -import org.opensearch.client.indices.CreateIndexRequest; -import org.opensearch.client.indices.CreateIndexResponse; -import org.opensearch.client.indices.GetIndexRequest; -import org.opensearch.client.indices.GetIndexResponse; -import org.opensearch.client.indices.GetMappingsRequest; -import org.opensearch.client.indices.GetMappingsResponse; -import org.opensearch.client.indices.PutIndexTemplateRequest; -import org.opensearch.client.indices.PutMappingRequest; -import org.opensearch.client.indices.ResizeRequest; -import org.opensearch.client.indices.ResizeResponse; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.cluster.metadata.IndexTemplateMetadata; -import org.opensearch.common.settings.Settings; -import org.opensearch.index.query.BoolQueryBuilder; -import org.opensearch.index.query.MatchQueryBuilder; -import org.opensearch.index.query.QueryBuilders; -import org.opensearch.index.reindex.BulkByScrollResponse; -import org.opensearch.index.reindex.ReindexRequest; -import org.opensearch.repositories.RepositoryMissingException; -import org.opensearch.core.rest.RestStatus; -import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.framework.AuditCompliance; -import org.opensearch.test.framework.AuditConfiguration; -import org.opensearch.test.framework.AuditFilters; -import org.opensearch.test.framework.TestSecurityConfig.Role; -import org.opensearch.test.framework.TestSecurityConfig.User; -import org.opensearch.test.framework.audit.AuditLogsRule; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasProperty; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.ADD; -import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.REMOVE; -import static org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions.Type.REMOVE_INDEX; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.opensearch.client.RequestOptions.DEFAULT; -import static org.opensearch.rest.RestRequest.Method.DELETE; -import static org.opensearch.rest.RestRequest.Method.GET; -import static org.opensearch.rest.RestRequest.Method.POST; -import static org.opensearch.rest.RestRequest.Method.PUT; -import static org.opensearch.core.rest.RestStatus.ACCEPTED; -import static org.opensearch.core.rest.RestStatus.FORBIDDEN; -import static org.opensearch.core.rest.RestStatus.INTERNAL_SERVER_ERROR; -import static org.opensearch.security.Song.FIELD_ARTIST; -import static org.opensearch.security.Song.FIELD_STARS; -import static org.opensearch.security.Song.FIELD_TITLE; -import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; -import static org.opensearch.security.Song.QUERY_TITLE_NEXT_SONG; -import static org.opensearch.security.Song.QUERY_TITLE_POISON; -import static org.opensearch.security.Song.SONGS; -import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; -import static org.opensearch.security.Song.TITLE_NEXT_SONG; -import static org.opensearch.security.Song.TITLE_POISON; -import static org.opensearch.security.Song.TITLE_SONG_1_PLUS_1; -import static org.opensearch.security.auditlog.impl.AuditCategory.INDEX_EVENT; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; -import static org.opensearch.test.framework.audit.AuditMessagePredicate.auditPredicate; -import static org.opensearch.test.framework.audit.AuditMessagePredicate.grantedPrivilege; -import static org.opensearch.test.framework.audit.AuditMessagePredicate.missingPrivilege; -import static org.opensearch.test.framework.audit.AuditMessagePredicate.userAuthenticated; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.averageAggregationRequest; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.getSearchScrollRequest; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.searchRequestWithScroll; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.statsAggregationRequest; -import static org.opensearch.test.framework.matcher.BulkResponseMatchers.bulkResponseContainExceptions; -import static org.opensearch.test.framework.matcher.BulkResponseMatchers.failureBulkResponse; -import static org.opensearch.test.framework.matcher.BulkResponseMatchers.successBulkResponse; -import static org.opensearch.test.framework.matcher.ClusterMatchers.aliasExists; -import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainSuccessSnapshot; -import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainTemplate; -import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainTemplateWithAlias; -import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainsDocument; -import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainsDocumentWithFieldValue; -import static org.opensearch.test.framework.matcher.ClusterMatchers.clusterContainsSnapshotRepository; -import static org.opensearch.test.framework.matcher.ClusterMatchers.indexExists; -import static org.opensearch.test.framework.matcher.ClusterMatchers.indexMappingIsEqualTo; -import static org.opensearch.test.framework.matcher.ClusterMatchers.indexSettingsContainValues; -import static org.opensearch.test.framework.matcher.ClusterMatchers.indexStateIsEqualTo; -import static org.opensearch.test.framework.matcher.ClusterMatchers.snapshotInClusterDoesNotExists; -import static org.opensearch.test.framework.matcher.DeleteResponseMatchers.isSuccessfulDeleteResponse; -import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; -import static org.opensearch.test.framework.matcher.FieldCapabilitiesResponseMatchers.containsExactlyIndices; -import static org.opensearch.test.framework.matcher.FieldCapabilitiesResponseMatchers.containsFieldWithNameAndType; -import static org.opensearch.test.framework.matcher.FieldCapabilitiesResponseMatchers.numberOfFieldsIsEqualTo; -import static org.opensearch.test.framework.matcher.GetResponseMatchers.containDocument; -import static org.opensearch.test.framework.matcher.GetResponseMatchers.documentContainField; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.getIndexResponseContainsIndices; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.getMappingsResponseContainsIndices; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.getSettingsResponseContainsIndices; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulClearIndicesCacheResponse; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulCloseIndexResponse; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulCreateIndexResponse; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulOpenIndexResponse; -import static org.opensearch.test.framework.matcher.IndexResponseMatchers.isSuccessfulResizeResponse; -import static org.opensearch.test.framework.matcher.MultiGetResponseMatchers.isSuccessfulMultiGetResponse; -import static org.opensearch.test.framework.matcher.MultiGetResponseMatchers.numberOfGetItemResponsesIsEqualTo; -import static org.opensearch.test.framework.matcher.MultiSearchResponseMatchers.isSuccessfulMultiSearchResponse; -import static org.opensearch.test.framework.matcher.MultiSearchResponseMatchers.numberOfSearchItemResponsesIsEqualTo; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.errorMessageContain; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containAggregationWithNameAndType; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.containNotEmptyScrollingId; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfHitsInPageIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; -import static org.opensearch.test.framework.matcher.UpdateResponseMatchers.isSuccessfulUpdateResponse; - -@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class SearchOperationTest { - - private static final Logger log = LogManager.getLogger(SearchOperationTest.class); - - public static final String SONG_INDEX_NAME = "song_lyrics"; - public static final String PROHIBITED_SONG_INDEX_NAME = "prohibited_song_lyrics"; - public static final String WRITE_SONG_INDEX_NAME = "write_song_index"; - - public static final String SONG_LYRICS_ALIAS = "song_lyrics_index_alias"; - public static final String PROHIBITED_SONG_ALIAS = "prohibited_song_lyrics_index_alias"; - private static final String COLLECTIVE_INDEX_ALIAS = "collective-index-alias"; - private static final String TEMPLATE_INDEX_PREFIX = "song-transcription*"; - public static final String TEMPORARY_ALIAS_NAME = "temporary-alias"; - public static final String ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001 = "alias-used-in-musical-index-template-0001"; - public static final String ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002 = "alias-used-in-musical-index-template-0002"; - public static final String ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003 = "alias-used-in-musical-index-template-0003"; - public static final String INDEX_NAME_SONG_TRANSCRIPTION_JAZZ = "song-transcription-jazz"; - - public static final String MUSICAL_INDEX_TEMPLATE = "musical-index-template"; - public static final String ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE = "alias_create_index_with_alias_positive"; - public static final String ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE = "alias_create_index_with_alias_negative"; - - public static final String UNDELETABLE_TEMPLATE_NAME = "undeletable-template-name"; - - public static final String ALIAS_FROM_UNDELETABLE_TEMPLATE = "alias-from-undeletable-template"; - - public static final String TEST_SNAPSHOT_REPOSITORY_NAME = "test-snapshot-repository"; - - public static final String UNUSED_SNAPSHOT_REPOSITORY_NAME = "unused-snapshot-repository"; - - public static final String RESTORED_SONG_INDEX_NAME = "restored_" + WRITE_SONG_INDEX_NAME; - - public static final String UPDATE_DELETE_OPERATION_INDEX_NAME = "update_delete_index"; - - public static final String DOCUMENT_TO_UPDATE_ID = "doc_to_update"; - - private static final String ID_P4 = "4"; - private static final String ID_S3 = "3"; - private static final String ID_S2 = "2"; - private static final String ID_S1 = "1"; - - static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS); - - /** - * All user read permissions are related to {@link #SONG_INDEX_NAME} index - */ - static final User LIMITED_READ_USER = new User("limited_read_user").roles( - new Role("limited-song-reader").clusterPermissions( - "indices:data/read/mget", - "indices:data/read/msearch", - "indices:data/read/scroll" - ) - .indexPermissions( - "indices:data/read/search", - "indices:data/read/get", - "indices:data/read/mget*", - "indices:admin/aliases", - "indices:data/read/field_caps", - "indices:data/read/field_caps*" - ) - .on(SONG_INDEX_NAME) - ); - - static final User LIMITED_WRITE_USER = new User("limited_write_user").roles( - new Role("limited-write-role").clusterPermissions( - "indices:data/write/bulk", - "indices:admin/template/put", - "indices:admin/template/delete", - "cluster:admin/repository/put", - "cluster:admin/repository/delete", - "cluster:admin/snapshot/create", - "cluster:admin/snapshot/status", - "cluster:admin/snapshot/status[nodes]", - "cluster:admin/snapshot/delete", - "cluster:admin/snapshot/get", - "cluster:admin/snapshot/restore" - ) - .indexPermissions( - "indices:data/write/index", - "indices:data/write/bulk[s]", - "indices:admin/create", - "indices:admin/mapping/put", - "indices:data/write/update", - "indices:data/write/bulk[s]", - "indices:data/write/delete", - "indices:data/write/bulk[s]" - ) - .on(WRITE_SONG_INDEX_NAME), - new Role("transcription-role").indexPermissions( - "indices:data/write/index", - "indices:admin/create", - "indices:data/write/bulk[s]", - "indices:admin/mapping/put" - ).on(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ), - new Role("limited-write-index-restore-role").indexPermissions( - "indices:data/write/index", - "indices:admin/create", - "indices:data/read/search" - ).on(RESTORED_SONG_INDEX_NAME) - ); - - /** - * User who is allowed read both index {@link #SONG_INDEX_NAME} and {@link #PROHIBITED_SONG_INDEX_NAME} - */ - static final User DOUBLE_READER_USER = new User("double_read_user").roles( - new Role("full-song-reader").indexPermissions("indices:data/read/search").on(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME) - ); - - static final User REINDEXING_USER = new User("reindexing_user").roles( - new Role("song-reindexing-target-write").clusterPermissions("indices:data/write/reindex", "indices:data/write/bulk") - .indexPermissions("indices:admin/create", "indices:data/write/index", "indices:data/write/bulk[s]", "indices:admin/mapping/put") - .on(WRITE_SONG_INDEX_NAME), - new Role("song-reindexing-source-read").clusterPermissions("indices:data/read/scroll") - .indexPermissions("indices:data/read/search") - .on(SONG_INDEX_NAME) - ); - - private Client internalClient; - /** - * User who is allowed to update and delete documents on index {@link #UPDATE_DELETE_OPERATION_INDEX_NAME} - */ - static final User UPDATE_DELETE_USER = new User("update_delete_user").roles( - new Role("document-updater").clusterPermissions("indices:data/write/bulk") - .indexPermissions( - "indices:data/write/update", - "indices:data/write/index", - "indices:data/write/bulk[s]", - "indices:admin/mapping/put" - ) - .on(UPDATE_DELETE_OPERATION_INDEX_NAME), - new Role("document-remover").indexPermissions("indices:data/write/delete").on(UPDATE_DELETE_OPERATION_INDEX_NAME) - ); - - static final String INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX = "index_operations_"; - - /** - * User who is allowed to perform index-related operations on - * indices with names prefixed by the {@link #INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX} - */ - static final User USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES = new User("index-operation-tester").roles( - new Role("index-manager").indexPermissions( - "indices:admin/create", - "indices:admin/get", - "indices:admin/delete", - "indices:admin/close", - "indices:admin/close*", - "indices:admin/open", - "indices:admin/resize", - "indices:monitor/stats", - "indices:monitor/settings/get", - "indices:admin/settings/update", - "indices:admin/mapping/put", - "indices:admin/mappings/get", - "indices:admin/cache/clear", - "indices:admin/aliases" - ).on(INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("*")) - ); - - private static User USER_ALLOWED_TO_CREATE_INDEX = new User("user-allowed-to-create-index").roles( - new Role("create-index-role").indexPermissions("indices:admin/create").on("*") - ); - - @ClassRule - public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) - .anonymousAuth(false) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .users( - ADMIN_USER, - LIMITED_READ_USER, - LIMITED_WRITE_USER, - DOUBLE_READER_USER, - REINDEXING_USER, - UPDATE_DELETE_USER, - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, - USER_ALLOWED_TO_CREATE_INDEX - ) - .audit( - new AuditConfiguration(true).compliance(new AuditCompliance().enabled(true)) - .filters(new AuditFilters().enabledRest(true).enabledTransport(true)) - ) - .build(); - - @Rule - public AuditLogsRule auditLogsRule = new AuditLogsRule(); - - @BeforeClass - public static void createTestData() { - try (Client client = cluster.getInternalNodeClient()) { - client.prepareIndex(SONG_INDEX_NAME).setId(ID_S1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); - client.prepareIndex(UPDATE_DELETE_OPERATION_INDEX_NAME) - .setId(DOCUMENT_TO_UPDATE_ID) - .setRefreshPolicy(IMMEDIATE) - .setSource("field", "value") - .get(); - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction(new AliasActions(ADD).indices(SONG_INDEX_NAME).alias(SONG_LYRICS_ALIAS)) - ) - .actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SONG_INDEX_NAME).id(ID_S2).source(SONGS[1].asMap())) - .actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SONG_INDEX_NAME).id(ID_S3).source(SONGS[2].asMap())) - .actionGet(); - - client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(ID_P4).setSource(SONGS[3].asMap()).setRefreshPolicy(IMMEDIATE).get(); - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction( - new AliasActions(ADD).indices(PROHIBITED_SONG_INDEX_NAME).alias(PROHIBITED_SONG_ALIAS) - ) - ) - .actionGet(); - - client.admin() - .indices() - .aliases( - new IndicesAliasesRequest().addAliasAction( - new AliasActions(ADD).indices(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME).alias(COLLECTIVE_INDEX_ALIAS) - ) - ) - .actionGet(); - var createTemplateRequest = new org.opensearch.action.admin.indices.template.put.PutIndexTemplateRequest( - UNDELETABLE_TEMPLATE_NAME - ); - createTemplateRequest.patterns(List.of("pattern-does-not-match-to-any-index")); - createTemplateRequest.alias(new Alias(ALIAS_FROM_UNDELETABLE_TEMPLATE)); - client.admin().indices().putTemplate(createTemplateRequest).actionGet(); - - client.admin() - .cluster() - .putRepository( - new PutRepositoryRequest(UNUSED_SNAPSHOT_REPOSITORY_NAME).type("fs") - .settings(Map.of("location", cluster.getSnapshotDirPath())) - ) - .actionGet(); - } - } - - @Before - public void retrieveClusterClient() { - this.internalClient = cluster.getInternalNodeClient(); - } - - @After - public void cleanData() throws ExecutionException, InterruptedException { - Stopwatch stopwatch = Stopwatch.createStarted(); - IndicesAdminClient indices = internalClient.admin().indices(); - List indicesToBeDeleted = List.of( - WRITE_SONG_INDEX_NAME, - INDEX_NAME_SONG_TRANSCRIPTION_JAZZ, - RESTORED_SONG_INDEX_NAME, - INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("*") - ); - for (String indexToBeDeleted : indicesToBeDeleted) { - IndicesExistsRequest indicesExistsRequest = new IndicesExistsRequest(indexToBeDeleted); - var indicesExistsResponse = indices.exists(indicesExistsRequest).get(); - if (indicesExistsResponse.isExists()) { - DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexToBeDeleted); - indices.delete(deleteIndexRequest).actionGet(); - Awaitility.await().ignoreExceptions().until(() -> indices.exists(indicesExistsRequest).get().isExists() == false); - } - } - - List aliasesToBeDeleted = List.of( - TEMPORARY_ALIAS_NAME, - ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001, - ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002, - ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE, - ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE - ); - for (String aliasToBeDeleted : aliasesToBeDeleted) { - if (indices.exists(new IndicesExistsRequest(aliasToBeDeleted)).get().isExists()) { - AliasActions aliasAction = new AliasActions(AliasActions.Type.REMOVE).indices(SONG_INDEX_NAME).alias(aliasToBeDeleted); - internalClient.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(aliasAction)).get(); - } - } - - GetIndexTemplatesResponse response = indices.getTemplates(new GetIndexTemplatesRequest(MUSICAL_INDEX_TEMPLATE)).get(); - for (IndexTemplateMetadata metadata : response.getIndexTemplates()) { - indices.deleteTemplate(new DeleteIndexTemplateRequest(metadata.getName())).get(); - } - - ClusterAdminClient clusterClient = internalClient.admin().cluster(); - try { - clusterClient.deleteRepository(new DeleteRepositoryRequest(TEST_SNAPSHOT_REPOSITORY_NAME)).actionGet(); - } catch (RepositoryMissingException e) { - log.debug("Repository '{}' does not exist. This is expected in most of test cases", TEST_SNAPSHOT_REPOSITORY_NAME, e); - } - internalClient.close(); - log.debug("Cleaning data after test took {}", stopwatch.stop()); - } - - @Test - public void shouldSearchForDocuments_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldSearchForDocuments_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(PROHIBITED_SONG_INDEX_NAME, QUERY_TITLE_POISON); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldSearchForDocumentsViaAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(SONG_LYRICS_ALIAS, QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics_index_alias/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldSearchForDocumentsViaAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(PROHIBITED_SONG_ALIAS, QUERY_TITLE_POISON); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics_index_alias/_search") - ); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchSongViaMultiIndexAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(COLLECTIVE_INDEX_ALIAS, QUERY_TITLE_NEXT_SONG); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S3)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/collective-index-alias/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchSongViaMultiIndexAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(COLLECTIVE_INDEX_ALIAS, QUERY_TITLE_POISON); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/collective-index-alias/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchAllIndexes_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(ADMIN_USER).withRestRequest(POST, "/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(ADMIN_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchAllIndexes_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest(QUERY_TITLE_MAGNUM_OPUS); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchSongIndexesWithAsterisk_prohibitedSongIndex_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("*" + SONG_INDEX_NAME, QUERY_TITLE_POISON); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, PROHIBITED_SONG_INDEX_NAME, ID_P4)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_POISON)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/*song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchSongIndexesWithAsterisk_singIndex_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("*" + SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S3)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/*song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); - } - - @Test - public void shouldBeAbleToSearchSongIndexesWithAsterisk_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("*" + SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/*song_lyrics/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldFindSongUsingDslQuery_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = new SearchRequest(SONG_INDEX_NAME); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - boolQueryBuilder.filter(QueryBuilders.regexpQuery(FIELD_ARTIST, "f.+")); - boolQueryBuilder.filter(new MatchQueryBuilder(FIELD_TITLE, TITLE_MAGNUM_OPUS)); - searchSourceBuilder.query(boolQueryBuilder); - searchRequest.source(searchSourceBuilder); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldFindSongUsingDslQuery_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = new SearchRequest(PROHIBITED_SONG_INDEX_NAME); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - boolQueryBuilder.filter(QueryBuilders.regexpQuery(FIELD_ARTIST, "n.+")); - boolQueryBuilder.filter(new MatchQueryBuilder(FIELD_TITLE, TITLE_POISON)); - searchSourceBuilder.query(boolQueryBuilder); - searchRequest.source(searchSourceBuilder); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldPerformSearchWithAllIndexAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); - assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(ADMIN_USER).withRestRequest(POST, "/_all/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(ADMIN_USER, "SearchRequest")); - } - - @Test - public void shouldPerformSearchWithAllIndexAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = queryStringQueryRequest("_all", QUERY_TITLE_MAGNUM_OPUS); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_all/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldScrollOverSearchResults_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = searchRequestWithScroll(SONG_INDEX_NAME, 2); - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containNotEmptyScrollingId()); - - SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); - - SearchResponse scrollResponse = restHighLevelClient.scroll(scrollRequest, DEFAULT); - assertThat(scrollResponse, isSuccessfulSearchResponse()); - assertThat(scrollResponse, containNotEmptyScrollingId()); - assertThat(scrollResponse, numberOfTotalHitsIsEqualTo(3)); - assertThat(scrollResponse, numberOfHitsInPageIsEqualTo(1)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_search/scroll")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchScrollRequest")); - } - - @Test - public void shouldScrollOverSearchResults_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { - SearchRequest searchRequest = searchRequestWithScroll(SONG_INDEX_NAME, 2); - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containNotEmptyScrollingId()); - - SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); - - assertThatThrownBy(() -> restHighLevelClient.scroll(scrollRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(DOUBLE_READER_USER, "SearchRequest")); - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/_search/scroll")); - auditLogsRule.assertExactlyOne(missingPrivilege(DOUBLE_READER_USER, "SearchScrollRequest")); - } - - @Test - public void shouldGetDocument_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - GetResponse response = restHighLevelClient.get(new GetRequest(SONG_INDEX_NAME, ID_S1), DEFAULT); - - assertThat(response, containDocument(SONG_INDEX_NAME, ID_S1)); - assertThat(response, documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/song_lyrics/_doc/1")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "GetRequest")); - } - - @Test - public void shouldGetDocument_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - GetRequest getRequest = new GetRequest(PROHIBITED_SONG_INDEX_NAME, ID_P4); - assertThatThrownBy(() -> restHighLevelClient.get(getRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/prohibited_song_lyrics/_doc/4")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "GetRequest")); - } - - @Test - public void shouldPerformMultiGetDocuments_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - MultiGetRequest request = new MultiGetRequest(); - request.add(new Item(SONG_INDEX_NAME, ID_S1)); - request.add(new Item(SONG_INDEX_NAME, ID_S2)); - - MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); - - assertThat(response, is(notNullValue())); - assertThat(response, isSuccessfulMultiGetResponse()); - assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); - - MultiGetItemResponse[] responses = response.getResponses(); - assertThat( - responses[0].getResponse(), - allOf(containDocument(SONG_INDEX_NAME, ID_S1), documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)) - ); - assertThat( - responses[1].getResponse(), - allOf(containDocument(SONG_INDEX_NAME, ID_S2), documentContainField(FIELD_TITLE, TITLE_SONG_1_PLUS_1)) - ); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_mget")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetShardRequest")); - } - - @Test - public void shouldPerformMultiGetDocuments_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { - MultiGetRequest request = new MultiGetRequest(); - request.add(new Item(SONG_INDEX_NAME, ID_S1)); - - assertThatThrownBy(() -> restHighLevelClient.mget(request, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/_mget")); - auditLogsRule.assertExactlyOne(missingPrivilege(DOUBLE_READER_USER, "MultiGetRequest")); - } - - @Test - public void shouldPerformMultiGetDocuments_partiallyPositive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - MultiGetRequest request = new MultiGetRequest(); - request.add(new Item(SONG_INDEX_NAME, ID_S1)); - request.add(new Item(PROHIBITED_SONG_INDEX_NAME, ID_P4)); - - MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); - - assertThat(request, notNullValue()); - assertThat(response, not(isSuccessfulMultiGetResponse())); - assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); - - MultiGetItemResponse[] responses = response.getResponses(); - assertThat(responses, arrayContaining(hasProperty("failure", nullValue()), hasProperty("failure", notNullValue()))); - assertThat(responses[1].getFailure().getFailure(), statusException(INTERNAL_SERVER_ERROR)); - assertThat(responses[1].getFailure().getFailure(), errorMessageContain("security_exception")); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_mget")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiGetShardRequest").withIndex(SONG_INDEX_NAME)); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "MultiGetShardRequest").withIndex(PROHIBITED_SONG_INDEX_NAME)); - } - - @Test - public void shouldBeAllowedToPerformMulitSearch_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - MultiSearchRequest request = new MultiSearchRequest(); - request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS)); - request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG)); - - MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response, isSuccessfulMultiSearchResponse()); - assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); - - MultiSearchResponse.Item[] responses = response.getResponses(); - - assertThat(responses[0].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - assertThat(responses[0].getResponse(), searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S1)); - assertThat(responses[1].getResponse(), searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_NEXT_SONG)); - assertThat(responses[1].getResponse(), searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, ID_S3)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_msearch")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiSearchRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldBeAllowedToPerformMulitSearch_partiallyPositive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - MultiSearchRequest request = new MultiSearchRequest(); - request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS)); - request.add(queryStringQueryRequest(PROHIBITED_SONG_INDEX_NAME, QUERY_TITLE_POISON)); - - MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response, not(isSuccessfulMultiSearchResponse())); - assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); - - MultiSearchResponse.Item[] responses = response.getResponses(); - assertThat(responses[0].getFailure(), nullValue()); - assertThat(responses[1].getFailure(), statusException(INTERNAL_SERVER_ERROR)); - assertThat(responses[1].getFailure(), errorMessageContain("security_exception")); - assertThat(responses[1].getResponse(), nullValue()); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_msearch")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "MultiSearchRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(SONG_INDEX_NAME)); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(PROHIBITED_SONG_INDEX_NAME)); - } - - @Test - public void shouldBeAllowedToPerformMulitSearch_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(DOUBLE_READER_USER)) { - MultiSearchRequest request = new MultiSearchRequest(); - request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS)); - request.add(queryStringQueryRequest(SONG_INDEX_NAME, QUERY_TITLE_NEXT_SONG)); - - assertThatThrownBy(() -> restHighLevelClient.msearch(request, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(DOUBLE_READER_USER).withRestRequest(POST, "/_msearch")); - auditLogsRule.assertExactlyOne(missingPrivilege(DOUBLE_READER_USER, "MultiSearchRequest")); - } - - @Test - public void shouldAggregateDataAndComputeAverage_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - final String aggregationName = "averageStars"; - SearchRequest searchRequest = averageAggregationRequest(SONG_INDEX_NAME, aggregationName, FIELD_STARS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(SONG_INDEX_NAME)); - } - - @Test - public void shouldAggregateDataAndComputeAverage_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = averageAggregationRequest(PROHIBITED_SONG_INDEX_NAME, "averageStars", FIELD_STARS); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest").withIndex(PROHIBITED_SONG_INDEX_NAME)); - } - - @Test - public void shouldPerformStatAggregation_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - final String aggregationName = "statsStars"; - SearchRequest searchRequest = statsAggregationRequest(SONG_INDEX_NAME, aggregationName, FIELD_STARS); - - SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "stats")); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/song_lyrics/_search")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldPerformStatAggregation_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SearchRequest searchRequest = statsAggregationRequest(PROHIBITED_SONG_INDEX_NAME, "statsStars", FIELD_STARS); - - assertThatThrownBy(() -> restHighLevelClient.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/prohibited_song_lyrics/_search")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "SearchRequest")); - } - - @Test - public void shouldIndexDocumentInBulkRequest_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); - bulkRequest.setRefreshPolicy(IMMEDIATE); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat(response, successBulkResponse()); - assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, "one")); - assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, "two")); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "one", FIELD_TITLE, TITLE_MAGNUM_OPUS)); - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) - ); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertAtLeast(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER));// sometimes 4 or 6 - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest"));// sometimes 2 or 4 - } - - @Test - public void shouldIndexDocumentInBulkRequest_partiallyPositive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); - bulkRequest.setRefreshPolicy(IMMEDIATE); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat( - response, - bulkResponseContainExceptions(0, allOf(statusException(INTERNAL_SERVER_ERROR), errorMessageContain("security_exception"))) - ); - assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, "two")); - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) - ); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); - } - - @Test - public void shouldIndexDocumentInBulkRequest_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); - bulkRequest.setRefreshPolicy(IMMEDIATE); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat( - response, - allOf( - failureBulkResponse(), - bulkResponseContainExceptions(statusException(INTERNAL_SERVER_ERROR)), - bulkResponseContainExceptions(errorMessageContain("security_exception")) - ) - ); - assertThat(internalClient, not(clusterContainsDocument(SONG_INDEX_NAME, "one"))); - assertThat(internalClient, not(clusterContainsDocument(SONG_INDEX_NAME, "two"))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); - } - - @Test - public void shouldUpdateDocumentsInBulk_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - final String titleOne = "shape of my mind"; - final String titleTwo = "forgiven"; - BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); - restHighLevelClient.bulk(bulkRequest, DEFAULT); - bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new UpdateRequest(WRITE_SONG_INDEX_NAME, "one").doc(Map.of(FIELD_TITLE, titleOne))); - bulkRequest.add(new UpdateRequest(WRITE_SONG_INDEX_NAME, "two").doc(Map.of(FIELD_TITLE, titleTwo))); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat(response, successBulkResponse()); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "one", FIELD_TITLE, titleOne)); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, titleTwo)); - } - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - - } - - @Test - public void shouldUpdateDocumentsInBulk_partiallyPositive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - final String titleOne = "shape of my mind"; - BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - restHighLevelClient.bulk(bulkRequest, DEFAULT); - bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new UpdateRequest(WRITE_SONG_INDEX_NAME, "one").doc(Map.of(FIELD_TITLE, titleOne))); - bulkRequest.add(new UpdateRequest(SONG_INDEX_NAME, ID_S2).doc(Map.of(FIELD_TITLE, "forgiven"))); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat( - response, - bulkResponseContainExceptions(1, allOf(statusException(INTERNAL_SERVER_ERROR), errorMessageContain("security_exception"))) - ); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "one", FIELD_TITLE, titleOne)); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S2, FIELD_TITLE, TITLE_SONG_1_PLUS_1)); - } - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); - } - - @Test - public void shouldUpdateDocumentsInBulk_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new UpdateRequest(SONG_INDEX_NAME, ID_S1).doc(Map.of(FIELD_TITLE, "shape of my mind"))); - bulkRequest.add(new UpdateRequest(SONG_INDEX_NAME, ID_S2).doc(Map.of(FIELD_TITLE, "forgiven"))); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat( - response, - allOf( - failureBulkResponse(), - bulkResponseContainExceptions(statusException(INTERNAL_SERVER_ERROR)), - bulkResponseContainExceptions(errorMessageContain("security_exception")) - ) - ); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S1, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S2, FIELD_TITLE, TITLE_SONG_1_PLUS_1)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); - } - - @Test - public void shouldDeleteDocumentInBulk_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("three").source(SONGS[2].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("four").source(SONGS[3].asMap())); - assertThat(restHighLevelClient.bulk(bulkRequest, DEFAULT), successBulkResponse()); - bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "one")); - bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "three")); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat(response, successBulkResponse()); - assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, "one"))); - assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, "three"))); - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) - ); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "four", FIELD_TITLE, TITLE_POISON)); - } - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - } - - @Test - public void shouldDeleteDocumentInBulk_partiallyPositive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1].asMap())); - assertThat(restHighLevelClient.bulk(bulkRequest, DEFAULT), successBulkResponse()); - bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "one")); - bulkRequest.add(new DeleteRequest(SONG_INDEX_NAME, ID_S3)); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, "one"))); - - assertThat( - response, - bulkResponseContainExceptions(1, allOf(statusException(INTERNAL_SERVER_ERROR), errorMessageContain("security_exception"))) - ); - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(WRITE_SONG_INDEX_NAME, "two", FIELD_TITLE, TITLE_SONG_1_PLUS_1) - ); - assertThat(internalClient, clusterContainsDocumentWithFieldValue(SONG_INDEX_NAME, ID_S3, FIELD_TITLE, TITLE_NEXT_SONG)); - } - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); - } - - @Test - public void shouldDeleteDocumentInBulk_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new DeleteRequest(SONG_INDEX_NAME, ID_S1)); - bulkRequest.add(new DeleteRequest(SONG_INDEX_NAME, ID_S3)); - - BulkResponse response = restHighLevelClient.bulk(bulkRequest, DEFAULT); - - assertThat( - response, - allOf( - failureBulkResponse(), - bulkResponseContainExceptions(statusException(INTERNAL_SERVER_ERROR)), - bulkResponseContainExceptions(errorMessageContain("security_exception")) - ) - ); - assertThat(internalClient, clusterContainsDocument(SONG_INDEX_NAME, ID_S1)); - assertThat(internalClient, clusterContainsDocument(SONG_INDEX_NAME, ID_S3)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); - - } - - @Test - public void shouldReindexDocuments_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { - ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(SONG_INDEX_NAME).setDestIndex(WRITE_SONG_INDEX_NAME); - - BulkByScrollResponse response = restHighLevelClient.reindex(reindexRequest, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response.getBulkFailures(), empty()); - assertThat(response.getSearchFailures(), empty()); - assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_S1)); - assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_S2)); - assertThat(internalClient, clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_S3)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(REINDEXING_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(REINDEXING_USER, "PutMappingRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchScrollRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(REINDEXING_USER)); - auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "ClearScrollRequest")); - } - - @Test - public void shouldReindexDocuments_negativeSource() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { - ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(PROHIBITED_SONG_INDEX_NAME) - .setDestIndex(WRITE_SONG_INDEX_NAME); - - assertThatThrownBy(() -> restHighLevelClient.reindex(reindexRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(internalClient, not(clusterContainsDocument(WRITE_SONG_INDEX_NAME, ID_P4))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "SearchRequest")); - } - - @Test - public void shouldReindexDocuments_negativeDestination() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { - ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(SONG_INDEX_NAME).setDestIndex(PROHIBITED_SONG_INDEX_NAME); - - assertThatThrownBy(() -> restHighLevelClient.reindex(reindexRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(internalClient, not(clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_S1))); - assertThat(internalClient, not(clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_S2))); - assertThat(internalClient, not(clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_S3))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "BulkRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "BulkShardRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "ClearScrollRequest")); - } - - @Test - public void shouldReindexDocuments_negativeSourceAndDestination() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(REINDEXING_USER)) { - ReindexRequest reindexRequest = new ReindexRequest().setSourceIndices(PROHIBITED_SONG_INDEX_NAME).setDestIndex(SONG_INDEX_NAME); - - assertThatThrownBy(() -> restHighLevelClient.reindex(reindexRequest, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(REINDEXING_USER).withRestRequest(POST, "/_reindex")); - auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "ReindexRequest")); - auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "SearchRequest")); - } - - @Test - public void shouldUpdateDocument_positive() throws IOException { - String newField = "newField"; - String newValue = "newValue"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { - UpdateRequest updateRequest = new UpdateRequest(UPDATE_DELETE_OPERATION_INDEX_NAME, DOCUMENT_TO_UPDATE_ID).doc( - newField, - newValue - ).setRefreshPolicy(IMMEDIATE); - - UpdateResponse response = restHighLevelClient.update(updateRequest, DEFAULT); - - assertThat(response, isSuccessfulUpdateResponse()); - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(UPDATE_DELETE_OPERATION_INDEX_NAME, DOCUMENT_TO_UPDATE_ID, newField, newValue) - ); - } - } - - @Test - public void shouldUpdateDocument_negative() throws IOException { - String newField = "newField"; - String newValue = "newValue"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { - UpdateRequest updateRequest = new UpdateRequest(PROHIBITED_SONG_INDEX_NAME, DOCUMENT_TO_UPDATE_ID).doc(newField, newValue) - .setRefreshPolicy(IMMEDIATE); - - assertThatThrownBy(() -> restHighLevelClient.update(updateRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldDeleteDocument_positive() throws IOException { - String docId = "shouldDeleteDocument_positive"; - try (Client client = cluster.getInternalNodeClient()) { - client.index( - new IndexRequest(UPDATE_DELETE_OPERATION_INDEX_NAME).id(docId).source("field", "value").setRefreshPolicy(IMMEDIATE) - ).actionGet(); - assertThat(internalClient, clusterContainsDocument(UPDATE_DELETE_OPERATION_INDEX_NAME, docId)); - } - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { - DeleteRequest deleteRequest = new DeleteRequest(UPDATE_DELETE_OPERATION_INDEX_NAME, docId).setRefreshPolicy(IMMEDIATE); - - DeleteResponse response = restHighLevelClient.delete(deleteRequest, DEFAULT); - - assertThat(response, isSuccessfulDeleteResponse()); - assertThat(internalClient, not(clusterContainsDocument(UPDATE_DELETE_OPERATION_INDEX_NAME, docId))); - } - } - - @Test - public void shouldDeleteDocument_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(UPDATE_DELETE_USER)) { - DeleteRequest deleteRequest = new DeleteRequest(PROHIBITED_SONG_INDEX_NAME, ID_S1).setRefreshPolicy(IMMEDIATE); - - assertThatThrownBy(() -> restHighLevelClient.delete(deleteRequest, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldCreateAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - AliasActions aliasAction = new AliasActions(ADD).indices(SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); - IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); - - var response = restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, clusterContainsDocument(TEMPORARY_ALIAS_NAME, ID_S1)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); - auditLogsRule.assertExactly(2, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_READ_USER)); - } - - @Test - public void shouldCreateAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - AliasActions aliasAction = new AliasActions(ADD).indices(PROHIBITED_SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); - IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); - - assertThatThrownBy( - () -> restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT), - statusException(FORBIDDEN) - ); - - assertThat(internalClient, not(clusterContainsDocument(TEMPORARY_ALIAS_NAME, ID_P4))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); - } - - @Test - public void shouldDeleteAlias_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - AliasActions aliasAction = new AliasActions(ADD).indices(SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); - IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); - restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT); - aliasAction = new AliasActions(REMOVE).indices(SONG_INDEX_NAME).alias(TEMPORARY_ALIAS_NAME); - indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); - - var response = restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, not(clusterContainsDocument(TEMPORARY_ALIAS_NAME, ID_S1))); - } - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); - auditLogsRule.assertExactly(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_READ_USER)); - } - - @Test - public void shouldDeleteAlias_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - AliasActions aliasAction = new AliasActions(REMOVE).indices(PROHIBITED_SONG_INDEX_NAME).alias(PROHIBITED_SONG_ALIAS); - IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest().addAliasAction(aliasAction); - - assertThatThrownBy( - () -> restHighLevelClient.indices().updateAliases(indicesAliasesRequest, DEFAULT), - statusException(FORBIDDEN) - ); - - assertThat(internalClient, clusterContainsDocument(PROHIBITED_SONG_INDEX_NAME, ID_P4)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(POST, "/_aliases")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); - } - - @Test - public void shouldCreateIndexTemplate_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002)); - - var response = restHighLevelClient.indices().putTemplate(request, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); - String documentId = "0001"; - IndexRequest indexRequest = new IndexRequest(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ).id(documentId) - .source(SONGS[0].asMap()) - .setRefreshPolicy(IMMEDIATE); - restHighLevelClient.index(indexRequest, DEFAULT); - assertThat(internalClient, clusterContainsDocument(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ, documentId)); - assertThat(internalClient, clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001, documentId)); - assertThat(internalClient, clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002, documentId)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_template/musical-index-template")); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/song-transcription-jazz/_doc/0001")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "IndexRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(8, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - } - - @Test - public void shouldCreateIndexTemplate_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002)); - - assertThatThrownBy(() -> restHighLevelClient.indices().putTemplate(request, DEFAULT), statusException(FORBIDDEN)); - assertThat(internalClient, not(clusterContainTemplate(MUSICAL_INDEX_TEMPLATE))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_template/musical-index-template")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "PutIndexTemplateRequest")); - } - - @Test - public void shouldDeleteTemplate_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)); - restHighLevelClient.indices().putTemplate(request, DEFAULT); - assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); - DeleteIndexTemplateRequest deleteRequest = new DeleteIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE); - - var response = restHighLevelClient.indices().deleteTemplate(deleteRequest, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, not(clusterContainTemplate(MUSICAL_INDEX_TEMPLATE))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_template/musical-index-template")); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(DELETE, "/_template/musical-index-template")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "DeleteIndexTemplateRequest")); - auditLogsRule.assertExactly(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - } - - @Test - public void shouldDeleteTemplate_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - DeleteIndexTemplateRequest deleteRequest = new DeleteIndexTemplateRequest(UNDELETABLE_TEMPLATE_NAME); - - assertThatThrownBy(() -> restHighLevelClient.indices().deleteTemplate(deleteRequest, DEFAULT), statusException(FORBIDDEN)); - - assertThat(internalClient, clusterContainTemplate(UNDELETABLE_TEMPLATE_NAME)); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_READ_USER).withRestRequest(DELETE, "/_template/undeletable-template-name") - ); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "DeleteIndexTemplateRequest")); - } - - @Test - public void shouldUpdateTemplate_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - PutIndexTemplateRequest request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002)); - restHighLevelClient.indices().putTemplate(request, DEFAULT); - assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); - request = new PutIndexTemplateRequest(MUSICAL_INDEX_TEMPLATE).patterns(List.of(TEMPLATE_INDEX_PREFIX)) - .alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003)); - - var response = restHighLevelClient.indices().putTemplate(request, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - String documentId = "000one"; - IndexRequest indexRequest = new IndexRequest(INDEX_NAME_SONG_TRANSCRIPTION_JAZZ).id(documentId) - .source(SONGS[0].asMap()) - .setRefreshPolicy(IMMEDIATE); - restHighLevelClient.index(indexRequest, DEFAULT); - assertThat(internalClient, clusterContainTemplate(MUSICAL_INDEX_TEMPLATE)); - assertThat(internalClient, clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003, documentId)); - assertThat(internalClient, not(clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0001, documentId))); - assertThat(internalClient, not(clusterContainsDocument(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0002, documentId))); - } - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_template/musical-index-template")); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/song-transcription-jazz/_doc/000one")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "IndexRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(10, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - } - - @Test - public void shouldUpdateTemplate_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - PutIndexTemplateRequest request = new PutIndexTemplateRequest(UNDELETABLE_TEMPLATE_NAME).patterns( - List.of(TEMPLATE_INDEX_PREFIX) - ).alias(new Alias(ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003)); - - assertThatThrownBy(() -> restHighLevelClient.indices().putTemplate(request, DEFAULT), statusException(FORBIDDEN)); - assertThat(internalClient, clusterContainTemplateWithAlias(UNDELETABLE_TEMPLATE_NAME, ALIAS_FROM_UNDELETABLE_TEMPLATE)); - assertThat( - internalClient, - not(clusterContainTemplateWithAlias(UNDELETABLE_TEMPLATE_NAME, ALIAS_USED_IN_MUSICAL_INDEX_TEMPLATE_0003)) - ); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_template/undeletable-template-name")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "PutIndexTemplateRequest")); - } - - @Test - public void shouldGetFieldCapabilitiesForAllIndexes_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { - FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().fields(FIELD_TITLE); - - FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response, containsExactlyIndices(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME, UPDATE_DELETE_OPERATION_INDEX_NAME)); - assertThat(response, numberOfFieldsIsEqualTo(1)); - assertThat(response, containsFieldWithNameAndType(FIELD_TITLE, "text")); - } - auditLogsRule.assertExactlyOne(userAuthenticated(ADMIN_USER).withRestRequest(GET, "/_field_caps")); - auditLogsRule.assertExactlyOne(grantedPrivilege(ADMIN_USER, "FieldCapabilitiesRequest")); - auditLogsRule.assertExactly(3, grantedPrivilege(ADMIN_USER, "FieldCapabilitiesIndexRequest")); - } - - @Test - public void shouldGetFieldCapabilitiesForAllIndexes_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().fields(FIELD_TITLE); - - assertThatThrownBy(() -> restHighLevelClient.fieldCaps(request, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/_field_caps")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "FieldCapabilitiesRequest")); - } - - @Test - public void shouldGetFieldCapabilitiesForParticularIndex_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(SONG_INDEX_NAME).fields(FIELD_TITLE); - - FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); - - assertThat(response, notNullValue()); - assertThat(response, containsExactlyIndices(SONG_INDEX_NAME)); - assertThat(response, numberOfFieldsIsEqualTo(1)); - assertThat(response, containsFieldWithNameAndType(FIELD_TITLE, "text")); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/song_lyrics/_field_caps")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "FieldCapabilitiesRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "FieldCapabilitiesIndexRequest")); - } - - @Test - public void shouldGetFieldCapabilitiesForParticularIndex_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(PROHIBITED_SONG_INDEX_NAME).fields(FIELD_TITLE); - - assertThatThrownBy(() -> restHighLevelClient.fieldCaps(request, DEFAULT), statusException(FORBIDDEN)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(GET, "/prohibited_song_lyrics/_field_caps")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "FieldCapabilitiesRequest")); - } - - @Test - public void shouldCreateSnapshotRepository_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - String snapshotDirPath = cluster.getSnapshotDirPath(); - - var response = steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotDirPath, "fs"); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - } - - @Test - public void shouldCreateSnapshotRepository_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - String snapshotDirPath = cluster.getSnapshotDirPath(); - - assertThatThrownBy( - () -> steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotDirPath, "fs"), - statusException(FORBIDDEN) - ); - assertThat(internalClient, not(clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "PutRepositoryRequest")); - } - - @Test - public void shouldDeleteSnapshotRepository_positive() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - assertThat(internalClient, clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME)); - - var response = steps.deleteSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME); - - assertThat(response, notNullValue()); - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, not(clusterContainsSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(DELETE, "/_snapshot/test-snapshot-repository") - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "DeleteRepositoryRequest")); - } - - @Test - public void shouldDeleteSnapshotRepository_negative() throws IOException { - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - - assertThatThrownBy(() -> steps.deleteSnapshotRepository(UNUSED_SNAPSHOT_REPOSITORY_NAME), statusException(FORBIDDEN)); - assertThat(internalClient, clusterContainsSnapshotRepository(UNUSED_SNAPSHOT_REPOSITORY_NAME)); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_READ_USER).withRestRequest(DELETE, "/_snapshot/unused-snapshot-repository") - ); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "DeleteRepositoryRequest")); - } - - @Test // Bug which can be reproduced with the below test: https://github.com/opensearch-project/security/issues/2169 - public void shouldCreateSnapshot_positive() throws IOException { - final String snapshotName = "snapshot-positive-test"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - - CreateSnapshotResponse response = steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME); - - assertThat(response, notNullValue()); - assertThat(response.status(), equalTo(RestStatus.ACCEPTED)); - steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - assertThat(internalClient, clusterContainSuccessSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/snapshot-positive-test") - ); - auditLogsRule.assertAtLeast( - 1, - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/snapshot-positive-test") - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); - } - - @Test - public void shouldCreateSnapshot_negative() throws IOException { - final String snapshotName = "snapshot-negative-test"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - - assertThatThrownBy( - () -> steps.createSnapshot(UNUSED_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME), - statusException(FORBIDDEN) - ); - - assertThat(internalClient, snapshotInClusterDoesNotExists(UNUSED_SNAPSHOT_REPOSITORY_NAME, snapshotName)); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_READ_USER).withRestRequest(PUT, "/_snapshot/unused-snapshot-repository/snapshot-negative-test") - ); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "CreateSnapshotRequest")); - } - - @Test - public void shouldDeleteSnapshot_positive() throws IOException { - String snapshotName = "delete-snapshot-positive"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - restHighLevelClient.snapshot(); - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME); - steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - - var response = steps.deleteSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - - assertThat(response.isAcknowledged(), equalTo(true)); - assertThat(internalClient, snapshotInClusterDoesNotExists(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") - ); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(DELETE, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") - ); - auditLogsRule.assertAtLeast( - 1, - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "DeleteSnapshotRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); - } - - @Test - public void shouldDeleteSnapshot_negative() throws IOException { - String snapshotName = "delete-snapshot-negative"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, SONG_INDEX_NAME); - steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - } - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - assertThatThrownBy(() -> steps.deleteSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName), statusException(FORBIDDEN)); - - assertThat(internalClient, clusterContainSuccessSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName)); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") - ); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_READ_USER).withRestRequest(DELETE, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") - ); - auditLogsRule.assertAtLeast( - 1, - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); - auditLogsRule.assertExactly(1, missingPrivilege(LIMITED_READ_USER, "DeleteSnapshotRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); - } - - @Test - public void shouldRestoreSnapshot_positive() throws IOException { - final String snapshotName = "restore-snapshot-positive"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - // 1. create some documents - BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Eins").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1].asMap())); - bulkRequest.setRefreshPolicy(IMMEDIATE); - restHighLevelClient.bulk(bulkRequest, DEFAULT); - - // 2. create snapshot repository - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - - // 3. create snapshot - steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, WRITE_SONG_INDEX_NAME); - - // 4. wait till snapshot is ready - steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - - // 5. introduce some changes - bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Drei").source(SONGS[2].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Vier").source(SONGS[3].asMap())); - bulkRequest.add(new DeleteRequest(WRITE_SONG_INDEX_NAME, "Eins")); - bulkRequest.setRefreshPolicy(IMMEDIATE); - restHighLevelClient.bulk(bulkRequest, DEFAULT); - - // 6. restore the snapshot - var response = steps.restoreSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, "(.+)", "restored_$1"); - - assertThat(response, notNullValue()); - assertThat(response.status(), equalTo(ACCEPTED)); - - // 7. wait until snapshot is restored - CountRequest countRequest = new CountRequest(RESTORED_SONG_INDEX_NAME); - Awaitility.await() - .ignoreExceptions() - .alias("Index contains proper number of documents restored from snapshot.") - .until(() -> restHighLevelClient.count(countRequest, DEFAULT).getCount() == 2); - - // 8. verify that document are present in restored index - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(RESTORED_SONG_INDEX_NAME, "Eins", FIELD_TITLE, TITLE_MAGNUM_OPUS) - ); - assertThat( - internalClient, - clusterContainsDocumentWithFieldValue(RESTORED_SONG_INDEX_NAME, "Zwei", FIELD_TITLE, TITLE_SONG_1_PLUS_1) - ); - assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Drei"))); - assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Vier"))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository/restore-snapshot-positive") - ); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest( - POST, - "/_snapshot/test-snapshot-repository/restore-snapshot-positive/_restore" - ) - ); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/restored_write_song_index/_count")); - auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertAtLeast( - 1, - userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/restore-snapshot-positive") - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "SearchRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - } - - @Test - public void shouldRestoreSnapshot_failureForbiddenIndex() throws IOException { - final String snapshotName = "restore-snapshot-negative-forbidden-index"; - String restoreToIndex = "forbidden_index"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - // 1. create some documents - BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Eins").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1].asMap())); - bulkRequest.setRefreshPolicy(IMMEDIATE); - restHighLevelClient.bulk(bulkRequest, DEFAULT); - - // 2. create snapshot repository - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - - // 3. create snapshot - steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, WRITE_SONG_INDEX_NAME); - - // 4. wait till snapshot is ready - steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - - // 5. restore the snapshot - assertThatThrownBy( - () -> steps.restoreSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, "(.+)", restoreToIndex), - statusException(FORBIDDEN) - ); - - // 6. verify that document are not present in restored index - assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Eins"))); - assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Zwei"))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest( - PUT, - "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index" - ) - ); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest( - POST, - "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index/_restore" - ) - ); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertAtLeast( - 1, - userAuthenticated(LIMITED_WRITE_USER).withRestRequest( - GET, - "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index" - ) - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); - } - - @Test - public void shouldRestoreSnapshot_failureOperationForbidden() throws IOException { - String snapshotName = "restore-snapshot-negative-forbidden-operation"; - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - // 1. create some documents - BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Eins").source(SONGS[0].asMap())); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1].asMap())); - bulkRequest.setRefreshPolicy(IMMEDIATE); - restHighLevelClient.bulk(bulkRequest, DEFAULT); - - // 2. create snapshot repository - steps.createSnapshotRepository(TEST_SNAPSHOT_REPOSITORY_NAME, cluster.getSnapshotDirPath(), "fs"); - - // 3. create snapshot - steps.createSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, WRITE_SONG_INDEX_NAME); - - // 4. wait till snapshot is ready - steps.waitForSnapshotCreation(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName); - } - // 5. restore the snapshot - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_READ_USER)) { - SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); - assertThatThrownBy( - () -> steps.restoreSnapshot(TEST_SNAPSHOT_REPOSITORY_NAME, snapshotName, "(.+)", "restored_$1"), - statusException(FORBIDDEN) - ); - - // 6. verify that documents does not exist - assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Eins"))); - assertThat(internalClient, not(clusterContainsDocument(RESTORED_SONG_INDEX_NAME, "Zwei"))); - } - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(PUT, "/_snapshot/test-snapshot-repository")); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_WRITE_USER).withRestRequest( - PUT, - "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation" - ) - ); - auditLogsRule.assertExactlyOne( - userAuthenticated(LIMITED_READ_USER).withRestRequest( - POST, - "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation/_restore" - ) - ); - auditLogsRule.assertExactlyOne(userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); - auditLogsRule.assertAtLeast( - 1, - userAuthenticated(LIMITED_WRITE_USER).withRestRequest( - GET, - "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation" - ) - ); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); - auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); - auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); - auditLogsRule.assertExactly(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); - auditLogsRule.assertExactly(1, missingPrivilege(LIMITED_READ_USER, "RestoreSnapshotRequest")); - auditLogsRule.assertAtLeast(2, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); - auditLogsRule.assertExactly(6, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); - } - - @Test - // required permissions: "indices:admin/create" - public void createIndex_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_positive"); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); - CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, DEFAULT); - - assertThat(createIndexResponse, isSuccessfulCreateIndexResponse(indexName)); - assertThat(cluster, indexExists(indexName)); - } - } - - @Test - public void createIndex_negative() throws IOException { - String indexName = "create_index_negative"; - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); - - assertThatThrownBy(() -> restHighLevelClient.indices().create(createIndexRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(indexName))); - } - } - - @Test - // required permissions: "indices:admin/get" - public void checkIfIndexExists_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("index_exists_positive"); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - boolean exists = restHighLevelClient.indices().exists(new GetIndexRequest(indexName), DEFAULT); - - assertThat(exists, is(false)); - } - } - - @Test - public void checkIfIndexExists_negative() throws IOException { - String indexThatUserHasNoAccessTo = "index_exists_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - assertThatThrownBy( - () -> restHighLevelClient.indices().exists(new GetIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .exists(new GetIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy(() -> restHighLevelClient.indices().exists(new GetIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - // required permissions: "indices:admin/delete" - public void deleteIndex_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("delete_index_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName); - var response = restHighLevelClient.indices().delete(deleteIndexRequest, DEFAULT); - - assertThat(response.isAcknowledged(), is(true)); - assertThat(cluster, not(indexExists(indexName))); - } - } - - @Test - public void deleteIndex_negative() throws IOException { - String indexThatUserHasNoAccessTo = "delete_index_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices().delete(new DeleteIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .delete(new DeleteIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().delete(new DeleteIndexRequest("*"), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - // required permissions: indices:admin/aliases, indices:admin/delete - public void shouldDeleteIndexByAliasRequest_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("delete_index_by_alias_request_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - IndicesAliasesRequest request = new IndicesAliasesRequest().addAliasAction(new AliasActions(REMOVE_INDEX).indices(indexName)); - - var response = restHighLevelClient.indices().updateAliases(request, DEFAULT); - - assertThat(response.isAcknowledged(), is(true)); - assertThat(cluster, not(indexExists(indexName))); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES).withRestRequest(POST, "/_aliases") - ); - auditLogsRule.assertExactly( - 2, - grantedPrivilege(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, "IndicesAliasesRequest") - ); - auditLogsRule.assertExactly( - 2, - auditPredicate(INDEX_EVENT).withEffectiveUser(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES) - ); - } - - @Test - public void shouldDeleteIndexByAliasRequest_negative() throws IOException { - String indexName = "delete_index_by_alias_request_negative"; - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - IndicesAliasesRequest request = new IndicesAliasesRequest().addAliasAction(new AliasActions(REMOVE_INDEX).indices(indexName)); - - assertThatThrownBy(() -> restHighLevelClient.indices().updateAliases(request, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - // required permissions: "indices:admin/get" - public void getIndex_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("get_index_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - GetIndexRequest getIndexRequest = new GetIndexRequest(indexName); - GetIndexResponse response = restHighLevelClient.indices().get(getIndexRequest, DEFAULT); - - assertThat(response, getIndexResponseContainsIndices(indexName)); - } - } - - @Test - public void getIndex_negative() throws IOException { - String indexThatUserHasNoAccessTo = "get_index_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices().get(new GetIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().get(new GetIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy(() -> restHighLevelClient.indices().get(new GetIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - // required permissions: "indices:admin/close", "indices:admin/close*" - public void closeIndex_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("close_index_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - CloseIndexRequest closeIndexRequest = new CloseIndexRequest(indexName); - CloseIndexResponse response = restHighLevelClient.indices().close(closeIndexRequest, DEFAULT); - - assertThat(response, isSuccessfulCloseIndexResponse()); - assertThat(cluster, indexStateIsEqualTo(indexName, IndexMetadata.State.CLOSE)); - } - } - - @Test - public void closeIndex_negative() throws IOException { - String indexThatUserHasNoAccessTo = "close_index_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices().close(new CloseIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .close(new CloseIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy(() -> restHighLevelClient.indices().close(new CloseIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - // required permissions: "indices:admin/open" - public void openIndex_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("open_index_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - IndexOperationsHelper.closeIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - OpenIndexRequest closeIndexRequest = new OpenIndexRequest(indexName); - OpenIndexResponse response = restHighLevelClient.indices().open(closeIndexRequest, DEFAULT); - - assertThat(response, isSuccessfulOpenIndexResponse()); - assertThat(cluster, indexStateIsEqualTo(indexName, IndexMetadata.State.OPEN)); - } - } - - @Test - public void openIndex_negative() throws IOException { - String indexThatUserHasNoAccessTo = "open_index_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices().open(new OpenIndexRequest(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .open(new OpenIndexRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy(() -> restHighLevelClient.indices().open(new OpenIndexRequest("*"), DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - @Ignore - // required permissions: "indices:admin/resize", "indices:monitor/stats - // todo even when I assign the `indices:admin/resize` and `indices:monitor/stats` permissions to test user, this test fails. - // Issue: https://github.com/opensearch-project/security/issues/2141 - public void shrinkIndex_positive() throws IOException { - String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_positive_source"); - Settings sourceIndexSettings = Settings.builder().put("index.blocks.write", true).put("index.number_of_shards", 2).build(); - String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_positive_target"); - IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - ResizeResponse response = restHighLevelClient.indices().shrink(resizeRequest, DEFAULT); - - assertThat(response, isSuccessfulResizeResponse(targetIndexName)); - assertThat(cluster, indexExists(targetIndexName)); - } - } - - @Test - public void shrinkIndex_negative() throws IOException { - // user cannot access target index - String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_negative_source"); - String targetIndexName = "shrink_index_negative_target"; - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - - assertThatThrownBy(() -> restHighLevelClient.indices().shrink(resizeRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(targetIndexName))); - } - - // user cannot access source index - sourceIndexName = "shrink_index_negative_source"; - targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_negative_target"); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - - assertThatThrownBy(() -> restHighLevelClient.indices().shrink(resizeRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(targetIndexName))); - } - } - - @Test - @Ignore - // required permissions: "indices:admin/resize", "indices:monitor/stats - // todo even when I assign the `indices:admin/resize` and `indices:monitor/stats` permissions to test user, this test fails. - // Issue: https://github.com/opensearch-project/security/issues/2141 - public void cloneIndex_positive() throws IOException { - String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_positive_source"); - Settings sourceIndexSettings = Settings.builder().put("index.blocks.write", true).build(); - String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_positive_target"); - IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - ResizeResponse response = restHighLevelClient.indices().clone(resizeRequest, DEFAULT); - - assertThat(response, isSuccessfulResizeResponse(targetIndexName)); - assertThat(cluster, indexExists(targetIndexName)); - } - } - - @Test - public void cloneIndex_negative() throws IOException { - // user cannot access target index - String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_negative_source"); - String targetIndexName = "clone_index_negative_target"; - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - - assertThatThrownBy(() -> restHighLevelClient.indices().clone(resizeRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(targetIndexName))); - } - - // user cannot access source index - sourceIndexName = "clone_index_negative_source"; - targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clone_index_negative_target"); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - - assertThatThrownBy(() -> restHighLevelClient.indices().clone(resizeRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(targetIndexName))); - } - } - - @Test - @Ignore - // required permissions: "indices:admin/resize", "indices:monitor/stats - // todo even when I assign the `indices:admin/resize` and `indices:monitor/stats` permissions to test user, this test fails. - // Issue: https://github.com/opensearch-project/security/issues/2141 - public void splitIndex_positive() throws IOException { - String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_positive_source"); - Settings sourceIndexSettings = Settings.builder().put("index.blocks.write", true).build(); - String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_positive_target"); - IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build()); - ResizeResponse response = restHighLevelClient.indices().split(resizeRequest, DEFAULT); - - assertThat(response, isSuccessfulResizeResponse(targetIndexName)); - assertThat(cluster, indexExists(targetIndexName)); - } - } - - @Test - public void splitIndex_negative() throws IOException { - // user cannot access target index - String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_negative_source"); - String targetIndexName = "split_index_negative_target"; - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build()); - - assertThatThrownBy(() -> restHighLevelClient.indices().split(resizeRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(targetIndexName))); - } - - // user cannot access source index - sourceIndexName = "split_index_negative_source"; - targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("split_index_negative_target"); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ResizeRequest resizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); - resizeRequest.setSettings(Settings.builder().put("index.number_of_shards", 2).build()); - - assertThatThrownBy(() -> restHighLevelClient.indices().split(resizeRequest, DEFAULT), statusException(FORBIDDEN)); - assertThat(cluster, not(indexExists(targetIndexName))); - } - } - - @Test - // required permissions: "indices:monitor/settings/get" - public void getIndexSettings_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("get_index_settings_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - GetSettingsRequest getSettingsRequest = new GetSettingsRequest().indices(indexName); - GetSettingsResponse response = restHighLevelClient.indices().getSettings(getSettingsRequest, DEFAULT); - - assertThat(response, getSettingsResponseContainsIndices(indexName)); - } - } - - @Test - public void getIndexSettings_negative() throws IOException { - String indexThatUserHasNoAccessTo = "get_index_settings_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - assertThatThrownBy( - () -> restHighLevelClient.indices().getSettings(new GetSettingsRequest().indices(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .getSettings(new GetSettingsRequest().indices(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().getSettings(new GetSettingsRequest().indices("*"), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - // required permissions: "indices:admin/settings/update" - public void updateIndexSettings_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("update_index_settings_positive"); - Settings initialSettings = Settings.builder().put("index.number_of_replicas", "2").build(); - Settings updatedSettings = Settings.builder().put("index.number_of_replicas", "4").build(); - IndexOperationsHelper.createIndex(cluster, indexName, initialSettings); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - UpdateSettingsRequest updateSettingsRequest = new UpdateSettingsRequest(indexName).settings(updatedSettings); - var response = restHighLevelClient.indices().putSettings(updateSettingsRequest, DEFAULT); - - assertThat(response.isAcknowledged(), is(true)); - assertThat(cluster, indexSettingsContainValues(indexName, updatedSettings)); - } - } - - @Test - public void updateIndexSettings_negative() throws IOException { - String indexThatUserHasNoAccessTo = "update_index_settings_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - Settings settingsToUpdate = Settings.builder().put("index.number_of_replicas", 2).build(); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices() - .putSettings(new UpdateSettingsRequest(indexThatUserHasNoAccessTo).settings(settingsToUpdate), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .putSettings( - new UpdateSettingsRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo).settings(settingsToUpdate), - DEFAULT - ), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().putSettings(new UpdateSettingsRequest("*").settings(settingsToUpdate), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - // required permissions: indices:admin/mapping/put - public void createIndexMappings_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_mappings_positive"); - Map indexMapping = Map.of("properties", Map.of("message", Map.of("type", "text"))); - IndexOperationsHelper.createIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - PutMappingRequest putMappingRequest = new PutMappingRequest(indexName).source(indexMapping); - var response = restHighLevelClient.indices().putMapping(putMappingRequest, DEFAULT); - - assertThat(response.isAcknowledged(), is(true)); - assertThat(cluster, indexMappingIsEqualTo(indexName, indexMapping)); - } - } - - @Test - public void createIndexMappings_negative() throws IOException { - String indexThatUserHasNoAccessTo = "create_index_mappings_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - Map indexMapping = Map.of("properties", Map.of("message", Map.of("type", "text"))); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices() - .putMapping(new PutMappingRequest(indexThatUserHasNoAccessTo).source(indexMapping), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .putMapping(new PutMappingRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo).source(indexMapping), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().putMapping(new PutMappingRequest("*").source(indexMapping), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - // required permissions: indices:admin/mappings/get - public void getIndexMappings_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("get_index_mappings_positive"); - Map indexMapping = Map.of("properties", Map.of("message", Map.of("type", "text"))); - IndexOperationsHelper.createIndex(cluster, indexName); - IndexOperationsHelper.createMapping(cluster, indexName, indexMapping); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - GetMappingsRequest getMappingsRequest = new GetMappingsRequest().indices(indexName); - GetMappingsResponse response = restHighLevelClient.indices().getMapping(getMappingsRequest, DEFAULT); - - assertThat(response, getMappingsResponseContainsIndices(indexName)); - } - } - - @Test - public void getIndexMappings_negative() throws IOException { - String indexThatUserHasNoAccessTo = "get_index_mappings_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices().getMapping(new GetMappingsRequest().indices(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .getMapping(new GetMappingsRequest().indices(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().getMapping(new GetMappingsRequest().indices("*"), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - // required permissions: "indices:admin/cache/clear" - public void clearIndexCache_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("clear_index_cache_positive"); - IndexOperationsHelper.createIndex(cluster, indexName); - - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - ClearIndicesCacheRequest clearIndicesCacheRequest = new ClearIndicesCacheRequest(indexName); - ClearIndicesCacheResponse response = restHighLevelClient.indices().clearCache(clearIndicesCacheRequest, DEFAULT); - - assertThat(response, isSuccessfulClearIndicesCacheResponse()); - } - } - - @Test - public void clearIndexCache_negative() throws IOException { - String indexThatUserHasNoAccessTo = "clear_index_cache_negative"; - String indexThatUserHasAccessTo = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat(indexThatUserHasNoAccessTo); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - - assertThatThrownBy( - () -> restHighLevelClient.indices().clearCache(new ClearIndicesCacheRequest(indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices() - .clearCache(new ClearIndicesCacheRequest(indexThatUserHasAccessTo, indexThatUserHasNoAccessTo), DEFAULT), - statusException(FORBIDDEN) - ); - assertThatThrownBy( - () -> restHighLevelClient.indices().clearCache(new ClearIndicesCacheRequest("*"), DEFAULT), - statusException(FORBIDDEN) - ); - } - } - - @Test - // required permissions: "indices:admin/create", "indices:admin/aliases" - public void shouldCreateIndexWithAlias_positive() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_with_alias_positive"); - try ( - RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( - USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES - ) - ) { - CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName).alias( - new Alias(ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE) - ); - - CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, DEFAULT); - - assertThat(createIndexResponse, isSuccessfulCreateIndexResponse(indexName)); - assertThat(cluster, indexExists(indexName)); - assertThat(internalClient, aliasExists(ALIAS_CREATE_INDEX_WITH_ALIAS_POSITIVE)); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES).withRestRequest( - PUT, - "/index_operations_create_index_with_alias_positive" - ) - ); - auditLogsRule.assertExactly( - 2, - grantedPrivilege(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, "CreateIndexRequest") - ); - auditLogsRule.assertExactly( - 2, - auditPredicate(INDEX_EVENT).withEffectiveUser(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES) - ); - } - - @Test - public void shouldCreateIndexWithAlias_negative() throws IOException { - String indexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("create_index_with_alias_negative"); - try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(USER_ALLOWED_TO_CREATE_INDEX)) { - CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName).alias( - new Alias(ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE) - ); - - assertThatThrownBy(() -> restHighLevelClient.indices().create(createIndexRequest, DEFAULT), statusException(FORBIDDEN)); - - assertThat(internalClient, not(aliasExists(ALIAS_CREATE_INDEX_WITH_ALIAS_NEGATIVE))); - } - auditLogsRule.assertExactlyOne( - userAuthenticated(USER_ALLOWED_TO_CREATE_INDEX).withRestRequest(PUT, "/index_operations_create_index_with_alias_negative") - ); - auditLogsRule.assertExactlyOne(missingPrivilege(USER_ALLOWED_TO_CREATE_INDEX, "CreateIndexRequest")); - } -} diff --git a/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java b/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java deleted file mode 100644 index 9df611e207..0000000000 --- a/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java +++ /dev/null @@ -1,270 +0,0 @@ -/* -* 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.http; - -import java.io.IOException; -import java.security.KeyPair; -import java.util.Base64; -import java.util.List; -import java.util.Map; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.security.Keys; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.message.BasicHeader; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.opensearch.action.search.SearchRequest; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.client.Client; -import org.opensearch.client.RestHighLevelClient; -import org.opensearch.test.framework.JwtConfigBuilder; -import org.opensearch.test.framework.TestSecurityConfig; -import org.opensearch.test.framework.TestSecurityConfig.Role; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; -import org.opensearch.test.framework.cluster.TestRestClient; -import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; -import org.opensearch.test.framework.log.LogsRule; - -import static java.nio.charset.StandardCharsets.US_ASCII; -import static org.apache.http.HttpHeaders.AUTHORIZATION; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.opensearch.client.RequestOptions.DEFAULT; -import static org.opensearch.core.rest.RestStatus.FORBIDDEN; -import static org.opensearch.security.Song.FIELD_TITLE; -import static org.opensearch.security.Song.QUERY_TITLE_MAGNUM_OPUS; -import static org.opensearch.security.Song.SONGS; -import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.BASIC_AUTH_DOMAIN_ORDER; -import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; -import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; - -@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class JwtAuthenticationTests { - - public static final String CLAIM_USERNAME = "preferred-username"; - public static final String CLAIM_ROLES = "backend-user-roles"; - - public static final String USER_SUPERHERO = "superhero"; - public static final String USERNAME_ROOT = "root"; - public static final String ROLE_ADMIN = "role_admin"; - public static final String ROLE_DEVELOPER = "role_developer"; - public static final String ROLE_QA = "role_qa"; - public static final String ROLE_CTO = "role_cto"; - public static final String ROLE_CEO = "role_ceo"; - public static final String ROLE_VP = "role_vp"; - public static final String POINTER_BACKEND_ROLES = "/backend_roles"; - public static final String POINTER_USERNAME = "/user_name"; - - public static final String QA_DEPARTMENT = "qa-department"; - - public static final String CLAIM_DEPARTMENT = "department"; - - public static final String DEPARTMENT_SONG_INDEX_PATTERN = String.format("song_lyrics_${attr.jwt.%s}", CLAIM_DEPARTMENT); - - public static final String QA_SONG_INDEX_NAME = String.format("song_lyrics_%s", QA_DEPARTMENT); - - private static final KeyPair KEY_PAIR = Keys.keyPairFor(SignatureAlgorithm.RS256); - private static final String PUBLIC_KEY = new String(Base64.getEncoder().encode(KEY_PAIR.getPublic().getEncoded()), US_ASCII); - - static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); - - private static final String JWT_AUTH_HEADER = "jwt-auth"; - - private static final JwtAuthorizationHeaderFactory tokenFactory = new JwtAuthorizationHeaderFactory( - KEY_PAIR.getPrivate(), - CLAIM_USERNAME, - CLAIM_ROLES, - JWT_AUTH_HEADER - ); - - public static final TestSecurityConfig.AuthcDomain JWT_AUTH_DOMAIN = new TestSecurityConfig.AuthcDomain( - "jwt", - BASIC_AUTH_DOMAIN_ORDER - 1 - ).jwtHttpAuthenticator( - new JwtConfigBuilder().jwtHeader(JWT_AUTH_HEADER).signingKey(PUBLIC_KEY).subjectKey(CLAIM_USERNAME).rolesKey(CLAIM_ROLES) - ).backend("noop"); - public static final String SONG_ID_1 = "song-id-01"; - - public static final Role DEPARTMENT_SONG_LISTENER_ROLE = new Role("department-song-listener-role").indexPermissions( - "indices:data/read/search" - ).on(DEPARTMENT_SONG_INDEX_PATTERN); - - @ClassRule - public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE) - .anonymousAuth(false) - .nodeSettings( - Map.of("plugins.security.restapi.roles_enabled", List.of("user_" + ADMIN_USER.getName() + "__" + ALL_ACCESS.getName())) - ) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .users(ADMIN_USER) - .roles(DEPARTMENT_SONG_LISTENER_ROLE) - .authc(JWT_AUTH_DOMAIN) - .build(); - - @Rule - public LogsRule logsRule = new LogsRule("com.amazon.dlic.auth.http.jwt.HTTPJwtAuthenticator"); - - @BeforeClass - public static void createTestData() { - try (Client client = cluster.getInternalNodeClient()) { - client.prepareIndex(QA_SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); - } - try (TestRestClient client = cluster.getRestClient(ADMIN_USER)) { - client.createRoleMapping(ROLE_VP, DEPARTMENT_SONG_LISTENER_ROLE.getName()); - } - } - - @Test - public void shouldAuthenticateWithJwtToken_positive() { - try (TestRestClient client = cluster.getRestClient(tokenFactory.generateValidToken(USER_SUPERHERO))) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USER_SUPERHERO)); - } - } - - @Test - public void shouldAuthenticateWithJwtToken_positiveWithAnotherUsername() { - try (TestRestClient client = cluster.getRestClient(tokenFactory.generateValidToken(USERNAME_ROOT))) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USERNAME_ROOT)); - } - } - - @Test - public void shouldAuthenticateWithJwtToken_failureLackingUserName() { - try (TestRestClient client = cluster.getRestClient(tokenFactory.generateTokenWithoutPreferredUsername(USER_SUPERHERO))) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - logsRule.assertThatContainExactly("No subject found in JWT token"); - } - } - - @Test - public void shouldAuthenticateWithJwtToken_failureExpiredToken() { - try (TestRestClient client = cluster.getRestClient(tokenFactory.generateExpiredToken(USER_SUPERHERO))) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - logsRule.assertThatContainExactly("Invalid or expired JWT token."); - } - } - - @Test - public void shouldAuthenticateWithJwtToken_failureIncorrectFormatOfToken() { - Header header = new BasicHeader(AUTHORIZATION, "not.a.token"); - try (TestRestClient client = cluster.getRestClient(header)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - logsRule.assertThatContainExactly(String.format("No JWT token found in '%s' header header", JWT_AUTH_HEADER)); - } - } - - @Test - public void shouldAuthenticateWithJwtToken_failureIncorrectSignature() { - KeyPair incorrectKeyPair = Keys.keyPairFor(SignatureAlgorithm.RS256); - Header header = tokenFactory.generateTokenSignedWithKey(incorrectKeyPair.getPrivate(), USER_SUPERHERO); - try (TestRestClient client = cluster.getRestClient(header)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - logsRule.assertThatContainExactly("Invalid or expired JWT token."); - } - } - - @Test - public void shouldReadRolesFromToken_positiveFirstRoleSet() { - Header header = tokenFactory.generateValidToken(USER_SUPERHERO, ROLE_ADMIN, ROLE_DEVELOPER, ROLE_QA); - try (TestRestClient client = cluster.getRestClient(header)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List roles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(roles, hasSize(3)); - assertThat(roles, containsInAnyOrder(ROLE_ADMIN, ROLE_DEVELOPER, ROLE_QA)); - } - } - - @Test - public void shouldReadRolesFromToken_positiveSecondRoleSet() { - Header header = tokenFactory.generateValidToken(USER_SUPERHERO, ROLE_CTO, ROLE_CEO, ROLE_VP); - try (TestRestClient client = cluster.getRestClient(header)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List roles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(roles, hasSize(3)); - assertThat(roles, containsInAnyOrder(ROLE_CTO, ROLE_CEO, ROLE_VP)); - } - } - - @Test - public void shouldExposeTokenClaimsAsUserAttributes_positive() throws IOException { - String[] roles = { ROLE_VP }; - Map additionalClaims = Map.of(CLAIM_DEPARTMENT, QA_DEPARTMENT); - Header header = tokenFactory.generateValidTokenWithCustomClaims(USER_SUPERHERO, roles, additionalClaims); - try (RestHighLevelClient client = cluster.getRestHighLevelClient(List.of(header))) { - SearchRequest searchRequest = queryStringQueryRequest(QA_SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS); - - SearchResponse response = client.search(searchRequest, DEFAULT); - - assertThat(response, isSuccessfulSearchResponse()); - assertThat(response, numberOfTotalHitsIsEqualTo(1)); - assertThat(response, searchHitsContainDocumentWithId(0, QA_SONG_INDEX_NAME, SONG_ID_1)); - assertThat(response, searchHitContainsFieldWithValue(0, FIELD_TITLE, TITLE_MAGNUM_OPUS)); - } - } - - @Test - public void shouldExposeTokenClaimsAsUserAttributes_negative() throws IOException { - String[] roles = { ROLE_VP }; - Map additionalClaims = Map.of(CLAIM_DEPARTMENT, "department-without-access-to-qa-song-index"); - Header header = tokenFactory.generateValidTokenWithCustomClaims(USER_SUPERHERO, roles, additionalClaims); - try (RestHighLevelClient client = cluster.getRestHighLevelClient(List.of(header))) { - SearchRequest searchRequest = queryStringQueryRequest(QA_SONG_INDEX_NAME, QUERY_TITLE_MAGNUM_OPUS); - - assertThatThrownBy(() -> client.search(searchRequest, DEFAULT), statusException(FORBIDDEN)); - } - } -} diff --git a/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java deleted file mode 100644 index bac79ffd12..0000000000 --- a/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java +++ /dev/null @@ -1,414 +0,0 @@ -/* -* 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.http; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; -import org.apache.hc.core5.http.message.BasicHeader; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.RuleChain; -import org.junit.runner.RunWith; - -import org.opensearch.action.search.SearchRequest; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.client.Client; -import org.opensearch.client.RestHighLevelClient; -import org.opensearch.test.framework.AuthorizationBackend; -import org.opensearch.test.framework.AuthzDomain; -import org.opensearch.test.framework.LdapAuthenticationConfigBuilder; -import org.opensearch.test.framework.LdapAuthorizationConfigBuilder; -import org.opensearch.test.framework.RolesMapping; -import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; -import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AuthenticationBackend; -import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.HttpAuthenticator; -import org.opensearch.test.framework.TestSecurityConfig.Role; -import org.opensearch.test.framework.TestSecurityConfig.User; -import org.opensearch.test.framework.certificate.TestCertificates; -import org.opensearch.test.framework.cluster.ClusterManager; -import org.opensearch.test.framework.cluster.LocalCluster; -import org.opensearch.test.framework.cluster.TestRestClient; -import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; -import org.opensearch.test.framework.ldap.EmbeddedLDAPServer; -import org.opensearch.test.framework.log.LogsRule; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.not; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; -import static org.opensearch.client.RequestOptions.DEFAULT; -import static org.opensearch.core.rest.RestStatus.FORBIDDEN; -import static org.opensearch.security.Song.SONGS; -import static org.opensearch.security.http.DirectoryInformationTrees.CN_GROUP_ADMIN; -import static org.opensearch.security.http.DirectoryInformationTrees.CN_GROUP_BRIDGE; -import static org.opensearch.security.http.DirectoryInformationTrees.CN_GROUP_CREW; -import static org.opensearch.security.http.DirectoryInformationTrees.DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG; -import static org.opensearch.security.http.DirectoryInformationTrees.DN_GROUPS_TEST_ORG; -import static org.opensearch.security.http.DirectoryInformationTrees.DN_OPEN_SEARCH_PEOPLE_TEST_ORG; -import static org.opensearch.security.http.DirectoryInformationTrees.DN_PEOPLE_TEST_ORG; -import static org.opensearch.security.http.DirectoryInformationTrees.LDIF_DATA; -import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_JEAN; -import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_KIRK; -import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_LEONARD; -import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_OPEN_SEARCH; -import static org.opensearch.security.http.DirectoryInformationTrees.PASSWORD_SPOCK; -import static org.opensearch.security.http.DirectoryInformationTrees.USERNAME_ATTRIBUTE; -import static org.opensearch.security.http.DirectoryInformationTrees.USER_JEAN; -import static org.opensearch.security.http.DirectoryInformationTrees.USER_KIRK; -import static org.opensearch.security.http.DirectoryInformationTrees.USER_LEONARD; -import static org.opensearch.security.http.DirectoryInformationTrees.USER_SEARCH; -import static org.opensearch.security.http.DirectoryInformationTrees.USER_SPOCK; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.BASIC_AUTH_DOMAIN_ORDER; -import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; -import static org.opensearch.test.framework.cluster.SearchRequestFactory.queryStringQueryRequest; -import static org.opensearch.test.framework.matcher.ExceptionMatcherAssert.assertThatThrownBy; -import static org.opensearch.test.framework.matcher.OpenSearchExceptionMatchers.statusException; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.isSuccessfulSearchResponse; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.numberOfTotalHitsIsEqualTo; -import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; - -/** -* Test uses plain TLS connection between OpenSearch and LDAP server. -*/ -@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) -@ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class LdapTlsAuthenticationTest { - - private static final String SONG_INDEX_NAME = "song_lyrics"; - - private static final String HEADER_NAME_IMPERSONATE = "opendistro_security_impersonate_as"; - - private static final String PERSONAL_INDEX_NAME_SPOCK = "personal-" + USER_SPOCK; - private static final String PERSONAL_INDEX_NAME_KIRK = "personal-" + USER_KIRK; - - private static final String POINTER_BACKEND_ROLES = "/backend_roles"; - private static final String POINTER_ROLES = "/roles"; - private static final String POINTER_USERNAME = "/user_name"; - private static final String POINTER_ERROR_REASON = "/error/reason"; - - private static final String SONG_ID_1 = "l0001"; - private static final String SONG_ID_2 = "l0002"; - private static final String SONG_ID_3 = "l0003"; - - private static final User ADMIN_USER = new User("admin").roles(ALL_ACCESS); - - private static final TestCertificates TEST_CERTIFICATES = new TestCertificates(); - - private static final Role ROLE_INDEX_ADMINISTRATOR = new Role("index_administrator").indexPermissions("*").on("*"); - private static final Role ROLE_PERSONAL_INDEX_ACCESS = new Role("personal_index_access").indexPermissions("*") - .on("personal-${attr.ldap.uid}"); - - private static final EmbeddedLDAPServer embeddedLDAPServer = new EmbeddedLDAPServer( - TEST_CERTIFICATES.getRootCertificateData(), - TEST_CERTIFICATES.getLdapCertificateData(), - LDIF_DATA - ); - - private static final Map USER_IMPERSONATION_CONFIGURATION = Map.of( - "plugins.security.authcz.rest_impersonation_user." + USER_KIRK, - List.of(USER_SPOCK) - ); - - private static final LocalCluster cluster = new LocalCluster.Builder().testCertificates(TEST_CERTIFICATES) - .clusterManager(ClusterManager.SINGLENODE) - .anonymousAuth(false) - .nodeSettings(USER_IMPERSONATION_CONFIGURATION) - .authc( - new AuthcDomain("ldap", BASIC_AUTH_DOMAIN_ORDER + 1, true).httpAuthenticator(new HttpAuthenticator("basic").challenge(false)) - .backend( - new AuthenticationBackend("ldap").config( - () -> LdapAuthenticationConfigBuilder.config() - // this port is available when embeddedLDAPServer is already started, therefore Supplier interface is used - .hosts(List.of("localhost:" + embeddedLDAPServer.getLdapTlsPort())) - .enableSsl(true) - .bindDn(DN_OPEN_SEARCH_PEOPLE_TEST_ORG) - .password(PASSWORD_OPEN_SEARCH) - .userBase(DN_PEOPLE_TEST_ORG) - .userSearch(USER_SEARCH) - .usernameAttribute(USERNAME_ATTRIBUTE) - .penTrustedCasFilePath(TEST_CERTIFICATES.getRootCertificate().getAbsolutePath()) - .build() - ) - ) - ) - .authc(AUTHC_HTTPBASIC_INTERNAL) - .users(ADMIN_USER) - .roles(ROLE_INDEX_ADMINISTRATOR, ROLE_PERSONAL_INDEX_ACCESS) - .rolesMapping( - new RolesMapping(ROLE_INDEX_ADMINISTRATOR).backendRoles(CN_GROUP_ADMIN), - new RolesMapping(ROLE_PERSONAL_INDEX_ACCESS).backendRoles(CN_GROUP_CREW) - ) - .authz( - new AuthzDomain("ldap_roles").httpEnabled(true) - .transportEnabled(true) - .authorizationBackend( - new AuthorizationBackend("ldap").config( - () -> new LdapAuthorizationConfigBuilder().hosts(List.of("localhost:" + embeddedLDAPServer.getLdapTlsPort())) - .enableSsl(true) - .bindDn(DN_OPEN_SEARCH_PEOPLE_TEST_ORG) - .password(PASSWORD_OPEN_SEARCH) - .userBase(DN_PEOPLE_TEST_ORG) - .userSearch(USER_SEARCH) - .usernameAttribute(USERNAME_ATTRIBUTE) - .penTrustedCasFilePath(TEST_CERTIFICATES.getRootCertificate().getAbsolutePath()) - .roleBase(DN_GROUPS_TEST_ORG) - .roleSearch("(uniqueMember={0})") - .userRoleAttribute(null) - .userRoleName("disabled") - .roleName("cn") - .resolveNestedRoles(true) - .build() - ) - ) - ) - .build(); - - @ClassRule - public static final RuleChain ruleChain = RuleChain.outerRule(embeddedLDAPServer).around(cluster); - - @Rule - public LogsRule logsRule = new LogsRule("com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend"); - - @BeforeClass - public static void createTestData() { - try (Client client = cluster.getInternalNodeClient()) { - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); - client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(SONG_ID_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); - client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(SONG_ID_3).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2].asMap()).get(); - } - } - - @Test - public void shouldAuthenticateUserWithLdap_positiveSpockUser() { - try (TestRestClient client = cluster.getRestClient(USER_SPOCK, PASSWORD_SPOCK)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USER_SPOCK)); - } - } - - @Test - public void shouldAuthenticateUserWithLdap_positiveKirkUser() { - try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USER_KIRK)); - } - } - - @Test - public void shouldAuthenticateUserWithLdap_negativeWhenIncorrectPassword() { - try (TestRestClient client = cluster.getRestClient(USER_SPOCK, "incorrect password")) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - String expectedStackTraceFragment = "Unable to bind as user '".concat(DN_CAPTAIN_SPOCK_PEOPLE_TEST_ORG) - .concat("' because the provided password was incorrect."); - logsRule.assertThatStackTraceContain(expectedStackTraceFragment); - } - } - - @Test - public void shouldAuthenticateUserWithLdap_negativeWhenIncorrectUsername() { - final String username = "invalid-user-name"; - try (TestRestClient client = cluster.getRestClient(username, PASSWORD_SPOCK)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - logsRule.assertThatStackTraceContain(String.format("No user %s found", username)); - } - } - - @Test - public void shouldAuthenticateUserWithLdap_negativeWhenUserDoesNotExist() { - final String username = "doesNotExist"; - try (TestRestClient client = cluster.getRestClient(username, "password")) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - logsRule.assertThatStackTraceContain(String.format("No user %s found", username)); - } - } - - @Test - public void shouldResolveUserRolesAgainstLdapBackend_positiveSpockUser() { - try (TestRestClient client = cluster.getRestClient(USER_SPOCK, PASSWORD_SPOCK)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, contains(CN_GROUP_CREW)); - assertThat(response.getTextArrayFromJsonBody(POINTER_ROLES), contains(ROLE_PERSONAL_INDEX_ACCESS.getName())); - } - } - - @Test - public void shouldResolveUserRolesAgainstLdapBackend_positiveKirkUser() { - try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - assertThat(response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES), contains(CN_GROUP_ADMIN)); - assertThat(response.getTextArrayFromJsonBody(POINTER_ROLES), contains(ROLE_INDEX_ADMINISTRATOR.getName())); - } - } - - @Test - public void shouldPerformAuthorizationAgainstLdapToAccessIndex_positive() throws IOException { - try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_KIRK, PASSWORD_KIRK)) { - SearchRequest request = queryStringQueryRequest(SONG_INDEX_NAME, "*"); - - SearchResponse searchResponse = client.search(request, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, SONG_INDEX_NAME, SONG_ID_1)); - } - } - - @Test - public void shouldPerformAuthorizationAgainstLdapToAccessIndex_negative() throws IOException { - try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_LEONARD, PASSWORD_LEONARD)) { - SearchRequest request = queryStringQueryRequest(SONG_INDEX_NAME, "*"); - - assertThatThrownBy(() -> client.search(request, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldResolveUserAttributesLoadedFromLdap_positive() throws IOException { - try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_SPOCK, PASSWORD_SPOCK)) { - SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_SPOCK, "*"); - - SearchResponse searchResponse = client.search(request, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, PERSONAL_INDEX_NAME_SPOCK, SONG_ID_2)); - } - } - - @Test - public void shouldResolveUserAttributesLoadedFromLdap_negative() throws IOException { - try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_SPOCK, PASSWORD_SPOCK)) { - SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_KIRK, "*"); - - assertThatThrownBy(() -> client.search(request, DEFAULT), statusException(FORBIDDEN)); - } - } - - @Test - public void shouldResolveNestedGroups_positive() { - try (TestRestClient client = cluster.getRestClient(USER_JEAN, PASSWORD_JEAN)) { - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, hasSize(2)); - // CN_GROUP_CREW is retrieved recursively: cn=Jean,ou=people,o=test.org -> cn=bridge,ou=groups,o=test.org -> - // cn=crew,ou=groups,o=test.org - assertThat(backendRoles, containsInAnyOrder(CN_GROUP_CREW, CN_GROUP_BRIDGE)); - assertThat(response.getTextArrayFromJsonBody(POINTER_ROLES), contains(ROLE_PERSONAL_INDEX_ACCESS.getName())); - } - } - - @Test - public void shouldResolveNestedGroups_negative() { - try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, not(containsInAnyOrder(CN_GROUP_CREW))); - } - } - - @Test - public void shouldImpersonateUser_positive() { - try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { - - HttpResponse response = client.getAuthInfo(new BasicHeader(HEADER_NAME_IMPERSONATE, USER_SPOCK)); - - response.assertStatusCode(200); - assertThat(response.getTextFromJsonBody(POINTER_USERNAME), equalTo(USER_SPOCK)); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, hasSize(1)); - assertThat(backendRoles, contains(CN_GROUP_CREW)); - } - } - - @Test - public void shouldImpersonateUser_negativeJean() { - try (TestRestClient client = cluster.getRestClient(USER_KIRK, PASSWORD_KIRK)) { - - HttpResponse response = client.getAuthInfo(new BasicHeader(HEADER_NAME_IMPERSONATE, USER_JEAN)); - - response.assertStatusCode(403); - String expectedMessage = String.format("'%s' is not allowed to impersonate as '%s'", USER_KIRK, USER_JEAN); - assertThat(response.getTextFromJsonBody(POINTER_ERROR_REASON), equalTo(expectedMessage)); - } - } - - @Test - public void shouldImpersonateUser_negativeKirk() { - try (TestRestClient client = cluster.getRestClient(USER_JEAN, PASSWORD_JEAN)) { - - HttpResponse response = client.getAuthInfo(new BasicHeader(HEADER_NAME_IMPERSONATE, USER_KIRK)); - - response.assertStatusCode(403); - String expectedMessage = String.format("'%s' is not allowed to impersonate as '%s'", USER_JEAN, USER_KIRK); - assertThat(response.getTextFromJsonBody(POINTER_ERROR_REASON), equalTo(expectedMessage)); - } - } - - @Test - public void shouldAccessImpersonatedUserPersonalIndex_positive() throws IOException { - BasicHeader impersonateHeader = new BasicHeader(HEADER_NAME_IMPERSONATE, USER_SPOCK); - try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_KIRK, PASSWORD_KIRK, impersonateHeader)) { - SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_SPOCK, "*"); - - SearchResponse searchResponse = client.search(request, DEFAULT); - - assertThat(searchResponse, isSuccessfulSearchResponse()); - assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); - assertThat(searchResponse, searchHitsContainDocumentWithId(0, PERSONAL_INDEX_NAME_SPOCK, SONG_ID_2)); - } - } - - @Test - public void shouldAccessImpersonatedUserPersonalIndex_negative() throws IOException { - BasicHeader impersonateHeader = new BasicHeader(HEADER_NAME_IMPERSONATE, USER_SPOCK); - try (RestHighLevelClient client = cluster.getRestHighLevelClient(USER_KIRK, PASSWORD_KIRK, impersonateHeader)) { - SearchRequest request = queryStringQueryRequest(PERSONAL_INDEX_NAME_KIRK, "*"); - - assertThatThrownBy(() -> client.search(request, DEFAULT), statusException(FORBIDDEN)); - } - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java deleted file mode 100644 index d44f10eba0..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java +++ /dev/null @@ -1,717 +0,0 @@ -/* -* Copyright 2021 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.test.framework; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.bouncycastle.crypto.generators.OpenBSDBCrypt; - -import org.opensearch.action.admin.indices.create.CreateIndexRequest; -import org.opensearch.action.index.IndexRequest; -import org.opensearch.action.update.UpdateRequest; -import org.opensearch.client.Client; -import org.opensearch.common.Strings; -import org.opensearch.core.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.core.xcontent.ToXContentObject; -import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.test.framework.cluster.OpenSearchClientProvider.UserCredentialsHolder; - -import static org.apache.http.HttpHeaders.AUTHORIZATION; -import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; - -/** -* This class allows the declarative specification of the security configuration; in particular: -* -* - config.yml -* - internal_users.yml -* - roles.yml -* - roles_mapping.yml -* -* The class does the whole round-trip, i.e., the configuration is serialized to YAML/JSON and then written to -* the configuration index of the security plugin. -*/ -public class TestSecurityConfig { - - private static final Logger log = LogManager.getLogger(TestSecurityConfig.class); - - private Config config = new Config(); - private Map internalUsers = new LinkedHashMap<>(); - private Map roles = new LinkedHashMap<>(); - private AuditConfiguration auditConfiguration; - private Map rolesMapping = new LinkedHashMap<>(); - - private String indexName = ".opendistro_security"; - - public TestSecurityConfig() { - - } - - public TestSecurityConfig configIndexName(String configIndexName) { - this.indexName = configIndexName; - return this; - } - - public TestSecurityConfig authFailureListeners(AuthFailureListeners listener) { - config.authFailureListeners(listener); - return this; - } - - public TestSecurityConfig anonymousAuth(boolean anonymousAuthEnabled) { - config.anonymousAuth(anonymousAuthEnabled); - return this; - } - - public TestSecurityConfig doNotFailOnForbidden(boolean doNotFailOnForbidden) { - config.doNotFailOnForbidden(doNotFailOnForbidden); - return this; - } - - public TestSecurityConfig xff(XffConfig xffConfig) { - config.xffConfig(xffConfig); - return this; - } - - public TestSecurityConfig authc(AuthcDomain authcDomain) { - config.authc(authcDomain); - return this; - } - - public TestSecurityConfig authz(AuthzDomain authzDomain) { - config.authz(authzDomain); - return this; - } - - public TestSecurityConfig user(User user) { - this.internalUsers.put(user.name, user); - - for (Role role : user.roles) { - this.roles.put(role.name, role); - } - - return this; - } - - public List getUsers() { - return new ArrayList<>(internalUsers.values()); - } - - public TestSecurityConfig roles(Role... roles) { - for (Role role : roles) { - if (this.roles.containsKey(role.name)) { - throw new IllegalStateException("Role with name " + role.name + " is already defined"); - } - this.roles.put(role.name, role); - } - - return this; - } - - public TestSecurityConfig audit(AuditConfiguration auditConfiguration) { - this.auditConfiguration = auditConfiguration; - return this; - } - - public TestSecurityConfig rolesMapping(RolesMapping... mappings) { - for (RolesMapping mapping : mappings) { - String roleName = mapping.getRoleName(); - if (rolesMapping.containsKey(roleName)) { - throw new IllegalArgumentException("Role mapping " + roleName + " already exists"); - } - this.rolesMapping.put(roleName, mapping); - } - return this; - } - - public static class Config implements ToXContentObject { - private boolean anonymousAuth; - - private Boolean doNotFailOnForbidden; - private XffConfig xffConfig; - private Map authcDomainMap = new LinkedHashMap<>(); - - private AuthFailureListeners authFailureListeners; - private Map authzDomainMap = new LinkedHashMap<>(); - - public Config anonymousAuth(boolean anonymousAuth) { - this.anonymousAuth = anonymousAuth; - return this; - } - - public Config doNotFailOnForbidden(Boolean doNotFailOnForbidden) { - this.doNotFailOnForbidden = doNotFailOnForbidden; - return this; - } - - public Config xffConfig(XffConfig xffConfig) { - this.xffConfig = xffConfig; - return this; - } - - public Config authc(AuthcDomain authcDomain) { - authcDomainMap.put(authcDomain.id, authcDomain); - return this; - } - - public Config authFailureListeners(AuthFailureListeners authFailureListeners) { - this.authFailureListeners = authFailureListeners; - return this; - } - - public Config authz(AuthzDomain authzDomain) { - authzDomainMap.put(authzDomain.getId(), authzDomain); - return this; - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - xContentBuilder.startObject("dynamic"); - - if (anonymousAuth || (xffConfig != null)) { - xContentBuilder.startObject("http"); - xContentBuilder.field("anonymous_auth_enabled", anonymousAuth); - if (xffConfig != null) { - xContentBuilder.field("xff", xffConfig); - } - xContentBuilder.endObject(); - } - if (doNotFailOnForbidden != null) { - xContentBuilder.field("do_not_fail_on_forbidden", doNotFailOnForbidden); - } - - xContentBuilder.field("authc", authcDomainMap); - if (authzDomainMap.isEmpty() == false) { - xContentBuilder.field("authz", authzDomainMap); - } - - if (authFailureListeners != null) { - xContentBuilder.field("auth_failure_listeners", authFailureListeners); - } - - xContentBuilder.endObject(); - xContentBuilder.endObject(); - return xContentBuilder; - } - } - - public static class User implements UserCredentialsHolder, ToXContentObject { - - public final static TestSecurityConfig.User USER_ADMIN = new TestSecurityConfig.User("admin").roles( - new Role("allaccess").indexPermissions("*").on("*").clusterPermissions("*") - ); - - String name; - private String password; - List roles = new ArrayList<>(); - private Map attributes = new HashMap<>(); - - public User(String name) { - this.name = name; - this.password = "secret"; - } - - public User password(String password) { - this.password = password; - return this; - } - - public User roles(Role... roles) { - // We scope the role names by user to keep tests free of potential side effects - String roleNamePrefix = "user_" + this.getName() + "__"; - this.roles.addAll( - Arrays.asList(roles).stream().map((r) -> r.clone().name(roleNamePrefix + r.getName())).collect(Collectors.toSet()) - ); - return this; - } - - public User attr(String key, Object value) { - this.attributes.put(key, value); - return this; - } - - public String getName() { - return name; - } - - public String getPassword() { - return password; - } - - public Set getRoleNames() { - return roles.stream().map(Role::getName).collect(Collectors.toSet()); - } - - public Object getAttribute(String attributeName) { - return attributes.get(attributeName); - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - - xContentBuilder.field("hash", hash(password.toCharArray())); - - Set roleNames = getRoleNames(); - - if (!roleNames.isEmpty()) { - xContentBuilder.field("opendistro_security_roles", roleNames); - } - - if (attributes != null && attributes.size() != 0) { - xContentBuilder.field("attributes", attributes); - } - - xContentBuilder.endObject(); - return xContentBuilder; - } - } - - public static class Role implements ToXContentObject { - public static Role ALL_ACCESS = new Role("all_access").clusterPermissions("*").indexPermissions("*").on("*"); - - private String name; - private List clusterPermissions = new ArrayList<>(); - - private List indexPermissions = new ArrayList<>(); - - public Role(String name) { - this.name = name; - } - - public Role clusterPermissions(String... clusterPermissions) { - this.clusterPermissions.addAll(Arrays.asList(clusterPermissions)); - return this; - } - - public IndexPermission indexPermissions(String... indexPermissions) { - return new IndexPermission(this, indexPermissions); - } - - public Role name(String name) { - this.name = name; - return this; - } - - public String getName() { - return name; - } - - public Role clone() { - Role role = new Role(this.name); - role.clusterPermissions.addAll(this.clusterPermissions); - role.indexPermissions.addAll(this.indexPermissions); - return role; - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - - if (!clusterPermissions.isEmpty()) { - xContentBuilder.field("cluster_permissions", clusterPermissions); - } - - if (!indexPermissions.isEmpty()) { - xContentBuilder.field("index_permissions", indexPermissions); - } - - xContentBuilder.endObject(); - return xContentBuilder; - } - } - - public static class IndexPermission implements ToXContentObject { - private List allowedActions; - private List indexPatterns; - private Role role; - private String dlsQuery; - private List fls; - private List maskedFields; - - IndexPermission(Role role, String... allowedActions) { - this.allowedActions = Arrays.asList(allowedActions); - this.role = role; - } - - public IndexPermission dls(String dlsQuery) { - this.dlsQuery = dlsQuery; - return this; - } - - public IndexPermission fls(String... fls) { - this.fls = Arrays.asList(fls); - return this; - } - - public IndexPermission maskedFields(String... maskedFields) { - this.maskedFields = Arrays.asList(maskedFields); - return this; - } - - public Role on(String... indexPatterns) { - this.indexPatterns = Arrays.asList(indexPatterns); - this.role.indexPermissions.add(this); - return this.role; - } - - public Role on(TestIndex... testindices) { - this.indexPatterns = Arrays.asList(testindices).stream().map(TestIndex::getName).collect(Collectors.toList()); - this.role.indexPermissions.add(this); - return this.role; - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - - xContentBuilder.field("index_patterns", indexPatterns); - xContentBuilder.field("allowed_actions", allowedActions); - - if (dlsQuery != null) { - xContentBuilder.field("dls", dlsQuery); - } - - if (fls != null) { - xContentBuilder.field("fls", fls); - } - - if (maskedFields != null) { - xContentBuilder.field("masked_fields", maskedFields); - } - - xContentBuilder.endObject(); - return xContentBuilder; - } - } - - public static class AuthcDomain implements ToXContentObject { - - private static String PUBLIC_KEY = - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoqZbjLUAWc+DZTkinQAdvy1GFjPHPnxheU89hSiWoDD3NOW76H3u3T7cCDdOah2msdxSlBmCBH6wik8qLYkcV8owWukQg3PQmbEhrdPaKo0QCgomWs4nLgtmEYqcZ+QQldd82MdTlQ1QmoQmI9Uxqs1SuaKZASp3Gy19y8su5CV+FZ6BruUw9HELK055sAwl3X7j5ouabXGbcib2goBF3P52LkvbJLuWr5HDZEOeSkwIeqSeMojASM96K5SdotD+HwEyjaTjzRPL2Aa1BEQFWOQ6CFJLyLH7ZStDuPM1mJU1VxIVfMbZrhsUBjAnIhRynmWxML7YlNqkP9j6jyOIYQIDAQAB"; - - public static final int BASIC_AUTH_DOMAIN_ORDER = 0; - public final static AuthcDomain AUTHC_HTTPBASIC_INTERNAL = new TestSecurityConfig.AuthcDomain("basic", BASIC_AUTH_DOMAIN_ORDER) - .httpAuthenticatorWithChallenge("basic") - .backend("internal"); - - public final static AuthcDomain AUTHC_HTTPBASIC_INTERNAL_WITHOUT_CHALLENGE = new TestSecurityConfig.AuthcDomain( - "basic", - BASIC_AUTH_DOMAIN_ORDER - ).httpAuthenticator("basic").backend("internal"); - - public final static AuthcDomain DISABLED_AUTHC_HTTPBASIC_INTERNAL = new TestSecurityConfig.AuthcDomain( - "basic", - BASIC_AUTH_DOMAIN_ORDER, - false - ).httpAuthenticator("basic").backend("internal"); - - public final static AuthcDomain JWT_AUTH_DOMAIN = new TestSecurityConfig.AuthcDomain("jwt", 1).jwtHttpAuthenticator( - new JwtConfigBuilder().jwtHeader(AUTHORIZATION).signingKey(PUBLIC_KEY) - ).backend("noop"); - - private final String id; - private boolean enabled = true; - private int order; - private List skipUsers = new ArrayList<>(); - private HttpAuthenticator httpAuthenticator; - private AuthenticationBackend authenticationBackend; - - public AuthcDomain(String id, int order, boolean enabled) { - this.id = id; - this.order = order; - this.enabled = enabled; - } - - public AuthcDomain(String id, int order) { - this(id, order, true); - } - - public AuthcDomain httpAuthenticator(String type) { - this.httpAuthenticator = new HttpAuthenticator(type); - return this; - } - - public AuthcDomain jwtHttpAuthenticator(JwtConfigBuilder builder) { - this.httpAuthenticator = new HttpAuthenticator("jwt").challenge(false).config(builder.build()); - return this; - } - - public AuthcDomain httpAuthenticatorWithChallenge(String type) { - this.httpAuthenticator = new HttpAuthenticator(type).challenge(true); - return this; - } - - public AuthcDomain httpAuthenticator(HttpAuthenticator httpAuthenticator) { - this.httpAuthenticator = httpAuthenticator; - return this; - } - - public AuthcDomain backend(String type) { - this.authenticationBackend = new AuthenticationBackend(type); - return this; - } - - public AuthcDomain backend(AuthenticationBackend authenticationBackend) { - this.authenticationBackend = authenticationBackend; - return this; - } - - public AuthcDomain skipUsers(String... users) { - this.skipUsers.addAll(Arrays.asList(users)); - return this; - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - - xContentBuilder.field("http_enabled", enabled); - xContentBuilder.field("order", order); - - if (httpAuthenticator != null) { - xContentBuilder.field("http_authenticator", httpAuthenticator); - } - - if (authenticationBackend != null) { - xContentBuilder.field("authentication_backend", authenticationBackend); - } - - if (skipUsers != null && skipUsers.size() > 0) { - xContentBuilder.field("skip_users", skipUsers); - } - - xContentBuilder.endObject(); - return xContentBuilder; - } - - public static class HttpAuthenticator implements ToXContentObject { - private final String type; - private boolean challenge; - private Map config = new HashMap(); - - public HttpAuthenticator(String type) { - this.type = type; - } - - public HttpAuthenticator challenge(boolean challenge) { - this.challenge = challenge; - return this; - } - - public HttpAuthenticator config(Map config) { - this.config.putAll(config); - return this; - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - - xContentBuilder.field("type", type); - xContentBuilder.field("challenge", challenge); - xContentBuilder.field("config", config); - - xContentBuilder.endObject(); - return xContentBuilder; - } - } - - public static class AuthenticationBackend implements ToXContentObject { - private final String type; - private Supplier> config = () -> new HashMap(); - - public AuthenticationBackend(String type) { - this.type = type; - } - - public AuthenticationBackend config(Map config) { - Map configCopy = new HashMap<>(config); - this.config = () -> configCopy; - return this; - } - - public AuthenticationBackend config(Supplier> configSupplier) { - this.config = configSupplier; - return this; - } - - @Override - public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { - xContentBuilder.startObject(); - - xContentBuilder.field("type", type); - xContentBuilder.field("config", config.get()); - - xContentBuilder.endObject(); - return xContentBuilder; - } - } - } - - public void initIndex(Client client) { - Map settings = new HashMap<>(); - if (indexName.startsWith(".")) { - settings.put("index.hidden", true); - } - client.admin().indices().create(new CreateIndexRequest(indexName).settings(settings)).actionGet(); - - writeSingleEntryConfigToIndex(client, CType.CONFIG, config); - if (auditConfiguration != null) { - writeSingleEntryConfigToIndex(client, CType.AUDIT, "config", auditConfiguration); - } - writeConfigToIndex(client, CType.ROLES, roles); - writeConfigToIndex(client, CType.INTERNALUSERS, internalUsers); - writeConfigToIndex(client, CType.ROLESMAPPING, rolesMapping); - writeEmptyConfigToIndex(client, CType.ACTIONGROUPS); - writeEmptyConfigToIndex(client, CType.TENANTS); - } - - public void updateInternalUsersConfiguration(Client client, List users) { - Map userMap = new HashMap<>(); - for (User user : users) { - userMap.put(user.getName(), user); - } - updateConfigInIndex(client, CType.INTERNALUSERS, userMap); - } - - static String hash(final char[] clearTextPassword) { - final byte[] salt = new byte[16]; - new SecureRandom().nextBytes(salt); - final String hash = OpenBSDBCrypt.generate((Objects.requireNonNull(clearTextPassword)), salt, 12); - Arrays.fill(salt, (byte) 0); - Arrays.fill(clearTextPassword, '\0'); - return hash; - } - - private void writeEmptyConfigToIndex(Client client, CType configType) { - writeConfigToIndex(client, configType, Collections.emptyMap()); - } - - private void writeConfigToIndex(Client client, CType configType, Map config) { - try { - String json = configToJson(configType, config); - - log.info("Writing security configuration into index " + configType + ":\n" + json); - - BytesReference bytesReference = toByteReference(json); - client.index( - new IndexRequest(indexName).id(configType.toLCString()) - .setRefreshPolicy(IMMEDIATE) - .source(configType.toLCString(), bytesReference) - ).actionGet(); - } catch (Exception e) { - throw new RuntimeException("Error while initializing config for " + indexName, e); - } - } - - private static BytesReference toByteReference(String string) throws UnsupportedEncodingException { - return BytesReference.fromByteBuffer(ByteBuffer.wrap(string.getBytes("utf-8"))); - } - - private void updateConfigInIndex(Client client, CType configType, Map config) { - try { - String json = configToJson(configType, config); - BytesReference bytesReference = toByteReference(json); - log.info("Update configuration of type '{}' in index '{}', new value '{}'.", configType, indexName, json); - UpdateRequest upsert = new UpdateRequest(indexName, configType.toLCString()).doc(configType.toLCString(), bytesReference) - .setRefreshPolicy(IMMEDIATE); - client.update(upsert).actionGet(); - } catch (Exception e) { - throw new RuntimeException("Error while updating config for " + indexName, e); - } - } - - private static String configToJson(CType configType, Map config) throws IOException { - XContentBuilder builder = XContentFactory.jsonBuilder(); - - builder.startObject(); - builder.startObject("_meta"); - builder.field("type", configType.toLCString()); - builder.field("config_version", 2); - builder.endObject(); - - for (Map.Entry entry : config.entrySet()) { - builder.field(entry.getKey(), entry.getValue()); - } - - builder.endObject(); - - return Strings.toString(builder); - } - - private void writeSingleEntryConfigToIndex(Client client, CType configType, ToXContentObject config) { - writeSingleEntryConfigToIndex(client, configType, configType.toLCString(), config); - } - - private void writeSingleEntryConfigToIndex(Client client, CType configType, String configurationRoot, ToXContentObject config) { - try { - XContentBuilder builder = XContentFactory.jsonBuilder(); - - builder.startObject(); - builder.startObject("_meta"); - builder.field("type", configType.toLCString()); - builder.field("config_version", 2); - builder.endObject(); - - builder.field(configurationRoot, config); - - builder.endObject(); - - String json = Strings.toString(builder); - - log.info("Writing security plugin configuration into index " + configType + ":\n" + json); - - client.index( - new IndexRequest(indexName).id(configType.toLCString()) - .setRefreshPolicy(IMMEDIATE) - .source(configType.toLCString(), toByteReference(json)) - ).actionGet(); - } catch (Exception e) { - throw new RuntimeException("Error while initializing config for " + indexName, e); - } - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java deleted file mode 100644 index 6e8519c230..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchExceptionMatchers.java +++ /dev/null @@ -1,37 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Matcher; - -import org.opensearch.core.rest.RestStatus; - -import static org.hamcrest.Matchers.containsString; - -public class OpenSearchExceptionMatchers { - - private OpenSearchExceptionMatchers() {} - - public static Matcher statusException(RestStatus expectedRestStatus) { - return new OpenSearchStatusExceptionMatcher(expectedRestStatus); - } - - public static Matcher errorMessage(Matcher errorMessageMatcher) { - return new ExceptionErrorMessageMatcher(errorMessageMatcher); - } - - public static Matcher errorMessageContain(String errorMessage) { - return errorMessage(containsString(errorMessage)); - } - - public static Matcher hasCause(Class clazz) { - return new ExceptionHasCauseMatcher(clazz); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java deleted file mode 100644 index e8efcf151f..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/OpenSearchStatusExceptionMatcher.java +++ /dev/null @@ -1,52 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.OpenSearchException; -import org.opensearch.core.rest.RestStatus; - -import static java.util.Objects.requireNonNull; - -class OpenSearchStatusExceptionMatcher extends TypeSafeDiagnosingMatcher { - - private final RestStatus expectedRestStatus; - - public OpenSearchStatusExceptionMatcher(RestStatus expectedRestStatus) { - this.expectedRestStatus = requireNonNull(expectedRestStatus, "Expected rest status is required."); - } - - @Override - protected boolean matchesSafely(Throwable throwable, Description mismatchDescription) { - if ((throwable instanceof OpenSearchException) == false) { - mismatchDescription.appendText("actual exception type is ") - .appendValue(throwable.getClass().getCanonicalName()) - .appendText(", error message ") - .appendValue(throwable.getMessage()); - return false; - } - OpenSearchException openSearchException = (OpenSearchException) throwable; - if (expectedRestStatus.equals(openSearchException.status()) == false) { - mismatchDescription.appendText("actual status code is ") - .appendValue(openSearchException.status()) - .appendText(", error message ") - .appendValue(throwable.getMessage()); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("OpenSearchException with status code ").appendValue(expectedRestStatus); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java deleted file mode 100644 index cf3a6d9e57..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseMatchers.java +++ /dev/null @@ -1,87 +0,0 @@ -/* -* 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.test.framework.matcher; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -import org.apache.commons.lang3.tuple.Pair; -import org.hamcrest.Matcher; - -import org.opensearch.action.search.SearchResponse; -import org.opensearch.core.rest.RestStatus; -import org.opensearch.search.SearchHits; - -public class SearchResponseMatchers { - - private SearchResponseMatchers() {} - - public static Matcher isSuccessfulSearchResponse() { - return new SuccessfulSearchResponseMatcher(); - } - - public static Matcher numberOfTotalHitsIsEqualTo(int expectedNumberOfHits) { - return new NumberOfTotalHitsIsEqualToMatcher(expectedNumberOfHits); - } - - public static Matcher numberOfHitsInPageIsEqualTo(int expectedNumberOfHits) { - return new NumberOfHitsInPageIsEqualToMatcher(expectedNumberOfHits); - } - - public static Matcher searchHitContainsFieldWithValue(int hitIndex, String fieldName, T expectedValue) { - return new SearchHitContainsFieldWithValueMatcher<>(hitIndex, fieldName, expectedValue); - } - - public static Matcher searchHitDoesNotContainField(int hitIndex, String fieldName) { - return new SearchHitDoesNotContainFieldMatcher(hitIndex, fieldName); - } - - public static Matcher searchHitsContainDocumentWithId(int hitIndex, String indexName, String documentId) { - return new SearchHitsContainDocumentWithIdMatcher(hitIndex, indexName, documentId); - } - - public static Matcher restStatusIs(RestStatus expectedRestStatus) { - return new SearchResponseWithStatusCodeMatcher(expectedRestStatus); - } - - public static Matcher containNotEmptyScrollingId() { - return new ContainNotEmptyScrollingIdMatcher(); - } - - public static Matcher containAggregationWithNameAndType( - String expectedAggregationName, - String expectedAggregationType - ) { - return new ContainsAggregationWithNameAndTypeMatcher(expectedAggregationName, expectedAggregationType); - } - - /** - * Matcher checks if search result contains all expected documents - * - * @param documentIds Pair contain index name and document id - * @return matcher - */ - public static Matcher searchHitsContainDocumentsInAnyOrder(List> documentIds) { - return new SearchHitsContainDocumentsInAnyOrderMatcher(documentIds); - } - - public static Matcher searchHitsContainDocumentsInAnyOrder(Pair... documentIds) { - return new SearchHitsContainDocumentsInAnyOrderMatcher(Arrays.asList(documentIds)); - } - - static Long readTotalHits(SearchResponse searchResponse) { - return Optional.ofNullable(searchResponse) - .map(SearchResponse::getHits) - .map(SearchHits::getTotalHits) - .map(totalHits -> totalHits.value) - .orElse(null); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java deleted file mode 100644 index 5b0f99e11b..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SearchResponseWithStatusCodeMatcher.java +++ /dev/null @@ -1,39 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.search.SearchResponse; -import org.opensearch.core.rest.RestStatus; - -class SearchResponseWithStatusCodeMatcher extends TypeSafeDiagnosingMatcher { - - private final RestStatus expectedRestStatus; - - public SearchResponseWithStatusCodeMatcher(RestStatus expectedRestStatus) { - this.expectedRestStatus = expectedRestStatus; - } - - @Override - protected boolean matchesSafely(SearchResponse searchResponse, Description mismatchDescription) { - if (expectedRestStatus.equals(searchResponse.status()) == false) { - mismatchDescription.appendText("actual response status is ").appendValue(searchResponse.status()); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Expected response status is ").appendValue(expectedRestStatus); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java deleted file mode 100644 index ca4b94d148..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessBulkResponseMatcher.java +++ /dev/null @@ -1,47 +0,0 @@ -/* -* 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.test.framework.matcher; - -import java.util.Arrays; -import java.util.stream.Collectors; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.bulk.BulkItemResponse; -import org.opensearch.action.bulk.BulkResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessBulkResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(BulkResponse response, Description mismatchDescription) { - RestStatus status = response.status(); - if (RestStatus.OK.equals(status) == false) { - mismatchDescription.appendText("incorrect response status ").appendValue(status); - return false; - } - if (response.hasFailures()) { - String failureDescription = Arrays.stream(response.getItems()) - .filter(BulkItemResponse::isFailed) - .map(BulkItemResponse::getFailure) - .map(Object::toString) - .collect(Collectors.joining(",\n")); - mismatchDescription.appendText("bulk response contains failures ").appendValue(failureDescription); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("success bulk response"); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java deleted file mode 100644 index b70b2c2f9e..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulClearIndicesCacheResponseMatcher.java +++ /dev/null @@ -1,37 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessfulClearIndicesCacheResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(ClearIndicesCacheResponse response, Description mismatchDescription) { - if (!RestStatus.OK.equals(response.getStatus())) { - mismatchDescription.appendText("Status is equal to ").appendValue(response.getStatus()); - return false; - } - if (response.getShardFailures().length != 0) { - mismatchDescription.appendText("Contains ").appendValue(response.getShardFailures().length).appendText(" shard failures"); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Successful clear index cache response"); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java deleted file mode 100644 index 66b59b1526..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulCreatePitResponseMatcher.java +++ /dev/null @@ -1,37 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.search.CreatePitResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessfulCreatePitResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(CreatePitResponse response, Description mismatchDescription) { - if (!RestStatus.OK.equals(response.status())) { - mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); - return false; - } - if (response.getShardFailures().length != 0) { - mismatchDescription.appendText("contains ").appendValue(response.getShardFailures().length).appendText(" shard failures"); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Successful create pit response"); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java deleted file mode 100644 index 20906946f1..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeletePitResponseMatcher.java +++ /dev/null @@ -1,42 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.search.DeletePitInfo; -import org.opensearch.action.search.DeletePitResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessfulDeletePitResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(DeletePitResponse response, Description mismatchDescription) { - if (!RestStatus.OK.equals(response.status())) { - mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); - return false; - } - for (DeletePitInfo deletePitInfo : response.getDeletePitResults()) { - if (!deletePitInfo.isSuccessful()) { - mismatchDescription.appendValue("Pit: ") - .appendValue(deletePitInfo.getPitId()) - .appendText(" - delete result was not successful"); - return false; - } - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Successful delete pit response"); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java deleted file mode 100644 index 6c10b2b6f8..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulDeleteResponseMatcher.java +++ /dev/null @@ -1,39 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.delete.DeleteResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessfulDeleteResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(DeleteResponse response, Description mismatchDescription) { - if (!RestStatus.OK.equals(response.status())) { - mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); - return false; - } - if (response.getShardInfo().getFailures().length != 0) { - mismatchDescription.appendText("contains ") - .appendValue(response.getShardInfo().getFailures().length) - .appendText(" shard failures"); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Successful delete response"); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java deleted file mode 100644 index 21017a9014..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulSearchResponseMatcher.java +++ /dev/null @@ -1,37 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.search.SearchResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessfulSearchResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(SearchResponse searchResponse, Description mismatchDescription) { - if (RestStatus.OK.equals(searchResponse.status()) == false) { - mismatchDescription.appendText("has status ").appendValue(searchResponse.status()).appendText(" which denotes failure."); - return false; - } - if (searchResponse.getShardFailures().length != 0) { - mismatchDescription.appendText("contains ").appendValue(searchResponse.getShardFailures().length).appendText(" shard failures"); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Successful search response"); - } -} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java deleted file mode 100644 index 14faab0c4c..0000000000 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulUpdateResponseMatcher.java +++ /dev/null @@ -1,39 +0,0 @@ -/* -* 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.test.framework.matcher; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeDiagnosingMatcher; - -import org.opensearch.action.update.UpdateResponse; -import org.opensearch.core.rest.RestStatus; - -class SuccessfulUpdateResponseMatcher extends TypeSafeDiagnosingMatcher { - - @Override - protected boolean matchesSafely(UpdateResponse response, Description mismatchDescription) { - if (!RestStatus.OK.equals(response.status())) { - mismatchDescription.appendText("has status ").appendValue(response.status()).appendText(" which denotes failure."); - return false; - } - if (response.getShardInfo().getFailures().length != 0) { - mismatchDescription.appendText("contains ") - .appendValue(response.getShardInfo().getFailures().length) - .appendText(" shard failures"); - return false; - } - return true; - } - - @Override - public void describeTo(Description description) { - description.appendText("Successful update response"); - } -} From 0d0021ec81d9752debeebaa42a18a152fe92a55b Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Thu, 20 Jul 2023 17:10:29 -0400 Subject: [PATCH 6/6] Updates bwc versions to fix bwc tests Signed-off-by: Darshit Chanpura --- .github/actions/create-bwc-build/action.yaml | 7 +++++++ .github/workflows/ci.yml | 4 ++-- bwc-test/build.gradle | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/actions/create-bwc-build/action.yaml b/.github/actions/create-bwc-build/action.yaml index 43ba5ea82e..fcfa612a7d 100644 --- a/.github/actions/create-bwc-build/action.yaml +++ b/.github/actions/create-bwc-build/action.yaml @@ -19,7 +19,14 @@ runs: run: git config --system core.longpaths true shell: pwsh + - name: Checkout Branch from Fork + if: ${{ inputs.plugin-branch == 'current_branch' }} + uses: actions/checkout@v2 + with: + path: ${{ inputs.plugin-branch }} + - uses: actions/checkout@v3 + if: ${{ inputs.plugin-branch != 'current_branch' }} with: repository: opensearch-project/security ref: ${{ inputs.plugin-branch }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b4ae5a76a..4b0841bd11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,8 +89,8 @@ jobs: - id: build-previous uses: ./.github/actions/run-bwc-suite with: - plugin-previous-branch: "2.8" - plugin-next-branch: "2.x" + plugin-previous-branch: "2.9" + plugin-next-branch: "current_branch" report-artifact-name: bwc-${{ matrix.platform }}-jdk${{ matrix.jdk }} code-ql: diff --git a/bwc-test/build.gradle b/bwc-test/build.gradle index f04914342f..9b8d9fcc0a 100644 --- a/bwc-test/build.gradle +++ b/bwc-test/build.gradle @@ -76,8 +76,8 @@ loggerUsageCheck.enabled = false testingConventions.enabled = false validateNebulaPom.enabled = false -String previousVersion = System.getProperty("bwc.version.previous", "2.8.0.0") -String nextVersion = System.getProperty("bwc.version.next", "2.9.0.0") +String previousVersion = System.getProperty("bwc.version.previous", "2.9.0.0") +String nextVersion = System.getProperty("bwc.version.next", "2.10.0.0") String bwcVersion = previousVersion String baseName = "securityBwcCluster"