@@ -317,36 +317,58 @@ public boolean checkResourcePrivileges(
317317 @ Nullable ResourcePrivilegesMap .Builder resourcePrivilegesMapBuilder
318318 ) {
319319 boolean allMatch = true ;
320- Map <Automaton , Automaton > indexGroupAutomatons = indexGroupAutomatons (
321- combineIndexGroups && checkForIndexPatterns .stream ().anyMatch (Automatons ::isLuceneRegex )
320+ Map <Automaton , Automaton > indexGroupAutomatonsForDataSelector = indexGroupAutomatons (
321+ combineIndexGroups && checkForIndexPatterns .stream ().anyMatch (Automatons ::isLuceneRegex ),
322+ IndexComponentSelector .DATA
322323 );
324+ // optimization: if there are no failures selector privileges in the set of privileges to check, we can skip building
325+ // the automaton map
326+ final boolean containsPrivilegesForFailuresSelector = containsPrivilegesForFailuresSelector (checkForPrivileges );
327+ Map <Automaton , Automaton > indexGroupAutomatonsForFailuresSelector = false == containsPrivilegesForFailuresSelector
328+ ? Map .of ()
329+ : indexGroupAutomatons (
330+ combineIndexGroups && checkForIndexPatterns .stream ().anyMatch (Automatons ::isLuceneRegex ),
331+ IndexComponentSelector .FAILURES
332+ );
323333 for (String forIndexPattern : checkForIndexPatterns ) {
324- IndexNameExpressionResolver .assertExpressionHasNullOrDataSelector (forIndexPattern );
325334 Automaton checkIndexAutomaton = Automatons .patterns (forIndexPattern );
326335 if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex (forIndexPattern )) {
327336 checkIndexAutomaton = Automatons .minusAndMinimize (checkIndexAutomaton , restrictedIndices .getAutomaton ());
328337 }
329338 if (false == Operations .isEmpty (checkIndexAutomaton )) {
330- Automaton allowedIndexPrivilegesAutomaton = null ;
331- for (var indexAndPrivilegeAutomaton : indexGroupAutomatons .entrySet ()) {
332- if (Automatons .subsetOf (checkIndexAutomaton , indexAndPrivilegeAutomaton .getValue ())) {
333- if (allowedIndexPrivilegesAutomaton != null ) {
334- allowedIndexPrivilegesAutomaton = Automatons .unionAndMinimize (
335- Arrays .asList (allowedIndexPrivilegesAutomaton , indexAndPrivilegeAutomaton .getKey ())
336- );
337- } else {
338- allowedIndexPrivilegesAutomaton = indexAndPrivilegeAutomaton .getKey ();
339- }
340- }
341- }
339+ Automaton allowedPrivilegesAutomatonForDataSelector = getIndexPrivilegesAutomaton (
340+ indexGroupAutomatonsForDataSelector ,
341+ checkIndexAutomaton
342+ );
343+ Automaton allowedPrivilegesAutomatonForFailuresSelector = getIndexPrivilegesAutomaton (
344+ indexGroupAutomatonsForFailuresSelector ,
345+ checkIndexAutomaton
346+ );
342347 for (String privilege : checkForPrivileges ) {
343- IndexPrivilege indexPrivilege = IndexPrivilege .get (privilege );
344- if (allowedIndexPrivilegesAutomaton != null
345- && Automatons .subsetOf (indexPrivilege .getAutomaton (), allowedIndexPrivilegesAutomaton )) {
348+ final IndexPrivilege indexPrivilege = IndexPrivilege .get (privilege );
349+ final boolean checkWithDataSelector = indexPrivilege .getSelectorPredicate ().test (IndexComponentSelector .DATA );
350+ final boolean checkWithFailuresSelector = indexPrivilege .getSelectorPredicate ().test (IndexComponentSelector .FAILURES );
351+ assert checkWithDataSelector || checkWithFailuresSelector
352+ : "index privilege must map to at least one of [data, failures] selectors" ;
353+ assert containsPrivilegesForFailuresSelector
354+ || indexPrivilege .getSelectorPredicate () != IndexComponentSelectorPredicate .FAILURES
355+ : "no failures access privileges should be present in the set of privileges to check" ;
356+ final Automaton automatonToCheck = indexPrivilege .getAutomaton ();
357+ if (checkWithDataSelector
358+ && allowedPrivilegesAutomatonForDataSelector != null
359+ && Automatons .subsetOf (automatonToCheck , allowedPrivilegesAutomatonForDataSelector )) {
346360 if (resourcePrivilegesMapBuilder != null ) {
347361 resourcePrivilegesMapBuilder .addResourcePrivilege (forIndexPattern , privilege , Boolean .TRUE );
348362 }
349- } else {
363+ } else if (checkWithFailuresSelector
364+ && allowedPrivilegesAutomatonForFailuresSelector != null
365+ && Automatons .subsetOf (automatonToCheck , allowedPrivilegesAutomatonForFailuresSelector )) {
366+ if (resourcePrivilegesMapBuilder != null ) {
367+ resourcePrivilegesMapBuilder .addResourcePrivilege (forIndexPattern , privilege , Boolean .TRUE );
368+ }
369+ }
370+ // comment to force correct else-block indent
371+ else {
350372 if (resourcePrivilegesMapBuilder != null ) {
351373 resourcePrivilegesMapBuilder .addResourcePrivilege (forIndexPattern , privilege , Boolean .FALSE );
352374 allMatch = false ;
@@ -806,13 +828,11 @@ private static boolean containsPrivilegeThatGrantsMappingUpdatesForBwc(Group gro
806828 *
807829 * @return a map of all index and privilege pattern automatons
808830 */
809- private Map <Automaton , Automaton > indexGroupAutomatons (boolean combine ) {
831+ private Map <Automaton , Automaton > indexGroupAutomatons (boolean combine , IndexComponentSelector selector ) {
810832 // Map of privilege automaton object references (cached by IndexPrivilege::CACHE)
811833 Map <Automaton , Automaton > allAutomatons = new HashMap <>();
812834 for (Group group : groups ) {
813- // TODO support failure store privileges
814- // we also check that the group does not support data access to avoid erroneously filtering out `all` privilege groups
815- if (group .checkSelector (IndexComponentSelector .FAILURES ) && false == group .checkSelector (IndexComponentSelector .DATA )) {
835+ if (false == group .checkSelector (selector )) {
816836 continue ;
817837 }
818838 Automaton indexAutomaton = group .getIndexMatcherAutomaton ();
@@ -845,6 +865,41 @@ private Map<Automaton, Automaton> indexGroupAutomatons(boolean combine) {
845865 return allAutomatons ;
846866 }
847867
868+ private static boolean containsPrivilegesForFailuresSelector (Set <String > checkForPrivileges ) {
869+ for (String privilege : checkForPrivileges ) {
870+ // use `getNamedOrNull` since only a named privilege can be a failures-only privilege (raw action names are always data access)
871+ IndexPrivilege named = IndexPrivilege .getNamedOrNull (privilege );
872+ // note: we are looking for failures-only privileges here, not `all` which does cover failures but is not a failures-only
873+ // privilege
874+ if (named != null && named .getSelectorPredicate () == IndexComponentSelectorPredicate .FAILURES ) {
875+ return true ;
876+ }
877+ }
878+ return false ;
879+ }
880+
881+ @ Nullable
882+ private static Automaton getIndexPrivilegesAutomaton (Map <Automaton , Automaton > indexGroupAutomatons , Automaton checkIndexAutomaton ) {
883+ if (indexGroupAutomatons .isEmpty ()) {
884+ return null ;
885+ }
886+ Automaton allowedPrivilegesAutomaton = null ;
887+ for (Map .Entry <Automaton , Automaton > indexAndPrivilegeAutomaton : indexGroupAutomatons .entrySet ()) {
888+ Automaton indexNameAutomaton = indexAndPrivilegeAutomaton .getValue ();
889+ if (Automatons .subsetOf (checkIndexAutomaton , indexNameAutomaton )) {
890+ Automaton privilegesAutomaton = indexAndPrivilegeAutomaton .getKey ();
891+ if (allowedPrivilegesAutomaton != null ) {
892+ allowedPrivilegesAutomaton = Automatons .unionAndMinimize (
893+ Arrays .asList (allowedPrivilegesAutomaton , privilegesAutomaton )
894+ );
895+ } else {
896+ allowedPrivilegesAutomaton = privilegesAutomaton ;
897+ }
898+ }
899+ }
900+ return allowedPrivilegesAutomaton ;
901+ }
902+
848903 public static class Group {
849904 public static final Group [] EMPTY_ARRAY = new Group [0 ];
850905
0 commit comments