Skip to content

Commit 5ae1518

Browse files
authored
AllowAll for indicesAccessControl (#78498)
This PR adds a fast path for computing indicesAccessControl if the role has all access to all indices. A role is considered to have all access to all indices if any of its IndicesPermission#Group satisfy the following criteria: 1. Any of the index patterns is a simple match-all wildcard, i.e. "*" 2. It allows access to restricted indices 3. It grants the "all" index privilege 4. It has no DLS or FLS An example of such role is the builtin superuser role. Note the fastpath does not apply to roles that have "effective" but not direct "all access of all indices". For example, if the "effective" access is achieved by combining multiple Groups belong to the role, or combining multiple index patterns within a single Group. This fast path is provided so that we have a reference baseline for authorization related performance which is useful for both production use and troubleshooting.
1 parent 778c569 commit 5ae1518

File tree

9 files changed

+126
-86
lines changed

9 files changed

+126
-86
lines changed

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
*/
3131
public class IndicesAccessControl {
3232

33-
public static final IndicesAccessControl ALLOW_ALL = new IndicesAccessControl(true, Collections.emptyMap());
3433
public static final IndicesAccessControl ALLOW_NO_INDICES = new IndicesAccessControl(true,
3534
Collections.singletonMap(IndicesAndAliasesResolverField.NO_INDEX_PLACEHOLDER,
3635
new IndicesAccessControl.IndexAccessControl(true, new FieldPermissions(), DocumentPermissions.allowAll())));
@@ -249,6 +248,12 @@ public int hashCode() {
249248
* @return {@link IndicesAccessControl}
250249
*/
251250
public IndicesAccessControl limitIndicesAccessControl(IndicesAccessControl limitedByIndicesAccessControl) {
251+
if (this instanceof AllowAllIndicesAccessControl) {
252+
return limitedByIndicesAccessControl;
253+
} else if (limitedByIndicesAccessControl instanceof AllowAllIndicesAccessControl) {
254+
return this;
255+
}
256+
252257
final boolean granted;
253258
if (this.granted == limitedByIndicesAccessControl.granted) {
254259
granted = this.granted;
@@ -275,4 +280,35 @@ public String toString() {
275280
", indexPermissions=" + indexPermissions +
276281
'}';
277282
}
283+
284+
public static IndicesAccessControl allowAll() {
285+
return AllowAllIndicesAccessControl.INSTANCE;
286+
}
287+
288+
private static class AllowAllIndicesAccessControl extends IndicesAccessControl {
289+
290+
private static final IndicesAccessControl INSTANCE = new AllowAllIndicesAccessControl();
291+
292+
private final IndexAccessControl allowAllIndexAccessControl = new IndexAccessControl(true, null, null);
293+
294+
private AllowAllIndicesAccessControl() {
295+
super(true, null);
296+
}
297+
298+
@Override
299+
public IndexAccessControl getIndexPermissions(String index) {
300+
return allowAllIndexAccessControl;
301+
}
302+
303+
@Override
304+
public boolean isGranted() {
305+
return true;
306+
}
307+
308+
@Override
309+
public Collection<?> getDeniedIndices() {
310+
return Set.of();
311+
}
312+
}
313+
278314
}

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,16 @@ public boolean canHaveBackingIndices() {
345345
/**
346346
* Authorizes the provided action against the provided indices, given the current cluster metadata
347347
*/
348-
public Map<String, IndicesAccessControl.IndexAccessControl> authorize(
348+
public IndicesAccessControl authorize(
349349
String action,
350350
Set<String> requestedIndicesOrAliases,
351351
Map<String, IndexAbstraction> lookup,
352352
FieldPermissionsCache fieldPermissionsCache
353353
) {
354+
// Short circuit if the indicesPermission allows all access to every index
355+
if (Arrays.stream(groups).anyMatch(Group::isTotal)) {
356+
return IndicesAccessControl.allowAll();
357+
}
354358

355359
final List<IndexResource> resources = new ArrayList<>(requestedIndicesOrAliases.size());
356360
int totalResourceCount = 0;
@@ -469,6 +473,7 @@ public Map<String, IndicesAccessControl.IndexAccessControl> authorize(
469473
}
470474
}
471475

476+
boolean overallGranted = true;
472477
Map<String, IndicesAccessControl.IndexAccessControl> indexPermissions = new HashMap<>(grantedBuilder.size());
473478
for (Map.Entry<String, Boolean> entry : grantedBuilder.entrySet()) {
474479
String index = entry.getKey();
@@ -488,10 +493,13 @@ public Map<String, IndicesAccessControl.IndexAccessControl> authorize(
488493
} else {
489494
fieldPermissions = FieldPermissions.DEFAULT;
490495
}
496+
if (entry.getValue() == false) {
497+
overallGranted = false;
498+
}
491499
indexPermissions.put(index, new IndicesAccessControl.IndexAccessControl(entry.getValue(), fieldPermissions,
492500
(roleQueries != null) ? DocumentPermissions.filteredBy(roleQueries) : DocumentPermissions.allowAll()));
493501
}
494-
return unmodifiableMap(indexPermissions);
502+
return new IndicesAccessControl(overallGranted, unmodifiableMap(indexPermissions));
495503
}
496504

497505
private boolean isConcreteRestrictedIndex(String indexPattern) {
@@ -515,7 +523,7 @@ public static class Group {
515523
private final IndexPrivilege privilege;
516524
private final Predicate<String> actionMatcher;
517525
private final String[] indices;
518-
private final Predicate<String> indexNameMatcher;
526+
private final StringMatcher indexNameMatcher;
519527
private final Supplier<Automaton> indexNameAutomaton;
520528
private final FieldPermissions fieldPermissions;
521529
private final Set<BytesReference> query;
@@ -583,6 +591,14 @@ public boolean allowRestrictedIndices() {
583591
public Automaton getIndexMatcherAutomaton() {
584592
return indexNameAutomaton.get();
585593
}
594+
595+
boolean isTotal() {
596+
return allowRestrictedIndices
597+
&& indexNameMatcher.isTotal()
598+
&& privilege == IndexPrivilege.ALL
599+
&& query == null
600+
&& false == fieldPermissions.hasFieldLevelSecurity();
601+
}
586602
}
587603

588604
private static class DocumentLevelPermissions {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ public IndicesAccessControl authorize(String action, Set<String> requestedIndice
8989
super.authorize(action, requestedIndicesOrAliases, aliasAndIndexLookup, fieldPermissionsCache);
9090
IndicesAccessControl limitedByIndicesAccessControl = limitedBy.authorize(action, requestedIndicesOrAliases, aliasAndIndexLookup,
9191
fieldPermissionsCache);
92-
9392
return indicesAccessControl.limitIndicesAccessControl(limitedByIndicesAccessControl);
9493
}
9594

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -183,19 +183,7 @@ public ResourcePrivilegesMap checkApplicationResourcePrivileges(final String app
183183
public IndicesAccessControl authorize(String action, Set<String> requestedIndicesOrAliases,
184184
Map<String, IndexAbstraction> aliasAndIndexLookup,
185185
FieldPermissionsCache fieldPermissionsCache) {
186-
Map<String, IndicesAccessControl.IndexAccessControl> indexPermissions = indices.authorize(
187-
action, requestedIndicesOrAliases, aliasAndIndexLookup, fieldPermissionsCache
188-
);
189-
190-
// At least one role / indices permission set need to match with all the requested indices/aliases:
191-
boolean granted = true;
192-
for (Map.Entry<String, IndicesAccessControl.IndexAccessControl> entry : indexPermissions.entrySet()) {
193-
if (entry.getValue().isGranted() == false) {
194-
granted = false;
195-
break;
196-
}
197-
}
198-
return new IndicesAccessControl(granted, indexPermissions);
186+
return indices.authorize(action, requestedIndicesOrAliases, aliasAndIndexLookup, fieldPermissionsCache);
199187
}
200188

201189
@Override

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
@@ -69,6 +69,10 @@ public boolean test(String s) {
6969
return predicate.test(s);
7070
}
7171

72+
public boolean isTotal() {
73+
return predicate == ALWAYS_TRUE_PREDICATE;
74+
}
75+
7276
// For testing
7377
Predicate<String> getPredicate() {
7478
return predicate;

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
import org.elasticsearch.xpack.core.security.action.CreateApiKeyRequest;
9797
import org.elasticsearch.xpack.core.security.action.GetApiKeyRequest;
9898
import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyRequest;
99+
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
99100
import org.elasticsearch.xpack.core.textstructure.action.FindStructureAction;
100101
import org.elasticsearch.xpack.core.ml.action.FlushJobAction;
101102
import org.elasticsearch.xpack.core.ml.action.ForecastJobAction;
@@ -167,7 +168,6 @@
167168
import org.elasticsearch.xpack.core.security.action.user.PutUserAction;
168169
import org.elasticsearch.xpack.core.security.authc.Authentication;
169170
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
170-
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
171171
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
172172
import org.elasticsearch.xpack.core.security.authz.permission.Role;
173173
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
@@ -214,7 +214,6 @@
214214
import java.util.Collection;
215215
import java.util.Collections;
216216
import java.util.List;
217-
import java.util.Map;
218217
import java.util.SortedMap;
219218

220219
import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON;
@@ -1135,12 +1134,12 @@ private void assertMonitoringOnRestrictedIndices(Role role) {
11351134
GetSettingsAction.NAME, IndicesShardStoresAction.NAME, RecoveryAction.NAME);
11361135
for (final String indexMonitoringActionName : indexMonitoringActionNamesList) {
11371136
String asyncSearchIndex = XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2);
1138-
final Map<String, IndexAccessControl> authzMap = role.indices().authorize(indexMonitoringActionName,
1137+
final IndicesAccessControl iac = role.indices().authorize(indexMonitoringActionName,
11391138
Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS, asyncSearchIndex),
11401139
metadata.getIndicesLookup(), fieldPermissionsCache);
1141-
assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true));
1142-
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
1143-
assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true));
1140+
assertThat(iac.getIndexPermissions(internalSecurityIndex).isGranted(), is(true));
1141+
assertThat(iac.getIndexPermissions(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
1142+
assertThat(iac.getIndexPermissions(asyncSearchIndex).isGranted(), is(true));
11441143
}
11451144
}
11461145

@@ -1233,25 +1232,25 @@ public void testSuperuserRole() {
12331232

12341233
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
12351234
SortedMap<String, IndexAbstraction> lookup = metadata.getIndicesLookup();
1236-
Map<String, IndexAccessControl> authzMap =
1235+
IndicesAccessControl iac =
12371236
superuserRole.indices().authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), lookup, fieldPermissionsCache);
1238-
assertThat(authzMap.get("a1").isGranted(), is(true));
1239-
assertThat(authzMap.get("b").isGranted(), is(true));
1240-
authzMap =
1237+
assertThat(iac.getIndexPermissions("a1").isGranted(), is(true));
1238+
assertThat(iac.getIndexPermissions("b").isGranted(), is(true));
1239+
iac =
12411240
superuserRole.indices().authorize(DeleteIndexAction.NAME, Sets.newHashSet("a1", "ba"), lookup, fieldPermissionsCache);
1242-
assertThat(authzMap.get("a1").isGranted(), is(true));
1243-
assertThat(authzMap.get("b").isGranted(), is(true));
1244-
authzMap = superuserRole.indices().authorize(IndexAction.NAME, Sets.newHashSet("a2", "ba"), lookup, fieldPermissionsCache);
1245-
assertThat(authzMap.get("a2").isGranted(), is(true));
1246-
assertThat(authzMap.get("b").isGranted(), is(true));
1247-
authzMap = superuserRole.indices()
1241+
assertThat(iac.getIndexPermissions("a1").isGranted(), is(true));
1242+
assertThat(iac.getIndexPermissions("b").isGranted(), is(true));
1243+
iac = superuserRole.indices().authorize(IndexAction.NAME, Sets.newHashSet("a2", "ba"), lookup, fieldPermissionsCache);
1244+
assertThat(iac.getIndexPermissions("a2").isGranted(), is(true));
1245+
assertThat(iac.getIndexPermissions("b").isGranted(), is(true));
1246+
iac = superuserRole.indices()
12481247
.authorize(UpdateSettingsAction.NAME, Sets.newHashSet("aaaaaa", "ba"), lookup, fieldPermissionsCache);
1249-
assertThat(authzMap.get("aaaaaa").isGranted(), is(true));
1250-
assertThat(authzMap.get("b").isGranted(), is(true));
1251-
authzMap = superuserRole.indices().authorize(randomFrom(IndexAction.NAME, DeleteIndexAction.NAME, SearchAction.NAME),
1248+
assertThat(iac.getIndexPermissions("aaaaaa").isGranted(), is(true));
1249+
assertThat(iac.getIndexPermissions("b").isGranted(), is(true));
1250+
iac = superuserRole.indices().authorize(randomFrom(IndexAction.NAME, DeleteIndexAction.NAME, SearchAction.NAME),
12521251
Sets.newHashSet(RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache);
1253-
assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
1254-
assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true));
1252+
assertThat(iac.getIndexPermissions(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true));
1253+
assertThat(iac.getIndexPermissions(internalSecurityIndex).isGranted(), is(true));
12551254
assertTrue(superuserRole.indices().check(SearchAction.NAME));
12561255
assertFalse(superuserRole.indices().check("unknown"));
12571256

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ private void authorizeAction(final RequestInfo requestInfo, final String request
338338
if (ClusterPrivilegeResolver.isClusterAction(action)) {
339339
final ActionListener<AuthorizationResult> clusterAuthzListener =
340340
wrapPreservingContext(new AuthorizationResultListener<>(result -> {
341-
threadContext.putTransient(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);
341+
threadContext.putTransient(INDICES_PERMISSIONS_KEY, IndicesAccessControl.allowAll());
342342
listener.onResponse(null);
343343
}, listener::onFailure, requestInfo, requestId, authzInfo), threadContext);
344344
authzEngine.authorizeClusterAction(requestInfo, authzInfo, ActionListener.wrap(result -> {
@@ -514,7 +514,7 @@ private void authorizeSystemUser(final Authentication authentication, final Stri
514514
final TransportRequest request, final ActionListener<Void> listener) {
515515
final AuditTrail auditTrail = auditTrailService.get();
516516
if (SystemUser.isAuthorized(action)) {
517-
threadContext.putTransient(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);
517+
threadContext.putTransient(INDICES_PERMISSIONS_KEY, IndicesAccessControl.allowAll());
518518
threadContext.putTransient(AUTHORIZATION_INFO_KEY, SYSTEM_AUTHZ_INFO);
519519
auditTrail.accessGranted(requestId, authentication, action, request, SYSTEM_AUTHZ_INFO);
520520
listener.onResponse(null);

0 commit comments

Comments
 (0)