diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 5c3201aaeb..c7612cf0d8 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -45,6 +45,7 @@ import com.google.common.collect.SetMultimap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.util.Strings; import org.opensearch.ExceptionsHelper; import org.opensearch.action.support.IndicesOptions; @@ -379,8 +380,7 @@ public EvaluatedDlsFlsConfig getDlsFls(User user, boolean dfmEmptyOverwritesAll, for (SecurityRole role : roles) { for (IndexPattern ip : role.getIpatterns()) { - Set concreteIndices; - concreteIndices = ip.getResolvedIndexPattern(user, resolver, cs, false); + final Set concreteIndices = ip.concreteIndexNames(user, resolver, cs); String dls = ip.getDlsQuery(user); if (dls != null && dls.length() > 0) { @@ -561,7 +561,7 @@ private Set getAllResolvedPermittedIndices(Resolved resolved, User user, // } if (patternMatch) { //resolved but can contain patterns for nonexistent indices - final WildcardMatcher permitted = WildcardMatcher.from(p.getResolvedIndexPattern(user, resolver, cs, true)); //maybe they do not exist + final WildcardMatcher permitted = WildcardMatcher.from(p.attemptResolveIndexNames(user, resolver, cs)); //maybe they do not exist final Set res = new HashSet<>(); if (!resolved.isLocalAll() && !resolved.getAllIndices().contains("*") && !resolved.getAllIndices().contains("_all")) { //resolved but can contain patterns for nonexistent indices @@ -753,35 +753,42 @@ public String getUnresolvedIndexPattern(User user) { return replaceProperties(indexPattern, user); } - public Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs, boolean appendUnresolved) { - String unresolved = getUnresolvedIndexPattern(user); - WildcardMatcher matcher = WildcardMatcher.from(unresolved); - String[] resolved = null; + /** Finds the indices accessible to the user and resolves them to concrete names */ + public Set concreteIndexNames(final User user, final IndexNameExpressionResolver resolver, final ClusterService cs) { + return getResolvedIndexPattern(user, resolver, cs, false); + } + + /** Finds the indices accessible to the user and attempts to resolve them to names, also includes any unresolved names */ + public Set attemptResolveIndexNames(final User user, final IndexNameExpressionResolver resolver, final ClusterService cs) { + return getResolvedIndexPattern(user, resolver, cs, true); + } + + public Set getResolvedIndexPattern(final User user, final IndexNameExpressionResolver resolver, final ClusterService cs, final boolean appendUnresolved) { + final String unresolved = getUnresolvedIndexPattern(user); + final ImmutableSet.Builder resolvedIndices = new ImmutableSet.Builder<>(); + + final WildcardMatcher matcher = WildcardMatcher.from(unresolved); if (!(matcher instanceof WildcardMatcher.Exact)) { final String[] aliasesForPermittedPattern = cs.state().getMetadata().getIndicesLookup().entrySet().stream() .filter(e -> e.getValue().getType() == ALIAS) .filter(e -> matcher.test(e.getKey())) .map(e -> e.getKey()) .toArray(String[]::new); - if (aliasesForPermittedPattern.length > 0) { - resolved = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), aliasesForPermittedPattern); + final String[] resolvedAliases = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), aliasesForPermittedPattern); + resolvedIndices.addAll(Arrays.asList(resolvedAliases)); } } - if (resolved == null && !unresolved.isEmpty()) { - resolved = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), unresolved); + if (Strings.isNotBlank(unresolved)) { + final String[] resolvedIndicesFromPattern = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), unresolved); + resolvedIndices.addAll(Arrays.asList(resolvedIndicesFromPattern)); } - if (resolved == null || resolved.length == 0) { - return ImmutableSet.of(unresolved); - } else { - ImmutableSet.Builder builder = ImmutableSet.builder() - .addAll(Arrays.asList(resolved)); - if (appendUnresolved) { - builder.add(unresolved); - } - return builder.build(); + + if (appendUnresolved || resolvedIndices.build().isEmpty()) { + resolvedIndices.add(unresolved); } + return resolvedIndices.build(); } public String getDlsQuery(User user) { @@ -996,12 +1003,12 @@ private static boolean impliesTypePerm(Set ipatterns, Resolved res indexMatcherAndPermissions = ipatterns .stream() .filter(indexPattern -> "*".equals(indexPattern.getUnresolvedIndexPattern(user))) - .map(p -> new IndexMatcherAndPermissions(p.getResolvedIndexPattern(user, resolver, cs, true), p.perms)) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms)) .toArray(IndexMatcherAndPermissions[]::new); } else { indexMatcherAndPermissions = ipatterns .stream() - .map(p -> new IndexMatcherAndPermissions(p.getResolvedIndexPattern(user, resolver, cs, true), p.perms)) + .map(p -> new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms)) .toArray(IndexMatcherAndPermissions[]::new); } return resolvedRequestedIndices diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/FlsIndexingTests.java b/src/test/java/org/opensearch/security/dlic/dlsfls/FlsIndexingTests.java new file mode 100644 index 0000000000..73a152eca4 --- /dev/null +++ b/src/test/java/org/opensearch/security/dlic/dlsfls/FlsIndexingTests.java @@ -0,0 +1,160 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.dlic.dlsfls; + +import org.apache.http.Header; +import org.apache.http.HttpStatus; +import org.junit.Test; + +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.WriteRequest.RefreshPolicy; +import org.opensearch.client.Client; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.security.test.DynamicSecurityConfig; +import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.StringContains.containsString; + +public class FlsIndexingTests extends AbstractDlsFlsTest { + + protected void populateData(final Client tc) { + // Create several documents in different indices with shared field names, + // different roles will have different levels of FLS restrictions + tc.index(new IndexRequest("yellow-pages").id("1").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"phone-all\":1001,\"phone-some\":1002,\"phone-one\":1003}", XContentType.JSON)).actionGet(); + tc.index(new IndexRequest("green-pages").id("2").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"phone-all\":2001,\"phone-some\":2002,\"phone-one\":2003}", XContentType.JSON)).actionGet(); + tc.index(new IndexRequest("blue-book").id("3").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"phone-all\":3001,\"phone-some\":3002,\"phone-one\":3003}", XContentType.JSON)).actionGet(); + + // Seperate index used to test aliasing + tc.index(new IndexRequest(".hidden").id("1").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{}", XContentType.JSON)).actionGet(); + } + + private Header asPhoneOneUser = encodeBasicHeader("user_aaa", "password"); + private Header asPhoneSomeUser = encodeBasicHeader("user_bbb", "password"); + private Header asPhoneAllUser = encodeBasicHeader("user_ccc", "password"); + + private final String searchQuery = "/*/_search?filter_path=hits.hits&pretty"; + + @Test + public void testSingleIndexFlsApplied() throws Exception { + setup(new DynamicSecurityConfig() + .setSecurityRoles("roles_fls_indexing.yml") + .setSecurityRolesMapping("roles_mapping_fls_indexing.yml")); + + final HttpResponse phoneOneFilteredResponse = rh.executeGetRequest(searchQuery, asPhoneOneUser); + assertThat(phoneOneFilteredResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("1003"))); + assertThat(phoneOneFilteredResponse.getBody(), containsString("1002")); + assertThat(phoneOneFilteredResponse.getBody(), containsString("1001")); + + assertThat(phoneOneFilteredResponse.getBody(), containsString("2003")); + assertThat(phoneOneFilteredResponse.getBody(), containsString("2002")); + assertThat(phoneOneFilteredResponse.getBody(), containsString("2001")); + + assertThat(phoneOneFilteredResponse.getBody(), containsString("3003")); + assertThat(phoneOneFilteredResponse.getBody(), containsString("3002")); + assertThat(phoneOneFilteredResponse.getBody(), containsString("3001")); + } + + @Test + public void testSingleIndexFlsAppliedForLimitedResults() throws Exception { + setup(new DynamicSecurityConfig() + .setSecurityRoles("roles_fls_indexing.yml") + .setSecurityRolesMapping("roles_mapping_fls_indexing.yml")); + + final HttpResponse phoneOneFilteredResponse = rh.executeGetRequest("/yellow-pages/_search?filter_path=hits.hits&pretty", asPhoneOneUser); + assertThat(phoneOneFilteredResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("1003"))); + assertThat(phoneOneFilteredResponse.getBody(), containsString("1002")); + assertThat(phoneOneFilteredResponse.getBody(), containsString("1001")); + + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("2003"))); + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("2002"))); + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("2001"))); + + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("3003"))); + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("3002"))); + assertThat(phoneOneFilteredResponse.getBody(), not(containsString("3001"))); + } + + @Test + public void testSeveralIndexFlsApplied() throws Exception { + setup(new DynamicSecurityConfig() + .setSecurityRoles("roles_fls_indexing.yml") + .setSecurityRolesMapping("roles_mapping_fls_indexing.yml")); + + final HttpResponse phoneSomeFilteredResponse = rh.executeGetRequest(searchQuery, asPhoneSomeUser); + assertThat(phoneSomeFilteredResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(phoneSomeFilteredResponse.getBody(), containsString("1003")); + assertThat(phoneSomeFilteredResponse.getBody(), not(containsString("1002"))); + assertThat(phoneSomeFilteredResponse.getBody(), containsString("1001")); + + assertThat(phoneSomeFilteredResponse.getBody(), containsString("2003")); + assertThat(phoneSomeFilteredResponse.getBody(), not(containsString("2002"))); + assertThat(phoneSomeFilteredResponse.getBody(), containsString("2001")); + + assertThat(phoneSomeFilteredResponse.getBody(), containsString("3003")); + assertThat(phoneSomeFilteredResponse.getBody(), containsString("3002")); + assertThat(phoneSomeFilteredResponse.getBody(), containsString("3001")); + } + + @Test + public void testAllIndexFlsApplied() throws Exception { + setup(new DynamicSecurityConfig() + .setSecurityRoles("roles_fls_indexing.yml") + .setSecurityRolesMapping("roles_mapping_fls_indexing.yml")); + + final HttpResponse phoneAllFilteredResponse = rh.executeGetRequest(searchQuery, asPhoneAllUser); + assertThat(phoneAllFilteredResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(phoneAllFilteredResponse.getBody(), containsString("1003")); + assertThat(phoneAllFilteredResponse.getBody(), containsString("1002")); + assertThat(phoneAllFilteredResponse.getBody(), not(containsString("1001"))); + + assertThat(phoneAllFilteredResponse.getBody(), containsString("2003")); + assertThat(phoneAllFilteredResponse.getBody(), containsString("2002")); + assertThat(phoneAllFilteredResponse.getBody(), not(containsString("2001"))); + + assertThat(phoneAllFilteredResponse.getBody(), containsString("3003")); + assertThat(phoneAllFilteredResponse.getBody(), containsString("3002")); + assertThat(phoneAllFilteredResponse.getBody(), not(containsString("3001"))); + } + + @Test + public void testAllIndexFlsAppliedWithAlias() throws Exception { + setup(new DynamicSecurityConfig() + .setSecurityRoles("roles_fls_indexing.yml") + .setSecurityRolesMapping("roles_mapping_fls_indexing.yml")); + + final HttpResponse createAlias = rh.executePostRequest("_aliases", "{\"actions\":[{\"add\":{\"index\":\".hidden\",\"alias\":\"ducky\"}}]}", asPhoneAllUser); + assertThat(createAlias.getStatusCode(), equalTo(HttpStatus.SC_OK)); + + final HttpResponse phoneAllFilteredResponse = rh.executeGetRequest(searchQuery, asPhoneAllUser); + assertThat(phoneAllFilteredResponse.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(phoneAllFilteredResponse.getBody(), containsString("1003")); + assertThat(phoneAllFilteredResponse.getBody(), containsString("1002")); + assertThat(phoneAllFilteredResponse.getBody(), not(containsString("1001"))); + + assertThat(phoneAllFilteredResponse.getBody(), containsString("2003")); + assertThat(phoneAllFilteredResponse.getBody(), containsString("2002")); + assertThat(phoneAllFilteredResponse.getBody(), not(containsString("2001"))); + + assertThat(phoneAllFilteredResponse.getBody(), containsString("3003")); + assertThat(phoneAllFilteredResponse.getBody(), containsString("3002")); + assertThat(phoneAllFilteredResponse.getBody(), not(containsString("3001"))); + } +} diff --git a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java new file mode 100644 index 0000000000..0c65e2bea9 --- /dev/null +++ b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java @@ -0,0 +1,225 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.securityconf.impl.v7; + +import java.util.Arrays; +import java.util.Set; +import java.util.TreeMap; + +import com.google.common.collect.ImmutableSet; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.quality.Strictness; + +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexAbstraction; +import org.opensearch.cluster.metadata.IndexAbstraction.Type; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.security.securityconf.ConfigModelV7.IndexPattern; +import org.opensearch.security.user.User; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +@RunWith(MockitoJUnitRunner.class) +public class IndexPatternTests { + + @Mock + private User user; + @Mock + private IndexNameExpressionResolver resolver; + @Mock + private ClusterService clusterService; + + private IndexPattern ip; + + @Before + public void before() { + ip = spy(new IndexPattern("defaultPattern")); + } + + @After + public void after() { + verifyNoMoreInteractions(user, resolver, clusterService); + } + + @Test + public void testCtor() { + assertThrows(NullPointerException.class, () -> new IndexPattern(null)); + } + + /** Ensure that concreteIndexNames sends correct parameters are sent to getResolvedIndexPattern */ + @Test + public void testConcreteIndexNamesOverload() { + doReturn(ImmutableSet.of("darn")).when(ip).getResolvedIndexPattern(user, resolver, clusterService, false); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("darn")); + + verify(ip).getResolvedIndexPattern(user, resolver, clusterService, false); + verify(ip).concreteIndexNames(user, resolver, clusterService); + verifyNoMoreInteractions(ip); + } + + /** Ensure that attemptResolveIndexNames sends correct parameters are sent to getResolvedIndexPattern */ + @Test + public void testAttemptResolveIndexNamesOverload() { + doReturn(ImmutableSet.of("yarn")).when(ip).getResolvedIndexPattern(user, resolver, clusterService, true); + + final Set results = ip.attemptResolveIndexNames(user, resolver, clusterService); + + assertThat(results, contains("yarn")); + + verify(ip).getResolvedIndexPattern(user, resolver, clusterService, true); + verify(ip).attemptResolveIndexNames(user, resolver, clusterService); + verifyNoMoreInteractions(ip); + } + + /** Verify concreteIndexNames when there are no matches */ + @Test + public void testExactNameWithNoMatches() { + doReturn("index-17").when(ip).getUnresolvedIndexPattern(user); + when(clusterService.state()).thenReturn(mock(ClusterState.class)); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-17"))).thenReturn(new String[]{}); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("index-17")); + + verify(clusterService).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-17")); + } + + /** Verify concreteIndexNames on exact name matches */ + @Test + public void testExactName() { + doReturn("index-17").when(ip).getUnresolvedIndexPattern(user); + when(clusterService.state()).thenReturn(mock(ClusterState.class)); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-17"))).thenReturn(new String[]{"resolved-index-17"}); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-17")); + + verify(clusterService).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-17")); + } + + /** Verify concreteIndexNames on multiple matches */ + @Test + public void testMultipleConcreteIndices() { + doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); + doReturn(createClusterState()).when(clusterService).state(); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-1*"))).thenReturn(new String[]{"resolved-index-17", "resolved-index-18"}); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-17", "resolved-index-18")); + + verify(clusterService, times(2)).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-1*")); + } + + /** Verify concreteIndexNames when there is an alias */ + @Test + public void testMultipleConcreteIndicesWithOneAlias() { + doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); + + doReturn(createClusterState( + new IndexShorthand("index-111", Type.DATA_STREAM), // Name matches/wrong type + new IndexShorthand("index-100", Type.ALIAS), // Name and type match + new IndexShorthand("19", Type.ALIAS) // Type matches/wrong name + )).when(clusterService).state(); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-100"))).thenReturn(new String[]{"resolved-index-100"}); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-1*"))).thenReturn(new String[]{"resolved-index-17", "resolved-index-18"}); + + final Set results = ip.concreteIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-100", "resolved-index-17", "resolved-index-18")); + + verify(clusterService, times(3)).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-100")); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-1*")); + } + + /** Verify attemptResolveIndexNames with multiple aliases */ + @Test + public void testMultipleConcreteAliasedAndUnresolved() { + doReturn("index-1*").when(ip).getUnresolvedIndexPattern(user); + doReturn(createClusterState( + new IndexShorthand("index-111", Type.DATA_STREAM), // Name matches/wrong type + new IndexShorthand("index-100", Type.ALIAS), // Name and type match + new IndexShorthand("index-101", Type.ALIAS), // Name and type match + new IndexShorthand("19", Type.ALIAS) // Type matches/wrong name + )).when(clusterService).state(); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-100"), eq("index-101"))).thenReturn(new String[]{"resolved-index-100", "resolved-index-101"}); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-1*"))).thenReturn(new String[]{"resolved-index-17", "resolved-index-18"}); + + final Set results = ip.attemptResolveIndexNames(user, resolver, clusterService); + + assertThat(results, contains("resolved-index-100", "resolved-index-101", "resolved-index-17", "resolved-index-18", "index-1*")); + + verify(clusterService, times(3)).state(); + verify(ip).getUnresolvedIndexPattern(user); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-100"), eq("index-101")); + verify(resolver).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq("index-1*")); + } + + private ClusterState createClusterState(final IndexShorthand... indices) { + final TreeMap indexMap = new TreeMap(); + Arrays.stream(indices).forEach(indexShorthand -> { + final IndexAbstraction indexAbstraction = mock(IndexAbstraction.class); + when(indexAbstraction.getType()).thenReturn(indexShorthand.type); + indexMap.put(indexShorthand.name, indexAbstraction); + }); + + final Metadata mockMetadata = mock(Metadata.class, withSettings().strictness(Strictness.LENIENT)); + when(mockMetadata.getIndicesLookup()).thenReturn(indexMap); + + final ClusterState mockClusterState = mock(ClusterState.class, withSettings().strictness(Strictness.LENIENT)); + when(mockClusterState.getMetadata()).thenReturn(mockMetadata); + + return mockClusterState; + } + + private class IndexShorthand { + public final String name; + public final Type type; + public IndexShorthand(final String name, final Type type) { + this.name = name; + this.type = type; + } + } +} diff --git a/src/test/resources/dlsfls/internal_users.yml b/src/test/resources/dlsfls/internal_users.yml index acda68c42a..c3347c103f 100644 --- a/src/test/resources/dlsfls/internal_users.yml +++ b/src/test/resources/dlsfls/internal_users.yml @@ -51,6 +51,13 @@ perf_named_only: backend_roles: [] attributes: {} description: "Migrated from v6" +user_ccc: + hash: "$2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe" + reserved: false + hidden: false + backend_roles: [] + attributes: {} + description: "Migrated from v6" user_bbb: hash: "$2a$12$YCBrpxYyFusK609FurY5Ee3BlmuzWw0qHwpwqEyNhM2.XnQY3Bxpe" reserved: false diff --git a/src/test/resources/dlsfls/roles_fls_indexing.yml b/src/test/resources/dlsfls/roles_fls_indexing.yml new file mode 100644 index 0000000000..fb08749a74 --- /dev/null +++ b/src/test/resources/dlsfls/roles_fls_indexing.yml @@ -0,0 +1,39 @@ +--- +_meta: + type: "roles" + config_version: 2 +all_indices_no_phone: + cluster_permissions: + - "*" + index_permissions: + - index_patterns: + - "*" + dls: "" + fls: + - "~phone-all" + allowed_actions: + - "ALL" +some_indices_no_phone: + index_permissions: + - index_patterns: + - "*pages*" + fls: + - "~phone-some" + allowed_actions: + - "ALL" + - index_patterns: + - "*" + allowed_actions: + - "read" +once_indices_no_phone: + index_permissions: + - index_patterns: + - "yellow-pages" + fls: + - "~phone-one" + allowed_actions: + - "ALL" + - index_patterns: + - "*" + allowed_actions: + - "read" diff --git a/src/test/resources/dlsfls/roles_mapping_fls_indexing.yml b/src/test/resources/dlsfls/roles_mapping_fls_indexing.yml new file mode 100644 index 0000000000..509f3811cf --- /dev/null +++ b/src/test/resources/dlsfls/roles_mapping_fls_indexing.yml @@ -0,0 +1,31 @@ +--- +_meta: + type: "rolesmapping" + config_version: 2 +all_indices_no_phone: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + and_backend_roles: [] + description: "All indices do not have phone access" + users: + - "user_ccc" +some_indices_no_phone: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + and_backend_roles: [] + description: "Some indices do not have phone access" + users: + - "user_bbb" +once_indices_no_phone: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + and_backend_roles: [] + description: "One indices do not have phone access" + users: + - "user_aaa"