5050import java .util .function .Supplier ;
5151
5252import static java .util .Collections .unmodifiableMap ;
53- import static org .elasticsearch .xpack .core .security .authz .permission .IndicesPermission .Group .maybeAddFailureExclusions ;
5453
5554/**
5655 * A permission that is based on privileges for index related actions executed
@@ -94,7 +93,6 @@ public Builder addGroup(
9493 public IndicesPermission build () {
9594 return new IndicesPermission (restrictedIndices , groups .toArray (Group .EMPTY_ARRAY ));
9695 }
97-
9896 }
9997
10098 private IndicesPermission (RestrictedIndices restrictedIndices , Group [] groups ) {
@@ -114,12 +112,19 @@ private IndicesPermission(RestrictedIndices restrictedIndices, Group[] groups) {
114112 * @return A matcher that will match all non-restricted index names in the ordinaryIndices
115113 * collection and all index names in the restrictedIndices collection.
116114 */
117- private StringMatcher indexMatcher (Collection <String > ordinaryIndices , Collection <String > restrictedIndices ) {
115+ private StringMatcher indexMatcher (
116+ Collection <String > ordinaryIndices ,
117+ Collection <String > restrictedIndices ,
118+ boolean allowFailureStoreAccess
119+ ) {
118120 StringMatcher matcher ;
119121 if (ordinaryIndices .isEmpty ()) {
120122 matcher = StringMatcher .of (restrictedIndices );
121123 } else {
122124 matcher = StringMatcher .of (ordinaryIndices );
125+ if (false == allowFailureStoreAccess ) {
126+ matcher = matcher .and ("<not-failure-store>" , name -> name .endsWith ("::failure" ) == false );
127+ }
123128 if (this .restrictedIndices != null ) {
124129 matcher = matcher .and ("<not-restricted>" , name -> this .restrictedIndices .isRestricted (name ) == false );
125130 }
@@ -148,6 +153,7 @@ public boolean hasFieldOrDocumentLevelSecurity() {
148153
149154 private IsResourceAuthorizedPredicate buildIndexMatcherPredicateForAction (String action ) {
150155 final Set <String > ordinaryIndices = new HashSet <>();
156+ final Set <String > ordinaryIndicesWithFailureStoreAccess = new HashSet <>();
151157 final Set <String > restrictedIndices = new HashSet <>();
152158 final Set <String > grantMappingUpdatesOnIndices = new HashSet <>();
153159 final Set <String > grantMappingUpdatesOnRestrictedIndices = new HashSet <>();
@@ -157,7 +163,11 @@ private IsResourceAuthorizedPredicate buildIndexMatcherPredicateForAction(String
157163 if (group .allowRestrictedIndices ) {
158164 restrictedIndices .addAll (Arrays .asList (group .indices ()));
159165 } else {
160- ordinaryIndices .addAll (Arrays .asList (maybeAddFailureExclusions (group .indices ())));
166+ if (group .allowFailureStoreAccess ) {
167+ ordinaryIndicesWithFailureStoreAccess .addAll (Arrays .asList (group .indices ()));
168+ } else {
169+ ordinaryIndices .addAll (Arrays .asList (group .indices ()));
170+ }
161171 }
162172 } else if (isMappingUpdateAction && containsPrivilegeThatGrantsMappingUpdatesForBwc (group )) {
163173 // special BWC case for certain privileges: allow put mapping on indices and aliases (but not on data streams), even if
@@ -169,43 +179,60 @@ private IsResourceAuthorizedPredicate buildIndexMatcherPredicateForAction(String
169179 }
170180 }
171181 }
172- final StringMatcher nameMatcher = indexMatcher (ordinaryIndices , restrictedIndices );
173- final StringMatcher bwcSpecialCaseMatcher = indexMatcher (grantMappingUpdatesOnIndices , grantMappingUpdatesOnRestrictedIndices );
174- return new IsResourceAuthorizedPredicate (nameMatcher , bwcSpecialCaseMatcher );
182+ final StringMatcher nameMatcher = indexMatcher (ordinaryIndices , restrictedIndices , false );
183+ // TODO restricted indices
184+ final StringMatcher nameMatcherWithFailureAccess = indexMatcher (ordinaryIndicesWithFailureStoreAccess , restrictedIndices , true );
185+ final StringMatcher bwcSpecialCaseMatcher = indexMatcher (
186+ grantMappingUpdatesOnIndices ,
187+ grantMappingUpdatesOnRestrictedIndices ,
188+ false
189+ );
190+ return new IsResourceAuthorizedPredicate (nameMatcher , nameMatcherWithFailureAccess , bwcSpecialCaseMatcher );
175191 }
176192
177193 /**
178194 * This encapsulates the authorization test for resources.
179195 * There is an additional test for resources that are missing or that are not a datastream or a backing index.
180196 */
181- public static class IsResourceAuthorizedPredicate implements BiPredicate < String , IndexAbstraction > {
197+ public static class IsResourceAuthorizedPredicate {
182198
183199 private final BiPredicate <String , IndexAbstraction > biPredicate ;
184- private final StringMatcher resourceNameMatcher ;
200+ private final BiPredicate < String , IndexAbstraction > failureAccessBiPredicate ;
185201
186202 // public for tests
187- public IsResourceAuthorizedPredicate (StringMatcher resourceNameMatcher , StringMatcher additionalNonDatastreamNameMatcher ) {
203+ public IsResourceAuthorizedPredicate (
204+ StringMatcher resourceNameMatcher ,
205+ StringMatcher resourceNameWithFailureAccessMatcher ,
206+ StringMatcher additionalNonDatastreamNameMatcher
207+ ) {
188208 this ((String name , @ Nullable IndexAbstraction indexAbstraction ) -> {
189209 assert indexAbstraction == null || name .equals (indexAbstraction .getName ());
190210 return resourceNameMatcher .test (name )
191211 || (isPartOfDatastream (indexAbstraction ) == false && additionalNonDatastreamNameMatcher .test (name ));
192- }, resourceNameMatcher );
193-
212+ }, (String name , @ Nullable IndexAbstraction indexAbstraction ) -> {
213+ assert indexAbstraction == null || name .equals (indexAbstraction .getName ());
214+ return resourceNameWithFailureAccessMatcher .test (name );
215+ });
194216 }
195217
196- private IsResourceAuthorizedPredicate (BiPredicate <String , IndexAbstraction > biPredicate , StringMatcher resourceNameMatcher ) {
218+ private IsResourceAuthorizedPredicate (
219+ BiPredicate <String , IndexAbstraction > biPredicate ,
220+ BiPredicate <String , IndexAbstraction > failureAccessBiPredicate
221+ ) {
197222 this .biPredicate = biPredicate ;
198- this .resourceNameMatcher = resourceNameMatcher ;
223+ this .failureAccessBiPredicate = failureAccessBiPredicate ;
199224 }
200225
201226 /**
202- * Given another {@link IsResourceAuthorizedPredicate} instance in {@param other},
203- * return a new {@link IsResourceAuthorizedPredicate} instance that is equivalent to the conjunction of
204- * authorization tests of that other instance and this one.
205- */
206- @ Override
207- public final IsResourceAuthorizedPredicate and (BiPredicate <? super String , ? super IndexAbstraction > other ) {
208- return new IsResourceAuthorizedPredicate (this .biPredicate .and (other ), this .resourceNameMatcher );
227+ * Given another {@link IsResourceAuthorizedPredicate} instance in {@param other},
228+ * return a new {@link IsResourceAuthorizedPredicate} instance that is equivalent to the conjunction of
229+ * authorization tests of that other instance and this one.
230+ */
231+ public final IsResourceAuthorizedPredicate and (IsResourceAuthorizedPredicate other ) {
232+ return new IsResourceAuthorizedPredicate (
233+ this .biPredicate .and (other .biPredicate ),
234+ this .failureAccessBiPredicate .and (other .failureAccessBiPredicate )
235+ );
209236 }
210237
211238 /**
@@ -217,26 +244,28 @@ public final boolean test(IndexAbstraction indexAbstraction) {
217244 return test (indexAbstraction .getName (), indexAbstraction );
218245 }
219246
220- /**
221- * Similar to {@link #test(IndexAbstraction)} but for the failures component of a data stream. Adds ::failures to name of the
222- * index abstraction to test if ::failures are allowed
223- */
224- public final boolean testDataStreamForFailureAccess (IndexAbstraction indexAbstraction ) {
225- assert indexAbstraction != null && indexAbstraction .getType () == IndexAbstraction .Type .DATA_STREAM ;
226- return resourceNameMatcher .test (
227- IndexNameExpressionResolver .combineSelector (indexAbstraction .getName (), IndexComponentSelector .FAILURES )
228- );
229- }
230-
231247 /**
232248 * Verifies if access is authorized to the resource with the given {@param name}.
233249 * The {@param indexAbstraction}, which is the resource to be accessed, must be supplied if the resource exists or be {@code null}
234250 * if it doesn't.
235251 * Returns {@code true} if access to the given resource is authorized or {@code false} otherwise.
236252 */
237- @ Override
238253 public boolean test (String name , @ Nullable IndexAbstraction indexAbstraction ) {
239- return biPredicate .test (name , indexAbstraction );
254+ // TODO clean up
255+ return biPredicate .test (name , indexAbstraction ) || failureAccessBiPredicate .test (name , indexAbstraction );
256+ }
257+
258+ /**
259+ * Similar to {@link #test(IndexAbstraction)} but for the failures component of a data stream. Adds ::failures to name of the
260+ * index abstraction to test if ::failures are allowed
261+ */
262+ public final boolean testDataStreamForFailureAccess (IndexAbstraction indexAbstraction ) {
263+ // TODO clean up
264+ assert indexAbstraction != null && indexAbstraction .getType () == IndexAbstraction .Type .DATA_STREAM ;
265+ return failureAccessBiPredicate .test (
266+ IndexNameExpressionResolver .combineSelector (indexAbstraction .getName (), IndexComponentSelector .FAILURES ),
267+ null
268+ );
240269 }
241270
242271 private static boolean isPartOfDatastream (IndexAbstraction indexAbstraction ) {
@@ -834,7 +863,7 @@ public static class Group {
834863 // users. Setting this flag true eliminates the special status for the purpose of this permission - restricted indices still have
835864 // to be covered by the "indices"
836865 private final boolean allowRestrictedIndices ;
837- private final boolean allowFailureStoreAccess ;
866+ public final boolean allowFailureStoreAccess ;
838867
839868 public Group (
840869 IndexPrivilege privilege ,
0 commit comments