diff --git a/src/integrationTest/java/org/opensearch/security/SnapshotSteps.java b/src/integrationTest/java/org/opensearch/security/SnapshotSteps.java index a03891ecca..95af31f975 100644 --- a/src/integrationTest/java/org/opensearch/security/SnapshotSteps.java +++ b/src/integrationTest/java/org/opensearch/security/SnapshotSteps.java @@ -33,7 +33,7 @@ import static java.util.Objects.requireNonNull; import static org.opensearch.client.RequestOptions.DEFAULT; -class SnapshotSteps { +public class SnapshotSteps { private final SnapshotClient snapshotClient; diff --git a/src/integrationTest/java/org/opensearch/security/legacy/DoNotFailOnForbiddenTests.java b/src/integrationTest/java/org/opensearch/security/legacy/DoNotFailOnForbiddenTests.java new file mode 100644 index 0000000000..33bfe27cab --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/legacy/DoNotFailOnForbiddenTests.java @@ -0,0 +1,546 @@ +/* + * 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.legacy; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +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.Request; +import org.opensearch.client.Response; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.security.privileges.PrivilegesEvaluator; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.TestSecurityConfig.Role; +import org.opensearch.test.framework.TestSecurityConfig.User; +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.apache.http.HttpStatus.SC_CREATED; +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.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; +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", + "cluster:monitor/state", + "cluster:monitor/health", + "cluster:monitor/term" + ) + .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", + "indices:monitor/settings/get", + "indices:monitor/stats", + "indices:admin/aliases/get" + ) + .on(MARVELOUS_SONGS) + ); + + private static final User STATS_USER = new User("stats_user").roles( + new Role("test_role").clusterPermissions("cluster:monitor/*").indexPermissions("read", "indices:monitor/*").on("hi1") + ); + + 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, STATS_USER) + .anonymousAuth(false) + .doNotFailOnForbidden(true) + .nodeSettings(Map.of(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), 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(MARVELOUS_SONGS, ID_1).add(MARVELOUS_SONGS, 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_partial() throws Exception { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + MultiGetRequest request = new MultiGetRequest().add(MARVELOUS_SONGS, ID_1).add(HORRIBLE_SONGS, 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( + firstResult.getResponse(), + allOf(containDocument(MARVELOUS_SONGS, ID_1), documentContainField(FIELD_TITLE, TITLE_MAGNUM_OPUS)) + ); + assertThat(secondResult.getFailure().getMessage(), containsString("no permissions for [indices:data/read/mget[shard]]")); + } + } + + @Test + public void shouldMGetDocument_negative() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + MultiGetRequest request = new MultiGetRequest().add(HORRIBLE_SONGS, ID_4); + MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); + MultiGetItemResponse[] responses = response.getResponses(); + assertThat(responses, arrayWithSize(1)); + MultiGetItemResponse firstResult = responses[0]; + assertThat(firstResult.getFailure().getMessage(), containsString("no permissions for [indices:data/read/mget[shard]]")); + } + } + + @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)); + MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); + MultiSearchResponse.Item[] responses = response.getResponses(); + assertThat(responses, Matchers.arrayWithSize(1)); + assertThat(responses[0].getFailure().getMessage(), containsString("no permissions for [indices:data/read/search]")); + } + } + + @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)); + } + } + + @Test + public void shouldPerformCatIndices_positive() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + Request getIndicesRequest = new Request("GET", "/_cat/indices"); + // High level client doesn't support _cat/_indices API + Response getIndicesResponse = restHighLevelClient.getLowLevelClient().performRequest(getIndicesRequest); + List indexes = new BufferedReader(new InputStreamReader(getIndicesResponse.getEntity().getContent())).lines() + .collect(Collectors.toList()); + + assertThat(indexes.size(), equalTo(1)); + assertThat(indexes.get(0), containsString("marvelous_songs")); + } + } + + @Test + public void shouldPerformCatAliases_positive() throws IOException { + // DNFOF works for limited access user + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_USER)) { + Request getAliasesRequest = new Request("GET", "/_cat/aliases"); + Response getAliasesResponse = restHighLevelClient.getLowLevelClient().performRequest(getAliasesRequest); + List aliases = new BufferedReader(new InputStreamReader(getAliasesResponse.getEntity().getContent())).lines() + .collect(Collectors.toList()); + + // Does not fail on forbidden, but alias response only contains index which user has access to + assertThat(getAliasesResponse.getStatusLine().getStatusCode(), equalTo(200)); + assertThat(aliases.size(), equalTo(1)); + assertThat(aliases.get(0), containsString("marvelous_songs")); + assertThat(aliases.get(0), not(containsString("horrible_songs"))); + + } + + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + Request getAliasesRequest = new Request("GET", "/_cat/aliases"); + Response getAliasesResponse = restHighLevelClient.getLowLevelClient().performRequest(getAliasesRequest); + List aliases = new BufferedReader(new InputStreamReader(getAliasesResponse.getEntity().getContent())).lines() + .collect(Collectors.toList()); + + // Admin has access to all + assertThat(getAliasesResponse.getStatusLine().getStatusCode(), equalTo(200)); + // Aliases have one entry for each index + // This response is [(both-indices: marvelous_songs), (both-indices: horrible_songs), (forbidden-index: horrible_songs)] + assertThat(aliases.size(), equalTo(3)); + assertThat(aliases, hasItem(containsString("marvelous_songs"))); + assertThat(aliases, hasItem(containsString("horrible_songs"))); + + } + } + + @Test + public void checkStatsApi() { + // As admin creates 2 documents in different indices, can find both indices in search, cat indice & stats APIs + try (final TestRestClient client = cluster.getRestClient(ADMIN_USER.getName(), ADMIN_USER.getPassword())) { + final HttpResponse createDoc1 = client.postJson("hi1/_doc?refresh=true", "{\"hi\":\"Hello1\"}"); + createDoc1.assertStatusCode(SC_CREATED); + final HttpResponse createDoc2 = client.postJson("hi2/_doc?refresh=true", "{\"hi\":\"Hello2\"}"); + createDoc2.assertStatusCode(SC_CREATED); + + final HttpResponse search = client.postJson("hi*/_search", "{}"); + assertThat("Unexpected document results in search:" + search.getBody(), search.getBody(), containsString("2")); + + final HttpResponse catIndices = client.get("_cat/indices"); + assertThat("Expected cat indices: " + catIndices.getBody(), catIndices.getBody(), containsString("hi1")); + assertThat("Expected cat indices: " + catIndices.getBody(), catIndices.getBody(), containsString("hi2")); + + final HttpResponse stats = client.get("hi*/_stats?filter_path=indices.*.uuid"); + assertThat("Expected stats indices: " + stats.getBody(), stats.getBody(), containsString("hi1")); + assertThat("Expected stats indices: " + stats.getBody(), stats.getBody(), containsString("hi2")); + } + + // As user who can only see the index "hi1" make sure that DNFOF is filtering out "hi2" + try (final TestRestClient client = cluster.getRestClient(STATS_USER.getName(), STATS_USER.getPassword())) { + final HttpResponse search = client.postJson("hi*/_search", "{}"); + assertThat("Unexpected document results in search:" + search.getBody(), search.getBody(), containsString("1")); + + final HttpResponse catIndices = client.get("_cat/indices"); + assertThat("Expected cat indices: " + catIndices.getBody(), catIndices.getBody(), containsString("hi1")); + assertThat("Unexpected cat indices: " + catIndices.getBody(), catIndices.getBody(), not(containsString("hi2"))); + + final HttpResponse stats = client.get("hi*/_stats?filter_path=indices.*.uuid"); + assertThat("Expected stats indices: " + stats.getBody(), stats.getBody(), containsString("hi1")); + assertThat("Unexpected stats indices: " + stats.getBody(), stats.getBody(), not(containsString("hi2"))); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/security/legacy/PointInTimeOperationTest.java b/src/integrationTest/java/org/opensearch/security/legacy/PointInTimeOperationTest.java new file mode 100644 index 0000000000..22ddc654ba --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/legacy/PointInTimeOperationTest.java @@ -0,0 +1,417 @@ +/* +* 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.legacy; + +import java.io.IOException; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.RandomizedRunner; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Before; +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.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.search.builder.PointInTimeBuilder; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.security.privileges.PrivilegesEvaluator; +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(); + } + } + + @Before + public void cleanUpPits() throws IOException { + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ADMIN_USER)) { + restHighLevelClient.deleteAllPits(DEFAULT); + } + } + + @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) + .nodeSettings(Map.of(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), true)) + .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)) { + 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)) { + 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(); + } + } + +} diff --git a/src/integrationTest/java/org/opensearch/security/legacy/SearchOperationTest.java b/src/integrationTest/java/org/opensearch/security/legacy/SearchOperationTest.java new file mode 100644 index 0000000000..b3aec598ec --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/legacy/SearchOperationTest.java @@ -0,0 +1,2761 @@ +/* +* 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.legacy; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +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.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +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.health.ClusterHealthStatus; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.IndexTemplateMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; +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.search.builder.SearchSourceBuilder; +import org.opensearch.security.IndexOperationsHelper; +import org.opensearch.security.SnapshotSteps; +import org.opensearch.security.auditlog.AuditLog; +import org.opensearch.security.privileges.PrivilegesEvaluator; +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.core.rest.RestStatus.ACCEPTED; +import static org.opensearch.core.rest.RestStatus.BAD_REQUEST; +import static org.opensearch.core.rest.RestStatus.FORBIDDEN; +import static org.opensearch.core.rest.RestStatus.INTERNAL_SERVER_ERROR; +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.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").clusterPermissions("cluster:monitor/health") + .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 final 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)) + ) + .nodeSettings(Map.of(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), 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()); + } + } + + 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.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactlyOne(auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactlyOne(auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertExactlyOne(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.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactlyOne(auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest").withIndex(SONG_INDEX_NAME)); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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 { + // create index + Settings sourceIndexSettings = Settings.builder().put("index.number_of_replicas", 2).put("index.number_of_shards", 2).build(); + IndexOperationsHelper.createIndex(cluster, WRITE_SONG_INDEX_NAME, sourceIndexSettings); + + 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, auditPredicate(null).withLayer(AuditLog.Origin.TRANSPORT)); + auditLogsRule.assertAtLeastTransportMessages(4, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + auditLogsRule.assertAtLeastTransportMessages(4, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + } + + @Test + public void shouldDeleteDocumentInBulk_partiallyPositive() throws IOException { + Settings indexSettings = Settings.builder().put("index.number_of_replicas", 0).put("index.number_of_shards", 1).build(); + IndexOperationsHelper.createIndex(cluster, WRITE_SONG_INDEX_NAME, indexSettings); + + 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.assertExactlyOne(missingPrivilege(LIMITED_WRITE_USER, "BulkShardRequest")); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "CreateIndexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(REINDEXING_USER, "SearchScrollRequest")); + auditLogsRule.assertExactlyOne(auditPredicate(INDEX_EVENT).withEffectiveUser(REINDEXING_USER)); + auditLogsRule.assertExactlyOne(missingPrivilege(REINDEXING_USER, "ClearScrollRequest")); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(REINDEXING_USER, "PutMappingRequest")); + } + + @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.assertExactlyOne(grantedPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); + auditLogsRule.assertExactlyOne(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(2, grantedPrivilege(LIMITED_READ_USER, "IndicesAliasesRequest")); + auditLogsRule.assertExactly(2, 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.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "IndexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(2, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "DeleteIndexTemplateRequest")); + auditLogsRule.assertExactly(2, 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(2, grantedPrivilege(LIMITED_WRITE_USER, "PutIndexTemplateRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "IndexRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateIndexRequest")); + auditLogsRule.assertExactly(3, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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.assertExactlyOne(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.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(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 + public void shouldCreateSnapshot_positive() throws IOException { + final String snapshotName = "snapshot-positive-test"; + long snapshotGetCount; + 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)); + snapshotGetCount = 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.assertExactly( + snapshotGetCount, + userAuthenticated(LIMITED_WRITE_USER).withEffectiveUser(LIMITED_WRITE_USER) + .withRestRequest(GET, "/_snapshot/test-snapshot-repository/snapshot-positive-test") + ); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertAtLeast(snapshotGetCount, 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"; + long snapshotGetCount; + 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); + snapshotGetCount = 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.assertExactly( + snapshotGetCount, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/delete-snapshot-positive") + ); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "DeleteSnapshotRequest")); + auditLogsRule.assertExactly(snapshotGetCount, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + } + + @Test + public void shouldDeleteSnapshot_negative() throws IOException { + String snapshotName = "delete-snapshot-negative"; + long snapshotGetCount; + 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); + snapshotGetCount = 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.assertExactly( + snapshotGetCount, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/delete-snapshot-negative") + ); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactlyOne(missingPrivilege(LIMITED_READ_USER, "DeleteSnapshotRequest")); + auditLogsRule.assertExactly(snapshotGetCount, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + } + + @Test + public void shouldRestoreSnapshot_positive() throws IOException { + final String snapshotName = "restore-snapshot-positive"; + long snapshotGetCount; + AtomicInteger restoredCount = new AtomicInteger(); + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { + SnapshotSteps steps = new SnapshotSteps(restHighLevelClient); + // 1. create some documents + Settings indexSettings = Settings.builder().put("index.number_of_replicas", 0).put("index.number_of_shards", 1).build(); + IndexOperationsHelper.createIndex(cluster, WRITE_SONG_INDEX_NAME, indexSettings); + + 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 + snapshotGetCount = 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() + .pollInterval(100, TimeUnit.MILLISECONDS) + .alias("Index contains proper number of documents restored from snapshot.") + .until(() -> { + restoredCount.incrementAndGet(); + return 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.assertExactly( + restoredCount.get(), + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/restored_write_song_index/_count") + ); + auditLogsRule.assertExactly(2, userAuthenticated(LIMITED_WRITE_USER).withRestRequest(POST, "/_bulk")); + auditLogsRule.assertExactly( + snapshotGetCount, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest(GET, "/_snapshot/test-snapshot-repository/restore-snapshot-positive") + ); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactly(2, grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); + auditLogsRule.assertExactly(restoredCount.get(), grantedPrivilege(LIMITED_WRITE_USER, "SearchRequest")); + auditLogsRule.assertExactly(snapshotGetCount, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @Test + public void shouldRestoreSnapshot_failureForbiddenIndex() throws IOException { + final String snapshotName = "restore-snapshot-negative-forbidden-index"; + String restoreToIndex = "forbidden_index"; + long snapshotGetCount; + Settings indexSettings = Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0).build(); + IndexOperationsHelper.createIndex(cluster, WRITE_SONG_INDEX_NAME, indexSettings); + 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 + snapshotGetCount = 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.assertExactly( + snapshotGetCount, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + GET, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-index" + ) + ); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactly(snapshotGetCount, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + auditLogsRule.assertAtLeastTransportMessages(1, auditPredicate(INDEX_EVENT).withEffectiveUser(LIMITED_WRITE_USER)); + auditLogsRule.assertExactlyScanAll(1, missingPrivilege(LIMITED_WRITE_USER, "RestoreSnapshotRequest")); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @Test + public void shouldRestoreSnapshot_failureOperationForbidden() throws IOException { + String snapshotName = "restore-snapshot-negative-forbidden-operation"; + long snapshotGetCount; + Settings indexSettings = Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0).build(); + IndexOperationsHelper.createIndex(cluster, WRITE_SONG_INDEX_NAME, indexSettings); + 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 + snapshotGetCount = 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.assertExactly( + snapshotGetCount, + userAuthenticated(LIMITED_WRITE_USER).withRestRequest( + GET, + "/_snapshot/test-snapshot-repository/restore-snapshot-negative-forbidden-operation" + ) + ); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "PutRepositoryRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "CreateSnapshotRequest")); + auditLogsRule.assertExactlyOne(grantedPrivilege(LIMITED_WRITE_USER, "BulkRequest")); + auditLogsRule.assertExactlyScanAll(1, missingPrivilege(LIMITED_READ_USER, "RestoreSnapshotRequest")); + auditLogsRule.assertExactly(snapshotGetCount, grantedPrivilege(LIMITED_WRITE_USER, "GetSnapshotsRequest")); + auditLogsRule.assertAtLeastTransportMessages(2, grantedPrivilege(LIMITED_WRITE_USER, "PutMappingRequest")); + } + + @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( + 1, + grantedPrivilege(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, "IndicesAliasesRequest") + ); + auditLogsRule.assertExactly( + 1, + 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 + // required permissions: "indices:admin/resize", "indices:monitor/stats + public void shrinkIndex_positive() throws IOException { + String sourceIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_positive_source"); + String targetIndexName = INDICES_ON_WHICH_USER_CAN_PERFORM_INDEX_OPERATIONS_PREFIX.concat("shrink_index_positive_target"); + Settings sourceIndexSettings = Settings.builder() + .put("index.number_of_replicas", 1) + .put("index.blocks.write", true) + .put("index.number_of_shards", 4) + .build(); + IndexOperationsHelper.createIndex(cluster, sourceIndexName, sourceIndexSettings); + + try ( + RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient( + USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES + ) + ) { + ClusterHealthResponse healthResponse = restHighLevelClient.cluster() + .health( + new ClusterHealthRequest(sourceIndexName).waitForNoRelocatingShards(true) + .waitForActiveShards(4) + .waitForNoInitializingShards(true) + .waitForGreenStatus(), + DEFAULT + ); + + assertThat(healthResponse.getStatus(), is(ClusterHealthStatus.GREEN)); + + 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 + // required permissions: "indices:admin/resize", "indices:monitor/stats + 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)); + + // can't clone the same index twice, target already exists + ResizeRequest repeatResizeRequest = new ResizeRequest(targetIndexName, sourceIndexName); + assertThatThrownBy(() -> restHighLevelClient.indices().clone(repeatResizeRequest, DEFAULT), statusException(BAD_REQUEST)); + } + } + + @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 + // required permissions: "indices:admin/resize", "indices:monitor/stats + 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( + 1, + grantedPrivilege(USER_ALLOWED_TO_PERFORM_INDEX_OPERATIONS_ON_SELECTED_INDICES, "CreateIndexRequest") + ); + auditLogsRule.assertExactly( + 1, + 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/legacy/SecurityRolesTests.java b/src/integrationTest/java/org/opensearch/security/legacy/SecurityRolesTests.java new file mode 100644 index 0000000000..15cb0b7455 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/legacy/SecurityRolesTests.java @@ -0,0 +1,67 @@ +/* +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +* Modifications Copyright OpenSearch Contributors. See +* GitHub history for details. +*/ + +package org.opensearch.security.legacy; + +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.http.HttpStatus; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.security.privileges.PrivilegesEvaluator; +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 static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class SecurityRolesTests { + + protected final static TestSecurityConfig.User USER_SR = new TestSecurityConfig.User("sr_user").roles( + new Role("abc_ber").indexPermissions("*").on("*").clusterPermissions("*"), + new Role("def_efg").indexPermissions("*").on("*").clusterPermissions("*") + ); + + @ClassRule + public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .anonymousAuth(true) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(USER_SR) + .nodeSettings(Map.of(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), true)) + .build(); + + @Test + public void testSecurityRoles() throws Exception { + try (TestRestClient client = cluster.getRestClient(USER_SR)) { + HttpResponse response = client.getAuthInfo(); + response.assertStatusCode(HttpStatus.SC_OK); + + // Check username + assertThat(response.getTextFromJsonBody("/user_name"), equalTo("sr_user")); + + // Check security roles + assertThat(response.getTextFromJsonBody("/roles/0"), equalTo("user_sr_user__abc_ber")); + assertThat(response.getTextFromJsonBody("/roles/1"), equalTo("user_sr_user__def_efg")); + + } + } + +} diff --git a/src/integrationTest/java/org/opensearch/security/legacy/SystemIndexTests.java b/src/integrationTest/java/org/opensearch/security/legacy/SystemIndexTests.java new file mode 100644 index 0000000000..8a1e7a03ee --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/legacy/SystemIndexTests.java @@ -0,0 +1,119 @@ +/* + * 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.legacy; + +import java.util.List; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.core.rest.RestStatus; +import org.opensearch.security.http.ExampleSystemIndexPlugin; +import org.opensearch.security.privileges.PrivilegesEvaluator; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; +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.hamcrest.Matchers.equalTo; +import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED; +import static org.opensearch.security.support.ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; +import static org.opensearch.test.framework.TestSecurityConfig.User.USER_ADMIN; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class SystemIndexTests { + + public static final AuthcDomain AUTHC_DOMAIN = new AuthcDomain("basic", 0).httpAuthenticatorWithChallenge("basic").backend("internal"); + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE) + .anonymousAuth(false) + .authc(AUTHC_DOMAIN) + .users(USER_ADMIN) + .plugin(ExampleSystemIndexPlugin.class) + .nodeSettings( + Map.of( + SECURITY_RESTAPI_ROLES_ENABLED, + List.of("user_" + USER_ADMIN.getName() + "__" + ALL_ACCESS.getName()), + SECURITY_SYSTEM_INDICES_ENABLED_KEY, + true, + PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), + true + ) + ) + .build(); + + @Before + public void setup() { + try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) { + client.delete(".system-index1"); + } + } + + @Test + public void adminShouldNotBeAbleToDeleteSecurityIndex() { + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + HttpResponse response = client.delete(".opendistro_security"); + + assertThat(response.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); + + // Create regular index + client.put("test-index"); + + // regular user can delete non-system index + HttpResponse response2 = client.delete("test-index"); + + assertThat(response2.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + + // regular use can create system index + HttpResponse response3 = client.put(".system-index1"); + + assertThat(response3.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + + // regular user cannot delete system index + HttpResponse response4 = client.delete(".system-index1"); + + assertThat(response4.getStatusCode(), equalTo(RestStatus.FORBIDDEN.getStatus())); + } + } + + @Test + public void regularUserShouldGetNoResultsWhenSearchingSystemIndex() { + // Create system index and index a dummy document as the super admin user, data returned to super admin + try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) { + HttpResponse response1 = client.put(".system-index1"); + + assertThat(response1.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + String doc = "{\"field\":\"value\"}"; + HttpResponse adminPostResponse = client.postJson(".system-index1/_doc/1?refresh=true", doc); + assertThat(adminPostResponse.getStatusCode(), equalTo(RestStatus.CREATED.getStatus())); + HttpResponse response2 = client.get(".system-index1/_search"); + + assertThat(response2.getStatusCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(response2.getBody(), response2.getBody().contains("\"hits\":{\"total\":{\"value\":1,\"relation\":\"eq\"}")); + } + + // Regular users should not be able to read it + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + // regular user cannot read system index + HttpResponse response1 = client.get(".system-index1/_search"); + + assertThat(response1.getBody(), response1.getBody().contains("\"hits\":{\"total\":{\"value\":0,\"relation\":\"eq\"}")); + } + } +} diff --git a/src/test/java/org/opensearch/security/AggregationTests.java b/src/test/java/org/opensearch/security/AggregationTests.java index bdd51960d0..a858d37ea9 100644 --- a/src/test/java/org/opensearch/security/AggregationTests.java +++ b/src/test/java/org/opensearch/security/AggregationTests.java @@ -26,6 +26,11 @@ package org.opensearch.security; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Test; @@ -37,6 +42,7 @@ import org.opensearch.client.Client; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; @@ -45,10 +51,22 @@ import static org.hamcrest.Matchers.is; public class AggregationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public AggregationTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } @Test public void testBasicAggregations() throws Exception { - final Settings settings = Settings.builder().build(); + final Settings settings = Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(settings); final RestHelper rh = nonSslRestHelper(); diff --git a/src/test/java/org/opensearch/security/DataStreamIntegrationTests.java b/src/test/java/org/opensearch/security/DataStreamIntegrationTests.java index 6ca83c83dc..842406d149 100644 --- a/src/test/java/org/opensearch/security/DataStreamIntegrationTests.java +++ b/src/test/java/org/opensearch/security/DataStreamIntegrationTests.java @@ -11,10 +11,17 @@ package org.opensearch.security; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Assert; import org.junit.Test; +import org.opensearch.common.settings.Settings; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; @@ -23,6 +30,16 @@ import static org.hamcrest.Matchers.is; public class DataStreamIntegrationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public DataStreamIntegrationTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } final String bulkDocsBody = "{ \"create\" : {} }" + System.lineSeparator() @@ -58,7 +75,12 @@ public void createSampleDataStreams(RestHelper rh) throws Exception { @Test public void testCreateDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); + RestHelper rh = nonSslRestHelper(); HttpResponse response; @@ -95,7 +117,11 @@ public void testCreateDataStream() throws Exception { @Test public void testGetDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; @@ -140,7 +166,11 @@ public void testGetDataStream() throws Exception { @Test public void testDeleteDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; @@ -188,7 +218,11 @@ public void testDeleteDataStream() throws Exception { @Test public void testDataStreamStats() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; @@ -236,7 +270,11 @@ public void testDataStreamStats() throws Exception { @Test public void testGetIndexOnBackingIndicesOfDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; @@ -281,7 +319,11 @@ public void testGetIndexOnBackingIndicesOfDataStream() throws Exception { @Test public void testDocumentLevelSecurityOnDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; @@ -350,7 +392,11 @@ public void testDocumentLevelSecurityOnDataStream() throws Exception { @Test public void testFLSOnBackingIndicesOfDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; @@ -428,7 +474,11 @@ public void testFLSOnBackingIndicesOfDataStream() throws Exception { @Test public void testFieldMaskingOnDataStream() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); createSampleDataStreams(rh); HttpResponse response; diff --git a/src/test/java/org/opensearch/security/HttpIntegrationTests.java b/src/test/java/org/opensearch/security/HttpIntegrationTests.java index a932ba98e1..e4a533a36c 100644 --- a/src/test/java/org/opensearch/security/HttpIntegrationTests.java +++ b/src/test/java/org/opensearch/security/HttpIntegrationTests.java @@ -29,7 +29,11 @@ import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collection; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.fasterxml.jackson.databind.JsonNode; import org.apache.http.HttpStatus; import org.apache.http.NoHttpResponseException; @@ -49,6 +53,7 @@ import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.ssl.util.SSLConfigConstants; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; @@ -62,11 +67,22 @@ import static org.opensearch.security.DefaultObjectMapper.readTree; public class HttpIntegrationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public HttpIntegrationTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } @Test public void testHTTPBasic() throws Exception { final Settings settings = Settings.builder() .putList(ConfigConstants.SECURITY_AUTHCZ_REST_IMPERSONATION_USERS + ".worf", "knuddel", "nonexists") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(settings); final RestHelper rh = nonSslRestHelper(); @@ -876,7 +892,10 @@ public void testHTTPBasic2() throws Exception { @Test public void testBulk() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles("roles_bulk.yml"), settings); final RestHelper rh = nonSslRestHelper(); @@ -897,7 +916,10 @@ public void testBulk() throws Exception { @Test public void testBulkWithOneIndexFailure() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles("roles_bulk.yml"), settings); final RestHelper rh = nonSslRestHelper(); @@ -920,7 +942,10 @@ public void testBulkWithOneIndexFailure() throws Exception { @Test public void test557() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig(), settings); try (Client tc = getClient()) { @@ -959,7 +984,10 @@ public void test557() throws Exception { @Test public void testITT1635() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_dnfof.yml").setSecurityRoles("roles_itt1635.yml"), settings); try (Client tc = getClient()) { @@ -1020,7 +1048,9 @@ public void testITT1635() throws Exception { @Test public void testTenantInfo() throws Exception { - final Settings settings = Settings.builder().build(); + final Settings settings = Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig(), settings); /* @@ -1111,6 +1141,7 @@ public void testTenantInfo() throws Exception { public void testRestImpersonation() throws Exception { final Settings settings = Settings.builder() .putList(ConfigConstants.SECURITY_AUTHCZ_REST_IMPERSONATION_USERS + ".worf", "someotherusernotininternalusersfile") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_rest_impersonation.yml"), settings); final RestHelper rh = nonSslRestHelper(); @@ -1147,7 +1178,9 @@ public void testSslOnlyMode() throws Exception { @Test public void testAll() throws Exception { - final Settings settings = Settings.builder().build(); + final Settings settings = Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(settings); final RestHelper rh = nonSslRestHelper(); diff --git a/src/test/java/org/opensearch/security/IndexIntegrationTests.java b/src/test/java/org/opensearch/security/IndexIntegrationTests.java index 648a9b1ade..7554137a54 100644 --- a/src/test/java/org/opensearch/security/IndexIntegrationTests.java +++ b/src/test/java/org/opensearch/security/IndexIntegrationTests.java @@ -28,9 +28,13 @@ import java.net.URLEncoder; import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; import java.util.Date; import java.util.TimeZone; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Assert; import org.junit.Ignore; @@ -48,6 +52,7 @@ import org.opensearch.common.xcontent.XContentType; import org.opensearch.index.query.QueryBuilders; import org.opensearch.indices.InvalidIndexNameException; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.SecurityUtils; import org.opensearch.security.test.DynamicSecurityConfig; @@ -59,6 +64,16 @@ import static org.hamcrest.Matchers.is; public class IndexIntegrationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public IndexIntegrationTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } @Test public void testComposite() throws Exception { @@ -66,7 +81,9 @@ public void testComposite() throws Exception { setup( Settings.EMPTY, new DynamicSecurityConfig().setConfig("composite_config.yml").setSecurityRoles("roles_composite.yml"), - Settings.EMPTY, + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), true ); final RestHelper rh = nonSslRestHelper(); @@ -105,7 +122,14 @@ public void testComposite() throws Exception { @Test public void testBulkShards() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles("roles_bs.yml"), Settings.EMPTY, true); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setSecurityRoles("roles_bs.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), + true + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { @@ -175,7 +199,11 @@ public void testBulkShards() throws Exception { @Test public void testCreateIndex() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); HttpResponse res; @@ -215,7 +243,11 @@ public void testCreateIndex() throws Exception { @Test public void testFilteredAlias() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { @@ -271,7 +303,11 @@ public void testFilteredAlias() throws Exception { @Test public void testIndexTypeEvaluation() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { tc.index( @@ -343,7 +379,11 @@ public void testIndexTypeEvaluation() throws Exception { @Test public void testIndices() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { tc.index(new IndexRequest("nopermindex").setRefreshPolicy(RefreshPolicy.IMMEDIATE).source("{\"content\":1}", XContentType.JSON)) @@ -541,8 +581,10 @@ public void testIndices() throws Exception { @Test public void testAliases() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); - + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(settings); try (Client tc = getClient()) { @@ -661,7 +703,11 @@ public void testAliases() throws Exception { @Test public void testIndexResolveInvalidIndexName() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); // invalid_index_name_exception should be thrown and responded when invalid index name is mentioned in requests. @@ -676,7 +722,11 @@ public void testIndexResolveInvalidIndexName() throws Exception { @Test public void testCCSIndexResolve() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { @@ -697,7 +747,11 @@ public void testCCSIndexResolve() throws Exception { @Ignore public void testCCSIndexResolve2() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { @@ -763,7 +817,9 @@ public void testIndexResolveIgnoreUnavailable() throws Exception { setup( Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_respect_indices_options.yml").setSecurityRoles("roles_bs.yml"), - Settings.EMPTY, + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), true ); final RestHelper rh = nonSslRestHelper(); @@ -789,7 +845,14 @@ public void testIndexResolveIgnoreUnavailable() throws Exception { @Test public void testIndexResolveIndicesAlias() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY, true); + setup( + Settings.EMPTY, + new DynamicSecurityConfig(), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), + true + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { @@ -821,7 +884,14 @@ public void testIndexResolveIndicesAlias() throws Exception { @Test public void testIndexResolveMinus() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY, true); + setup( + Settings.EMPTY, + new DynamicSecurityConfig(), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), + true + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { diff --git a/src/test/java/org/opensearch/security/IndexTemplateClusterPermissionsCheckTest.java b/src/test/java/org/opensearch/security/IndexTemplateClusterPermissionsCheckTest.java index 72a5c2006c..adad074a50 100644 --- a/src/test/java/org/opensearch/security/IndexTemplateClusterPermissionsCheckTest.java +++ b/src/test/java/org/opensearch/security/IndexTemplateClusterPermissionsCheckTest.java @@ -11,10 +11,17 @@ package org.opensearch.security; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Before; import org.junit.Test; +import org.opensearch.common.settings.Settings; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; @@ -23,6 +30,19 @@ import static org.hamcrest.Matchers.is; public class IndexTemplateClusterPermissionsCheckTest extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public IndexTemplateClusterPermissionsCheckTest( + @Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation + ) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } + private RestHelper rh; final static String indexTemplateBody = @@ -34,7 +54,11 @@ private String getFailureResponseReason(String user) { @Before public void setupRestHelper() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); rh = nonSslRestHelper(); } diff --git a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java index d0bcb317f0..2470a80cfd 100644 --- a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java +++ b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java @@ -27,8 +27,12 @@ package org.opensearch.security; import java.io.File; +import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.fasterxml.jackson.databind.JsonNode; import org.apache.http.Header; import org.apache.http.HttpStatus; @@ -50,6 +54,7 @@ import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.ssl.util.SSLConfigConstants; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; @@ -64,6 +69,19 @@ public class InitializationIntegrationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public InitializationIntegrationTests( + @Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation + ) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } + @Test public void testEnsureInitViaRestDoesWork() throws Exception { @@ -72,6 +90,7 @@ public void testEnsureInitViaRestDoesWork() throws Exception { .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(Settings.EMPTY, null, settings, false); final RestHelper rh = restHelper(); // ssl resthelper @@ -107,6 +126,7 @@ public void testInitWithInjectedUser() throws Exception { final Settings settings = Settings.builder() .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()) .put("plugins.security.unsupported.inject_user.enabled", true) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_disable_all.yml"), settings, true); @@ -130,6 +150,7 @@ public void testWhoAmI() throws Exception { .put("plugins.security.ssl.http.enabled", true) .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( Settings.EMPTY, @@ -152,7 +173,11 @@ public void testWhoAmI() throws Exception { @Test public void testConfigHotReload() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); Header spock = encodeBasicHeader("spock", "spock"); @@ -243,7 +268,10 @@ public void testConfigHotReload() throws Exception { @Test public void testDefaultConfig() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true).build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, null, settings, false); RestHelper rh = nonSslRestHelper(); Thread.sleep(10000); @@ -259,7 +287,10 @@ public void testInvalidDefaultConfig() throws Exception { final String defaultInitDirectory = ClusterHelper.updateDefaultDirectory( new File(TEST_RESOURCE_RELATIVE_PATH + "invalid_config").getAbsolutePath() ); - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true).build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, null, settings, false); RestHelper rh = nonSslRestHelper(); Thread.sleep(10000); @@ -275,7 +306,10 @@ public void testInvalidDefaultConfig() throws Exception { @Test public void testDisabled() throws Exception { - final Settings settings = Settings.builder().put("plugins.security.disabled", true).build(); + final Settings settings = Settings.builder() + .put("plugins.security.disabled", true) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, null, settings, false); RestHelper rh = nonSslRestHelper(); @@ -287,7 +321,14 @@ public void testDisabled() throws Exception { @Test public void testDiscoveryWithoutInitialization() throws Exception { - setup(Settings.EMPTY, null, Settings.EMPTY, false); + setup( + Settings.EMPTY, + null, + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), + false + ); assertThat( clusterInfo.numNodes, is( diff --git a/src/test/java/org/opensearch/security/IntegrationTests.java b/src/test/java/org/opensearch/security/IntegrationTests.java index 6fc953ef59..b34de787fc 100644 --- a/src/test/java/org/opensearch/security/IntegrationTests.java +++ b/src/test/java/org/opensearch/security/IntegrationTests.java @@ -26,8 +26,12 @@ package org.opensearch.security; +import java.util.Arrays; +import java.util.Collection; import java.util.TreeSet; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.fasterxml.jackson.databind.JsonNode; import org.apache.http.HttpStatus; import org.apache.http.message.BasicHeader; @@ -48,6 +52,7 @@ import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; import org.opensearch.security.http.HTTPClientCertAuthenticator; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.ssl.util.SSLConfigConstants; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; @@ -63,11 +68,22 @@ import static org.opensearch.security.DefaultObjectMapper.readTree; public class IntegrationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public IntegrationTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } @Test public void testSearchScroll() throws Exception { final Settings settings = Settings.builder() .putList(ConfigConstants.SECURITY_AUTHCZ_REST_IMPERSONATION_USERS + ".worf", "knuddel", "nonexists") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(settings); final RestHelper rh = nonSslRestHelper(); @@ -197,6 +213,7 @@ public void testDNSpecials1() throws Exception { "EMAILADDREss=unt@xxx.com, cn=node-untspec6.example.com, OU=SSL,O=Te\\, st,L=Test, c=DE" ) .put("plugins.security.cert.oid", "1.2.3.4.5.6") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); Settings tcSettings = Settings.builder() @@ -223,7 +240,11 @@ public void testEnsureOpenSSLAvailability() { @Test public void testMultiget() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { tc.index( @@ -261,6 +282,7 @@ public void testRestImpersonation() throws Exception { final Settings settings = Settings.builder() .putList(ConfigConstants.SECURITY_AUTHCZ_REST_IMPERSONATION_USERS + ".spock", "knuddel", "userwhonotexists") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(settings); @@ -305,7 +327,11 @@ public void testRestImpersonation() throws Exception { @Test public void testSingle() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { tc.index( @@ -336,7 +362,11 @@ public void testSingle() throws Exception { @Test public void testSpecialUsernames() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); assertThat(rh.executeGetRequest("", encodeBasicHeader("bug.88", "nagilum")).getStatusCode(), is(HttpStatus.SC_OK)); @@ -349,7 +379,14 @@ public void testSpecialUsernames() throws Exception { @Test public void testXff() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_xff.yml"), Settings.EMPTY, true); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setConfig("config_xff.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(), + true + ); RestHelper rh = nonSslRestHelper(); HttpResponse resc = rh.executeGetRequest( "_opendistro/_security/authinfo", @@ -363,7 +400,14 @@ public void testXff() throws Exception { @Test public void testRegexExcludes() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig(), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { tc.index(new IndexRequest("indexa").setRefreshPolicy(RefreshPolicy.IMMEDIATE).source("{\"indexa\":1}", XContentType.JSON)) @@ -400,7 +444,13 @@ public void testRegexExcludes() throws Exception { @Test public void testMultiRoleSpan() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_multirolespan.yml"), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setConfig("config_multirolespan.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { @@ -421,7 +471,13 @@ public void testMultiRoleSpan() throws Exception { @Test public void testMultiRoleSpan2() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_multirolespan.yml"), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setConfig("config_multirolespan.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { @@ -449,7 +505,11 @@ public void testMultiRoleSpan2() throws Exception { @Test public void testSecurityUnderscore() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); HttpResponse res = rh.executePostRequest( @@ -470,7 +530,13 @@ public void testSecurityUnderscore() throws Exception { @Test public void testDeleteByQueryDnfof() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_dnfof.yml"), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setConfig("config_dnfof.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client tc = getClient()) { for (int i = 0; i < 3; i++) { @@ -498,7 +564,10 @@ public void testDeleteByQueryDnfof() throws Exception { @Test public void testUpdate() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(settings); final RestHelper rh = nonSslRestHelper(); @@ -519,7 +588,10 @@ public void testUpdate() throws Exception { @Test public void testDnfof() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_dnfof.yml"), settings); final RestHelper rh = nonSslRestHelper(); @@ -766,7 +838,10 @@ public void testDnfof() throws Exception { @Test public void testNoDnfof() throws Exception { - final Settings settings = Settings.builder().put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH").build(); + final Settings settings = Settings.builder() + .put(ConfigConstants.SECURITY_ROLES_MAPPING_RESOLUTION, "BOTH") + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig(), settings); final RestHelper rh = nonSslRestHelper(); @@ -991,7 +1066,11 @@ public void testNoDnfof() throws Exception { @Test public void testSecurityIndexSecurity() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final RestHelper rh = nonSslRestHelper(); HttpResponse res = rh.executePutRequest( @@ -1059,7 +1138,13 @@ public void testSecurityIndexSecurity() throws Exception { @Test public void testMonitorHealth() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig(), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); assertThat(rh.executeGetRequest("_cat/health", encodeBasicHeader("picard", "picard")).getStatusCode(), is(HttpStatus.SC_OK)); diff --git a/src/test/java/org/opensearch/security/PitIntegrationTests.java b/src/test/java/org/opensearch/security/PitIntegrationTests.java index 11c9ba8ddd..081950cb98 100644 --- a/src/test/java/org/opensearch/security/PitIntegrationTests.java +++ b/src/test/java/org/opensearch/security/PitIntegrationTests.java @@ -11,8 +11,12 @@ package org.opensearch.security; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Test; @@ -21,7 +25,9 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; @@ -32,10 +38,24 @@ * Integration tests to test point in time APIs permission model */ public class PitIntegrationTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public PitIntegrationTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } @Test public void testPitExplicitAPIAccess() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); try (Client tc = getClient()) { // create alias @@ -116,7 +136,11 @@ public void testPitExplicitAPIAccess() throws Exception { @Test public void testPitAllAPIAccess() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); // Create two indices @@ -187,7 +211,11 @@ public void testPitAllAPIAccess() throws Exception { @Test public void testDataStreamWithPits() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); RestHelper rh = nonSslRestHelper(); String indexTemplate = "{\"index_patterns\": [ \"my-data-stream*\" ], \"data_stream\": { }, \"priority\": 200, " + "\"template\": {\"settings\": { } } }"; diff --git a/src/test/java/org/opensearch/security/PrivilegesEvaluationTest.java b/src/test/java/org/opensearch/security/PrivilegesEvaluationTest.java index d52853c05b..f6b3d095cc 100644 --- a/src/test/java/org/opensearch/security/PrivilegesEvaluationTest.java +++ b/src/test/java/org/opensearch/security/PrivilegesEvaluationTest.java @@ -11,6 +11,11 @@ package org.opensearch.security; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.google.common.collect.ImmutableMap; import org.junit.Test; @@ -18,7 +23,9 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.WriteRequest.RefreshPolicy; import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; @@ -26,10 +33,25 @@ import static org.hamcrest.Matchers.is; public class PrivilegesEvaluationTest extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public PrivilegesEvaluationTest(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } + @Test public void resolveTestHidden() throws Exception { - setup(); + setup( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); try (Client client = getClient()) { diff --git a/src/test/java/org/opensearch/security/ResolveAPITests.java b/src/test/java/org/opensearch/security/ResolveAPITests.java index 9d1c77341e..f38762787f 100644 --- a/src/test/java/org/opensearch/security/ResolveAPITests.java +++ b/src/test/java/org/opensearch/security/ResolveAPITests.java @@ -15,6 +15,11 @@ package org.opensearch.security; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -27,6 +32,7 @@ import org.opensearch.client.Client; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; @@ -35,14 +41,25 @@ import static org.hamcrest.Matchers.is; public class ResolveAPITests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public ResolveAPITests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } protected final Logger log = LogManager.getLogger(this.getClass()); @Test public void testResolveDnfofFalse() throws Exception { - Settings settings = Settings.builder().build(); - + Settings settings = Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(settings); setupIndices(); @@ -97,7 +114,9 @@ public void testResolveDnfofFalse() throws Exception { @Test public void testResolveDnfofTrue() throws Exception { - final Settings settings = Settings.builder().build(); + Settings settings = Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_dnfof.yml"), settings); setupIndices(); diff --git a/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java b/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java index 2492ff2fb0..104435836f 100644 --- a/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java +++ b/src/test/java/org/opensearch/security/RolesInjectorIntegTest.java @@ -17,9 +17,12 @@ import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.function.Supplier; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.google.common.collect.Lists; import org.junit.Assert; import org.junit.Test; @@ -45,6 +48,7 @@ import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.script.ScriptService; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.AbstractSecurityUnitTest; import org.opensearch.security.test.DynamicSecurityConfig; @@ -57,6 +61,16 @@ import static org.hamcrest.Matchers.is; public class RolesInjectorIntegTest extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public RolesInjectorIntegTest(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } public static class RolesInjectorPlugin extends Plugin implements ActionPlugin { Settings settings; @@ -88,7 +102,13 @@ public Collection createComponents( @Test public void testRolesInject() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles("roles.yml"), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setSecurityRoles("roles.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); assertThat( clusterInfo.numNodes, diff --git a/src/test/java/org/opensearch/security/RolesValidationIntegTest.java b/src/test/java/org/opensearch/security/RolesValidationIntegTest.java index 8dc3a45fca..01fb037ba0 100644 --- a/src/test/java/org/opensearch/security/RolesValidationIntegTest.java +++ b/src/test/java/org/opensearch/security/RolesValidationIntegTest.java @@ -13,9 +13,12 @@ import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.function.Supplier; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.google.common.collect.Lists; import org.junit.Assert; import org.junit.Test; @@ -39,6 +42,7 @@ import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.script.ScriptService; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.AbstractSecurityUnitTest; import org.opensearch.security.test.DynamicSecurityConfig; @@ -48,6 +52,16 @@ import org.opensearch.watcher.ResourceWatcherService; public class RolesValidationIntegTest extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public RolesValidationIntegTest(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } public static class RolesValidationPlugin extends Plugin implements ActionPlugin { Settings settings; @@ -82,7 +96,13 @@ public Collection createComponents( @Test public void testRolesValidation() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles("roles.yml"), Settings.EMPTY); + setup( + Settings.EMPTY, + new DynamicSecurityConfig().setSecurityRoles("roles.yml"), + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); final Settings tcSettings = AbstractSecurityUnitTest.nodeRolesSettings(Settings.builder(), false, false) .put(minimumSecuritySettings(Settings.EMPTY).get(0)) diff --git a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java index 0291b79577..5c5be7b199 100644 --- a/src/test/java/org/opensearch/security/SnapshotRestoreTests.java +++ b/src/test/java/org/opensearch/security/SnapshotRestoreTests.java @@ -27,8 +27,11 @@ package org.opensearch.security; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Assert; import org.junit.Test; @@ -45,6 +48,7 @@ import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; import org.opensearch.security.action.configupdate.ConfigUpdateResponse; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.cluster.ClusterConfiguration; @@ -56,6 +60,17 @@ import static org.hamcrest.Matchers.not; public class SnapshotRestoreTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public SnapshotRestoreTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } + private ClusterConfiguration currentClusterConfig = ClusterConfiguration.DEFAULT; @Test @@ -65,6 +80,7 @@ public void testSnapshotEnableSecurityIndexRestore() throws Exception { .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()) .put("plugins.security.check_snapshot_restore_write_privileges", false) .put("plugins.security.unsupported.restore.securityindex.enabled", true) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup(settings, currentClusterConfig); @@ -858,7 +874,10 @@ public void testSnapshotRestoreSpecialIndicesPatterns() throws Exception { final List listOfIndexesToTest = Arrays.asList("foo", "bar", "baz"); - final Settings settings = Settings.builder().putList("path.repo", repositoryPath.getRoot().getAbsolutePath()).build(); + final Settings settings = Settings.builder() + .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build(); setup( Settings.EMPTY, @@ -940,6 +959,7 @@ public void testNoSnapshotRestore() throws Exception { final Settings settings = Settings.builder() .putList("path.repo", repositoryPath.getRoot().getAbsolutePath()) .put("plugins.security.enable_snapshot_restore_privilege", false) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/RoleBasedAccessTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/RoleBasedAccessTest.java index fa4096596d..60ab44567e 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/RoleBasedAccessTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/RoleBasedAccessTest.java @@ -11,6 +11,11 @@ package org.opensearch.security.dlic.rest.api; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.HttpStatus; import org.junit.Assert; import org.junit.Test; @@ -18,6 +23,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.SecurityJsonNode; import org.opensearch.security.test.helper.file.FileHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; @@ -27,20 +33,32 @@ import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX; public class RoleBasedAccessTest extends AbstractRestApiUnitTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + private final String ENDPOINT; protected String getEndpointPrefix() { return PLUGINS_PREFIX; } - public RoleBasedAccessTest() { + public RoleBasedAccessTest(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; ENDPOINT = getEndpointPrefix() + "/api"; } @Test public void testActionGroupsApi() throws Exception { - setupWithRestRoles(); + setupWithRestRoles( + Settings.builder() + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) + .build() + ); rh.sendAdminCertificate = false; diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRoleBasedAccessTests.java b/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRoleBasedAccessTests.java index 329404dfe7..e891d7b49f 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRoleBasedAccessTests.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRoleBasedAccessTests.java @@ -11,11 +11,27 @@ package org.opensearch.security.dlic.rest.api.legacy; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.security.dlic.rest.api.RoleBasedAccessTest; import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX; public class LegacyRoleBasedAccessTests extends RoleBasedAccessTest { + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public LegacyRoleBasedAccessTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + super(useOldPrivilegeEvaluationImplementation); + } + @Override protected String getEndpointPrefix() { return LEGACY_OPENDISTRO_PREFIX; 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 03f11d74c0..057c49b99e 100644 --- a/src/test/java/org/opensearch/security/protected_indices/ProtectedIndicesTests.java +++ b/src/test/java/org/opensearch/security/protected_indices/ProtectedIndicesTests.java @@ -28,8 +28,11 @@ package org.opensearch.security.protected_indices; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.Header; import org.apache.http.HttpStatus; import org.junit.Test; @@ -49,6 +52,7 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; @@ -60,6 +64,17 @@ public class ProtectedIndicesTests extends SingleClusterTest { + private boolean useOldPrivilegeEvaluationImplementation; + + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public ProtectedIndicesTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } + private static final List listOfIndexesToTest = Arrays.asList("logs1", "logs2", "logs3", "no_match"); private static final List listOfIndexPatternsToTest = Arrays.asList("*logs*", "logs*", "*lo*"); private static final List protectedIndexRoles = Arrays.asList("protected_index_role1", "protected_index_role2"); @@ -88,6 +103,7 @@ public void setupSettingsEnabled() throws Exception { .put(ConfigConstants.SECURITY_PROTECTED_INDICES_ENABLED_KEY, true) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_KEY, listOfIndexesToTest) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_ROLES_KEY, protectedIndexRoles) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( Settings.EMPTY, @@ -106,6 +122,7 @@ public void setupSettingsIndexPatterns() throws Exception { .put(ConfigConstants.SECURITY_PROTECTED_INDICES_ENABLED_KEY, true) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_KEY, listOfIndexPatternsToTest) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_ROLES_KEY, protectedIndexRoles) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( Settings.EMPTY, @@ -131,6 +148,7 @@ public void setupSettingsDisabled() throws Exception { .put(ConfigConstants.SECURITY_PROTECTED_INDICES_ENABLED_KEY, false) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_KEY, listOfIndexesToTest) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_ROLES_KEY, protectedIndexRoles) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( Settings.EMPTY, @@ -149,6 +167,7 @@ public void setupSettingsEnabledSnapshot() throws Exception { .put(ConfigConstants.SECURITY_PROTECTED_INDICES_ENABLED_KEY, true) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_KEY, listOfIndexesToTest) .putList(ConfigConstants.SECURITY_PROTECTED_INDICES_ROLES_KEY, protectedIndexRoles) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( diff --git a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java index 2e154060e9..ed250db6c8 100644 --- a/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java +++ b/src/test/java/org/opensearch/security/system_indices/AbstractSystemIndicesTests.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.List; +import com.carrotsearch.randomizedtesting.annotations.Name; import org.apache.http.Header; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -31,6 +32,7 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.test.DynamicSecurityConfig; import org.opensearch.security.test.SingleClusterTest; @@ -80,6 +82,12 @@ public abstract class AbstractSystemIndicesTests extends SingleClusterTest { static final String updateIndexSettings = "{\n" + " \"index\" : {\n" + " \"refresh_interval\" : null\n" + " }\n" + "}"; static final String newMappings = "{\"properties\": {" + "\"user_name\": {" + "\"type\": \"text\"" + "}}}"; + protected final boolean useOldPrivilegeEvaluationImplementation; + + public AbstractSystemIndicesTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + this.useOldPrivilegeEvaluationImplementation = useOldPrivilegeEvaluationImplementation; + } + void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionEnabled) throws Exception { Settings systemIndexSettings = Settings.builder() @@ -90,6 +98,7 @@ void setupWithSsl(boolean isSystemIndexEnabled, boolean isSystemIndexPermissionE .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) .put("path.repo", repositoryPath.getRoot().getAbsolutePath()) + .put(PrivilegesEvaluator.USE_LEGACY_PRIVILEGE_EVALUATOR.getKey(), useOldPrivilegeEvaluationImplementation) .build(); setup( Settings.EMPTY, diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java index eb56cc6fb6..7c0a09c715 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexDisabledTests.java @@ -12,7 +12,11 @@ package org.opensearch.security.system_indices; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.Header; import org.apache.http.HttpStatus; import org.junit.Before; @@ -33,6 +37,15 @@ */ public class SystemIndexDisabledTests extends AbstractSystemIndicesTests { + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public SystemIndexDisabledTests(@Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation) { + super(useOldPrivilegeEvaluationImplementation); + } + @Before public void setup() throws Exception { setupWithSsl(false, false); diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java index da868aa5f8..a2daa9fae0 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionDisabledTests.java @@ -12,7 +12,11 @@ package org.opensearch.security.system_indices; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.Header; import org.apache.http.HttpStatus; import org.junit.Before; @@ -31,6 +35,16 @@ * Adds test for scenario when system index feature is enabled, but system index permission feature is disabled */ public class SystemIndexPermissionDisabledTests extends AbstractSystemIndicesTests { + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public SystemIndexPermissionDisabledTests( + @Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation + ) { + super(useOldPrivilegeEvaluationImplementation); + } @Before public void setup() throws Exception { diff --git a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java index 4c22b2ed50..8a694d2410 100644 --- a/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java +++ b/src/test/java/org/opensearch/security/system_indices/SystemIndexPermissionEnabledTests.java @@ -11,6 +11,11 @@ package org.opensearch.security.system_indices; +import java.util.Arrays; +import java.util.Collection; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.http.Header; import org.apache.http.HttpStatus; import org.junit.Before; @@ -26,6 +31,16 @@ import static org.junit.Assert.assertFalse; public class SystemIndexPermissionEnabledTests extends AbstractSystemIndicesTests { + @ParametersFactory() + public static Collection params() { + return Arrays.asList(new Object[] { false }, new Object[] { true }); + } + + public SystemIndexPermissionEnabledTests( + @Name("useOldPrivilegeEvaluationImplementation") boolean useOldPrivilegeEvaluationImplementation + ) { + super(useOldPrivilegeEvaluationImplementation); + } @Before public void setup() throws Exception {