Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,27 @@ public ActionRequestValidationException validate(ActionRequestValidationExceptio
if (index == null) {
validationException = addValidationError("indexPrivileges must not be null", validationException);
} else {
for (int i = 0; i < index.length; i++) {
BytesReference query = index[i].getQuery();
for (RoleDescriptor.IndicesPrivileges indicesPrivileges : index) {
BytesReference query = indicesPrivileges.getQuery();
if (query != null) {
validationException = addValidationError(
"may only check index privileges without any DLS query [" + query.utf8ToString() + "]",
validationException
);
}
if (DataStream.isFailureStoreFeatureFlagEnabled()) {
// best effort prevent users from attempting to use selectors in privilege check
for (String indexPattern : indicesPrivileges.getIndices()) {
if (IndexNameExpressionResolver.hasSelector(indexPattern, IndexComponentSelector.FAILURES)
|| IndexNameExpressionResolver.hasSelector(indexPattern, IndexComponentSelector.DATA)) {
validationException = addValidationError(
"may only check index privileges without selectors in index patterns [" + indexPattern + "]",
validationException
);
break;
}
}
}
}
}
if (application == null) {
Expand All @@ -368,31 +381,6 @@ public ActionRequestValidationException validate(ActionRequestValidationExceptio
&& application.length == 0) {
validationException = addValidationError("must specify at least one privilege", validationException);
}
if (index != null) {
// no need to validate failure-store related constraints if it's not enabled
if (DataStream.isFailureStoreFeatureFlagEnabled()) {
for (RoleDescriptor.IndicesPrivileges indexPrivilege : index) {
if (indexPrivilege.getIndices() != null
&& Arrays.stream(indexPrivilege.getIndices())
// best effort prevent users from attempting to check failure selectors
.anyMatch(idx -> IndexNameExpressionResolver.hasSelector(idx, IndexComponentSelector.FAILURES))) {
validationException = addValidationError(
// TODO adjust message once HasPrivileges check supports checking failure store privileges
"failures selector is not supported in index patterns",
validationException
);
}
if (indexPrivilege.getPrivileges() != null
&& Arrays.stream(indexPrivilege.getPrivileges())
.anyMatch(p -> "read_failure_store".equals(p) || "manage_failure_store".equals(p))) {
validationException = addValidationError(
"checking failure store privileges is not supported",
validationException
);
}
}
}
}
return validationException;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,36 +317,58 @@ public boolean checkResourcePrivileges(
@Nullable ResourcePrivilegesMap.Builder resourcePrivilegesMapBuilder
) {
boolean allMatch = true;
Map<Automaton, Automaton> indexGroupAutomatons = indexGroupAutomatons(
combineIndexGroups && checkForIndexPatterns.stream().anyMatch(Automatons::isLuceneRegex)
Map<Automaton, Automaton> indexGroupAutomatonsForDataSelector = indexGroupAutomatons(
combineIndexGroups && checkForIndexPatterns.stream().anyMatch(Automatons::isLuceneRegex),
IndexComponentSelector.DATA
);
// optimization: if there are no failures selector privileges in the set of privileges to check, we can skip building
// the automaton map
final boolean containsPrivilegesForFailuresSelector = containsPrivilegesForFailuresSelector(checkForPrivileges);
Map<Automaton, Automaton> indexGroupAutomatonsForFailuresSelector = false == containsPrivilegesForFailuresSelector
? Map.of()
: indexGroupAutomatons(
combineIndexGroups && checkForIndexPatterns.stream().anyMatch(Automatons::isLuceneRegex),
IndexComponentSelector.FAILURES
);
for (String forIndexPattern : checkForIndexPatterns) {
IndexNameExpressionResolver.assertExpressionHasNullOrDataSelector(forIndexPattern);
Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern);
if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) {
checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, restrictedIndices.getAutomaton());
}
if (false == Operations.isEmpty(checkIndexAutomaton)) {
Automaton allowedIndexPrivilegesAutomaton = null;
for (var indexAndPrivilegeAutomaton : indexGroupAutomatons.entrySet()) {
if (Operations.subsetOf(checkIndexAutomaton, indexAndPrivilegeAutomaton.getValue())) {
if (allowedIndexPrivilegesAutomaton != null) {
allowedIndexPrivilegesAutomaton = Automatons.unionAndMinimize(
Arrays.asList(allowedIndexPrivilegesAutomaton, indexAndPrivilegeAutomaton.getKey())
);
} else {
allowedIndexPrivilegesAutomaton = indexAndPrivilegeAutomaton.getKey();
}
}
}
Automaton allowedPrivilegesAutomatonForDataSelector = getIndexPrivilegesAutomaton(
indexGroupAutomatonsForDataSelector,
checkIndexAutomaton
);
Automaton allowedPrivilegesAutomatonForFailuresSelector = getIndexPrivilegesAutomaton(
indexGroupAutomatonsForFailuresSelector,
checkIndexAutomaton
);
for (String privilege : checkForPrivileges) {
IndexPrivilege indexPrivilege = IndexPrivilege.get(privilege);
if (allowedIndexPrivilegesAutomaton != null
&& Operations.subsetOf(indexPrivilege.getAutomaton(), allowedIndexPrivilegesAutomaton)) {
final IndexPrivilege indexPrivilege = IndexPrivilege.get(privilege);
final boolean checkWithDataSelector = indexPrivilege.getSelectorPredicate().test(IndexComponentSelector.DATA);
final boolean checkWithFailuresSelector = indexPrivilege.getSelectorPredicate().test(IndexComponentSelector.FAILURES);
assert checkWithDataSelector || checkWithFailuresSelector
: "index privilege must map to at least one of [data, failures] selectors";
assert containsPrivilegesForFailuresSelector
|| indexPrivilege.getSelectorPredicate() != IndexComponentSelectorPredicate.FAILURES
: "no failures access privileges should be present in the set of privileges to check";
final Automaton automatonToCheck = indexPrivilege.getAutomaton();
if (checkWithDataSelector
&& allowedPrivilegesAutomatonForDataSelector != null
&& Operations.subsetOf(automatonToCheck, allowedPrivilegesAutomatonForDataSelector)) {
if (resourcePrivilegesMapBuilder != null) {
resourcePrivilegesMapBuilder.addResourcePrivilege(forIndexPattern, privilege, Boolean.TRUE);
}
} else {
} else if (checkWithFailuresSelector
&& allowedPrivilegesAutomatonForFailuresSelector != null
&& Operations.subsetOf(automatonToCheck, allowedPrivilegesAutomatonForFailuresSelector)) {
if (resourcePrivilegesMapBuilder != null) {
resourcePrivilegesMapBuilder.addResourcePrivilege(forIndexPattern, privilege, Boolean.TRUE);
}
}
// comment to force correct else-block indent
else {
if (resourcePrivilegesMapBuilder != null) {
resourcePrivilegesMapBuilder.addResourcePrivilege(forIndexPattern, privilege, Boolean.FALSE);
allMatch = false;
Expand Down Expand Up @@ -806,13 +828,11 @@ private static boolean containsPrivilegeThatGrantsMappingUpdatesForBwc(Group gro
*
* @return a map of all index and privilege pattern automatons
*/
private Map<Automaton, Automaton> indexGroupAutomatons(boolean combine) {
private Map<Automaton, Automaton> indexGroupAutomatons(boolean combine, IndexComponentSelector selector) {
// Map of privilege automaton object references (cached by IndexPrivilege::CACHE)
Map<Automaton, Automaton> allAutomatons = new HashMap<>();
for (Group group : groups) {
// TODO support failure store privileges
// we also check that the group does not support data access to avoid erroneously filtering out `all` privilege groups
if (group.checkSelector(IndexComponentSelector.FAILURES) && false == group.checkSelector(IndexComponentSelector.DATA)) {
if (false == group.checkSelector(selector)) {
continue;
}
Automaton indexAutomaton = group.getIndexMatcherAutomaton();
Expand Down Expand Up @@ -845,6 +865,41 @@ private Map<Automaton, Automaton> indexGroupAutomatons(boolean combine) {
return allAutomatons;
}

private static boolean containsPrivilegesForFailuresSelector(Set<String> checkForPrivileges) {
for (String privilege : checkForPrivileges) {
// use `getNamedOrNull` since only a named privilege can be a failures-only privilege (raw action names are always data access)
IndexPrivilege named = IndexPrivilege.getNamedOrNull(privilege);
// note: we are looking for failures-only privileges here, not `all` which does cover failures but is not a failures-only
// privilege
if (named != null && named.getSelectorPredicate() == IndexComponentSelectorPredicate.FAILURES) {
return true;
}
}
return false;
}

@Nullable
private static Automaton getIndexPrivilegesAutomaton(Map<Automaton, Automaton> indexGroupAutomatons, Automaton checkIndexAutomaton) {
if (indexGroupAutomatons.isEmpty()) {
return null;
}
Automaton allowedPrivilegesAutomaton = null;
for (Map.Entry<Automaton, Automaton> indexAndPrivilegeAutomaton : indexGroupAutomatons.entrySet()) {
Automaton indexNameAutomaton = indexAndPrivilegeAutomaton.getValue();
if (Operations.subsetOf(checkIndexAutomaton, indexNameAutomaton)) {
Automaton privilegesAutomaton = indexAndPrivilegeAutomaton.getKey();
if (allowedPrivilegesAutomaton != null) {
allowedPrivilegesAutomaton = Automatons.unionAndMinimize(
Arrays.asList(allowedPrivilegesAutomaton, privilegesAutomaton)
);
} else {
allowedPrivilegesAutomaton = privilegesAutomaton;
}
}
}
return allowedPrivilegesAutomaton;
}

public static class Group {
public static final Group[] EMPTY_ARRAY = new Group[0];

Expand Down
Loading