From 5116d9902dde90ce4c16e88ca7bfb51183eca834 Mon Sep 17 00:00:00 2001 From: kt-eliatra <103500997+kt-eliatra@users.noreply.github.com> Date: Thu, 15 Dec 2022 16:41:23 +0100 Subject: [PATCH] Fls dls field masking tests (#2258) Adds integration tests for FLS DLS field masking Signed-off-by: Kacper Trochimiak --- .../security/CrossClusterSearchTests.java | 12 +- .../security/DoNotFailOnForbiddenTests.java | 8 +- .../security/FlsDlsAndFieldMaskingTest.java | 975 ++++++++++++++++++ .../security/SearchOperationTest.java | 113 +- .../java/org/opensearch/security/Song.java | 35 +- .../http/ExtendedProxyAuthenticationTest.java | 4 +- .../security/http/JwtAuthenticationTests.java | 2 +- .../http/LdapTlsAuthenticationTest.java | 6 +- .../cluster/SearchRequestFactory.java | 9 + .../framework/cluster/TestRestClient.java | 12 + .../ContainsExactlyIndicesMatcher.java | 46 + .../matcher/ContainsFieldWithTypeMatcher.java | 51 + .../FieldCapabilitiesResponseMatchers.java | 32 + ...esponseContainsDocumentWithIdMatcher.java} | 8 +- ...ContainsExactlyFieldsWithNamesMatcher.java | 49 + ...nseDocumentDoesNotContainFieldMatcher.java | 47 + .../matcher/GetResponseMatchers.java | 10 +- .../matcher/MultiGetResponseMatchers.java | 29 + .../matcher/MultiSearchResponseMatchers.java | 29 + .../NumberOfFieldsIsEqualToMatcher.java | 39 + ...berOfGetItemResponsesIsEqualToMatcher.java | 40 + ...OfSearchItemResponsesIsEqualToMatcher.java | 41 + .../SuccessfulMultiGetResponseMatcher.java | 37 + .../SuccessfulMultiSearchResponseMatcher.java | 35 + 24 files changed, 1584 insertions(+), 85 deletions(-) create mode 100644 src/integrationTest/java/org/opensearch/security/FlsDlsAndFieldMaskingTest.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsExactlyIndicesMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsFieldWithTypeMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/FieldCapabilitiesResponseMatchers.java rename src/integrationTest/java/org/opensearch/test/framework/matcher/{GetResponseDocumentIdMatcher.java => GetResponseContainsDocumentWithIdMatcher.java} (83%) create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentContainsExactlyFieldsWithNamesMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentDoesNotContainFieldMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/MultiGetResponseMatchers.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/MultiSearchResponseMatchers.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfFieldsIsEqualToMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfGetItemResponsesIsEqualToMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfSearchItemResponsesIsEqualToMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiGetResponseMatcher.java create mode 100644 src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiSearchResponseMatcher.java diff --git a/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java b/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java index 5f812b921b..6762274133 100644 --- a/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java +++ b/src/integrationTest/java/org/opensearch/security/CrossClusterSearchTests.java @@ -155,14 +155,14 @@ public CrossClusterSearchTests(Boolean ccsMinimizeRoundtrips) { @BeforeClass public static void createTestData() { try(Client client = remoteCluster.getInternalNodeClient()){ - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0]).get(); - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_6R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[5]).get(); - client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_3R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1]).get(); - client.prepareIndex(LIMITED_USER_INDEX_NAME).setId(SONG_ID_5R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[4]).get(); + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_6R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[5].asMap()).get(); + client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_3R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); + client.prepareIndex(LIMITED_USER_INDEX_NAME).setId(SONG_ID_5R).setRefreshPolicy(IMMEDIATE).setSource(SONGS[4].asMap()).get(); } try(Client client = cluster.getInternalNodeClient()){ - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_2L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2]).get(); - client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_4L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[3]).get(); + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_2L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2].asMap()).get(); + client.prepareIndex(PROHIBITED_SONG_INDEX_NAME).setId(SONG_ID_4L).setRefreshPolicy(IMMEDIATE).setSource(SONGS[3].asMap()).get(); } try(TestRestClient client = cluster.getRestClient(ADMIN_USER)) { client.assignRoleToUser(LIMITED_USER.getName(), LIMITED_ROLE.getName()).assertStatusCode(200); diff --git a/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java b/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java index b4e582507e..67d61b7d6f 100644 --- a/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java +++ b/src/integrationTest/java/org/opensearch/security/DoNotFailOnForbiddenTests.java @@ -115,11 +115,11 @@ public class DoNotFailOnForbiddenTests { @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])).actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_2).source(SONGS[1])).actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(MARVELOUS_SONGS).id(ID_3).source(SONGS[2])).actionGet(); + 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])).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(); diff --git a/src/integrationTest/java/org/opensearch/security/FlsDlsAndFieldMaskingTest.java b/src/integrationTest/java/org/opensearch/security/FlsDlsAndFieldMaskingTest.java new file mode 100644 index 0000000000..4bef7ef4f6 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/FlsDlsAndFieldMaskingTest.java @@ -0,0 +1,975 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.security; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +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.GetRequest; +import org.opensearch.action.get.GetResponse; +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.index.IndexResponse; +import org.opensearch.action.search.MultiSearchRequest; +import org.opensearch.action.search.MultiSearchResponse; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchScrollRequest; +import org.opensearch.client.Client; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.search.aggregations.Aggregation; +import org.opensearch.search.aggregations.metrics.ParsedAvg; +import org.opensearch.search.sort.SortOrder; +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 static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.everyItem; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +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.security.Song.ARTIST_FIRST; +import static org.opensearch.security.Song.ARTIST_NO; +import static org.opensearch.security.Song.ARTIST_STRING; +import static org.opensearch.security.Song.ARTIST_TWINS; +import static org.opensearch.security.Song.FIELD_ARTIST; +import static org.opensearch.security.Song.FIELD_LYRICS; +import static org.opensearch.security.Song.FIELD_STARS; +import static org.opensearch.security.Song.FIELD_TITLE; +import static org.opensearch.security.Song.QUERY_TITLE_NEXT_SONG; +import static org.opensearch.security.Song.SONGS; +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.queryByIdsRequest; +import static org.opensearch.test.framework.cluster.SearchRequestFactory.searchRequestWithScroll; +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.GetResponseMatchers.documentDoesNotContainField; +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.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.numberOfTotalHitsIsEqualTo; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitContainsFieldWithValue; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitDoesNotContainField; +import static org.opensearch.test.framework.matcher.SearchResponseMatchers.searchHitsContainDocumentWithId; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class FlsDlsAndFieldMaskingTest { + + static final String FIRST_INDEX_ID_SONG_1 = "INDEX_1_S1"; + static final String FIRST_INDEX_ID_SONG_2 = "INDEX_1_S2"; + static final String FIRST_INDEX_ID_SONG_3 = "INDEX_1_S3"; + static final String FIRST_INDEX_ID_SONG_4 = "INDEX_1_S4"; + static final String SECOND_INDEX_ID_SONG_1 = "INDEX_2_S1"; + static final String SECOND_INDEX_ID_SONG_2 = "INDEX_2_S2"; + static final String SECOND_INDEX_ID_SONG_3 = "INDEX_2_S3"; + static final String SECOND_INDEX_ID_SONG_4 = "INDEX_2_S4"; + + static final String INDEX_NAME_SUFFIX = "-test-index"; + static final String FIRST_INDEX_NAME = "first".concat(INDEX_NAME_SUFFIX); + static final String SECOND_INDEX_NAME = "second".concat(INDEX_NAME_SUFFIX); + static final String FIRST_INDEX_ALIAS = FIRST_INDEX_NAME.concat("-alias"); + static final String SECOND_INDEX_ALIAS = SECOND_INDEX_NAME.concat("-alias"); + static final String FIRST_INDEX_ALIAS_FILTERED_BY_NEXT_SONG_TITLE = FIRST_INDEX_NAME.concat("-filtered-by-next-song-title"); + static final String FIRST_INDEX_ALIAS_FILTERED_BY_TWINS_ARTIST = FIRST_INDEX_NAME.concat("-filtered-by-twins-artist"); + static final String FIRST_INDEX_ALIAS_FILTERED_BY_FIRST_ARTIST = FIRST_INDEX_NAME.concat("-filtered-by-first-artist"); + static final String ALL_INDICES_ALIAS = "_all"; + + static final String MASK_VALUE = "*"; + + static final TestSecurityConfig.User ADMIN_USER = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); + + /** + * User who is allowed to see all fields on all indices. Values of the title and artist fields should be masked. + */ + static final TestSecurityConfig.User ALL_INDICES_MASKED_TITLE_ARTIST_READER = new TestSecurityConfig.User("masked_artist_title_reader") + .roles( + new TestSecurityConfig.Role("masked_artist_title_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .maskedFields( + FIELD_TITLE.concat("::/(?<=.{1})./::").concat(MASK_VALUE), + FIELD_ARTIST.concat("::/(?<=.{1})./::").concat(MASK_VALUE) + ) + .on("*") + ); + + /** + * User who is allowed to see all fields on indices {@link #FIRST_INDEX_NAME} and {@link #SECOND_INDEX_NAME}. + * + */ + static final TestSecurityConfig.User MASKED_ARTIST_LYRICS_READER = new TestSecurityConfig.User("masked_title_artist_lyrics_reader") + .roles( + new TestSecurityConfig.Role("masked_title_artist_lyrics_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .maskedFields( + FIELD_ARTIST.concat("::/(?<=.{1})./::").concat(MASK_VALUE), + FIELD_LYRICS.concat("::/(?<=.{1})./::").concat(MASK_VALUE) + ) + .on(FIRST_INDEX_NAME), + new TestSecurityConfig.Role("masked_lyrics_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .maskedFields(FIELD_LYRICS.concat("::/(?<=.{1})./::").concat(MASK_VALUE)) + .on(SECOND_INDEX_NAME) + ); + + /** + * Function that converts field value to value masked with {@link #MASK_VALUE} + */ + static final Function VALUE_TO_MASKED_VALUE = value -> value.substring(0, 1) + .concat(MASK_VALUE.repeat(value.length() - 1)); + + /** + * User who is allowed to see documents on all indices where value of the {@link Song#FIELD_ARTIST} field matches {@link Song#ARTIST_STRING}. + */ + static final TestSecurityConfig.User ALL_INDICES_STRING_ARTIST_READER = new TestSecurityConfig.User("string_artist_reader") + .roles( + new TestSecurityConfig.Role("string_artist_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_ARTIST, ARTIST_STRING)) + .on("*") + ); + + /** + * User who is allowed to see documents on index: + * + */ + static final TestSecurityConfig.User TWINS_FIRST_ARTIST_READER = new TestSecurityConfig.User("twins_first_artist_reader") + .roles( + new TestSecurityConfig.Role("twins_artist_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_ARTIST, ARTIST_TWINS)) + .on(FIRST_INDEX_NAME), + new TestSecurityConfig.Role("first_artist_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .dls(String.format("{\"match\":{\"%s\":\"%s\"}}", FIELD_ARTIST, ARTIST_FIRST)) + .on(SECOND_INDEX_NAME) + ); + + /** + * User who is allowed to see documents on all indices where value of the {@link Song#FIELD_STARS} is less than zero. + */ + static final TestSecurityConfig.User ALL_INDICES_STARS_LESS_THAN_ZERO_READER = new TestSecurityConfig.User("stars_less_than_zero_reader") + .roles( + new TestSecurityConfig.Role("stars_less_than_zero_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .dls(String.format("{\"range\":{\"%s\":{\"lt\":%d}}}", FIELD_STARS, 0)) + .on("*") + ); + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder() + .clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS).anonymousAuth(false) + .nodeSettings(Map.of("plugins.security.restapi.roles_enabled", List.of("user_" + ADMIN_USER.getName() +"__" + ALL_ACCESS.getName()))) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users( + ADMIN_USER, + ALL_INDICES_MASKED_TITLE_ARTIST_READER, MASKED_ARTIST_LYRICS_READER, + ALL_INDICES_STRING_ARTIST_READER, ALL_INDICES_STARS_LESS_THAN_ZERO_READER, TWINS_FIRST_ARTIST_READER + ) + .build(); + + /** + * Function that returns id assigned to song with title equal to given title or throws {@link RuntimeException} + * when no song matches. + */ + static final BiFunction, String, String> FIND_ID_OF_SONG_WITH_TITLE = (map, title) -> map.entrySet() + .stream().filter(entry -> title.equals(entry.getValue().getTitle())) + .findAny() + .map(Map.Entry::getKey) + .orElseThrow(() -> new RuntimeException("Cannot find id of song with title: " + title)); + + /** + * Function that returns id assigned to song with artist equal to given artist or throws {@link RuntimeException} + * when no song matches. + */ + static final BiFunction, String, String> FIND_ID_OF_SONG_WITH_ARTIST = (map, artist) -> map.entrySet() + .stream().filter(entry -> artist.equals(entry.getValue().getArtist())) + .findAny() + .map(Map.Entry::getKey) + .orElseThrow(() -> new RuntimeException("Cannot find id of song with artist: " + artist)); + + static final TreeMap FIRST_INDEX_SONGS_BY_ID = new TreeMap<>() {{ + put(FIRST_INDEX_ID_SONG_1, SONGS[0]); + put(FIRST_INDEX_ID_SONG_2, SONGS[1]); + put(FIRST_INDEX_ID_SONG_3, SONGS[2]); + put(FIRST_INDEX_ID_SONG_4, SONGS[3]); + }}; + + static final TreeMap SECOND_INDEX_SONGS_BY_ID = new TreeMap<>() {{ + put(SECOND_INDEX_ID_SONG_1, SONGS[3]); + put(SECOND_INDEX_ID_SONG_2, SONGS[2]); + put(SECOND_INDEX_ID_SONG_3, SONGS[1]); + put(SECOND_INDEX_ID_SONG_4, SONGS[0]); + }}; + + @BeforeClass + public static void createTestData() { + try(Client client = cluster.getInternalNodeClient()){ + FIRST_INDEX_SONGS_BY_ID.forEach((id, song) -> { + client.prepareIndex(FIRST_INDEX_NAME).setId(id).setRefreshPolicy(IMMEDIATE).setSource(song.asMap()).get(); + }); + + client.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(new IndicesAliasesRequest.AliasActions(ADD) + .indices(FIRST_INDEX_NAME) + .alias(FIRST_INDEX_ALIAS) + )).actionGet(); + client.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(new IndicesAliasesRequest.AliasActions(ADD) + .index(FIRST_INDEX_NAME) + .alias(FIRST_INDEX_ALIAS_FILTERED_BY_NEXT_SONG_TITLE) + .filter(QueryBuilders.queryStringQuery(QUERY_TITLE_NEXT_SONG)) + )).actionGet(); + client.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(new IndicesAliasesRequest.AliasActions(ADD) + .index(FIRST_INDEX_NAME) + .alias(FIRST_INDEX_ALIAS_FILTERED_BY_TWINS_ARTIST) + .filter(QueryBuilders.queryStringQuery(String.format("%s:%s", FIELD_ARTIST, ARTIST_TWINS))) + )).actionGet(); + client.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(new IndicesAliasesRequest.AliasActions(ADD) + .index(FIRST_INDEX_NAME) + .alias(FIRST_INDEX_ALIAS_FILTERED_BY_FIRST_ARTIST) + .filter(QueryBuilders.queryStringQuery(String.format("%s:%s", FIELD_ARTIST, ARTIST_FIRST))) + )).actionGet(); + + SECOND_INDEX_SONGS_BY_ID.forEach((id, song) -> { + client.prepareIndex(SECOND_INDEX_NAME).setId(id).setRefreshPolicy(IMMEDIATE).setSource(song.asMap()).get(); + }); + client.admin().indices().aliases(new IndicesAliasesRequest().addAliasAction(new IndicesAliasesRequest.AliasActions(ADD) + .indices(SECOND_INDEX_NAME) + .alias(SECOND_INDEX_ALIAS) + )).actionGet(); + } + } + + @Test + public void flsEnabledFieldsAreHiddenForNormalUsers() throws IOException { + String indexName = "fls_index"; + String indexAlias = "fls_index_alias"; + String indexFilteredAlias = "fls_index_filtered_alias"; + TestSecurityConfig.Role userRole = new TestSecurityConfig.Role("fls_exclude_stars_reader") + .clusterPermissions("cluster_composite_ops_ro") + .indexPermissions("read") + .fls("~".concat(FIELD_STARS)) + .on("*"); + TestSecurityConfig.User user = createUserWithRole("fls_user", userRole); + List docIds = createIndexWithDocs(indexName, SONGS[0], SONGS[1]); + addAliasToIndex(indexName, indexAlias); + addAliasToIndex(indexName, indexFilteredAlias, QueryBuilders.queryStringQuery(String.format("%s:%s", FIELD_ARTIST, SONGS[0].getArtist()))); + + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(user)) { + //search + SearchResponse searchResponse = restHighLevelClient.search(new SearchRequest(indexName), DEFAULT); + + assertSearchHitsDoNotContainField(searchResponse, FIELD_STARS); + + //search with index pattern + searchResponse = restHighLevelClient.search(new SearchRequest("*".concat(indexName)), DEFAULT); + + assertSearchHitsDoNotContainField(searchResponse, FIELD_STARS); + + //search via alias + searchResponse = restHighLevelClient.search(new SearchRequest(indexAlias), DEFAULT); + + assertSearchHitsDoNotContainField(searchResponse, FIELD_STARS); + + //search via filtered alias + searchResponse = restHighLevelClient.search(new SearchRequest(indexFilteredAlias), DEFAULT); + + assertSearchHitsDoNotContainField(searchResponse, FIELD_STARS); + + //search via all indices alias + searchResponse = restHighLevelClient.search(new SearchRequest(ALL_INDICES_ALIAS), DEFAULT); + + assertSearchHitsDoNotContainField(searchResponse, FIELD_STARS); + + //scroll + searchResponse = restHighLevelClient.search(searchRequestWithScroll(indexName, 1), DEFAULT); + + assertSearchHitsDoNotContainField(searchResponse, FIELD_STARS); + + SearchScrollRequest scrollRequest = getSearchScrollRequest(searchResponse); + SearchResponse scrollResponse = restHighLevelClient.scroll(scrollRequest, DEFAULT); + + assertSearchHitsDoNotContainField(scrollResponse, FIELD_STARS); + + //aggregate data and compute avg + String aggregationName = "averageStars"; + searchResponse = restHighLevelClient.search(averageAggregationRequest(indexName, aggregationName, FIELD_STARS), DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); + Aggregation actualAggregation = searchResponse.getAggregations().get(aggregationName); + assertThat(actualAggregation, instanceOf(ParsedAvg.class)); + assertThat(((ParsedAvg) actualAggregation).getValue(), is(Double.POSITIVE_INFINITY)); //user cannot see the STARS field + + //get document + GetResponse getResponse = restHighLevelClient.get(new GetRequest(indexName, docIds.get(0)), DEFAULT); + + assertThat(getResponse, documentDoesNotContainField(FIELD_STARS)); + + //multi get + for (String index: List.of(indexName, indexAlias)) { + MultiGetRequest multiGetRequest = new MultiGetRequest(); + docIds.forEach(id -> multiGetRequest.add(new MultiGetRequest.Item(index, id))); + + MultiGetResponse multiGetResponse = restHighLevelClient.mget(multiGetRequest, DEFAULT); + + List getResponses = Arrays.stream(multiGetResponse.getResponses()) + .map(MultiGetItemResponse::getResponse) + .collect(Collectors.toList()); + assertThat(getResponses, everyItem(documentDoesNotContainField(FIELD_STARS))); + } + + //multi search + for (String index: List.of(indexName, indexAlias)) { + MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); + docIds.forEach(id -> multiSearchRequest.add(queryByIdsRequest(index, id))); + MultiSearchResponse multiSearchResponse = restHighLevelClient.msearch(multiSearchRequest, DEFAULT); + + assertThat(multiSearchResponse, isSuccessfulMultiSearchResponse()); + List itemResponses = List.of(multiSearchResponse.getResponses()); + itemResponses.forEach(item -> assertSearchHitsDoNotContainField(item.getResponse(), FIELD_STARS)); + } + + //field capabilities + FieldCapabilitiesResponse fieldCapsResponse = restHighLevelClient.fieldCaps( + new FieldCapabilitiesRequest().indices(indexName).fields(FIELD_TITLE, FIELD_STARS), DEFAULT + ); + assertThat(fieldCapsResponse.getField(FIELD_STARS), nullValue()); + } + } + + private static List createIndexWithDocs(String indexName, Song... songs) { + try(Client client = cluster.getInternalNodeClient()){ + return Stream.of(songs).map(song -> { + IndexResponse response = client.index( + new IndexRequest(indexName).setRefreshPolicy(IMMEDIATE).source(song.asMap()) + ).actionGet(); + return response.getId(); + }).collect(Collectors.toList()); + } + } + + private static void addAliasToIndex(String indexName, String alias) { + addAliasToIndex(indexName, alias, QueryBuilders.matchAllQuery()); + } + + private static void addAliasToIndex(String indexName, String alias, QueryBuilder filterQuery) { + try(Client client = cluster.getInternalNodeClient()){ + client.admin().indices() + .aliases(new IndicesAliasesRequest().addAliasAction(new IndicesAliasesRequest.AliasActions(ADD) + .indices(indexName) + .alias(alias) + .filter(filterQuery) + )).actionGet(); + } + } + + private static TestSecurityConfig.User createUserWithRole(String userName, TestSecurityConfig.Role role) { + TestSecurityConfig.User user = new TestSecurityConfig.User(userName); + try(TestRestClient client = cluster.getRestClient(ADMIN_USER)) { + client.createRole(role.getName(), role).assertStatusCode(201); + client.createUser(user.getName(), user).assertStatusCode(201); + client.assignRoleToUser(user.getName(), role.getName()).assertStatusCode(200); + } + return user; + } + + private static void assertSearchHitsDoNotContainField(SearchResponse response, String excludedField) { + assertThat(response, isSuccessfulSearchResponse()); + assertThat(response.getHits().getHits().length, greaterThan(0)); + IntStream.range(0, response.getHits().getHits().length).boxed() + .forEach(index -> assertThat(response, searchHitDoesNotContainField(index, excludedField))); + } + + @Test + public void searchForDocuments() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String songId = FIRST_INDEX_ID_SONG_1; + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + + SearchRequest searchRequest = queryByIdsRequest(FIRST_INDEX_NAME, songId); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + + songId = SECOND_INDEX_ID_SONG_2; + song = SECOND_INDEX_SONGS_BY_ID.get(songId); + + searchRequest = queryByIdsRequest(SECOND_INDEX_NAME, songId); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SECOND_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, song.getArtist())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + SearchRequest searchRequest = new SearchRequest(FIRST_INDEX_NAME); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_TWINS)); + + searchRequest = new SearchRequest(SECOND_INDEX_NAME); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_FIRST)); + } + } + + @Test + public void searchForDocumentsWithIndexPattern() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String songId = FIRST_INDEX_ID_SONG_2; + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + + SearchRequest searchRequest = queryByIdsRequest("*".concat(FIRST_INDEX_NAME), songId); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + + + songId = SECOND_INDEX_ID_SONG_3; + song = SECOND_INDEX_SONGS_BY_ID.get(songId); + + searchRequest = queryByIdsRequest("*".concat(SECOND_INDEX_NAME), songId); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SECOND_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, song.getArtist())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + SearchRequest searchRequest = new SearchRequest("*".concat(FIRST_INDEX_NAME)); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_TWINS)); + + searchRequest = new SearchRequest("*".concat(SECOND_INDEX_NAME)); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_FIRST)); + } + } + + @Test + public void searchForDocumentsViaAlias() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String songId = FIRST_INDEX_ID_SONG_3; + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + + SearchRequest searchRequest = queryByIdsRequest(FIRST_INDEX_ALIAS, songId); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + + songId = SECOND_INDEX_ID_SONG_4; + song = SECOND_INDEX_SONGS_BY_ID.get(songId); + + searchRequest = queryByIdsRequest("*".concat(SECOND_INDEX_ALIAS), songId); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SECOND_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, song.getArtist())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + SearchRequest searchRequest = new SearchRequest(FIRST_INDEX_ALIAS); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_TWINS)); + + searchRequest = new SearchRequest(SECOND_INDEX_ALIAS); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_FIRST)); + } + } + + @Test + public void searchForDocumentsViaFilteredAlias() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String songId = FIND_ID_OF_SONG_WITH_TITLE.apply(FIRST_INDEX_SONGS_BY_ID, TITLE_NEXT_SONG); + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + + SearchRequest searchRequest = new SearchRequest(FIRST_INDEX_ALIAS_FILTERED_BY_NEXT_SONG_TITLE); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + SearchRequest searchRequest = new SearchRequest(FIRST_INDEX_ALIAS_FILTERED_BY_TWINS_ARTIST); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_TWINS)); + + searchRequest = new SearchRequest(FIRST_INDEX_ALIAS_FILTERED_BY_FIRST_ARTIST); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(0)); + } + } + + @Test + public void searchForDocumentsViaAllIndicesAlias() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ALL_INDICES_MASKED_TITLE_ARTIST_READER)) { + String songId = FIRST_INDEX_ID_SONG_4; + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + + SearchRequest searchRequest = queryByIdsRequest(ALL_INDICES_ALIAS, songId); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, VALUE_TO_MASKED_VALUE.apply(song.getTitle()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, song.getLyrics())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + + + songId = SECOND_INDEX_ID_SONG_1; + song = SECOND_INDEX_SONGS_BY_ID.get(songId); + + searchRequest = queryByIdsRequest(ALL_INDICES_ALIAS, songId); + searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitsContainDocumentWithId(0, SECOND_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, VALUE_TO_MASKED_VALUE.apply(song.getTitle()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, song.getLyrics())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ALL_INDICES_STRING_ARTIST_READER)) { + SearchRequest searchRequest = new SearchRequest(ALL_INDICES_ALIAS); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, numberOfTotalHitsIsEqualTo(2)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_STRING)); + assertThat(searchResponse, searchHitContainsFieldWithValue(1, FIELD_ARTIST, ARTIST_STRING)); + } + } + + @Test + public void scrollOverSearchResults() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String songId = FIRST_INDEX_SONGS_BY_ID.firstKey(); + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + + SearchRequest searchRequest = searchRequestWithScroll(FIRST_INDEX_NAME, 1); + searchRequest.source().sort("_id", SortOrder.ASC); + 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(searchResponse, searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, songId)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_TITLE, song.getTitle())); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + SearchRequest searchRequest = searchRequestWithScroll(FIRST_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(searchResponse, numberOfTotalHitsIsEqualTo(1)); + assertThat(searchResponse, searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_TWINS)); + } + } + + @Test + public void aggregateDataAndComputeAverage() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String aggregationName = "averageStars"; + Double expectedValue = FIRST_INDEX_SONGS_BY_ID.values() + .stream() + .mapToDouble(Song::getStars) + .average().orElseThrow(() -> new RuntimeException("Cannot compute average stars - list of docs is empty")); + SearchRequest searchRequest = averageAggregationRequest(FIRST_INDEX_NAME, aggregationName, FIELD_STARS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); + Aggregation actualAggregation = searchResponse.getAggregations().get(aggregationName); + assertThat(actualAggregation, instanceOf(ParsedAvg.class)); + assertThat(((ParsedAvg) actualAggregation).getValue(), is(expectedValue)); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + String aggregationName = "averageStars"; + Song song = FIRST_INDEX_SONGS_BY_ID.get(FIND_ID_OF_SONG_WITH_ARTIST.apply(FIRST_INDEX_SONGS_BY_ID, ARTIST_TWINS)); + + SearchRequest searchRequest = averageAggregationRequest(FIRST_INDEX_NAME, aggregationName, FIELD_STARS); + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); + Aggregation actualAggregation = searchResponse.getAggregations().get(aggregationName); + assertThat(actualAggregation, instanceOf(ParsedAvg.class)); + assertThat(((ParsedAvg) actualAggregation).getValue(), is(song.getStars()*1.0)); + } + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(ALL_INDICES_STARS_LESS_THAN_ZERO_READER)) { + String aggregationName = "averageStars"; + SearchRequest searchRequest = averageAggregationRequest(FIRST_INDEX_NAME, aggregationName, FIELD_STARS); + + SearchResponse searchResponse = restHighLevelClient.search(searchRequest, DEFAULT); + + assertThat(searchResponse, isSuccessfulSearchResponse()); + assertThat(searchResponse, containAggregationWithNameAndType(aggregationName, "avg")); + Aggregation actualAggregation = searchResponse.getAggregations().get(aggregationName); + assertThat(actualAggregation, instanceOf(ParsedAvg.class)); + assertThat(((ParsedAvg) actualAggregation).getValue(), is(Double.POSITIVE_INFINITY)); + } + } + + @Test + public void getDocument() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + String songId = FIRST_INDEX_ID_SONG_4; + Song song = FIRST_INDEX_SONGS_BY_ID.get(songId); + GetResponse response = restHighLevelClient.get(new GetRequest(FIRST_INDEX_NAME, songId), DEFAULT); + + assertThat(response, containDocument(FIRST_INDEX_NAME, songId)); + assertThat(response, documentContainField(FIELD_TITLE, song.getTitle())); + assertThat(response, documentContainField(FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(response, documentContainField(FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(song.getArtist()))); + assertThat(response, documentContainField(FIELD_STARS, song.getStars())); + + songId = SECOND_INDEX_ID_SONG_1; + song = SECOND_INDEX_SONGS_BY_ID.get(songId); + response = restHighLevelClient.get(new GetRequest(SECOND_INDEX_NAME, songId), DEFAULT); + + assertThat(response, containDocument(SECOND_INDEX_NAME, songId)); + assertThat(response, documentContainField(FIELD_TITLE, song.getTitle())); + assertThat(response, documentContainField(FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(song.getLyrics()))); + assertThat(response, documentContainField(FIELD_ARTIST, song.getArtist())); + assertThat(response, documentContainField(FIELD_STARS, song.getStars())); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + String songId = FIND_ID_OF_SONG_WITH_ARTIST.apply(FIRST_INDEX_SONGS_BY_ID, ARTIST_TWINS); + + GetResponse response = restHighLevelClient.get(new GetRequest(FIRST_INDEX_NAME, songId), DEFAULT); + + assertThat(response, containDocument(FIRST_INDEX_NAME, songId)); + + songId = FIND_ID_OF_SONG_WITH_ARTIST.apply(FIRST_INDEX_SONGS_BY_ID, ARTIST_STRING); + + response = restHighLevelClient.get(new GetRequest(FIRST_INDEX_NAME, songId), DEFAULT); + + assertThat(response, not(containDocument(FIRST_INDEX_NAME, songId))); + } + } + + @Test + public void multiGetDocuments() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + List> indicesToCheck = List.of( + List.of(FIRST_INDEX_NAME, SECOND_INDEX_NAME), + List.of(FIRST_INDEX_ALIAS, SECOND_INDEX_ALIAS) + ); + String firstSongId = FIRST_INDEX_ID_SONG_1; + Song firstSong = FIRST_INDEX_SONGS_BY_ID.get(firstSongId); + String secondSongId = SECOND_INDEX_ID_SONG_2; + Song secondSong = SECOND_INDEX_SONGS_BY_ID.get(secondSongId); + + for (List indices : indicesToCheck) { + MultiGetRequest request = new MultiGetRequest(); + request.add(new MultiGetRequest.Item(indices.get(0), firstSongId)); + request.add(new MultiGetRequest.Item(indices.get(1), secondSongId)); + MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); + + assertThat(response, isSuccessfulMultiGetResponse()); + assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); + + MultiGetItemResponse[] responses = response.getResponses(); + assertThat(responses[0].getResponse(), allOf( + containDocument(FIRST_INDEX_NAME, FIRST_INDEX_ID_SONG_1), + documentContainField(FIELD_TITLE, firstSong.getTitle()), + documentContainField(FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(firstSong.getLyrics())), + documentContainField(FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(firstSong.getArtist())), + documentContainField(FIELD_STARS, firstSong.getStars()) + )); + assertThat(responses[1].getResponse(), allOf( + containDocument(SECOND_INDEX_NAME, secondSongId), + documentContainField(FIELD_TITLE, secondSong.getTitle()), + documentContainField(FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(secondSong.getLyrics())), + documentContainField(FIELD_ARTIST, secondSong.getArtist()), + documentContainField(FIELD_STARS, secondSong.getStars()) + )); + } + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + List indicesToCheck = List.of(FIRST_INDEX_NAME, FIRST_INDEX_ALIAS); + String firstSongId = FIND_ID_OF_SONG_WITH_ARTIST.apply(FIRST_INDEX_SONGS_BY_ID, ARTIST_NO); + String secondSongId = FIND_ID_OF_SONG_WITH_ARTIST.apply(FIRST_INDEX_SONGS_BY_ID, ARTIST_STRING); + + for(String index : indicesToCheck) { + MultiGetRequest request = new MultiGetRequest(); + request.add(new MultiGetRequest.Item(index, firstSongId)); + request.add(new MultiGetRequest.Item(index, secondSongId)); + + MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); + + assertThat(response, isSuccessfulMultiGetResponse()); + assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); + + MultiGetItemResponse[] responses = response.getResponses(); + assertThat(responses[0].getResponse(), allOf( + not(containDocument(FIRST_INDEX_NAME, firstSongId))) + ); + assertThat(responses[1].getResponse(), allOf( + not(containDocument(FIRST_INDEX_NAME, secondSongId))) + ); + } + } + } + + @Test + public void multiSearchDocuments() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + List> indicesToCheck = List.of( + List.of(FIRST_INDEX_NAME, SECOND_INDEX_NAME), + List.of(FIRST_INDEX_ALIAS, SECOND_INDEX_ALIAS) + ); + String firstSongId = FIRST_INDEX_ID_SONG_3; + Song firstSong = FIRST_INDEX_SONGS_BY_ID.get(firstSongId); + String secondSongId = SECOND_INDEX_ID_SONG_4; + Song secondSong = SECOND_INDEX_SONGS_BY_ID.get(secondSongId); + + for (List indices : indicesToCheck) { + MultiSearchRequest request = new MultiSearchRequest(); + request.add(queryByIdsRequest(indices.get(0), firstSongId)); + request.add(queryByIdsRequest(indices.get(1), secondSongId)); + MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); + + assertThat(response, isSuccessfulMultiSearchResponse()); + assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); + + MultiSearchResponse.Item[] responses = response.getResponses(); + + assertThat(responses[0].getResponse(), allOf( + searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, firstSongId), + searchHitContainsFieldWithValue(0, FIELD_TITLE, firstSong.getTitle()), + searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(firstSong.getLyrics())), + searchHitContainsFieldWithValue(0, FIELD_ARTIST, VALUE_TO_MASKED_VALUE.apply(firstSong.getArtist())), + searchHitContainsFieldWithValue(0, FIELD_STARS, firstSong.getStars()) + )); + assertThat(responses[1].getResponse(), allOf( + searchHitsContainDocumentWithId(0, SECOND_INDEX_NAME, secondSongId), + searchHitContainsFieldWithValue(0, FIELD_TITLE, secondSong.getTitle()), + searchHitContainsFieldWithValue(0, FIELD_LYRICS, VALUE_TO_MASKED_VALUE.apply(secondSong.getLyrics())), + searchHitContainsFieldWithValue(0, FIELD_ARTIST, secondSong.getArtist()), + searchHitContainsFieldWithValue(0, FIELD_STARS, secondSong.getStars()) + )); + } + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + List> indicesToCheck = List.of( + List.of(FIRST_INDEX_NAME, SECOND_INDEX_NAME), + List.of(FIRST_INDEX_ALIAS, SECOND_INDEX_ALIAS) + ); + String firstSongId = FIND_ID_OF_SONG_WITH_ARTIST.apply(FIRST_INDEX_SONGS_BY_ID, ARTIST_TWINS); + String secondSongId = FIND_ID_OF_SONG_WITH_ARTIST.apply(SECOND_INDEX_SONGS_BY_ID, ARTIST_FIRST); + + for (List indices : indicesToCheck) { + MultiSearchRequest request = new MultiSearchRequest(); + indices.forEach(index -> request.add(new SearchRequest(index))); + + MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); + + assertThat(response, isSuccessfulMultiSearchResponse()); + assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); + + MultiSearchResponse.Item[] responses = response.getResponses(); + + assertThat(responses[0].getResponse(), searchHitsContainDocumentWithId(0, FIRST_INDEX_NAME, firstSongId)); + assertThat(responses[0].getResponse(), searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_TWINS)); + assertThat(responses[1].getResponse(), searchHitsContainDocumentWithId(0, SECOND_INDEX_NAME, secondSongId)); + assertThat(responses[1].getResponse(), searchHitContainsFieldWithValue(0, FIELD_ARTIST, ARTIST_FIRST)); + } + } + } + + @Test + public void getFieldCapabilities() throws IOException { + //FIELD MASKING + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(MASKED_ARTIST_LYRICS_READER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(FIRST_INDEX_NAME).fields(FIELD_ARTIST, FIELD_TITLE, FIELD_LYRICS); + FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); + + assertThat(response, containsExactlyIndices(FIRST_INDEX_NAME)); + assertThat(response, numberOfFieldsIsEqualTo(3)); + assertThat(response, containsFieldWithNameAndType(FIELD_ARTIST, "text")); + assertThat(response, containsFieldWithNameAndType(FIELD_TITLE, "text")); + assertThat(response, containsFieldWithNameAndType(FIELD_LYRICS, "text")); + } + + //DLS + try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(TWINS_FIRST_ARTIST_READER)) { + FieldCapabilitiesRequest request = new FieldCapabilitiesRequest().indices(FIRST_INDEX_NAME).fields(FIELD_ARTIST, FIELD_TITLE, FIELD_LYRICS); + FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); + + assertThat(response, containsExactlyIndices(FIRST_INDEX_NAME)); + assertThat(response, numberOfFieldsIsEqualTo(3)); + assertThat(response, containsFieldWithNameAndType(FIELD_ARTIST, "text")); + assertThat(response, containsFieldWithNameAndType(FIELD_TITLE, "text")); + assertThat(response, containsFieldWithNameAndType(FIELD_LYRICS, "text")); + } + } + +} diff --git a/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java b/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java index f664ab47cd..08e8394ec8 100644 --- a/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java +++ b/src/integrationTest/java/org/opensearch/security/SearchOperationTest.java @@ -19,8 +19,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.awaitility.Awaitility; -import org.hamcrest.Matcher; -import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -106,14 +104,10 @@ import org.opensearch.test.framework.cluster.LocalCluster; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; -import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -171,6 +165,9 @@ 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; @@ -181,6 +178,10 @@ 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; @@ -225,7 +226,7 @@ public class SearchOperationTest { 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"; @@ -327,13 +328,13 @@ public class SearchOperationTest { @BeforeClass public static void createTestData() { try(Client client = cluster.getInternalNodeClient()) { - client.prepareIndex(SONG_INDEX_NAME).setId(ID_S1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0]).get(); + 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])).actionGet(); - client.index(new IndexRequest().setRefreshPolicy(IMMEDIATE).index(SONG_INDEX_NAME).id(ID_S3).source(SONGS[2])).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]).setRefreshPolicy(IMMEDIATE).get(); + 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(); @@ -681,11 +682,10 @@ public void shouldPerformMultiGetDocuments_positive() throws IOException { MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); assertThat(response, is(notNullValue())); - MultiGetItemResponse[] responses = response.getResponses(); - assertThat(responses, arrayWithSize(2)); - Matcher withNullFailureProperty = hasProperty("failure", nullValue()); - assertThat(responses, arrayContaining(withNullFailureProperty, withNullFailureProperty)); + 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)) @@ -722,8 +722,10 @@ public void shouldPerformMultiGetDocuments_partiallyPositive() throws IOExceptio MultiGetResponse response = restHighLevelClient.mget(request, DEFAULT); assertThat(request, notNullValue()); + assertThat(response, not(isSuccessfulMultiGetResponse())); + assertThat(response, numberOfGetItemResponsesIsEqualTo(2)); + MultiGetItemResponse[] responses = response.getResponses(); - assertThat(responses, arrayWithSize(2)); assertThat(responses, arrayContaining( hasProperty("failure", nullValue()), hasProperty("failure", notNullValue()) @@ -747,14 +749,10 @@ public void shouldBeAllowedToPerformMulitSearch_positive() throws IOException { MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); assertThat(response, notNullValue()); + assertThat(response, isSuccessfulMultiSearchResponse()); + assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); + MultiSearchResponse.Item[] responses = response.getResponses(); - assertThat(responses, Matchers.arrayWithSize(2)); - assertThat(responses, arrayContaining( - notNullValue(), - notNullValue() - )); - 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, SONG_INDEX_NAME, ID_S1)); @@ -776,9 +774,10 @@ public void shouldBeAllowedToPerformMulitSearch_partiallyPositive() throws IOExc MultiSearchResponse response = restHighLevelClient.msearch(request, DEFAULT); assertThat(response, notNullValue()); + assertThat(response, not(isSuccessfulMultiSearchResponse())); + assertThat(response, numberOfSearchItemResponsesIsEqualTo(2)); + MultiSearchResponse.Item[] responses = response.getResponses(); - assertThat(responses, Matchers.arrayWithSize(2)); - assertThat(responses, arrayContaining(notNullValue(), notNullValue())); assertThat(responses[0].getFailure(), nullValue()); assertThat(responses[1].getFailure(), statusException(INTERNAL_SERVER_ERROR)); assertThat(responses[1].getFailure(), errorMessageContain("security_exception")); @@ -859,8 +858,8 @@ public void shouldPerformStatAggregation_negative() throws IOException { 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])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1])); + 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); @@ -882,8 +881,8 @@ public void shouldIndexDocumentInBulkRequest_positive() throws IOException { 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])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1])); + 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); @@ -907,8 +906,8 @@ public void shouldIndexDocumentInBulkRequest_partiallyPositive() throws IOExcept 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])); - bulkRequest.add(new IndexRequest(SONG_INDEX_NAME).id("two").source(SONGS[1])); + 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); @@ -932,8 +931,8 @@ public void shouldUpdateDocumentsInBulk_positive() throws IOException { 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])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1])); + 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))); @@ -958,7 +957,7 @@ 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])); + 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))); @@ -1007,10 +1006,10 @@ public void shouldUpdateDocumentsInBulk_negative() throws IOException { public void shouldDeleteDocumentInBulk_positive() throws IOException { try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("three").source(SONGS[2])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("four").source(SONGS[3])); + 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")); @@ -1035,8 +1034,8 @@ public void shouldDeleteDocumentInBulk_positive() throws IOException { public void shouldDeleteDocumentInBulk_partiallyPositive() throws IOException { try (RestHighLevelClient restHighLevelClient = cluster.getRestHighLevelClient(LIMITED_WRITE_USER)) { BulkRequest bulkRequest = new BulkRequest().setRefreshPolicy(IMMEDIATE); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("one").source(SONGS[0])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("two").source(SONGS[1])); + 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")); @@ -1280,7 +1279,7 @@ public void shouldCreateIndexTemplate_positive() throws IOException { 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]) + 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)); @@ -1365,7 +1364,7 @@ public void shouldUpdateTemplate_positive() throws IOException { 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]) + 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)); @@ -1405,10 +1404,9 @@ public void shouldGetFieldCapabilitiesForAllIndexes_positive() throws IOExceptio FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); assertThat(response, notNullValue()); - assertThat(response.get(), aMapWithSize(1)); - assertThat(response.getIndices(), arrayWithSize(3)); - assertThat(response.getField(FIELD_TITLE), hasKey("text")); - assertThat(response.getIndices(), arrayContainingInAnyOrder(SONG_INDEX_NAME, PROHIBITED_SONG_INDEX_NAME, UPDATE_DELETE_OPERATION_INDEX_NAME)); + 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")); @@ -1434,10 +1432,9 @@ public void shouldGetFieldCapabilitiesForParticularIndex_positive() throws IOExc FieldCapabilitiesResponse response = restHighLevelClient.fieldCaps(request, DEFAULT); assertThat(response, notNullValue()); - assertThat(response.get(), aMapWithSize(1)); - assertThat(response.getIndices(), arrayWithSize(1)); - assertThat(response.getField(FIELD_TITLE), hasKey("text")); - assertThat(response.getIndices(), arrayContainingInAnyOrder(SONG_INDEX_NAME)); + 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")); @@ -1608,8 +1605,8 @@ public void shouldRestoreSnapshot_positive() throws IOException { 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])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1])); + 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); @@ -1624,8 +1621,8 @@ public void shouldRestoreSnapshot_positive() throws IOException { // 5. introduce some changes bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Drei").source(SONGS[2])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Vier").source(SONGS[3])); + 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); @@ -1672,8 +1669,8 @@ public void shouldRestoreSnapshot_failureForbiddenIndex() throws IOException { 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])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1])); + 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); @@ -1718,8 +1715,8 @@ public void shouldRestoreSnapshot_failureOperationForbidden() throws IOException 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])); - bulkRequest.add(new IndexRequest(WRITE_SONG_INDEX_NAME).id("Zwei").source(SONGS[1])); + 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); diff --git a/src/integrationTest/java/org/opensearch/security/Song.java b/src/integrationTest/java/org/opensearch/security/Song.java index 14b1ce9131..c0e5749d29 100644 --- a/src/integrationTest/java/org/opensearch/security/Song.java +++ b/src/integrationTest/java/org/opensearch/security/Song.java @@ -50,20 +50,19 @@ public class Song { public static final String QUERY_TITLE_POISON = FIELD_TITLE + ":" + TITLE_POISON; public static final String QUERY_TITLE_MAGNUM_OPUS = FIELD_TITLE + ":" + TITLE_MAGNUM_OPUS; - public static final Map[] SONGS = { - new Song(ARTIST_FIRST, TITLE_MAGNUM_OPUS ,LYRICS_1, 1, GENRE_ROCK).asMap(), - new Song(ARTIST_STRING, TITLE_SONG_1_PLUS_1, LYRICS_2, 2, GENRE_BLUES).asMap(), - new Song(ARTIST_TWINS, TITLE_NEXT_SONG, LYRICS_3, 3, GENRE_JAZZ).asMap(), - new Song(ARTIST_NO, TITLE_POISON, LYRICS_4, 4, GENRE_ROCK).asMap(), - new Song(ARTIST_YES, TITLE_AFFIRMATIVE,LYRICS_5, 5, GENRE_BLUES).asMap(), - new Song(ARTIST_UNKNOWN, TITLE_CONFIDENTIAL, LYRICS_6, 6, GENRE_JAZZ).asMap() + public static final Song[] SONGS = { + new Song(ARTIST_FIRST, TITLE_MAGNUM_OPUS ,LYRICS_1, 1, GENRE_ROCK), + new Song(ARTIST_STRING, TITLE_SONG_1_PLUS_1, LYRICS_2, 2, GENRE_BLUES), + new Song(ARTIST_TWINS, TITLE_NEXT_SONG, LYRICS_3, 3, GENRE_JAZZ), + new Song(ARTIST_NO, TITLE_POISON, LYRICS_4, 4, GENRE_ROCK), + new Song(ARTIST_YES, TITLE_AFFIRMATIVE,LYRICS_5, 5, GENRE_BLUES), + new Song(ARTIST_UNKNOWN, TITLE_CONFIDENTIAL, LYRICS_6, 6, GENRE_JAZZ) }; private final String artist; private final String title; private final String lyrics; private final Integer stars; - private final String genre; public Song(String artist, String title, String lyrics, Integer stars, String genre) { @@ -74,6 +73,26 @@ public Song(String artist, String title, String lyrics, Integer stars, String ge this.genre = Objects.requireNonNull(genre, "Genre field is required"); } + public String getArtist() { + return artist; + } + + public String getTitle() { + return title; + } + + public String getLyrics() { + return lyrics; + } + + public Integer getStars() { + return stars; + } + + public String getGenre() { + return genre; + } + public Map asMap() { return Map.of(FIELD_ARTIST, artist, FIELD_TITLE, title, diff --git a/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java index 7ba2a6a72e..4278a07a53 100644 --- a/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java +++ b/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java @@ -75,8 +75,8 @@ protected LocalCluster getCluster() { @BeforeClass public static void createTestData() { try(Client client = cluster.getInternalNodeClient()){ - client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(ID_ONE_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0]).get(); - client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(ID_TWO_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1]).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(ID_ONE_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(ID_TWO_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); } } diff --git a/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java b/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java index 341a956b4a..5875a120a4 100644 --- a/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java +++ b/src/integrationTest/java/org/opensearch/security/http/JwtAuthenticationTests.java @@ -126,7 +126,7 @@ public class JwtAuthenticationTests { @BeforeClass public static void createTestData() { try (Client client = cluster.getInternalNodeClient()) { - client.prepareIndex(QA_SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0]).get(); + client.prepareIndex(QA_SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); } try(TestRestClient client = cluster.getRestClient(ADMIN_USER)){ client.createRoleMapping(ROLE_VP, DEPARTMENT_SONG_LISTENER_ROLE.getName()); diff --git a/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java index ce51e2f920..be17b6c4cf 100644 --- a/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java +++ b/src/integrationTest/java/org/opensearch/security/http/LdapTlsAuthenticationTest.java @@ -174,9 +174,9 @@ public class LdapTlsAuthenticationTest { @BeforeClass public static void createTestData() { try(Client client = cluster.getInternalNodeClient()){ - client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0]).get(); - client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(SONG_ID_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1]).get(); - client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(SONG_ID_3).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2]).get(); + client.prepareIndex(SONG_INDEX_NAME).setId(SONG_ID_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0].asMap()).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(SONG_ID_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1].asMap()).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(SONG_ID_3).setRefreshPolicy(IMMEDIATE).setSource(SONGS[2].asMap()).get(); } } diff --git a/src/integrationTest/java/org/opensearch/test/framework/cluster/SearchRequestFactory.java b/src/integrationTest/java/org/opensearch/test/framework/cluster/SearchRequestFactory.java index 9e28b3a2fe..781ad4505c 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/cluster/SearchRequestFactory.java +++ b/src/integrationTest/java/org/opensearch/test/framework/cluster/SearchRequestFactory.java @@ -26,6 +26,15 @@ public final class SearchRequestFactory { private SearchRequestFactory() { } + + public static SearchRequest queryByIdsRequest(String indexName, String... ids) { + SearchRequest searchRequest = new SearchRequest(indexName); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(QueryBuilders.idsQuery().addIds(ids)); + searchRequest.source(searchSourceBuilder); + return searchRequest; + } + public static SearchRequest queryStringQueryRequest(String indexName, String queryString) { SearchRequest searchRequest = new SearchRequest(indexName); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); diff --git a/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java b/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java index cf6565ea12..8619421085 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java +++ b/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java @@ -192,6 +192,18 @@ public HttpResponse assignRoleToUser(String username, String roleName) { return patch("_plugins/_security/api/internalusers/" + username, body); } + public HttpResponse createRole(String roleName, ToXContentObject role) { + Objects.requireNonNull(roleName, "Role name is required"); + Objects.requireNonNull(role, "Role is required"); + return putJson("_plugins/_security/api/roles/" + roleName, role); + } + + public HttpResponse createUser(String userName, ToXContentObject user) { + Objects.requireNonNull(userName, "User name is required"); + Objects.requireNonNull(user, "User is required"); + return putJson("_plugins/_security/api/internalusers/" + userName, user); + } + public HttpResponse executeRequest(HttpUriRequest uriRequest, Header... requestSpecificHeaders) { try(CloseableHttpClient httpClient = getHTTPClient()) { diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsExactlyIndicesMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsExactlyIndicesMatcher.java new file mode 100644 index 0000000000..551a1823bf --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsExactlyIndicesMatcher.java @@ -0,0 +1,46 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import java.util.Set; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; + +import static java.util.Objects.isNull; + +class ContainsExactlyIndicesMatcher extends TypeSafeDiagnosingMatcher { + + private final Set expectedIndices; + + ContainsExactlyIndicesMatcher(String... expectedIndices) { + if (isNull(expectedIndices) || expectedIndices.length == 0) { + throw new IllegalArgumentException("expectedIndices cannot be null or empty"); + } + this.expectedIndices = Set.of(expectedIndices); + } + + @Override + protected boolean matchesSafely(FieldCapabilitiesResponse response, Description mismatchDescription) { + Set actualIndices = Set.of(response.getIndices()); + if (!expectedIndices.equals(actualIndices)) { + mismatchDescription.appendText("Actual indices: ").appendValue(actualIndices); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Response contains indices: ").appendValue(expectedIndices); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsFieldWithTypeMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsFieldWithTypeMatcher.java new file mode 100644 index 0000000000..41db8a9ed5 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/ContainsFieldWithTypeMatcher.java @@ -0,0 +1,51 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import java.util.Map; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.fieldcaps.FieldCapabilities; +import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; + +import static java.util.Objects.requireNonNull; + +class ContainsFieldWithTypeMatcher extends TypeSafeDiagnosingMatcher { + + private final String expectedFieldName; + private final String expectedFieldType; + + ContainsFieldWithTypeMatcher(String expectedFieldName, String expectedFieldType) { + this.expectedFieldName = requireNonNull(expectedFieldName, "Field name is required");; + this.expectedFieldType = requireNonNull(expectedFieldType, "Field type is required");; + } + + @Override + protected boolean matchesSafely(FieldCapabilitiesResponse response, Description mismatchDescription) { + Map> fieldCapabilitiesMap = response.get(); + if (!fieldCapabilitiesMap.containsKey(expectedFieldName)) { + mismatchDescription.appendText("Response does not contain field with name ").appendText(expectedFieldName); + return false; + } + if (!fieldCapabilitiesMap.get(expectedFieldName).containsKey(expectedFieldType)) { + mismatchDescription.appendText("Field type does not match ").appendText(expectedFieldType); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Response contains field with name ").appendValue(expectedFieldName) + .appendText(" and type ").appendValue(expectedFieldType); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/FieldCapabilitiesResponseMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/FieldCapabilitiesResponseMatchers.java new file mode 100644 index 0000000000..c5680f6055 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/FieldCapabilitiesResponseMatchers.java @@ -0,0 +1,32 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Matcher; + +import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; + +public class FieldCapabilitiesResponseMatchers { + + private FieldCapabilitiesResponseMatchers() {} + + public static Matcher containsExactlyIndices(String... expectedIndices) { + return new ContainsExactlyIndicesMatcher(expectedIndices); + } + + public static Matcher containsFieldWithNameAndType(String expectedFieldName, String expectedFieldType) { + return new ContainsFieldWithTypeMatcher(expectedFieldName, expectedFieldType); + } + + public static Matcher numberOfFieldsIsEqualTo(int expectedNumberOfFields) { + return new NumberOfFieldsIsEqualToMatcher(expectedNumberOfFields); + } + +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentIdMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseContainsDocumentWithIdMatcher.java similarity index 83% rename from src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentIdMatcher.java rename to src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseContainsDocumentWithIdMatcher.java index 85519a7261..5dd0c72576 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentIdMatcher.java +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseContainsDocumentWithIdMatcher.java @@ -16,12 +16,12 @@ import static java.util.Objects.requireNonNull; -class GetResponseDocumentIdMatcher extends TypeSafeDiagnosingMatcher { +class GetResponseContainsDocumentWithIdMatcher extends TypeSafeDiagnosingMatcher { private final String indexName; private final String documentId; - public GetResponseDocumentIdMatcher(String indexName, String documentId) { + public GetResponseContainsDocumentWithIdMatcher(String indexName, String documentId) { this.indexName = requireNonNull(indexName, "Index name is required"); this.documentId = requireNonNull(documentId, "Document id is required"); } @@ -40,6 +40,10 @@ protected boolean matchesSafely(GetResponse response, Description mismatchDescri mismatchDescription.appendText("Document does not exist or is inaccessible"); return false; } + if(response.isSourceEmpty()) { + mismatchDescription.appendText("Document source is empty"); + return false; + } return true; } diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentContainsExactlyFieldsWithNamesMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentContainsExactlyFieldsWithNamesMatcher.java new file mode 100644 index 0000000000..aa8daa0128 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentContainsExactlyFieldsWithNamesMatcher.java @@ -0,0 +1,49 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import java.util.Map; +import java.util.Set; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.get.GetResponse; + +import static java.util.Objects.isNull; + +class GetResponseDocumentContainsExactlyFieldsWithNamesMatcher extends TypeSafeDiagnosingMatcher { + + private final Set expectedFieldsNames; + + GetResponseDocumentContainsExactlyFieldsWithNamesMatcher(String... expectedFieldsNames) { + if (isNull(expectedFieldsNames) || expectedFieldsNames.length == 0) { + throw new IllegalArgumentException("expectedFieldsNames cannot be null or empty"); + } + this.expectedFieldsNames = Set.of(expectedFieldsNames); + } + + @Override + protected boolean matchesSafely(GetResponse response, Description mismatchDescription) { + Map sourceMap = response.getSourceAsMap(); + Set actualFieldsNames = sourceMap.keySet(); + if (!expectedFieldsNames.equals(actualFieldsNames)) { + mismatchDescription.appendValue("Document with id ").appendValue(response.getId()) + .appendText(" contains fields with names: ").appendValue(actualFieldsNames); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Document contain exactly fields with names: ").appendValue(expectedFieldsNames); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentDoesNotContainFieldMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentDoesNotContainFieldMatcher.java new file mode 100644 index 0000000000..f8f81f0e95 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseDocumentDoesNotContainFieldMatcher.java @@ -0,0 +1,47 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import java.util.Map; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.get.GetResponse; + +import static java.util.Objects.requireNonNull; + +class GetResponseDocumentDoesNotContainFieldMatcher extends TypeSafeDiagnosingMatcher { + + private final String fieldName; + + public GetResponseDocumentDoesNotContainFieldMatcher(String fieldName) { + this.fieldName = requireNonNull(fieldName, "Field name is required."); + } + + @Override + protected boolean matchesSafely(GetResponse response, Description mismatchDescription) { + Map source = response.getSource(); + if(source == null) { + mismatchDescription.appendText("Source is not available in search results"); + return false; + } + if(source.containsKey(fieldName)) { + mismatchDescription.appendText("Document contains field ").appendValue(fieldName); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Document does not contain field ").appendValue(fieldName); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseMatchers.java index 87f346d704..6a494dc9cd 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseMatchers.java +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/GetResponseMatchers.java @@ -18,7 +18,7 @@ public class GetResponseMatchers { private GetResponseMatchers() {} public static Matcher containDocument(String indexName, String documentId) { - return new GetResponseDocumentIdMatcher(indexName, documentId); + return new GetResponseContainsDocumentWithIdMatcher(indexName, documentId); } public static Matcher containOnlyDocumentId(String indexName, String documentId) { @@ -28,4 +28,12 @@ public static Matcher containOnlyDocumentId(String indexName, Strin public static Matcher documentContainField(String fieldName, Object fieldValue) { return new GetResponseDocumentFieldValueMatcher(fieldName, fieldValue); } + + public static Matcher documentDoesNotContainField(String fieldName) { + return new GetResponseDocumentDoesNotContainFieldMatcher(fieldName); + } + + public static Matcher documentContainsExactlyFieldsWithNames(String... expectedFieldsNames) { + return new GetResponseDocumentContainsExactlyFieldsWithNamesMatcher(expectedFieldsNames); + } } diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/MultiGetResponseMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/MultiGetResponseMatchers.java new file mode 100644 index 0000000000..30b3037752 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/MultiGetResponseMatchers.java @@ -0,0 +1,29 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Matcher; + +import org.opensearch.action.get.MultiGetResponse; + +public class MultiGetResponseMatchers { + + private MultiGetResponseMatchers() { + } + + public static Matcher isSuccessfulMultiGetResponse() { + return new SuccessfulMultiGetResponseMatcher(); + } + + public static Matcher numberOfGetItemResponsesIsEqualTo(int expectedNumberOfResponses) { + return new NumberOfGetItemResponsesIsEqualToMatcher(expectedNumberOfResponses); + } + +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/MultiSearchResponseMatchers.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/MultiSearchResponseMatchers.java new file mode 100644 index 0000000000..39a2645ce4 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/MultiSearchResponseMatchers.java @@ -0,0 +1,29 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Matcher; + +import org.opensearch.action.search.MultiSearchResponse; + +public class MultiSearchResponseMatchers { + + private MultiSearchResponseMatchers() { + } + + public static Matcher isSuccessfulMultiSearchResponse() { + return new SuccessfulMultiSearchResponseMatcher(); + } + + public static Matcher numberOfSearchItemResponsesIsEqualTo(int expectedNumberOfResponses) { + return new NumberOfSearchItemResponsesIsEqualToMatcher(expectedNumberOfResponses); + } + +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfFieldsIsEqualToMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfFieldsIsEqualToMatcher.java new file mode 100644 index 0000000000..a11d360b98 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfFieldsIsEqualToMatcher.java @@ -0,0 +1,39 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; + +class NumberOfFieldsIsEqualToMatcher extends TypeSafeDiagnosingMatcher { + + private final int expectedNumberOfFields; + + NumberOfFieldsIsEqualToMatcher(int expectedNumberOfFields) { + this.expectedNumberOfFields = expectedNumberOfFields; + } + + @Override + protected boolean matchesSafely(FieldCapabilitiesResponse response, Description mismatchDescription) { + if (expectedNumberOfFields != response.get().size()) { + mismatchDescription.appendText("Actual number of fields: ").appendValue(response.get().size()); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Response contains information about ") + .appendValue(expectedNumberOfFields).appendText(" fields"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfGetItemResponsesIsEqualToMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfGetItemResponsesIsEqualToMatcher.java new file mode 100644 index 0000000000..141b235f2f --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfGetItemResponsesIsEqualToMatcher.java @@ -0,0 +1,40 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.get.MultiGetResponse; + +class NumberOfGetItemResponsesIsEqualToMatcher extends TypeSafeDiagnosingMatcher { + + private final int expectedNumberOfResponses; + + NumberOfGetItemResponsesIsEqualToMatcher(int expectedNumberOfResponses) { + this.expectedNumberOfResponses = expectedNumberOfResponses; + } + + + @Override + protected boolean matchesSafely(MultiGetResponse response, Description mismatchDescription) { + if (expectedNumberOfResponses != response.getResponses().length) { + mismatchDescription.appendText("Actual number of responses: ").appendValue(response.getResponses().length); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Multi get response contains: ").appendValue(expectedNumberOfResponses) + .appendText(" item responses"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfSearchItemResponsesIsEqualToMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfSearchItemResponsesIsEqualToMatcher.java new file mode 100644 index 0000000000..971473148c --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/NumberOfSearchItemResponsesIsEqualToMatcher.java @@ -0,0 +1,41 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.search.MultiSearchResponse; + +class NumberOfSearchItemResponsesIsEqualToMatcher extends TypeSafeDiagnosingMatcher { + + private final int expectedNumberOfResponses; + + NumberOfSearchItemResponsesIsEqualToMatcher(int expectedNumberOfResponses) { + this.expectedNumberOfResponses = expectedNumberOfResponses; + } + + + @Override + protected boolean matchesSafely(MultiSearchResponse response, Description mismatchDescription) { + if (expectedNumberOfResponses != response.getResponses().length) { + mismatchDescription.appendText("Actual number of responses: ").appendValue(response.getResponses().length); + return false; + } + + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Multi search response contains: ").appendValue(expectedNumberOfResponses) + .appendText(" item responses"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiGetResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiGetResponseMatcher.java new file mode 100644 index 0000000000..3aedb0174b --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiGetResponseMatcher.java @@ -0,0 +1,37 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.get.MultiGetItemResponse; +import org.opensearch.action.get.MultiGetResponse; + +class SuccessfulMultiGetResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(MultiGetResponse response, Description mismatchDescription) { + for (MultiGetItemResponse getItemResponse : response.getResponses()) { + if (getItemResponse.isFailed()) { + mismatchDescription.appendValue("Get an item from index: ").appendValue(getItemResponse.getIndex()) + .appendText(" failed: ").appendValue(getItemResponse.getFailure().getMessage()); + return false; + } + } + + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful multi get response"); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiSearchResponseMatcher.java b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiSearchResponseMatcher.java new file mode 100644 index 0000000000..b1dbd64c33 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/matcher/SuccessfulMultiSearchResponseMatcher.java @@ -0,0 +1,35 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.test.framework.matcher; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import org.opensearch.action.search.MultiSearchResponse; + +class SuccessfulMultiSearchResponseMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(MultiSearchResponse response, Description mismatchDescription) { + for (MultiSearchResponse.Item itemResponse : response.getResponses()) { + if (itemResponse.isFailure()) { + mismatchDescription.appendValue("Get an item failed: ").appendValue(itemResponse.getFailureMessage()); + return false; + } + } + + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("Successful multi search response"); + } +}