Skip to content

Commit 95af75e

Browse files
committed
Moar
1 parent 4d9cf2f commit 95af75e

File tree

6 files changed

+71
-24
lines changed

6 files changed

+71
-24
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,11 @@ interface AuthorizedIndices {
295295
/**
296296
* Checks if an index-like resource name is authorized, for an action by a user. The resource might or might not exist.
297297
*/
298-
boolean check(String name);
298+
default boolean check(String name) {
299+
return check(name, null);
300+
}
301+
302+
boolean check(String name, String selector);
299303
}
300304

301305
/**

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

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ public boolean hasFieldOrDocumentLevelSecurity() {
145145
}
146146

147147
private IsResourceAuthorizedPredicate buildIndexMatcherPredicateForAction(String action) {
148-
final Set<String> ordinaryIndices = new HashSet<>();
148+
final Set<String> dataAccessOrdinaryIndices = new HashSet<>();
149+
final Set<String> failureAccessOrdinaryIndices = new HashSet<>();
149150
final Set<String> restrictedIndices = new HashSet<>();
150151
final Set<String> grantMappingUpdatesOnIndices = new HashSet<>();
151152
final Set<String> grantMappingUpdatesOnRestrictedIndices = new HashSet<>();
@@ -155,7 +156,11 @@ private IsResourceAuthorizedPredicate buildIndexMatcherPredicateForAction(String
155156
if (group.allowRestrictedIndices) {
156157
restrictedIndices.addAll(Arrays.asList(group.indices()));
157158
} else {
158-
ordinaryIndices.addAll(Arrays.asList(group.indices()));
159+
if (group.failureStoreOnly) {
160+
failureAccessOrdinaryIndices.addAll(Arrays.asList(group.indices()));
161+
} else {
162+
dataAccessOrdinaryIndices.addAll(Arrays.asList(group.indices()));
163+
}
159164
}
160165
} else if (isMappingUpdateAction && containsPrivilegeThatGrantsMappingUpdatesForBwc(group)) {
161166
// special BWC case for certain privileges: allow put mapping on indices and aliases (but not on data streams), even if
@@ -167,40 +172,56 @@ private IsResourceAuthorizedPredicate buildIndexMatcherPredicateForAction(String
167172
}
168173
}
169174
}
170-
final StringMatcher nameMatcher = indexMatcher(ordinaryIndices, restrictedIndices);
175+
final StringMatcher dataAccessNameMatcher = indexMatcher(dataAccessOrdinaryIndices, restrictedIndices);
176+
// TODO handle restricted indices for failure access
177+
final StringMatcher failureAccessNameMatcher = indexMatcher(failureAccessOrdinaryIndices, Set.of());
171178
final StringMatcher bwcSpecialCaseMatcher = indexMatcher(grantMappingUpdatesOnIndices, grantMappingUpdatesOnRestrictedIndices);
172-
return new IsResourceAuthorizedPredicate(nameMatcher, bwcSpecialCaseMatcher);
179+
return new IsResourceAuthorizedPredicate(dataAccessNameMatcher, failureAccessNameMatcher, bwcSpecialCaseMatcher);
173180
}
174181

175182
/**
176183
* This encapsulates the authorization test for resources.
177184
* There is an additional test for resources that are missing or that are not a datastream or a backing index.
178185
*/
179-
public static class IsResourceAuthorizedPredicate implements BiPredicate<String, IndexAbstraction> {
186+
public static class IsResourceAuthorizedPredicate {
180187

181188
private final BiPredicate<String, IndexAbstraction> biPredicate;
189+
private final BiPredicate<String, IndexAbstraction> failureAccessBiPredicate;
182190

183191
// public for tests
184-
public IsResourceAuthorizedPredicate(StringMatcher resourceNameMatcher, StringMatcher additionalNonDatastreamNameMatcher) {
192+
public IsResourceAuthorizedPredicate(
193+
StringMatcher resourceNameMatcher,
194+
StringMatcher failureAccessNameMatcher,
195+
StringMatcher additionalNonDatastreamNameMatcher
196+
) {
185197
this((String name, @Nullable IndexAbstraction indexAbstraction) -> {
186198
assert indexAbstraction == null || name.equals(indexAbstraction.getName());
187199
return resourceNameMatcher.test(name)
188200
|| (isPartOfDatastream(indexAbstraction) == false && additionalNonDatastreamNameMatcher.test(name));
201+
}, (String name, @Nullable IndexAbstraction indexAbstraction) -> {
202+
assert indexAbstraction == null || name.equals(indexAbstraction.getName());
203+
return failureAccessNameMatcher.test(name);
189204
});
190205
}
191206

192-
private IsResourceAuthorizedPredicate(BiPredicate<String, IndexAbstraction> biPredicate) {
207+
private IsResourceAuthorizedPredicate(
208+
BiPredicate<String, IndexAbstraction> biPredicate,
209+
BiPredicate<String, IndexAbstraction> failureAccessBiPredicate
210+
) {
193211
this.biPredicate = biPredicate;
212+
this.failureAccessBiPredicate = failureAccessBiPredicate;
194213
}
195214

196215
/**
197216
* Given another {@link IsResourceAuthorizedPredicate} instance in {@param other},
198217
* return a new {@link IsResourceAuthorizedPredicate} instance that is equivalent to the conjunction of
199218
* authorization tests of that other instance and this one.
200219
*/
201-
@Override
202-
public final IsResourceAuthorizedPredicate and(BiPredicate<? super String, ? super IndexAbstraction> other) {
203-
return new IsResourceAuthorizedPredicate(this.biPredicate.and(other));
220+
public final IsResourceAuthorizedPredicate and(IsResourceAuthorizedPredicate other) {
221+
return new IsResourceAuthorizedPredicate(
222+
this.biPredicate.and(other.biPredicate),
223+
this.failureAccessBiPredicate.and(other.failureAccessBiPredicate)
224+
);
204225
}
205226

206227
/**
@@ -209,7 +230,11 @@ public final IsResourceAuthorizedPredicate and(BiPredicate<? super String, ? sup
209230
* Returns {@code true} if access to the given resource is authorized or {@code false} otherwise.
210231
*/
211232
public final boolean test(IndexAbstraction indexAbstraction) {
212-
return test(indexAbstraction.getName(), indexAbstraction);
233+
return test(indexAbstraction.getName(), indexAbstraction, null);
234+
}
235+
236+
public final boolean test(IndexAbstraction indexAbstraction, @Nullable String selector) {
237+
return test(indexAbstraction.getName(), indexAbstraction, selector);
213238
}
214239

215240
/**
@@ -218,9 +243,19 @@ public final boolean test(IndexAbstraction indexAbstraction) {
218243
* if it doesn't.
219244
* Returns {@code true} if access to the given resource is authorized or {@code false} otherwise.
220245
*/
221-
@Override
222246
public boolean test(String name, @Nullable IndexAbstraction indexAbstraction) {
223-
return biPredicate.test(name, indexAbstraction);
247+
return test(name, indexAbstraction, null);
248+
}
249+
250+
public boolean test(String name, @Nullable IndexAbstraction indexAbstraction, @Nullable String selector) {
251+
if (selector == null || IndexComponentSelector.DATA.getKey().equals(selector)) {
252+
return biPredicate.test(name, indexAbstraction);
253+
} else if (IndexComponentSelector.FAILURES.getKey().equals(selector)) {
254+
return failureAccessBiPredicate.test(name, indexAbstraction);
255+
} else {
256+
assert selector.equals(IndexComponentSelector.ALL_APPLICABLE.getKey()) : "unexpected selector [" + selector + "]";
257+
return biPredicate.test(name, indexAbstraction) && failureAccessBiPredicate.test(name, indexAbstraction);
258+
}
224259
}
225260

226261
private static boolean isPartOfDatastream(IndexAbstraction indexAbstraction) {
@@ -354,7 +389,7 @@ public Automaton allowedActionsMatcher(String index) {
354389
* Represents the set of data required about an IndexAbstraction (index/alias/datastream) in order to perform authorization on that
355390
* object (including setting up the necessary data structures for Field and Document Level Security).
356391
*/
357-
private static class IndexResource {
392+
public static class IndexResource {
358393
/**
359394
* The name of the IndexAbstraction on which authorization is being performed
360395
*/

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ static SimpleRole buildFromRoleDescriptor(
433433
);
434434

435435
for (RoleDescriptor.IndicesPrivileges indexPrivilege : roleDescriptor.getIndicesPrivileges()) {
436+
// TODO properly handle this
436437
if (Arrays.asList(indexPrivilege.getIndices()).contains("*")) {
437438
builder.add(
438439
fieldPermissionsCache.getFieldPermissions(
@@ -441,7 +442,6 @@ static SimpleRole buildFromRoleDescriptor(
441442
indexPrivilege.getQuery() == null ? null : Collections.singleton(indexPrivilege.getQuery()),
442443
IndexPrivilege.get(Sets.newHashSet(indexPrivilege.getPrivileges())),
443444
indexPrivilege.allowRestrictedIndices(),
444-
// TODO properly handle this
445445
true,
446446
indexPrivilege.getIndices()
447447
);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/StringMatcher.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public static StringMatcher of(Iterable<String> patterns) {
5050
return StringMatcher.builder().includeAll(patterns).build();
5151
}
5252

53+
public static StringMatcher never() {
54+
return MATCH_NOTHING;
55+
}
56+
5357
public static StringMatcher of(String... patterns) {
5458
return StringMatcher.builder().includeAll(patterns).build();
5559
}

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import java.util.Objects;
107107
import java.util.Set;
108108
import java.util.TreeSet;
109+
import java.util.function.BiPredicate;
109110
import java.util.function.Consumer;
110111
import java.util.function.Predicate;
111112
import java.util.function.Supplier;
@@ -868,7 +869,7 @@ static AuthorizedIndices resolveAuthorizedIndicesFromRole(
868869
// do not include data streams for actions that do not operate on data streams
869870
TransportRequest request = requestInfo.getRequest();
870871
final boolean includeDataStreams = (request instanceof IndicesRequest) && ((IndicesRequest) request).includeDataStreams();
871-
872+
// TODO need a function not a supplier
872873
return new AuthorizedIndices(() -> {
873874
Consumer<Collection<String>> timeChecker = timerSupplier.get();
874875
Set<String> indicesAndAliases = new HashSet<>();
@@ -898,18 +899,18 @@ static AuthorizedIndices resolveAuthorizedIndicesFromRole(
898899
}
899900
timeChecker.accept(indicesAndAliases);
900901
return indicesAndAliases;
901-
}, name -> {
902+
}, (name, selector) -> {
902903
final IndexAbstraction indexAbstraction = lookup.get(name);
903904
if (indexAbstraction == null) {
904905
// test access (by name) to a resource that does not currently exist
905906
// the action handler must handle the case of accessing resources that do not exist
906-
return predicate.test(name, null);
907+
return predicate.test(name, null, selector);
907908
} else {
908909
// We check the parent data stream first if there is one. For testing requested indices, this is most likely
909910
// more efficient than checking the index name first because we recommend grant privileges over data stream
910911
// instead of backing indices.
911912
return (indexAbstraction.getParentDataStream() != null && predicate.test(indexAbstraction.getParentDataStream()))
912-
|| predicate.test(indexAbstraction);
913+
|| predicate.test(indexAbstraction, selector);
913914
}
914915
});
915916
}
@@ -1037,9 +1038,9 @@ private static boolean isAsyncRelatedAction(String action) {
10371038
static final class AuthorizedIndices implements AuthorizationEngine.AuthorizedIndices {
10381039

10391040
private final CachedSupplier<Set<String>> allAuthorizedAndAvailableSupplier;
1040-
private final Predicate<String> isAuthorizedPredicate;
1041+
private final BiPredicate<String, String> isAuthorizedPredicate;
10411042

1042-
AuthorizedIndices(Supplier<Set<String>> allAuthorizedAndAvailableSupplier, Predicate<String> isAuthorizedPredicate) {
1043+
AuthorizedIndices(Supplier<Set<String>> allAuthorizedAndAvailableSupplier, BiPredicate<String, String> isAuthorizedPredicate) {
10431044
this.allAuthorizedAndAvailableSupplier = CachedSupplier.wrap(allAuthorizedAndAvailableSupplier);
10441045
this.isAuthorizedPredicate = Objects.requireNonNull(isAuthorizedPredicate);
10451046
}
@@ -1050,8 +1051,8 @@ public Supplier<Set<String>> all() {
10501051
}
10511052

10521053
@Override
1053-
public boolean check(String name) {
1054-
return this.isAuthorizedPredicate.test(name);
1054+
public boolean check(String name, String selector) {
1055+
return isAuthorizedPredicate.test(name, selector);
10551056
}
10561057
}
10571058
}

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
@@ -694,6 +694,7 @@ public void testResourceAuthorizedPredicateForDatastreams() {
694694
);
695695
IndicesPermission.IsResourceAuthorizedPredicate predicate = new IndicesPermission.IsResourceAuthorizedPredicate(
696696
StringMatcher.of("other"),
697+
StringMatcher.never(),
697698
StringMatcher.of(dataStreamName, backingIndex.getName(), concreteIndex.getName(), alias.getName())
698699
);
699700
assertThat(predicate.test(dataStream), is(false));
@@ -709,10 +710,12 @@ public void testResourceAuthorizedPredicateForDatastreams() {
709710
public void testResourceAuthorizedPredicateAnd() {
710711
IndicesPermission.IsResourceAuthorizedPredicate predicate1 = new IndicesPermission.IsResourceAuthorizedPredicate(
711712
StringMatcher.of("c", "a"),
713+
StringMatcher.never(),
712714
StringMatcher.of("b", "d")
713715
);
714716
IndicesPermission.IsResourceAuthorizedPredicate predicate2 = new IndicesPermission.IsResourceAuthorizedPredicate(
715717
StringMatcher.of("c", "b"),
718+
StringMatcher.never(),
716719
StringMatcher.of("a", "d")
717720
);
718721
Metadata.Builder mb = Metadata.builder(

0 commit comments

Comments
 (0)