diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 438a78e39e..74ec631c5b 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -661,6 +661,20 @@ public String getName() { } + public static final class IndexMatcherAndPermissions { + private WildcardMatcher matcher; + private WildcardMatcher perms; + + public IndexMatcherAndPermissions(Set patterns, Set perms) { + this.matcher = WildcardMatcher.from(patterns); + this.perms = WildcardMatcher.from(perms); + } + + public boolean matches(String index, String action) { + return matcher.test(index) && perms.test(action); + } + } + // sg roles public static class IndexPattern { private final String indexPattern; @@ -702,6 +716,17 @@ public IndexPattern setDlsQuery(String dlsQuery) { return this; } + public IndexMatcherAndPermissions getIndexMatcherAndPermissions( + User user, + IndexNameExpressionResolver resolver, + ClusterService cs + ) { + if ("*".equals(getUnresolvedIndexPattern(user))) { + return new IndexMatcherAndPermissions(ALL_INDICES, perms); + } + return new IndexMatcherAndPermissions(attemptResolveIndexNames(user, resolver, cs), perms); + } + @Override public int hashCode() { final int prime = 31; @@ -974,20 +999,6 @@ public String toString() { } } - private static final class IndexMatcherAndPermissions { - private WildcardMatcher matcher; - private WildcardMatcher perms; - - public IndexMatcherAndPermissions(Set patterns, Set perms) { - this.matcher = WildcardMatcher.from(patterns); - this.perms = WildcardMatcher.from(perms); - } - - public boolean matches(String index, String action) { - return matcher.test(index) && perms.test(action); - } - } - private static boolean impliesTypePerm( Set ipatterns, Resolved resolved, @@ -1001,15 +1012,12 @@ private static boolean impliesTypePerm( if (resolved.isLocalAll()) { indexMatcherAndPermissions = ipatterns.stream() .filter(indexPattern -> "*".equals(indexPattern.getUnresolvedIndexPattern(user))) - .map(p -> new IndexMatcherAndPermissions(ALL_INDICES, p.perms)) + .map(p -> p.getIndexMatcherAndPermissions(user, resolver, cs)) .toArray(IndexMatcherAndPermissions[]::new); } else { - indexMatcherAndPermissions = ipatterns.stream().map(p -> { - if ("*".equals(p.getUnresolvedIndexPattern(user))) { - return new IndexMatcherAndPermissions(ALL_INDICES, p.perms); - } - return new IndexMatcherAndPermissions(p.attemptResolveIndexNames(user, resolver, cs), p.perms); - }).toArray(IndexMatcherAndPermissions[]::new); + indexMatcherAndPermissions = ipatterns.stream() + .map(p -> p.getIndexMatcherAndPermissions(user, resolver, cs)) + .toArray(IndexMatcherAndPermissions[]::new); } return resolvedRequestedIndices.stream() .allMatch( 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 index 4ea364f663..f9826f18f5 100644 --- a/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java +++ b/src/test/java/org/opensearch/security/securityconf/impl/v7/IndexPatternTests.java @@ -28,6 +28,7 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.securityconf.ConfigModelV7.IndexPattern; import org.opensearch.security.user.User; @@ -37,6 +38,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -45,6 +47,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; @@ -76,6 +79,37 @@ public void testCtor() { assertThrows(NullPointerException.class, () -> new IndexPattern(null)); } + @Test + public void testGetIndexMatcherAndPermissionsWithAllIndices() { + IndexPattern testIp = new IndexPattern("*"); + testIp.addPerm(Set.of("indices:data/write/*")); + User user = new User("test-user"); + ConfigModelV7.IndexMatcherAndPermissions matcher = testIp.getIndexMatcherAndPermissions(user, resolver, clusterService); + verifyNoInteractions(resolver); + verifyNoInteractions(clusterService); + assertThat(matcher.matches("testPattern", "indices:data/write/index"), is(true)); + assertThat(matcher.matches("testPattern", "indices:data/read/index"), is(false)); + assertThat(matcher.matches("otherPattern", "indices:data/write/index"), is(true)); + assertThat(matcher.matches("otherPattern", "indices:data/read/index"), is(false)); + } + + @Test + public void testGetIndexMatcherAndPermissionsWithScopedIndex() { + IndexPattern testIp = new IndexPattern("testPattern"); + testIp.addPerm(Set.of("indices:data/write/*")); + User user = new User("test-user"); + when(resolver.concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("testPattern"))).thenReturn( + new String[] { "testPattern" } + ); + ConfigModelV7.IndexMatcherAndPermissions matcher = testIp.getIndexMatcherAndPermissions(user, resolver, clusterService); + verify(resolver, times(1)).concreteIndexNames(any(), eq(IndicesOptions.lenientExpandOpen()), eq(true), eq("testPattern")); + verify(clusterService, times(1)).state(); + assertThat(matcher.matches("testPattern", "indices:data/write/index"), is(true)); + assertThat(matcher.matches("testPattern", "indices:data/read/index"), is(false)); + assertThat(matcher.matches("otherPattern", "indices:data/write/index"), is(false)); + assertThat(matcher.matches("otherPattern", "indices:data/read/index"), is(false)); + } + /** Ensure that concreteIndexNames sends correct parameters are sent to getResolvedIndexPattern */ @Test public void testConcreteIndexNamesOverload() {