Skip to content

Commit 87ef806

Browse files
committed
Fix predicate
1 parent 4c51737 commit 87ef806

File tree

4 files changed

+68
-70
lines changed

4 files changed

+68
-70
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
import java.util.function.Supplier;
5151

5252
import 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,

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermissionTests.java

Lines changed: 0 additions & 35 deletions
This file was deleted.

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ public static void buildRoleFromDescriptors(
504504
runAs.addAll(Arrays.asList(descriptor.getRunAs()));
505505
}
506506

507+
// TODO need to handle collation to not collate groups that don't match failure store access
507508
MergeableIndicesPrivilege.collatePrivilegesByIndices(descriptor.getIndicesPrivileges(), true, restrictedIndicesPrivilegesMap);
508509
MergeableIndicesPrivilege.collatePrivilegesByIndices(descriptor.getIndicesPrivileges(), false, indicesPrivilegesMap);
509510

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ public void testResourceAuthorizedPredicateForDatastreams() {
700700
);
701701
IndicesPermission.IsResourceAuthorizedPredicate predicate = new IndicesPermission.IsResourceAuthorizedPredicate(
702702
StringMatcher.of("other"),
703+
StringMatcher.of(),
703704
StringMatcher.of(dataStreamName, backingIndex.getName(), concreteIndex.getName(), alias.getName())
704705
);
705706
assertThat(predicate.test(dataStream), is(false));
@@ -715,10 +716,12 @@ public void testResourceAuthorizedPredicateForDatastreams() {
715716
public void testResourceAuthorizedPredicateAnd() {
716717
IndicesPermission.IsResourceAuthorizedPredicate predicate1 = new IndicesPermission.IsResourceAuthorizedPredicate(
717718
StringMatcher.of("c", "a"),
719+
StringMatcher.of(),
718720
StringMatcher.of("b", "d")
719721
);
720722
IndicesPermission.IsResourceAuthorizedPredicate predicate2 = new IndicesPermission.IsResourceAuthorizedPredicate(
721723
StringMatcher.of("c", "b"),
724+
StringMatcher.of(),
722725
StringMatcher.of("a", "d")
723726
);
724727
Metadata.Builder mb = Metadata.builder(

0 commit comments

Comments
 (0)