@@ -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