diff --git a/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java b/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java index 1f60cf92d5..8618e27306 100644 --- a/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java +++ b/src/integrationTest/java/org/opensearch/security/privileges/ActionPrivilegesTest.java @@ -929,6 +929,37 @@ public void hasExplicitIndexPrivilege_errors() throws Exception { .startsWith("Exceptions encountered during privilege evaluation:\n" + "Error while evaluating role role_with_errors") ); } + + @Test + public void aliasesOnDataStreamBackingIndices() throws Exception { + // We create a meta data object with a data stream ds_a. Implicitly, the utility method will create + // the backing indices ".ds-ds_a-000001", ".ds-ds_a-000002" and ".ds-ds_a-000003". + // Additionally, we create an alias which only contains ".ds-ds_a-000001", but not the other backing indices. + Map metadata = dataStreams("ds_a").alias("alias_a").of(".ds-ds_a-000001").build().getIndicesLookup(); + SecurityDynamicConfiguration roles = SecurityDynamicConfiguration.fromYaml( + "role:\n" + + " index_permissions:\n" + + " - index_patterns: ['alias_a']\n" + + " allowed_actions: ['indices:data/write/index']", + CType.ROLES + ); + ActionPrivileges subject = new ActionPrivileges(roles, FlattenedActionGroups.EMPTY, () -> metadata, Settings.EMPTY); + subject.updateStatefulIndexPrivileges(metadata, 2); + + PrivilegesEvaluatorResponse resultForIndexCoveredByAlias = subject.hasIndexPrivilege( + ctx("role"), + Set.of("indices:data/write/index"), + IndexResolverReplacer.Resolved.ofIndex(".ds-ds_a-000001") + ); + assertThat(resultForIndexCoveredByAlias, isAllowed()); + + PrivilegesEvaluatorResponse resultForIndexNotCoveredByAlias = subject.hasIndexPrivilege( + ctx("role"), + Set.of("indices:data/write/index"), + IndexResolverReplacer.Resolved.ofIndex(".ds-ds_a-000002") + ); + assertThat(resultForIndexNotCoveredByAlias, isForbidden()); + } } /** diff --git a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java index eb560ed901..f095d31b2c 100644 --- a/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java +++ b/src/main/java/org/opensearch/security/privileges/ActionPrivileges.java @@ -1026,7 +1026,23 @@ static class StatefulIndexPrivileges { if (indicesEntry.getValue() instanceof IndexAbstraction.Alias) { // For aliases we additionally add the sub-indices to the privilege map for (IndexMetadata subIndex : indicesEntry.getValue().getIndices()) { - indexToRoles.get(subIndex.getIndex().getName()).add(roleName); + String subIndexName = subIndex.getIndex().getName(); + // We need to check whether the subIndex is part of the global indices + // metadata map because that map has been filtered by relevantOnly(). + // This method removes all closed indices and data stream backing indices + // because these indices get a separate treatment. However, these indices + // might still appear as member indices of aliases. Trying to add these + // to the SubSetBuilder indexToRoles would result in an IllegalArgumentException + // because the subIndex will not be part of the super set. + if (indices.containsKey(subIndexName)) { + indexToRoles.get(subIndexName).add(roleName); + } else { + log.debug( + "Ignoring member index {} of alias {}. This is usually the case because the index is closed or a data stream backing index.", + subIndexName, + indicesEntry.getKey() + ); + } } }