From f863e011bd596763f7819c2a5eb142a8a5b365f4 Mon Sep 17 00:00:00 2001 From: jaymode Date: Fri, 30 Apr 2021 09:29:51 -0600 Subject: [PATCH 01/29] System indices treated as restricted indices System indices should be treated as a special set of indices and not be accessible by all users. The existing security codebase has the notion of restricted indices, which are currently a subset of system indices. This change unifies the two concepts by making system indices the set of restricted indices. This means that going forward, consumers of system indices will need access to restricted indices. Closes #69298 --- .../metadata/IndexNameExpressionResolver.java | 9 ++ .../elasticsearch/indices/SystemIndices.java | 57 +++++-- .../authz/permission/IndicesPermission.java | 126 +++++++++------- .../core/security/authz/permission/Role.java | 79 +++++++--- .../authz/store/ReservedRolesStore.java | 2 - .../core/security/user/AsyncSearchUser.java | 5 +- .../xpack/core/security/user/XPackUser.java | 21 +-- .../authz/permission/LimitedRoleTests.java | 53 +++---- .../authz/store/ReservedRolesStoreTests.java | 54 +++---- .../authc/esnative/NativeRealmIntegTests.java | 9 +- .../xpack/security/Security.java | 3 +- .../authz/store/CompositeRolesStore.java | 111 +++++++++----- .../service/ElasticServiceAccountsTests.java | 4 +- .../authz/AuthorizationServiceTests.java | 3 +- .../authz/AuthorizedIndicesTests.java | 18 ++- .../authz/IndicesAndAliasesResolverTests.java | 3 +- .../xpack/security/authz/RBACEngineTests.java | 35 ++--- .../accesscontrol/IndicesPermissionTests.java | 53 ++++--- .../ResizeRequestInterceptorTests.java | 3 +- .../authz/permission/PermissionTests.java | 7 +- .../authz/store/CompositeRolesStoreTests.java | 139 +++++++++++++++--- .../authz/store/FileRolesStoreTests.java | 26 ++-- .../security/user/AsyncSearchUserTests.java | 72 --------- .../xpack/security/user/XPackUserTests.java | 81 ---------- 24 files changed, 543 insertions(+), 430 deletions(-) delete mode 100644 x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/AsyncSearchUserTests.java delete mode 100644 x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index efb3e1129e249..312f825e56198 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -8,6 +8,7 @@ package org.elasticsearch.cluster.metadata; +import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; import org.elasticsearch.action.IndicesRequest; @@ -721,6 +722,10 @@ boolean isPatternMatchingAllIndices(Metadata metadata, String[] indicesOrAliases return false; } + public boolean isSystemName(String name) { + return systemIndices.isSystemName(name); + } + public SystemIndexAccessLevel getSystemIndexAccessLevel() { return systemIndices.getSystemIndexAccessLevel(threadContext); } @@ -739,6 +744,10 @@ public Predicate getSystemIndexAccessPredicate() { return systemIndexAccessLevelPredicate; } + public Automaton getSystemNameAutomaton() { + return systemIndices.getSystemNameAutomaton(); + } + public static class Context { private final ClusterState state; diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java index ba56ea418bb3a..a4c4199149233 100644 --- a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java +++ b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java @@ -66,9 +66,11 @@ public class SystemIndices { TASKS_FEATURE_NAME, new Feature(TASKS_FEATURE_NAME, "Manages task results", List.of(TASKS_DESCRIPTOR)) ); - private final CharacterRunAutomaton systemIndexAutomaton; - private final CharacterRunAutomaton systemDataStreamIndicesAutomaton; - private final Predicate systemDataStreamAutomaton; + private final Automaton systemNameAutomaton; + private final CharacterRunAutomaton systemNameRunAutomaton; + private final CharacterRunAutomaton systemIndexRunAutomaton; + private final CharacterRunAutomaton systemDataStreamIndicesRunAutomaton; + private final Predicate systemDataStreamPredicate; private final Map featureDescriptors; private final Map productToSystemIndicesMatcher; private final ExecutorSelector executorSelector; @@ -82,11 +84,18 @@ public SystemIndices(Map pluginAndModulesDescriptors) { featureDescriptors = buildSystemIndexDescriptorMap(pluginAndModulesDescriptors); checkForOverlappingPatterns(featureDescriptors); checkForDuplicateAliases(this.getSystemIndexDescriptors()); - this.systemIndexAutomaton = buildIndexCharacterRunAutomaton(featureDescriptors); - this.systemDataStreamIndicesAutomaton = buildDataStreamBackingIndicesAutomaton(featureDescriptors); - this.systemDataStreamAutomaton = buildDataStreamNamePredicate(featureDescriptors); + Automaton systemIndexAutomata = buildIndexAutomaton(featureDescriptors); + this.systemIndexRunAutomaton = new CharacterRunAutomaton(systemIndexAutomata); + Automaton systemDataStreamIndicesAutomata = buildDataStreamBackingIndicesAutomaton(featureDescriptors); + this.systemDataStreamIndicesRunAutomaton = new CharacterRunAutomaton(systemDataStreamIndicesAutomata); + this.systemDataStreamPredicate = buildDataStreamNamePredicate(featureDescriptors); this.productToSystemIndicesMatcher = getProductToSystemIndicesMap(featureDescriptors); this.executorSelector = new ExecutorSelector(this); + this.systemNameAutomaton = MinimizationOperations.minimize( + Operations.union(List.of(systemIndexAutomata, systemDataStreamIndicesAutomata, buildDataStreamAutomaton(featureDescriptors))), + Integer.MAX_VALUE + ); + this.systemNameRunAutomaton = new CharacterRunAutomaton(systemNameAutomaton); } private static void checkForDuplicateAliases(Collection descriptors) { @@ -148,7 +157,7 @@ private static Map getProductToSystemIndicesMap(M * is checked against index names, aliases, data stream names, and the names of indices that back a system data stream. */ public boolean isSystemName(String name) { - return isSystemIndex(name) || isSystemDataStream(name) || isSystemIndexBackingDataStream(name); + return systemNameRunAutomaton.run(name); } /** @@ -167,7 +176,7 @@ public boolean isSystemIndex(Index index) { * @return true if the index name matches a pattern from a {@link SystemIndexDescriptor} */ public boolean isSystemIndex(String indexName) { - return systemIndexAutomaton.run(indexName); + return systemIndexRunAutomaton.run(indexName); } /** @@ -175,14 +184,18 @@ public boolean isSystemIndex(String indexName) { * {@link SystemDataStreamDescriptor} */ public boolean isSystemDataStream(String name) { - return systemDataStreamAutomaton.test(name); + return systemDataStreamPredicate.test(name); } /** * Determines whether the provided name matches that of an index that backs a system data stream. */ public boolean isSystemIndexBackingDataStream(String name) { - return systemDataStreamIndicesAutomaton.run(name); + return systemDataStreamIndicesRunAutomaton.run(name); + } + + public Automaton getSystemNameAutomaton() { + return systemNameAutomaton; } /** @@ -287,11 +300,11 @@ public Map getFeatures() { return featureDescriptors; } - private static CharacterRunAutomaton buildIndexCharacterRunAutomaton(Map descriptors) { + private static Automaton buildIndexAutomaton(Map descriptors) { Optional automaton = descriptors.values().stream() .map(SystemIndices::featureToIndexAutomaton) .reduce(Operations::union); - return new CharacterRunAutomaton(MinimizationOperations.minimize(automaton.orElse(EMPTY), Integer.MAX_VALUE)); + return MinimizationOperations.minimize(automaton.orElse(EMPTY), Integer.MAX_VALUE); } private static Automaton featureToIndexAutomaton(Feature feature) { @@ -302,6 +315,19 @@ private static Automaton featureToIndexAutomaton(Feature feature) { return systemIndexAutomaton.orElse(EMPTY); } + private static Automaton buildDataStreamAutomaton(Map descriptors) { + Optional automaton = descriptors.values().stream() + .map(feature -> + feature.getDataStreamDescriptors().stream() + .map(SystemDataStreamDescriptor::getDataStreamName) + .map(dsName -> SystemIndexDescriptor.buildAutomaton(dsName, null)) + .reduce(Operations::union) + .orElse(EMPTY)) + .reduce(Operations::union); + + return automaton.isPresent() ? MinimizationOperations.minimize(automaton.get(), Integer.MAX_VALUE) : EMPTY; + } + private static Predicate buildDataStreamNamePredicate(Map descriptors) { Set systemDataStreamNames = descriptors.values().stream() .flatMap(feature -> feature.getDataStreamDescriptors().stream()) @@ -310,11 +336,11 @@ private static Predicate buildDataStreamNamePredicate(Map descriptors) { + private static Automaton buildDataStreamBackingIndicesAutomaton(Map descriptors) { Optional automaton = descriptors.values().stream() .map(SystemIndices::featureToDataStreamBackingIndicesAutomaton) .reduce(Operations::union); - return new CharacterRunAutomaton(automaton.orElse(EMPTY)); + return MinimizationOperations.minimize(automaton.orElse(EMPTY), Integer.MAX_VALUE); } private static Automaton featureToDataStreamBackingIndicesAutomaton(Feature feature) { @@ -328,7 +354,7 @@ private static Automaton featureToDataStreamBackingIndicesAutomaton(Feature feat } public SystemDataStreamDescriptor validateDataStreamAccess(String dataStreamName, ThreadContext threadContext) { - if (systemDataStreamAutomaton.test(dataStreamName)) { + if (systemDataStreamPredicate.test(dataStreamName)) { SystemDataStreamDescriptor dataStreamDescriptor = featureDescriptors.values().stream() .flatMap(feature -> feature.getDataStreamDescriptors().stream()) .filter(descriptor -> descriptor.getDataStreamName().equals(dataStreamName)) @@ -378,6 +404,7 @@ IllegalArgumentException dataStreamAccessException(@Nullable String product, Str /** * Determines what level of system index access should be allowed in the current context. * + * @param threadContext the current thread context that has headers associated with the current request * @return {@link SystemIndexAccessLevel#ALL} if unrestricted system index access should be allowed, * {@link SystemIndexAccessLevel#RESTRICTED} if a subset of system index access should be allowed, or * {@link SystemIndexAccessLevel#NONE} if no system index access should be allowed. diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index 9c53fc6b1fa8f..52622a92a7e11 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.core.security.authz.permission; import org.apache.lucene.util.automaton.Automaton; +import org.apache.lucene.util.automaton.CharacterRunAutomaton; import org.apache.lucene.util.automaton.Operations; import org.elasticsearch.action.admin.indices.mapping.put.AutoPutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; @@ -59,13 +60,17 @@ public IndicesPermission(Group... groups) { this.groups = groups; } - private static StringMatcher indexMatcher(Collection ordinaryIndices, Collection restrictedIndices) { + private static StringMatcher indexMatcher(Collection ordinaryIndices, Collection restrictedIndices, + Automaton restrictedNamesAutomaton) { StringMatcher matcher; if (ordinaryIndices.isEmpty()) { matcher = StringMatcher.of(restrictedIndices); } else { - matcher = StringMatcher.of(ordinaryIndices) - .and("", index -> false == RestrictedIndicesNames.isRestricted(index)); + matcher = StringMatcher.of(ordinaryIndices); + if (restrictedNamesAutomaton != null) { + CharacterRunAutomaton characterRunAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); + matcher = matcher.and(name -> characterRunAutomaton.run(name) == false); + } if (restrictedIndices.isEmpty() == false) { matcher = StringMatcher.of(restrictedIndices).or(matcher); } @@ -82,7 +87,47 @@ public Group[] groups() { * has the privilege for executing the given action on. */ public Predicate allowedIndicesMatcher(String action) { - return allowedIndicesMatchersForAction.computeIfAbsent(action, a -> Group.buildIndexMatcherPredicateForAction(a, groups)); + return allowedIndicesMatchersForAction.computeIfAbsent(action, this::buildIndexMatcherPredicateForAction); + } + + private Predicate buildIndexMatcherPredicateForAction(String action) { + final Set ordinaryIndices = new HashSet<>(); + final Set restrictedIndices = new HashSet<>(); + final Set grantMappingUpdatesOnIndices = new HashSet<>(); + final Set grantMappingUpdatesOnRestrictedIndices = new HashSet<>(); + final boolean isMappingUpdateAction = isMappingUpdateAction(action); + Automaton restrictedNamesAutomaton = null; + for (final Group group : groups) { + if (group.actionMatcher.test(action)) { + if (group.allowRestrictedIndices) { + restrictedIndices.addAll(Arrays.asList(group.indices())); + } else { + ordinaryIndices.addAll(Arrays.asList(group.indices())); + if (restrictedNamesAutomaton == null) { + restrictedNamesAutomaton = group.restrictedNamesAutomaton; + } + } + } else if (isMappingUpdateAction && containsPrivilegeThatGrantsMappingUpdatesForBwc(group)) { + // special BWC case for certain privileges: allow put mapping on indices and aliases (but not on data streams), even if + // the privilege definition does not currently allow it + if (group.allowRestrictedIndices) { + grantMappingUpdatesOnRestrictedIndices.addAll(Arrays.asList(group.indices())); + } else { + grantMappingUpdatesOnIndices.addAll(Arrays.asList(group.indices())); + if (restrictedNamesAutomaton == null) { + restrictedNamesAutomaton = group.restrictedNamesAutomaton; + } + } + } + } + final StringMatcher nameMatcher = indexMatcher(ordinaryIndices, restrictedIndices, restrictedNamesAutomaton); + final StringMatcher bwcSpecialCaseMatcher = indexMatcher(grantMappingUpdatesOnIndices, + grantMappingUpdatesOnRestrictedIndices, restrictedNamesAutomaton); + return indexAbstraction -> + nameMatcher.test(indexAbstraction.getName()) || + (indexAbstraction.getType() != IndexAbstraction.Type.DATA_STREAM && + (indexAbstraction.getParentDataStream() == null) && + bwcSpecialCaseMatcher.test(indexAbstraction.getName())); } /** @@ -123,8 +168,7 @@ public ResourcePrivilegesMap checkResourcePrivileges(Set checkForIndexPa if (false == Operations.isEmpty(checkIndexAutomaton)) { Automaton allowedIndexPrivilegesAutomaton = null; for (Group group : groups) { - final Automaton groupIndexAutomaton = predicateCache.computeIfAbsent(group, - g -> IndicesPermission.Group.buildIndexMatcherAutomaton(g.allowRestrictedIndices(), g.indices())); + final Automaton groupIndexAutomaton = predicateCache.computeIfAbsent(group, Group::buildIndexMatcherAutomaton); if (Operations.subsetOf(checkIndexAutomaton, groupIndexAutomaton)) { if (allowedIndexPrivilegesAutomaton != null) { allowedIndexPrivilegesAutomaton = Automatons @@ -231,20 +275,20 @@ public Map authorize(String act // if more than one permission matches for a concrete index here and if // a single permission doesn't have a role query then DLS will not be // applied even when other permissions do have a role query - permissions.setAllowAll(true); + permissions.setAllowAll(); } } if (false == actionCheck) { for (String privilegeName : group.privilege.name()) { if (PRIVILEGE_NAME_SET_BWC_ALLOW_MAPPING_UPDATE.contains(privilegeName)) { - bwcDeprecationLogActions.add(() -> { + bwcDeprecationLogActions.add(() -> deprecationLogger.deprecate(DeprecationCategory.SECURITY, "[" + indexOrAlias + "] mapping update for ingest privilege [" + privilegeName + "]", "the index privilege [" + privilegeName + "] allowed the update " + "mapping action [" + action + "] on index [" + indexOrAlias + "], this privilege " + "will not permit mapping updates in the next major release - users who require access " + - "to update mappings must be granted explicit privileges"); - }); + "to update mappings must be granted explicit privileges") + ); } } } @@ -255,7 +299,7 @@ public Map authorize(String act if (false == granted && bwcGrantMappingUpdate) { // the action is granted only due to the deprecated behaviour of certain privileges granted = true; - bwcDeprecationLogActions.forEach(deprecationLogAction -> deprecationLogAction.run()); + bwcDeprecationLogActions.forEach(Runnable::run); } if (concreteIndices.isEmpty()) { @@ -309,6 +353,8 @@ private static boolean containsPrivilegeThatGrantsMappingUpdatesForBwc(Group gro } public static class Group { + public static final Group[] EMPTY_ARRAY = new Group[0]; + private final IndexPrivilege privilege; private final Predicate actionMatcher; private final String[] indices; @@ -319,17 +365,25 @@ public static class Group { // users. Setting this flag true eliminates the special status for the purpose of this permission - restricted indices still have // to be covered by the "indices" private final boolean allowRestrictedIndices; + private final Automaton restrictedNamesAutomaton; public Group(IndexPrivilege privilege, FieldPermissions fieldPermissions, @Nullable Set query, - boolean allowRestrictedIndices, String... indices) { + boolean allowRestrictedIndices, Automaton restrictedNamesAutomaton, String... indices) { assert indices.length != 0; this.privilege = privilege; this.actionMatcher = privilege.predicate(); this.indices = indices; - this.indexNameMatcher = StringMatcher.of(Arrays.asList(indices)); + this.allowRestrictedIndices = allowRestrictedIndices; + this.restrictedNamesAutomaton = Objects.requireNonNull(restrictedNamesAutomaton); + if (allowRestrictedIndices) { + this.indexNameMatcher = StringMatcher.of(indices); + } else { + final CharacterRunAutomaton restrictedNamesRunAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); + this.indexNameMatcher = StringMatcher.of(indices) + .and(name -> restrictedNamesRunAutomaton.run(name) == false); + } this.fieldPermissions = Objects.requireNonNull(fieldPermissions); this.query = query; - this.allowRestrictedIndices = allowRestrictedIndices; } public IndexPrivilege privilege() { @@ -355,7 +409,7 @@ private boolean checkAction(String action) { private boolean checkIndex(String index) { assert index != null; - return indexNameMatcher.test(index) && (allowRestrictedIndices || (false == RestrictedIndicesNames.isRestricted(index))); + return indexNameMatcher.test(index); } boolean hasQuery() { @@ -366,48 +420,16 @@ public boolean allowRestrictedIndices() { return allowRestrictedIndices; } - public static Automaton buildIndexMatcherAutomaton(boolean allowRestrictedIndices, String... indices) { + public Automaton buildIndexMatcherAutomaton() { final Automaton indicesAutomaton = Automatons.patterns(indices); if (allowRestrictedIndices) { return indicesAutomaton; } else { - return Automatons.minusAndMinimize(indicesAutomaton, RestrictedIndicesNames.NAMES_AUTOMATON); + return Automatons.minusAndMinimize(indicesAutomaton, restrictedNamesAutomaton); } } - private static Predicate buildIndexMatcherPredicateForAction(String action, Group... groups) { - final Set ordinaryIndices = new HashSet<>(); - final Set restrictedIndices = new HashSet<>(); - final Set grantMappingUpdatesOnIndices = new HashSet<>(); - final Set grantMappingUpdatesOnRestrictedIndices = new HashSet<>(); - final boolean isMappingUpdateAction = isMappingUpdateAction(action); - for (final Group group : groups) { - if (group.actionMatcher.test(action)) { - if (group.allowRestrictedIndices) { - restrictedIndices.addAll(Arrays.asList(group.indices())); - } else { - ordinaryIndices.addAll(Arrays.asList(group.indices())); - } - } else if (isMappingUpdateAction && containsPrivilegeThatGrantsMappingUpdatesForBwc(group)) { - // special BWC case for certain privileges: allow put mapping on indices and aliases (but not on data streams), even if - // the privilege definition does not currently allow it - if (group.allowRestrictedIndices) { - grantMappingUpdatesOnRestrictedIndices.addAll(Arrays.asList(group.indices())); - } else { - grantMappingUpdatesOnIndices.addAll(Arrays.asList(group.indices())); - } - } - } - final StringMatcher nameMatcher = indexMatcher(ordinaryIndices, restrictedIndices); - final StringMatcher bwcSpecialCaseMatcher = indexMatcher(grantMappingUpdatesOnIndices, - grantMappingUpdatesOnRestrictedIndices); - return indexAbstraction -> { - return nameMatcher.test(indexAbstraction.getName()) || - (indexAbstraction.getType() != IndexAbstraction.Type.DATA_STREAM && - (indexAbstraction.getParentDataStream() == null) && - bwcSpecialCaseMatcher.test(indexAbstraction.getName())); - }; - } + } private static class DocumentLevelPermissions { @@ -428,8 +450,8 @@ private boolean isAllowAll() { return allowAll; } - private void setAllowAll(boolean allowAll) { - this.allowAll = allowAll; + private void setAllowAll() { + this.allowAll = true; } } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java index 355f4dbaa732d..6b3b5438f9d3c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java @@ -16,6 +16,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; +import org.elasticsearch.xpack.core.security.authz.permission.IndicesPermission.Group; import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor; import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege; @@ -23,6 +24,7 @@ import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.Privilege; +import org.elasticsearch.xpack.core.security.support.Automatons; import java.util.ArrayList; import java.util.Arrays; @@ -36,7 +38,7 @@ public class Role { - public static final Role EMPTY = Role.builder("__empty").build(); + public static final Role EMPTY = Role.builder(Automatons.EMPTY, "__empty").build(); private final String[] names; private final ClusterPermission cluster; @@ -72,12 +74,12 @@ public RunAsPermission runAs() { return runAs; } - public static Builder builder(String... names) { - return new Builder(names); + public static Builder builder(Automaton restrictedIndices, String... names) { + return new Builder(restrictedIndices, names); } - public static Builder builder(RoleDescriptor rd, FieldPermissionsCache fieldPermissionsCache) { - return new Builder(rd, fieldPermissionsCache); + public static Builder builder(RoleDescriptor rd, FieldPermissionsCache fieldPermissionsCache, Automaton restrictedIndices) { + return new Builder(rd, fieldPermissionsCache, restrictedIndices); } /** @@ -191,32 +193,34 @@ public static class Builder { private final String[] names; private ClusterPermission cluster = ClusterPermission.NONE; private RunAsPermission runAs = RunAsPermission.NONE; - private List groups = new ArrayList<>(); - private List>> applicationPrivs = new ArrayList<>(); + private final List groups = new ArrayList<>(); + private final List>> applicationPrivs = new ArrayList<>(); + private final Automaton restrictedNamesAutomaton; - private Builder(String[] names) { + private Builder(Automaton restrictedNamesAutomaton, String[] names) { + this.restrictedNamesAutomaton = restrictedNamesAutomaton; this.names = names; } - private Builder(RoleDescriptor rd, @Nullable FieldPermissionsCache fieldPermissionsCache) { + private Builder(RoleDescriptor rd, @Nullable FieldPermissionsCache fieldPermissionsCache, Automaton restrictedNamesAutomaton) { this.names = new String[] { rd.getName() }; cluster(Sets.newHashSet(rd.getClusterPrivileges()), Arrays.asList(rd.getConditionalClusterPrivileges())); groups.addAll(convertFromIndicesPrivileges(rd.getIndicesPrivileges(), fieldPermissionsCache)); final RoleDescriptor.ApplicationResourcePrivileges[] applicationPrivileges = rd.getApplicationPrivileges(); - for (int i = 0; i < applicationPrivileges.length; i++) { - applicationPrivs.add(convertApplicationPrivilege(rd.getName(), i, applicationPrivileges[i])); + for (RoleDescriptor.ApplicationResourcePrivileges applicationPrivilege : applicationPrivileges) { + applicationPrivs.add(convertApplicationPrivilege(applicationPrivilege)); } String[] rdRunAs = rd.getRunAs(); if (rdRunAs != null && rdRunAs.length > 0) { this.runAs(new Privilege(Sets.newHashSet(rdRunAs), rdRunAs)); } + this.restrictedNamesAutomaton = restrictedNamesAutomaton; } public Builder cluster(Set privilegeNames, Iterable configurableClusterPrivileges) { ClusterPermission.Builder builder = ClusterPermission.builder(); - List clusterPermissions = new ArrayList<>(); if (privilegeNames.isEmpty() == false) { for (String name : privilegeNames) { builder = ClusterPrivilegeResolver.resolve(name).buildPermission(builder); @@ -235,13 +239,13 @@ public Builder runAs(Privilege privilege) { } public Builder add(IndexPrivilege privilege, String... indices) { - groups.add(new IndicesPermission.Group(privilege, FieldPermissions.DEFAULT, null, false, indices)); + groups.add(new IndicesPermissionGroupDefinition(privilege, FieldPermissions.DEFAULT, null, false, indices)); return this; } public Builder add(FieldPermissions fieldPermissions, Set query, IndexPrivilege privilege, boolean allowRestrictedIndices, String... indices) { - groups.add(new IndicesPermission.Group(privilege, fieldPermissions, query, allowRestrictedIndices, indices)); + groups.add(new IndicesPermissionGroupDefinition(privilege, fieldPermissions, query, allowRestrictedIndices, indices)); return this; } @@ -251,16 +255,24 @@ public Builder addApplicationPrivilege(ApplicationPrivilege privilege, Set convertFromIndicesPrivileges(RoleDescriptor.IndicesPrivileges[] indicesPrivileges, + static List convertFromIndicesPrivileges(RoleDescriptor.IndicesPrivileges[] indicesPrivileges, @Nullable FieldPermissionsCache fieldPermissionsCache) { - List list = new ArrayList<>(indicesPrivileges.length); + List list = new ArrayList<>(indicesPrivileges.length); for (RoleDescriptor.IndicesPrivileges privilege : indicesPrivileges) { final FieldPermissions fieldPermissions; if (fieldPermissionsCache != null) { @@ -270,19 +282,42 @@ static List convertFromIndicesPrivileges(RoleDescriptor new FieldPermissionsDefinition(privilege.getGrantedFields(), privilege.getDeniedFields())); } final Set query = privilege.getQuery() == null ? null : Collections.singleton(privilege.getQuery()); - list.add(new IndicesPermission.Group(IndexPrivilege.get(Sets.newHashSet(privilege.getPrivileges())), fieldPermissions, - query, privilege.allowRestrictedIndices(), privilege.getIndices())); + list.add(new IndicesPermissionGroupDefinition(IndexPrivilege.get(Sets.newHashSet(privilege.getPrivileges())), + fieldPermissions, query, privilege.allowRestrictedIndices(), privilege.getIndices())); } return list; } - static Tuple> convertApplicationPrivilege(String role, int index, - RoleDescriptor.ApplicationResourcePrivileges arp) { + static Tuple> convertApplicationPrivilege(RoleDescriptor.ApplicationResourcePrivileges arp) { return new Tuple<>(new ApplicationPrivilege(arp.getApplication(), Sets.newHashSet(arp.getPrivileges()), arp.getPrivileges() ), Sets.newHashSet(arp.getResources())); } + + private static class IndicesPermissionGroupDefinition { + private final IndexPrivilege privilege; + private final FieldPermissions fieldPermissions; + private final @Nullable Set query; + private final boolean allowRestrictedIndices; + private final String[] indices; + + private IndicesPermissionGroupDefinition(IndexPrivilege privilege, + FieldPermissions fieldPermissions, + @Nullable Set query, + boolean allowRestrictedIndices, + String... indices) { + this.privilege = privilege; + this.fieldPermissions = fieldPermissions; + this.query = query; + this.allowRestrictedIndices = allowRestrictedIndices; + this.indices = indices; + } + + private IndicesPermission.Group toGroup(Automaton automaton) { + return new Group(privilege, fieldPermissions, query, allowRestrictedIndices, automaton, indices); + } + } } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index 485ba6ee80a57..214ec8685ae9c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -17,7 +17,6 @@ import org.elasticsearch.xpack.core.security.action.InvalidateApiKeyAction; import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesAction; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges.ManageApplicationPrivileges; import org.elasticsearch.xpack.core.security.support.MetadataUtils; @@ -49,7 +48,6 @@ public class ReservedRolesStore implements BiConsumer, ActionListene }, null, new String[] { "*" }, MetadataUtils.DEFAULT_RESERVED_METADATA, Collections.emptyMap()); - public static final Role SUPERUSER_ROLE = Role.builder(SUPERUSER_ROLE_DESCRIPTOR, null).build(); private static final Map RESERVED_ROLES = initializeReservedRoles(); private static Map initializeReservedRoles() { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java index 56d154bd2b683..a38ddaf7365a5 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.core.security.user; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.support.MetadataUtils; @@ -16,7 +15,7 @@ public class AsyncSearchUser extends User { public static final String NAME = UsernamesField.ASYNC_SEARCH_NAME; public static final AsyncSearchUser INSTANCE = new AsyncSearchUser(); public static final String ROLE_NAME = UsernamesField.ASYNC_SEARCH_ROLE; - public static final Role ROLE = Role.builder(new RoleDescriptor(ROLE_NAME, + public static final RoleDescriptor ROLE_DESCRIPTOR = new RoleDescriptor(ROLE_NAME, new String[] { "cancel_task" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() @@ -28,7 +27,7 @@ public class AsyncSearchUser extends User { null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, - null), null).build(); + null); private AsyncSearchUser() { super(NAME, ROLE_NAME); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java index a91cff203aeac..b2501585b8408 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.core.security.user; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.index.IndexAuditTrailField; import org.elasticsearch.xpack.core.security.support.MetadataUtils; @@ -18,14 +17,18 @@ public class XPackUser extends User { public static final String NAME = UsernamesField.XPACK_NAME; public static final String ROLE_NAME = UsernamesField.XPACK_ROLE; - public static final Role ROLE = Role.builder(new RoleDescriptor(ROLE_NAME, new String[] { "all" }, - new RoleDescriptor.IndicesPrivileges[] { - RoleDescriptor.IndicesPrivileges.builder().indices("/@&~(\\.security.*)/").privileges("all").build(), - RoleDescriptor.IndicesPrivileges.builder().indices(IndexAuditTrailField.INDEX_NAME_PREFIX + "-*") - .privileges("read").build() - }, - new String[] { "*" }, - MetadataUtils.DEFAULT_RESERVED_METADATA), null).build(); + public static final RoleDescriptor ROLE_DESCRIPTOR = new RoleDescriptor(ROLE_NAME, new String[] { "all" }, + new RoleDescriptor.IndicesPrivileges[] { + RoleDescriptor.IndicesPrivileges.builder() + .indices("/@&~(\\.security.*)/") + .privileges("all") + .allowRestrictedIndices(true) + .build(), + RoleDescriptor.IndicesPrivileges.builder().indices(IndexAuditTrailField.INDEX_NAME_PREFIX + "-*") + .privileges("read").build() + }, + new String[] { "*" }, + MetadataUtils.DEFAULT_RESERVED_METADATA); public static final XPackUser INSTANCE = new XPackUser(); private XPackUser() { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/LimitedRoleTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/LimitedRoleTests.java index 1bda5f27ff560..0f6d107b85fbd 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/LimitedRoleTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/LimitedRoleTests.java @@ -53,8 +53,8 @@ public void setup() { } public void testRoleConstructorWithLimitedRole() { - Role fromRole = Role.builder("a-role").build(); - Role limitedByRole = Role.builder("limited-role").build(); + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").build(); Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole); assertNotNull(role); assertThat(role.names(), is(limitedByRole.names())); @@ -74,7 +74,7 @@ public void testAuthorize() { .putAlias(AliasMetadata.builder("_alias1")); Metadata md = Metadata.builder().put(imbBuilder).put(imbBuilder1).build(); FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - Role fromRole = Role.builder("a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList()) + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList()) .add(IndexPrivilege.ALL, "_index").add(IndexPrivilege.CREATE_INDEX, "_index1").build(); IndicesAccessControl iac = fromRole.authorize(SearchAction.NAME, Sets.newHashSet("_index", "_alias1"), md.getIndicesLookup(), @@ -91,7 +91,7 @@ public void testAuthorize() { assertThat(iac.getIndexPermissions("_index1").isGranted(), is(true)); { - Role limitedByRole = Role.builder("limited-role") + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role") .cluster(Collections.singleton("all"), Collections.emptyList()).add(IndexPrivilege.READ, "_index") .add(IndexPrivilege.NONE, "_index1").build(); iac = limitedByRole.authorize(SearchAction.NAME, Sets.newHashSet("_index", "_alias1"), md.getIndicesLookup(), @@ -136,12 +136,12 @@ public void testAuthorize() { } public void testCheckClusterAction() { - Role fromRole = Role.builder("a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList()) + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList()) .build(); Authentication authentication = mock(Authentication.class); assertThat(fromRole.checkClusterAction("cluster:admin/xpack/security/x", mock(TransportRequest.class), authentication), is(true)); { - Role limitedByRole = Role.builder("limited-role") + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role") .cluster(Collections.singleton("all"), Collections.emptyList()).build(); assertThat(limitedByRole.checkClusterAction("cluster:admin/xpack/security/x", mock(TransportRequest.class), authentication), is(true)); @@ -151,7 +151,7 @@ public void testCheckClusterAction() { assertThat(role.checkClusterAction("cluster:other-action", mock(TransportRequest.class), authentication), is(false)); } { - Role limitedByRole = Role.builder("limited-role") + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role") .cluster(Collections.singleton("monitor"), Collections.emptyList()).build(); assertThat(limitedByRole.checkClusterAction("cluster:monitor/me", mock(TransportRequest.class), authentication), is(true)); Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole); @@ -161,12 +161,12 @@ public void testCheckClusterAction() { } public void testCheckIndicesAction() { - Role fromRole = Role.builder("a-role").add(IndexPrivilege.READ, "ind-1").build(); + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").add(IndexPrivilege.READ, "ind-1").build(); assertThat(fromRole.checkIndicesAction(SearchAction.NAME), is(true)); assertThat(fromRole.checkIndicesAction(CreateIndexAction.NAME), is(false)); { - Role limitedByRole = Role.builder("limited-role").add(IndexPrivilege.ALL, "ind-1").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").add(IndexPrivilege.ALL, "ind-1").build(); assertThat(limitedByRole.checkIndicesAction(SearchAction.NAME), is(true)); assertThat(limitedByRole.checkIndicesAction(CreateIndexAction.NAME), is(true)); Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole); @@ -174,7 +174,7 @@ public void testCheckIndicesAction() { assertThat(role.checkIndicesAction(CreateIndexAction.NAME), is(false)); } { - Role limitedByRole = Role.builder("limited-role").add(IndexPrivilege.NONE, "ind-1").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").add(IndexPrivilege.NONE, "ind-1").build(); assertThat(limitedByRole.checkIndicesAction(SearchAction.NAME), is(false)); Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole); assertThat(role.checkIndicesAction(SearchAction.NAME), is(false)); @@ -183,13 +183,13 @@ public void testCheckIndicesAction() { } public void testAllowedIndicesMatcher() { - Role fromRole = Role.builder("a-role").add(IndexPrivilege.READ, "ind-1*").build(); + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").add(IndexPrivilege.READ, "ind-1*").build(); assertThat(fromRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-1")), is(true)); assertThat(fromRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-11")), is(true)); assertThat(fromRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-2")), is(false)); { - Role limitedByRole = Role.builder("limited-role").add(IndexPrivilege.READ, "ind-1", "ind-2").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").add(IndexPrivilege.READ, "ind-1", "ind-2").build(); assertThat(limitedByRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-1")), is(true)); assertThat(limitedByRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-11")), is(false)); assertThat(limitedByRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-2")), is(true)); @@ -199,7 +199,7 @@ public void testAllowedIndicesMatcher() { assertThat(role.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-2")), is(false)); } { - Role limitedByRole = Role.builder("limited-role").add(IndexPrivilege.READ, "ind-*").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").add(IndexPrivilege.READ, "ind-*").build(); assertThat(limitedByRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-1")), is(true)); assertThat(limitedByRole.allowedIndicesMatcher(SearchAction.NAME).test(mockIndexAbstraction("ind-2")), is(true)); Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole); @@ -209,7 +209,7 @@ public void testAllowedIndicesMatcher() { } public void testAllowedActionsMatcher() { - Role fromRole = Role.builder("fromRole") + Role fromRole = Role.builder(Automatons.EMPTY, "fromRole") .add(IndexPrivilege.WRITE, "ind*") .add(IndexPrivilege.READ, "ind*") .add(IndexPrivilege.READ, "other*") @@ -219,7 +219,7 @@ public void testAllowedActionsMatcher() { assertThat(fromRolePredicate.test(SearchAction.NAME), is(true)); assertThat(fromRolePredicate.test(BulkAction.NAME), is(true)); - Role limitedByRole = Role.builder("limitedRole") + Role limitedByRole = Role.builder(Automatons.EMPTY, "limitedRole") .add(IndexPrivilege.READ, "index1", "index2") .build(); Automaton limitedByRoleAutomaton = limitedByRole.allowedActionsMatcher("index1"); @@ -245,13 +245,13 @@ public void testAllowedActionsMatcher() { } public void testCheckClusterPrivilege() { - Role fromRole = Role.builder("a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList()) + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList()) .build(); assertThat(fromRole.grants(ClusterPrivilegeResolver.ALL), is(false)); assertThat(fromRole.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(true)); { - Role limitedByRole = Role.builder("scoped-role") + Role limitedByRole = Role.builder(Automatons.EMPTY, "scoped-role") .cluster(Collections.singleton("all"), Collections.emptyList()).build(); assertThat(limitedByRole.grants(ClusterPrivilegeResolver.ALL), is(true)); assertThat(limitedByRole.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(true)); @@ -260,7 +260,7 @@ public void testCheckClusterPrivilege() { assertThat(role.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(true)); } { - Role limitedByRole = Role.builder("scoped-role") + Role limitedByRole = Role.builder(Automatons.EMPTY, "scoped-role") .cluster(Collections.singleton("monitor"), Collections.emptyList()).build(); assertThat(limitedByRole.grants(ClusterPrivilegeResolver.ALL), is(false)); assertThat(limitedByRole.grants(ClusterPrivilegeResolver.MONITOR), is(true)); @@ -272,7 +272,7 @@ public void testCheckClusterPrivilege() { } public void testGetPrivilegesForIndexPatterns() { - Role fromRole = Role.builder("a-role").add(IndexPrivilege.READ, "ind-1*").build(); + Role fromRole = Role.builder(Automatons.EMPTY, "a-role").add(IndexPrivilege.READ, "ind-1*").build(); ResourcePrivilegesMap resourcePrivileges = fromRole.checkIndicesPrivileges(Collections.singleton("ind-1-1-*"), true, Sets.newHashSet("read", "write")); ResourcePrivilegesMap expectedAppPrivsByResource = new ResourcePrivilegesMap(false, Collections.singletonMap("ind-1-1-*", @@ -285,7 +285,7 @@ public void testGetPrivilegesForIndexPatterns() { verifyResourcesPrivileges(resourcePrivileges, expectedAppPrivsByResource); { - Role limitedByRole = Role.builder("limited-role").add(IndexPrivilege.READ, "ind-1", "ind-2").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").add(IndexPrivilege.READ, "ind-1", "ind-2").build(); resourcePrivileges = limitedByRole.checkIndicesPrivileges(Collections.singleton("ind-1"), true, Collections.singleton("read")); expectedAppPrivsByResource = new ResourcePrivilegesMap(true, Collections.singletonMap("ind-1", ResourcePrivileges.builder("ind-1").addPrivilege("read", true).build())); @@ -315,7 +315,7 @@ public void testGetPrivilegesForIndexPatterns() { verifyResourcesPrivileges(resourcePrivileges, expectedAppPrivsByResource); } { - fromRole = Role.builder("a-role") + fromRole = Role.builder(Automatons.EMPTY, "a-role") .add(FieldPermissions.DEFAULT, Collections.emptySet(), IndexPrivilege.READ, true, "ind-1*", ".security").build(); resourcePrivileges = fromRole.checkIndicesPrivileges(Sets.newHashSet("ind-1", ".security"), true, Collections.singleton("read")); @@ -326,7 +326,7 @@ public void testGetPrivilegesForIndexPatterns() { .put(".security", ResourcePrivileges.builder(".security").addPrivilege("read", true).build()).map()); verifyResourcesPrivileges(resourcePrivileges, expectedAppPrivsByResource); - Role limitedByRole = Role.builder("limited-role").add(IndexPrivilege.READ, "ind-1", "ind-2").build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "limited-role").add(IndexPrivilege.READ, "ind-1", "ind-2").build(); resourcePrivileges = limitedByRole.checkIndicesPrivileges(Sets.newHashSet("ind-1", "ind-2", ".security"), true, Collections.singleton("read")); @@ -354,7 +354,7 @@ public void testGetApplicationPrivilegesByResource() { final ApplicationPrivilege app2Read = defineApplicationPrivilege("app2", "read", "data:read/*"); final ApplicationPrivilege app2Write = defineApplicationPrivilege("app2", "write", "data:write/*"); - Role fromRole = Role.builder("test-role").addApplicationPrivilege(app1Read, Collections.singleton("foo/*")) + Role fromRole = Role.builder(Automatons.EMPTY, "test-role").addApplicationPrivilege(app1Read, Collections.singleton("foo/*")) .addApplicationPrivilege(app1All, Collections.singleton("foo/bar/baz")) .addApplicationPrivilege(app2Read, Collections.singleton("foo/bar/*")) .addApplicationPrivilege(app2Write, Collections.singleton("*/bar/*")).build(); @@ -385,9 +385,10 @@ public void testGetApplicationPrivilegesByResource() { verifyResourcesPrivileges(appPrivsByResource, expectedAppPrivsByResource); { - Role limitedByRole = Role.builder("test-role-scoped").addApplicationPrivilege(app1Read, Collections.singleton("foo/scoped/*")) - .addApplicationPrivilege(app2Read, Collections.singleton("foo/bar/*")) - .addApplicationPrivilege(app2Write, Collections.singleton("moo/bar/*")).build(); + Role limitedByRole = Role.builder(Automatons.EMPTY, "test-role-scoped") + .addApplicationPrivilege(app1Read, Collections.singleton("foo/scoped/*")) + .addApplicationPrivilege(app2Read, Collections.singleton("foo/bar/*")) + .addApplicationPrivilege(app2Write, Collections.singleton("moo/bar/*")).build(); appPrivsByResource = limitedByRole.checkApplicationResourcePrivileges("app1", Collections.singleton("*"), forPrivilegeNames, applicationPrivilegeDescriptors); expectedAppPrivsByResource = new ResourcePrivilegesMap(false, Collections.singletonMap("*", ResourcePrivileges.builder("*") diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index e830bec9ab4b7..e36603f8eaa5f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.core.security.authz.store; +import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.remote.RemoteInfoAction; @@ -75,6 +76,7 @@ import org.elasticsearch.xpack.core.ml.action.EvaluateDataFrameAction; import org.elasticsearch.xpack.core.ml.action.ExplainDataFrameAnalyticsAction; import org.elasticsearch.xpack.core.ml.action.FinalizeJobExecutionAction; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.textstructure.action.FindStructureAction; import org.elasticsearch.xpack.core.ml.action.FlushJobAction; import org.elasticsearch.xpack.core.ml.action.ForecastJobAction; @@ -199,6 +201,8 @@ public class ReservedRolesStoreTests extends ESTestCase { private static final String READ_CROSS_CLUSTER_NAME = "internal:transport/proxy/indices:data/read/query"; + public Automaton reservedNamesAutomaton = Automatons.EMPTY; + public void testIsReserved() { assertThat(ReservedRolesStore.isReserved("kibana_system"), is(true)); assertThat(ReservedRolesStore.isReserved("superuser"), is(true)); @@ -242,7 +246,7 @@ public void testSnapshotUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role snapshotUserRole = Role.builder(roleDescriptor, null).build(); + Role snapshotUserRole = Role.builder(roleDescriptor, null, Automatons.EMPTY).build(); assertThat(snapshotUserRole.cluster().check(GetRepositoriesAction.NAME, request, authentication), is(true)); assertThat(snapshotUserRole.cluster().check(CreateSnapshotAction.NAME, request, authentication), is(true)); assertThat(snapshotUserRole.cluster().check(SnapshotsStatusAction.NAME, request, authentication), is(true)); @@ -299,7 +303,7 @@ public void testIngestAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role ingestAdminRole = Role.builder(roleDescriptor, null).build(); + Role ingestAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(ingestAdminRole.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(true)); assertThat(ingestAdminRole.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(true)); assertThat(ingestAdminRole.cluster().check(DeleteIndexTemplateAction.NAME, request, authentication), is(true)); @@ -329,7 +333,7 @@ public void testKibanaSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role kibanaRole = Role.builder(roleDescriptor, null).build(); + Role kibanaRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(kibanaRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(kibanaRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(kibanaRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -557,7 +561,7 @@ public void testKibanaAdminRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); - Role kibanaAdminRole = Role.builder(roleDescriptor, null).build(); + Role kibanaAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(kibanaAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(kibanaAdminRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(kibanaAdminRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -604,7 +608,7 @@ public void testKibanaUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role kibanaUserRole = Role.builder(roleDescriptor, null).build(); + Role kibanaUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(kibanaUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(kibanaUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(kibanaUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -644,7 +648,7 @@ public void testMonitoringUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role monitoringUserRole = Role.builder(roleDescriptor, null).build(); + Role monitoringUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(monitoringUserRole.cluster().check(MainAction.NAME, request, authentication), is(true)); assertThat(monitoringUserRole.cluster().check(XPackInfoAction.NAME, request, authentication), is(true)); assertThat(monitoringUserRole.cluster().check(RemoteInfoAction.NAME, request, authentication), is(true)); @@ -710,7 +714,7 @@ public void testRemoteMonitoringAgentRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role remoteMonitoringAgentRole = Role.builder(roleDescriptor, null).build(); + Role remoteMonitoringAgentRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(remoteMonitoringAgentRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringAgentRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringAgentRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -808,7 +812,7 @@ public void testRemoteMonitoringCollectorRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role remoteMonitoringCollectorRole = Role.builder(roleDescriptor, null).build(); + Role remoteMonitoringCollectorRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -949,7 +953,7 @@ public void testReportingUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role reportingUserRole = Role.builder(roleDescriptor, null).build(); + Role reportingUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(reportingUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(reportingUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(reportingUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -996,7 +1000,7 @@ public void testKibanaDashboardOnlyUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role dashboardsOnlyUserRole = Role.builder(roleDescriptor, null).build(); + Role dashboardsOnlyUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(dashboardsOnlyUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(dashboardsOnlyUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(dashboardsOnlyUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -1033,7 +1037,7 @@ public void testSuperuserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role superuserRole = Role.builder(roleDescriptor, null).build(); + Role superuserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(superuserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(superuserRole.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(true)); assertThat(superuserRole.cluster().check(PutUserAction.NAME, request, authentication), is(true)); @@ -1105,7 +1109,7 @@ public void testLogstashSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role logstashSystemRole = Role.builder(roleDescriptor, null).build(); + Role logstashSystemRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(logstashSystemRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(logstashSystemRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(logstashSystemRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -1136,7 +1140,7 @@ public void testBeatsAdminRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - final Role beatsAdminRole = Role.builder(roleDescriptor, null).build(); + final Role beatsAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(beatsAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(beatsAdminRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(beatsAdminRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -1176,7 +1180,7 @@ public void testBeatsSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role beatsSystemRole = Role.builder(roleDescriptor, null).build(); + Role beatsSystemRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(beatsSystemRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(beatsSystemRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(beatsSystemRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -1212,7 +1216,7 @@ public void testAPMSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role APMSystemRole = Role.builder(roleDescriptor, null).build(); + Role APMSystemRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(APMSystemRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(APMSystemRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(APMSystemRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -1255,7 +1259,7 @@ public void testAPMUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false)); assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 12)), is(false)); @@ -1301,7 +1305,7 @@ public void testMachineLearningAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertRoleHasManageMl(role); assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false)); @@ -1407,7 +1411,7 @@ public void testMachineLearningUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(role.cluster().check(CloseJobAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(DeleteCalendarAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(DeleteCalendarEventAction.NAME, request, authentication), is(false)); @@ -1504,7 +1508,7 @@ public void testTransformAdminRole() { assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); } - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true)); @@ -1562,7 +1566,7 @@ public void testTransformUserRole() { assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); } - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true)); @@ -1610,7 +1614,7 @@ public void testWatcherAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(role.cluster().check(PutWatchAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetWatchAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(DeleteWatchAction.NAME, request, authentication), is(true)); @@ -1641,7 +1645,7 @@ public void testWatcherUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(role.cluster().check(PutWatchAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(GetWatchAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(DeleteWatchAction.NAME, request, authentication), is(false)); @@ -1674,7 +1678,7 @@ public void testPredefinedViewerRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); // No cluster privileges assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); @@ -1723,7 +1727,7 @@ public void testPredefinedEditorRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null).build(); + Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); // No cluster privileges assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); @@ -1834,7 +1838,7 @@ public void testLogstashAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role logstashAdminRole = Role.builder(roleDescriptor, null).build(); + Role logstashAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); assertThat(logstashAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(logstashAdminRole.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false)); assertThat(logstashAdminRole.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false)); diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java index 238709f1b949f..da96074a62720 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java @@ -56,6 +56,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.ElasticUser; @@ -378,7 +379,9 @@ public void testCreateAndUpdateRole() { GetRolesResponse getRolesResponse = new GetRolesRequestBuilder(client()).names("test_role").get(); assertTrue("test_role does not exist!", getRolesResponse.hasRoles()); assertTrue("any cluster permission should be authorized", - Role.builder(getRolesResponse.roles()[0], null).build().cluster().check("cluster:admin/foo", request, authentication)); + Role.builder(getRolesResponse.roles()[0], null, Automatons.EMPTY).build() + .cluster() + .check("cluster:admin/foo", request, authentication)); preparePutRole("test_role") .cluster("none") @@ -389,7 +392,9 @@ public void testCreateAndUpdateRole() { assertTrue("test_role does not exist!", getRolesResponse.hasRoles()); assertFalse("no cluster permission should be authorized", - Role.builder(getRolesResponse.roles()[0], null).build().cluster().check("cluster:admin/bar", request, authentication)); + Role.builder(getRolesResponse.roles()[0], null, Automatons.EMPTY).build() + .cluster() + .check("cluster:admin/bar", request, authentication)); } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index b61e6dab7ba41..e011926303b8b 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -530,7 +530,8 @@ Collection createComponents(Client client, ThreadPool threadPool, Cluste final CompositeRolesStore allRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, reservedRolesStore, privilegeStore, rolesProviders, threadPool.getThreadContext(), getLicenseState(), fieldPermissionsCache, apiKeyService, - serviceAccountService, dlsBitsetCache.get(), new DeprecationRoleDescriptorConsumer(clusterService, threadPool)); + serviceAccountService, dlsBitsetCache.get(), expressionResolver, + new DeprecationRoleDescriptorConsumer(clusterService, threadPool)); securityIndex.get().addIndexStateListener(allRolesStore::onSecurityIndexStateChange); // to keep things simple, just invalidate all cached entries on license change. this happens so rarely that the impact should be diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java index f2de500a6cb8a..c77e42ed45e96 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java @@ -9,9 +9,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ContextPreservingActionListener; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.core.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; @@ -113,13 +115,17 @@ public class CompositeRolesStore { private final boolean isAnonymousEnabled; private final List, ActionListener>> builtInRoleProviders; private final List, ActionListener>> allRoleProviders; + private final Role superuserRole; + private final Role xpackUserRole; + private final Role asyncSearchUserRole; + private final Automaton restrictedIndicesAutomaton; public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, NativeRolesStore nativeRolesStore, ReservedRolesStore reservedRolesStore, NativePrivilegeStore privilegeStore, List, ActionListener>> rolesProviders, ThreadContext threadContext, XPackLicenseState licenseState, FieldPermissionsCache fieldPermissionsCache, ApiKeyService apiKeyService, ServiceAccountService serviceAccountService, - DocumentSubsetBitsetCache dlsBitsetCache, + DocumentSubsetBitsetCache dlsBitsetCache, IndexNameExpressionResolver resolver, Consumer> effectiveRoleDescriptorsConsumer) { this.fileRolesStore = Objects.requireNonNull(fileRolesStore); this.dlsBitsetCache = Objects.requireNonNull(dlsBitsetCache); @@ -137,7 +143,7 @@ public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, Nat builder.setMaximumWeight(cacheSize); } this.roleCache = builder.build(); - this.roleCacheHelper = new CacheIteratorHelper(roleCache); + this.roleCacheHelper = new CacheIteratorHelper<>(roleCache); this.threadContext = threadContext; CacheBuilder nlcBuilder = CacheBuilder.builder(); final int nlcCacheSize = NEGATIVE_LOOKUP_CACHE_SIZE_SETTING.get(settings); @@ -157,6 +163,14 @@ public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, Nat } this.anonymousUser = new AnonymousUser(settings); this.isAnonymousEnabled = AnonymousUser.isAnonymousEnabled(settings); + this.restrictedIndicesAutomaton = resolver.getSystemNameAutomaton(); + this.superuserRole = Role.builder( + ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR, + fieldPermissionsCache, + restrictedIndicesAutomaton + ).build(); + xpackUserRole = Role.builder(XPackUser.ROLE_DESCRIPTOR, fieldPermissionsCache, restrictedIndicesAutomaton).build(); + asyncSearchUserRole = Role.builder(AsyncSearchUser.ROLE_DESCRIPTOR, fieldPermissionsCache, restrictedIndicesAutomaton).build(); } public void roles(Set roleNames, ActionListener roleActionListener) { @@ -207,6 +221,16 @@ void logDeprecatedRoles(Set roleDescriptors) { }); } + // for testing + Role getXpackUserRole() { + return xpackUserRole; + } + + // for testing + Role getAsyncSearchUserRole() { + return asyncSearchUserRole; + } + public void getRoles(User user, Authentication authentication, ActionListener roleActionListener) { // we need to special case the internal users in this method, if we apply the anonymous roles to every user including these system // user accounts then we run into the chance of a deadlock because then we need to get a role that we may be trying to get as the @@ -218,15 +242,15 @@ public void getRoles(User user, Authentication authentication, ActionListener roleDesc private void buildThenMaybeCacheRole(RoleKey roleKey, Collection roleDescriptors, Set missing, boolean tryCache, long invalidationCounter, ActionListener listener) { logger.trace("Building role from descriptors [{}] for names [{}] from source [{}]", roleDescriptors, roleKey.names, roleKey.source); - buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, privilegeStore, ActionListener.wrap(role -> { - if (role != null && tryCache) { - try (ReleasableLock ignored = roleCacheHelper.acquireUpdateLock()) { - /* this is kinda spooky. We use a read/write lock to ensure we don't modify the cache if we hold - * the write lock (fetching stats for instance - which is kinda overkill?) but since we fetching - * stuff in an async fashion we need to make sure that if the cache got invalidated since we - * started the request we don't put a potential stale result in the cache, hence the - * numInvalidation.get() comparison to the number of invalidation when we started. we just try to - * be on the safe side and don't cache potentially stale results - */ - if (invalidationCounter == numInvalidation.get()) { - roleCache.computeIfAbsent(roleKey, (s) -> role); + buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, privilegeStore, restrictedIndicesAutomaton, + ActionListener.wrap(role -> { + if (role != null && tryCache) { + try (ReleasableLock ignored = roleCacheHelper.acquireUpdateLock()) { + /* this is kinda spooky. We use a read/write lock to ensure we don't modify the cache if we hold + * the write lock (fetching stats for instance - which is kinda overkill?) but since we fetching + * stuff in an async fashion we need to make sure that if the cache got invalidated since we + * started the request we don't put a potential stale result in the cache, hence the + * numInvalidation.get() comparison to the number of invalidation when we started. we just try to + * be on the safe side and don't cache potentially stale results + */ + if (invalidationCounter == numInvalidation.get()) { + roleCache.computeIfAbsent(roleKey, (s) -> role); + } } - } - for (String missingRole : missing) { - negativeLookupCache.computeIfAbsent(missingRole, s -> Boolean.TRUE); + for (String missingRole : missing) { + negativeLookupCache.computeIfAbsent(missingRole, s -> Boolean.TRUE); + } } - } - listener.onResponse(role); - }, listener::onFailure)); + listener.onResponse(role); + }, listener::onFailure) + ); } private void buildAndCacheRoleForApiKey(Authentication authentication, boolean limitedBy, ActionListener roleActionListener) { @@ -420,7 +446,8 @@ private String names(Collection descriptors) { } public static void buildRoleFromDescriptors(Collection roleDescriptors, FieldPermissionsCache fieldPermissionsCache, - NativePrivilegeStore privilegeStore, ActionListener listener) { + NativePrivilegeStore privilegeStore, Automaton restrictedIndicesAutomaton, + ActionListener listener) { if (roleDescriptors.isEmpty()) { listener.onResponse(Role.EMPTY); return; @@ -463,19 +490,27 @@ public static void buildRoleFromDescriptors(Collection roleDescr } final Privilege runAsPrivilege = runAs.isEmpty() ? Privilege.NONE : new Privilege(runAs, runAs.toArray(Strings.EMPTY_ARRAY)); - final Role.Builder builder = Role.builder(roleNames.toArray(new String[roleNames.size()])) + final Role.Builder builder = Role.builder(restrictedIndicesAutomaton, roleNames.toArray(Strings.EMPTY_ARRAY)) .cluster(clusterPrivileges, configurableClusterPrivileges) .runAs(runAsPrivilege); - indicesPrivilegesMap.entrySet().forEach((entry) -> { - MergeableIndicesPrivilege privilege = entry.getValue(); - builder.add(fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition), privilege.query, - IndexPrivilege.get(privilege.privileges), false, privilege.indices.toArray(Strings.EMPTY_ARRAY)); - }); - restrictedIndicesPrivilegesMap.entrySet().forEach((entry) -> { - MergeableIndicesPrivilege privilege = entry.getValue(); - builder.add(fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition), privilege.query, - IndexPrivilege.get(privilege.privileges), true, privilege.indices.toArray(Strings.EMPTY_ARRAY)); - }); + indicesPrivilegesMap.forEach((key, privilege) -> + builder.add( + fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition), + privilege.query, + IndexPrivilege.get(privilege.privileges), + false, + privilege.indices.toArray(Strings.EMPTY_ARRAY) + ) + ); + restrictedIndicesPrivilegesMap.forEach((key, privilege) -> + builder.add( + fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition), + privilege.query, + IndexPrivilege.get(privilege.privileges), + true, + privilege.indices.toArray(Strings.EMPTY_ARRAY) + ) + ); if (applicationPrivilegesMap.isEmpty()) { listener.onResponse(builder.build()); @@ -544,8 +579,8 @@ boolean isValueInNegativeLookupCache(String key) { * A mutable class that can be used to represent the combination of one or more {@link IndicesPrivileges} */ private static class MergeableIndicesPrivilege { - private Set indices; - private Set privileges; + private final Set indices; + private final Set privileges; private FieldPermissionsDefinition fieldPermissionsDefinition; private Set query = null; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java index bf40fd4e351ab..14362cfbfbd6b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/service/ElasticServiceAccountsTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.permission.Role; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.authc.service.ElasticServiceAccounts.ElasticServiceAccount; @@ -47,7 +48,8 @@ public class ElasticServiceAccountsTests extends ESTestCase { public void testElasticFleetPrivileges() { - final Role role = Role.builder(ElasticServiceAccounts.ACCOUNTS.get("elastic/fleet-server").roleDescriptor(), null).build(); + final Role role = + Role.builder(ElasticServiceAccounts.ACCOUNTS.get("elastic/fleet-server").roleDescriptor(), null, Automatons.EMPTY).build(); final Authentication authentication = mock(Authentication.class); assertThat(role.cluster().check(CreateApiKeyAction.NAME, new CreateApiKeyRequest(randomAlphaOfLengthBetween(3, 8), null, null), authentication), is(true)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index eb35b5501439a..073418054478b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -139,6 +139,7 @@ import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.ElasticUser; import org.elasticsearch.xpack.core.security.user.KibanaUser; @@ -273,7 +274,7 @@ public void setup() { callback.onResponse(Role.EMPTY); } else { CompositeRolesStore.buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, privilegesStore, - ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) + Automatons.EMPTY, ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) ); } return Void.TYPE; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java index b21bfe10a8aaa..621502688abc7 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; import java.util.List; @@ -75,7 +76,8 @@ public void testAuthorizedIndicesUserWithSomeRoles() { .build(); final PlainActionFuture future = new PlainActionFuture<>(); final Set descriptors = Sets.newHashSet(aStarRole, bRole); - CompositeRolesStore.buildRoleFromDescriptors(descriptors, new FieldPermissionsCache(Settings.EMPTY), null, future); + CompositeRolesStore.buildRoleFromDescriptors( + descriptors, new FieldPermissionsCache(Settings.EMPTY), null, Automatons.EMPTY, future); Role roles = future.actionGet(); Set list = RBACEngine.resolveAuthorizedIndicesFromRole(roles, getRequestInfo(SearchAction.NAME), metadata.getIndicesLookup()); @@ -87,21 +89,21 @@ public void testAuthorizedIndicesUserWithSomeRoles() { } public void testAuthorizedIndicesUserWithSomeRolesEmptyMetadata() { - Role role = Role.builder("role").add(IndexPrivilege.ALL, "*").build(); + Role role = Role.builder(Automatons.EMPTY, "role").add(IndexPrivilege.ALL, "*").build(); Set authorizedIndices = RBACEngine.resolveAuthorizedIndicesFromRole(role, getRequestInfo(SearchAction.NAME), Metadata.EMPTY_METADATA.getIndicesLookup()); assertTrue(authorizedIndices.isEmpty()); } public void testSecurityIndicesAreRemovedFromRegularUser() { - Role role = Role.builder("user_role").add(IndexPrivilege.ALL, "*").cluster(Set.of("all"), Set.of()).build(); + Role role = Role.builder(Automatons.EMPTY, "user_role").add(IndexPrivilege.ALL, "*").cluster(Set.of("all"), Set.of()).build(); Set authorizedIndices = RBACEngine.resolveAuthorizedIndicesFromRole(role, getRequestInfo(SearchAction.NAME), Metadata.EMPTY_METADATA.getIndicesLookup()); assertTrue(authorizedIndices.isEmpty()); } public void testSecurityIndicesAreRestrictedForDefaultRole() { - Role role = Role.builder(randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) + Role role = Role.builder(Automatons.EMPTY, randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) .add(IndexPrivilege.ALL, "*") .cluster(Set.of("all"), Set.of()) .build(); @@ -128,7 +130,7 @@ public void testSecurityIndicesAreRestrictedForDefaultRole() { } public void testSecurityIndicesAreNotRemovedFromUnrestrictedRole() { - Role role = Role.builder(randomAlphaOfLength(8)) + Role role = Role.builder(Automatons.EMPTY, randomAlphaOfLength(8)) .add(FieldPermissions.DEFAULT, null, IndexPrivilege.ALL, true, "*") .cluster(Set.of("all"), Set.of()) .build(); @@ -190,7 +192,8 @@ public void testDataStreamsAreNotIncludedInAuthorizedIndices() { .build(); final PlainActionFuture future = new PlainActionFuture<>(); final Set descriptors = Sets.newHashSet(aStarRole, bRole); - CompositeRolesStore.buildRoleFromDescriptors(descriptors, new FieldPermissionsCache(Settings.EMPTY), null, future); + CompositeRolesStore.buildRoleFromDescriptors( + descriptors, new FieldPermissionsCache(Settings.EMPTY), null, Automatons.EMPTY, future); Role roles = future.actionGet(); Set list = RBACEngine.resolveAuthorizedIndicesFromRole(roles, getRequestInfo(SearchAction.NAME), metadata.getIndicesLookup()); @@ -235,7 +238,8 @@ public void testDataStreamsAreIncludedInAuthorizedIndices() { .build(); final PlainActionFuture future = new PlainActionFuture<>(); final Set descriptors = Sets.newHashSet(aStarRole, bRole); - CompositeRolesStore.buildRoleFromDescriptors(descriptors, new FieldPermissionsCache(Settings.EMPTY), null, future); + CompositeRolesStore.buildRoleFromDescriptors( + descriptors, new FieldPermissionsCache(Settings.EMPTY), null, Automatons.EMPTY, future); Role roles = future.actionGet(); TransportRequest request = new ResolveIndexAction.Request(new String[]{"a*"}); AuthorizationEngine.RequestInfo requestInfo = new AuthorizationEngine.RequestInfo(null, request, SearchAction.NAME); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index 3b791646a970c..d3a8b871de94d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -64,6 +64,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.security.user.XPackSecurityUser; import org.elasticsearch.xpack.core.security.user.XPackUser; @@ -276,7 +277,7 @@ public void setup() { callback.onResponse(Role.EMPTY); } else { CompositeRolesStore.buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, null, - ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) + Automatons.EMPTY, ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) ); } return Void.TYPE; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java index d766467b0216a..df20647c97ad1 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java @@ -18,8 +18,8 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.metadata.DataStreamTestHelper; import org.elasticsearch.cluster.metadata.DataStream; +import org.elasticsearch.cluster.metadata.DataStreamTestHelper; import org.elasticsearch.cluster.metadata.IndexAbstraction; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.common.Strings; @@ -63,6 +63,7 @@ import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.Privilege; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; @@ -354,7 +355,7 @@ public void testNamedIndexPrivilegesMatchApplicableActions() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test1") + Role role = Role.builder(Automatons.EMPTY, "test1") .cluster(Collections.singleton("all"), Collections.emptyList()) .add(IndexPrivilege.WRITE, "academy") .build(); @@ -396,7 +397,7 @@ public void testMatchSubsetOfPrivileges() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test2") + Role role = Role.builder(Automatons.EMPTY, "test2") .cluster(Set.of("monitor"), Set.of()) .add(IndexPrivilege.INDEX, "academy") .add(IndexPrivilege.WRITE, "initiative") @@ -455,7 +456,7 @@ public void testMatchNothing() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test3") + Role role = Role.builder(Automatons.EMPTY, "test3") .cluster(Set.of("monitor"), Set.of()) .build(); RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); @@ -494,7 +495,7 @@ public void testWildcardHandling() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test3") + Role role = Role.builder(Automatons.EMPTY, "test3") .add(IndexPrivilege.ALL, "logstash-*", "foo?") .add(IndexPrivilege.READ, "abc*") .add(IndexPrivilege.WRITE, "*xyz") @@ -585,7 +586,7 @@ public void testCheckingIndexPermissionsDefinedOnDifferentPatterns() throws Exce User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test-write") + Role role = Role.builder(Automatons.EMPTY, "test-write") .add(IndexPrivilege.INDEX, "apache-*") .add(IndexPrivilege.DELETE, "apache-2016-*") .build(); @@ -613,7 +614,7 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { when(authentication.getUser()).thenReturn(user); final String patternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, randomIntBetween(2, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.length() - 2)); - Role role = Role.builder("role") + Role role = Role.builder(Automatons.EMPTY, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, patternPrefix + "*") .build(); RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); @@ -701,7 +702,7 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) .put("index", false).map()).build())); - role = Role.builder("role") + role = Role.builder(Automatons.EMPTY, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, true, patternPrefix + "*") .build(); authzInfo = new RBACAuthorizationInfo(role, null); @@ -724,7 +725,7 @@ public void testCheckExplicitRestrictedIndexPermissions() throws Exception { when(authentication.getUser()).thenReturn(user); final boolean restrictedIndexPermission = randomBoolean(); final boolean restrictedMonitorPermission = randomBoolean(); - Role role = Role.builder("role") + Role role = Role.builder(Automatons.EMPTY, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, restrictedIndexPermission, ".sec*") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.MONITOR, restrictedMonitorPermission, ".security*") .build(); @@ -767,7 +768,7 @@ public void testCheckRestrictedIndexWildcardPermissions() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("role") + Role role = Role.builder(Automatons.EMPTY, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, ".sec*") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.MONITOR, true, ".security*") .build(); @@ -804,7 +805,7 @@ public void testCheckRestrictedIndexWildcardPermissions() throws Exception { .put("index", false).put("monitor", true).map()).build() )); - role = Role.builder("role") + role = Role.builder(Automatons.EMPTY, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, true, ".sec*") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.MONITOR, false, ".security*") .build(); @@ -854,7 +855,7 @@ public void testCheckingApplicationPrivilegesOnDifferentApplicationsAndResources User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test-role") + Role role = Role.builder(Automatons.EMPTY, "test-role") .addApplicationPrivilege(app1Read, Collections.singleton("foo/*")) .addApplicationPrivilege(app1All, Collections.singleton("foo/bar/baz")) .addApplicationPrivilege(app2Read, Collections.singleton("foo/bar/*")) @@ -917,7 +918,7 @@ public void testCheckingApplicationPrivilegesWithComplexNames() throws Exception User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test-write") + Role role = Role.builder(Automatons.EMPTY, "test-write") .addApplicationPrivilege(priv1, Collections.singleton("user/*/name")) .build(); RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); @@ -952,7 +953,7 @@ public void testIsCompleteMatch() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test-write") + Role role = Role.builder(Automatons.EMPTY, "test-write") .cluster(Set.of("monitor"), Set.of()) .add(IndexPrivilege.READ, "read-*") .add(IndexPrivilege.ALL, "all-*") @@ -1004,7 +1005,7 @@ public void testIsCompleteMatch() throws Exception { public void testBuildUserPrivilegeResponse() { final ManageApplicationPrivileges manageApplicationPrivileges = new ManageApplicationPrivileges(Sets.newHashSet("app01", "app02")); final BytesArray query = new BytesArray("{\"term\":{\"public\":true}}"); - final Role role = Role.builder("test", "role") + final Role role = Role.builder(Automatons.EMPTY, "test", "role") .cluster(Sets.newHashSet("monitor", "manage_watcher"), Collections.singleton(manageApplicationPrivileges)) .add(IndexPrivilege.get(Sets.newHashSet("read", "write")), "index-1") .add(IndexPrivilege.ALL, "index-2", "index-3") @@ -1051,7 +1052,7 @@ public void testBackingIndicesAreIncludedForAuthorizedDataStreams() { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test1") + Role role = Role.builder(Automatons.EMPTY, "test1") .cluster(Collections.singleton("all"), Collections.emptyList()) .add(IndexPrivilege.READ, dataStreamName) .build(); @@ -1083,7 +1084,7 @@ public void testExplicitMappingUpdatesAreNotGrantedWithIngestPrivileges() { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder("test1") + Role role = Role.builder(Automatons.EMPTY, "test1") .cluster(Collections.emptySet(), Collections.emptyList()) .add(IndexPrivilege.CREATE, "my_*") .add(IndexPrivilege.WRITE, "my_data*") diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index 9a9925969f52c..d7b69f9442127 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; +import org.elasticsearch.xpack.core.security.support.Automatons; import java.io.IOException; import java.util.ArrayList; @@ -67,7 +68,7 @@ public void testAuthorize() { // basics: Set query = Collections.singleton(new BytesArray("{}")); String[] fields = new String[]{"_field"}; - Role role = Role.builder("_role") + Role role = Role.builder(Automatons.EMPTY, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_index") .build(); IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), lookup, fieldPermissionsCache); @@ -79,7 +80,7 @@ public void testAuthorize() { assertThat(permissions.getIndexPermissions("_index").getDocumentPermissions().getQueries(), equalTo(query)); // no document level security: - role = Role.builder("_role") + role = Role.builder(Automatons.EMPTY, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), null, IndexPrivilege.ALL, randomBoolean(), "_index") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), lookup, fieldPermissionsCache); @@ -90,7 +91,9 @@ public void testAuthorize() { assertThat(permissions.getIndexPermissions("_index").getDocumentPermissions().getQueries(), nullValue()); // no field level security: - role = Role.builder("_role").add(new FieldPermissions(), query, IndexPrivilege.ALL, randomBoolean(), "_index").build(); + role = Role.builder(Automatons.EMPTY, "_role") + .add(new FieldPermissions(), query, IndexPrivilege.ALL, randomBoolean(), "_index") + .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), lookup, fieldPermissionsCache); assertThat(permissions.getIndexPermissions("_index"), notNullValue()); assertFalse(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity()); @@ -99,7 +102,7 @@ public void testAuthorize() { assertThat(permissions.getIndexPermissions("_index").getDocumentPermissions().getQueries(), equalTo(query)); // index group associated with an alias: - role = Role.builder("_role") + role = Role.builder(Automatons.EMPTY, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_alias") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), lookup, fieldPermissionsCache); @@ -120,7 +123,7 @@ public void testAuthorize() { // match all fields String[] allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"}, new String[]{randomAlphaOfLengthBetween(1, 10), "*"}); - role = Role.builder("_role") + role = Role.builder(Automatons.EMPTY, "_role") .add(new FieldPermissions(fieldPermissionDef(allFields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_alias") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), lookup, fieldPermissionsCache); @@ -150,7 +153,7 @@ public void testAuthorize() { Set fooQuery = Collections.singleton(new BytesArray("{foo}")); allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"}, new String[]{randomAlphaOfLengthBetween(1, 10), "*"}); - role = Role.builder("_role") + role = Role.builder(Automatons.EMPTY, "_role") .add(new FieldPermissions(fieldPermissionDef(allFields, null)), fooQuery, IndexPrivilege.ALL, randomBoolean(), "_alias") .add(new FieldPermissions(fieldPermissionDef(allFields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_alias") .build(); @@ -190,7 +193,7 @@ public void testAuthorizeMultipleGroupsMixedDls() { Set query = Collections.singleton(new BytesArray("{}")); String[] fields = new String[]{"_field"}; - Role role = Role.builder("_role") + Role role = Role.builder(Automatons.EMPTY, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_index") .add(new FieldPermissions(fieldPermissionDef(null, null)), null, IndexPrivilege.ALL, randomBoolean(), "*") .build(); @@ -245,9 +248,10 @@ public void testCorePermissionAuthorize() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), - "a1"); + Automatons.EMPTY, "a1"); IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.READ, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), "a1"); + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), Automatons.EMPTY, + "a1"); IndicesPermission core = new IndicesPermission(group1, group2); Map authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), lookup, fieldPermissionsCache); @@ -262,15 +266,16 @@ public void testCorePermissionAuthorize() { assertFalse(core.check("unknown")); // test with two indices - group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), "a1"); + group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), Automatons.EMPTY, "a1"); group2 = new IndicesPermission.Group(IndexPrivilege.ALL, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), "a1"); + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), Automatons.EMPTY, + "a1"); IndicesPermission.Group group3 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(fieldPermissionDef(new String[] { "*_field" }, new String[] { "denied_field" })), null, - randomBoolean(), "a2"); + randomBoolean(), Automatons.EMPTY, "a2"); IndicesPermission.Group group4 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(fieldPermissionDef(new String[] { "*_field2" }, new String[] { "denied_field2" })), null, - randomBoolean(), "a2"); + randomBoolean(), Automatons.EMPTY, "a2"); core = new IndicesPermission(group1, group2, group3, group4); authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), lookup, fieldPermissionsCache); assertFalse(authzMap.get("a1").getFieldPermissions().hasFieldLevelSecurity()); @@ -294,7 +299,7 @@ public void testErrorMessageIfIndexPatternIsTooComplex() { indices.add("*" + prefix + "*" + suffixBegin + "*"); } final ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, - () -> new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), + () -> new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), Automatons.EMPTY, indices.toArray(Strings.EMPTY_ARRAY))); assertThat(e.getMessage(), containsString(indices.get(0))); assertThat(e.getMessage(), containsString("too complex to evaluate")); @@ -316,7 +321,8 @@ public void testSecurityIndicesPermissions() { SortedMap lookup = metadata.getIndicesLookup(); // allow_restricted_indices: false - IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, "*"); + IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, + Automatons.EMPTY, "*"); Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache); @@ -324,7 +330,7 @@ public void testSecurityIndicesPermissions() { assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(false)); // allow_restricted_indices: true - group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, "*"); + group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, Automatons.EMPTY, "*"); authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache); @@ -346,13 +352,14 @@ public void testAsyncSearchIndicesPermissions() { SortedMap lookup = metadata.getIndicesLookup(); // allow_restricted_indices: false - IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, "*"); + IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, + Automatons.EMPTY, "*"); Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(false)); // allow_restricted_indices: true - group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, "*"); + group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, Automatons.EMPTY, "*"); authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true)); @@ -377,7 +384,7 @@ public void testAuthorizationForBackingIndices() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); SortedMap lookup = metadata.getIndicesLookup(); IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.READ, new FieldPermissions(), null, false, - dataStreamName); + Automatons.EMPTY, dataStreamName); Map authzMap = new IndicesPermission(group).authorize( SearchAction.NAME, Sets.newHashSet(backingIndices.stream().map(im -> im.getIndex().getName()).collect(Collectors.toList())), @@ -388,7 +395,8 @@ public void testAuthorizationForBackingIndices() { assertThat(authzMap.get(im.getIndex().getName()).isGranted(), is(true)); } - group = new IndicesPermission.Group(IndexPrivilege.CREATE_DOC, new FieldPermissions(), null, false, dataStreamName); + group = new IndicesPermission.Group(IndexPrivilege.CREATE_DOC, new FieldPermissions(), null, false, Automatons.EMPTY, + dataStreamName); authzMap = new IndicesPermission(group).authorize( randomFrom(PutMappingAction.NAME, AutoPutMappingAction.NAME), Sets.newHashSet(backingIndices.stream().map(im -> im.getIndex().getName()).collect(Collectors.toList())), @@ -422,9 +430,10 @@ public void testAuthorizationForMappingUpdates() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.INDEX, new FieldPermissions(), null, randomBoolean(), - "test*"); + Automatons.EMPTY, "test*"); IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.WRITE, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), "test_write*"); + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), Automatons.EMPTY, + "test_write*"); IndicesPermission core = new IndicesPermission(group1, group2); Map authzMap = core.authorize(PutMappingAction.NAME, Sets.newHashSet("test1", "test_write1"), lookup, fieldPermissionsCache); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/interceptor/ResizeRequestInterceptorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/interceptor/ResizeRequestInterceptorTests.java index 518ad872577e0..a24e25254785e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/interceptor/ResizeRequestInterceptorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/interceptor/ResizeRequestInterceptorTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.audit.AuditTrailService; @@ -111,7 +112,7 @@ public void testResizeRequestInterceptorThrowsWhenTargetHasGreaterPermissions() when(threadPool.getThreadContext()).thenReturn(threadContext); AuditTrailService auditTrailService = new AuditTrailService(Collections.emptyList(), licenseState); final Authentication authentication = new Authentication(new User("john", "role"), new RealmRef(null, null, null), null); - Role role = Role.builder() + Role role = Role.builder(Automatons.EMPTY) .add(IndexPrivilege.ALL, "target") .add(IndexPrivilege.READ, "source") .build(); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/PermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/PermissionTests.java index dd22b8519eba2..ebb346fd13730 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/PermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/PermissionTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.Privilege; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.junit.Before; import java.util.List; @@ -31,7 +32,7 @@ public class PermissionTests extends ESTestCase { @Before public void init() { - Role.Builder builder = Role.builder("test"); + Role.Builder builder = Role.builder(Automatons.EMPTY, "test"); builder.add(MONITOR, "test_*", "/foo.*/"); builder.add(READ, "baz_*foo", "/fool.*bar/"); builder.add(MONITOR, "/bar.*/"); @@ -69,7 +70,7 @@ public void testAllowedIndicesMatcherActionCaching() throws Exception { } public void testBuildEmptyRole() { - Role.Builder permission = Role.builder(new String[] { "some_role" }); + Role.Builder permission = Role.builder(Automatons.EMPTY, "some_role"); Role role = permission.build(); assertThat(role, notNullValue()); assertThat(role.cluster(), notNullValue()); @@ -78,7 +79,7 @@ public void testBuildEmptyRole() { } public void testRunAs() { - Role permission = Role.builder("some_role") + Role permission = Role.builder(Automatons.EMPTY, "some_role") .runAs(new Privilege("name", "user1", "run*")) .build(); assertThat(permission.runAs().check("user1"), is(true)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 8c2776b3bace6..939dc14e48e13 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -8,15 +8,21 @@ import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsAction; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction; import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; +import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction; +import org.elasticsearch.action.delete.DeleteAction; import org.elasticsearch.action.get.GetAction; import org.elasticsearch.action.index.IndexAction; +import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.action.update.UpdateAction; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.metadata.IndexAbstraction; import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.core.Nullable; @@ -30,6 +36,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.indices.TestIndexNameExpressionResolver; import org.elasticsearch.license.License.OperationMode; import org.elasticsearch.license.TestUtils.UpdatableLicenseState; import org.elasticsearch.license.XPackLicenseState; @@ -60,18 +67,24 @@ import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; +import org.elasticsearch.xpack.core.security.index.IndexAuditTrailField; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; +import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.support.MetadataUtils; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.SystemUser; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.security.user.XPackUser; +import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchAction; import org.elasticsearch.xpack.security.audit.AuditUtil; +import org.elasticsearch.xpack.security.audit.index.IndexNameResolver; import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; +import org.hamcrest.Matchers; +import org.joda.time.DateTime; import java.io.IOException; import java.time.Clock; @@ -127,6 +140,7 @@ public class CompositeRolesStoreTests extends ESTestCase { .put(XPackSettings.SECURITY_ENABLED.getKey(), true) .build(); + private final IndexNameExpressionResolver resolver = TestIndexNameExpressionResolver.newInstance(); private final FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY); private final String concreteSecurityIndexName = randomFrom( RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6, RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7); @@ -360,7 +374,7 @@ public void testNegativeLookupsCacheDisabled() { final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(settings), new XPackLicenseState(settings, () -> 0), cache, mock(ApiKeyService.class), - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor @@ -400,7 +414,7 @@ public void testNegativeLookupsAreNotCachedWithFailures() { new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor @@ -488,7 +502,7 @@ public void testCustomRolesProviders() { new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, inMemoryProvider2), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), - cache, mock(ApiKeyService.class), mock(ServiceAccountService.class), documentSubsetBitsetCache, + cache, mock(ApiKeyService.class), mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); final Set roleNames = Sets.newHashSet("roleA", "roleB", "unknown"); @@ -550,7 +564,8 @@ public void testMergingRolesWithFls() { }, null); FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY); PlainActionFuture future = new PlainActionFuture<>(); - CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(flsRole, addsL1Fields), cache, null, future); + CompositeRolesStore.buildRoleFromDescriptors( + Sets.newHashSet(flsRole, addsL1Fields), cache, null, Automatons.EMPTY, future); Role role = future.actionGet(); Metadata metadata = Metadata.builder() @@ -646,7 +661,7 @@ public ClusterPermission.Builder buildPermission(ClusterPermission.Builder build listener.onResponse(set); return null; }).when(privilegeStore).getPrivileges(any(Collection.class), any(Collection.class), any(ActionListener.class)); - CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(role1, role2), cache, privilegeStore, future); + CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(role1, role2), cache, privilegeStore, Automatons.EMPTY, future); Role role = future.actionGet(); assertThat(role.cluster().check(ClusterStateAction.NAME, randomFrom(request1, request2, request3), authentication), equalTo(true)); @@ -718,7 +733,7 @@ public void testCustomRolesProviderFailures() throws Exception { mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, failingProvider), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), mock(ServiceAccountService.class), - documentSubsetBitsetCache, rds -> effectiveRoleDescriptors.set(rds)); + documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); final Set roleNames = Sets.newHashSet("roleA", "roleB", "unknown"); PlainActionFuture future = new PlainActionFuture<>(); @@ -767,7 +782,7 @@ public void testCustomRolesProvidersLicensing() { Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState, cache, mock(ApiKeyService.class), mock(ServiceAccountService.class), - documentSubsetBitsetCache, rds -> effectiveRoleDescriptors.set(rds)); + documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); Set roleNames = Sets.newHashSet("roleA"); PlainActionFuture future = new PlainActionFuture<>(); @@ -783,7 +798,7 @@ Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(Nativ Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState, cache, mock(ApiKeyService.class), mock(ServiceAccountService.class), - documentSubsetBitsetCache, rds -> effectiveRoleDescriptors.set(rds)); + documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); // these licenses allow custom role providers xPackLicenseState.update(randomFrom(OperationMode.PLATINUM, OperationMode.ENTERPRISE, OperationMode.TRIAL), true, Long.MAX_VALUE, null); @@ -802,7 +817,7 @@ Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(Nativ Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState, cache, mock(ApiKeyService.class), mock(ServiceAccountService.class), - documentSubsetBitsetCache, rds -> effectiveRoleDescriptors.set(rds)); + documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); xPackLicenseState.update(randomFrom(OperationMode.PLATINUM, OperationMode.ENTERPRISE, OperationMode.TRIAL), false, Long.MAX_VALUE, null); roleNames = Sets.newHashSet("roleA"); @@ -837,7 +852,7 @@ public void testCacheClearOnIndexHealthChange() { Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(Settings.EMPTY), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> {}) { @Override public void invalidateAll() { @@ -892,7 +907,7 @@ public void testCacheClearOnIndexOutOfDateChange() { fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), - mock(ServiceAccountService.class), documentSubsetBitsetCache, rds -> {}) { + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> {}) { @Override public void invalidateAll() { numInvalidation.incrementAndGet(); @@ -989,7 +1004,7 @@ public void testDoesNotUseRolesStoreForXPacAndAsyncSearchUser() { new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor @@ -998,7 +1013,7 @@ public void testDoesNotUseRolesStoreForXPacAndAsyncSearchUser() { Authentication auth = new Authentication(XPackUser.INSTANCE, new RealmRef("name", "type", "node"), null); compositeRolesStore.getRoles(XPackUser.INSTANCE, auth, rolesFuture); Role roles = rolesFuture.actionGet(); - assertThat(roles, equalTo(XPackUser.ROLE)); + assertThat(roles, equalTo(compositeRolesStore.getXpackUserRole())); assertThat(effectiveRoleDescriptors.get(), is(nullValue())); verifyNoMoreInteractions(fileRolesStore, nativeRolesStore, reservedRolesStore); @@ -1007,7 +1022,7 @@ public void testDoesNotUseRolesStoreForXPacAndAsyncSearchUser() { auth = new Authentication(AsyncSearchUser.INSTANCE, new RealmRef("name", "type", "node"), null); compositeRolesStore.getRoles(AsyncSearchUser.INSTANCE, auth, rolesFuture); roles = rolesFuture.actionGet(); - assertThat(roles, equalTo(AsyncSearchUser.ROLE)); + assertThat(roles, equalTo(compositeRolesStore.getAsyncSearchUserRole())); assertThat(effectiveRoleDescriptors.get(), is(nullValue())); verifyNoMoreInteractions(fileRolesStore, nativeRolesStore, reservedRolesStore); } @@ -1031,7 +1046,7 @@ public void testGetRolesForSystemUserThrowsException() { new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, mock(ApiKeyService.class), - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, @@ -1070,7 +1085,7 @@ public void testApiKeyAuthUsesApiKeyService() throws Exception { new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, nativePrivStore, Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, apiKeyService, - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); AuditUtil.getOrGenerateRequestId(threadContext); final Version version = randomFrom(Version.CURRENT, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_8_1)); @@ -1124,7 +1139,7 @@ public void testApiKeyAuthUsesApiKeyServiceWithScopedRole() throws Exception { new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, nativePrivStore, Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS, () -> 0), cache, apiKeyService, - mock(ServiceAccountService.class), documentSubsetBitsetCache, + mock(ServiceAccountService.class), documentSubsetBitsetCache, resolver, rds -> effectiveRoleDescriptors.set(rds)); AuditUtil.getOrGenerateRequestId(threadContext); final Version version = randomFrom(Version.CURRENT, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_8_1)); @@ -1263,6 +1278,7 @@ public void testCacheEntryIsReusedForIdenticalApiKeyRoles() { apiKeyService, mock(ServiceAccountService.class), documentSubsetBitsetCache, + resolver, rds -> effectiveRoleDescriptors.set(rds)); AuditUtil.getOrGenerateRequestId(threadContext); final BytesArray roleBytes = new BytesArray("{\"a role\": {\"cluster\": [\"all\"]}}"); @@ -1348,6 +1364,87 @@ Version.CURRENT, randomFrom(AuthenticationType.REALM, AuthenticationType.TOKEN, AuthenticationType.ANONYMOUS), Collections.emptyMap()); } + public void testXPackUserCanAccessNonSecurityIndices() { + for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { + Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); + IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); + if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + assertThat(predicate.test(index), Matchers.is(true)); + } + index = mockIndexAbstraction("." + randomAlphaOfLengthBetween(3, 12)); + if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + assertThat(predicate.test(index), Matchers.is(true)); + } + } + } + + public void testXPackUserCannotAccessRestrictedIndices() { + for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { + Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); + for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { + assertThat(predicate.test(mockIndexAbstraction(index)), Matchers.is(false)); + } + assertThat(predicate.test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), + Matchers.is(false)); + } + } + + public void testXPackUserCanReadAuditTrail() { + final String action = randomFrom(GetAction.NAME, SearchAction.NAME); + final Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); + assertThat(predicate.test(mockIndexAbstraction(getAuditLogName())), Matchers.is(true)); + } + + public void testXPackUserCannotWriteToAuditTrail() { + final String action = randomFrom(IndexAction.NAME, UpdateAction.NAME); + final Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); + assertThat(predicate.test(mockIndexAbstraction(getAuditLogName())), Matchers.is(false)); + } + + public void testAsyncSearchUserCannotAccessNonRestrictedIndices() { + for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { + Predicate predicate = getAsyncSearchUserRole().indices().allowedIndicesMatcher(action); + IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); + if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + assertThat(predicate.test(index), Matchers.is(false)); + } + index = mockIndexAbstraction("." + randomAlphaOfLengthBetween(3, 12)); + if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + assertThat(predicate.test(index), Matchers.is(false)); + } + } + } + + public void testAsyncSearchUserCanAccessOnlyAsyncSearchRestrictedIndices() { + for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { + final Predicate predicate = getAsyncSearchUserRole().indices().allowedIndicesMatcher(action); + for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { + assertThat(predicate.test(mockIndexAbstraction(index)), Matchers.is(false)); + } + assertThat(predicate.test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 3))), + Matchers.is(true)); + } + } + + public void testAsyncSearchUserHasNoClusterPrivileges() { + for (String action : Arrays.asList(ClusterStateAction.NAME, GetWatchAction.NAME, ClusterStatsAction.NAME, NodesStatsAction.NAME)) { + assertThat(getAsyncSearchUserRole().cluster().check(action, mock(TransportRequest.class), mock(Authentication.class)), + Matchers.is(false)); + } + } + + private Role getXPackUserRole() { + CompositeRolesStore compositeRolesStore = + buildCompositeRolesStore(SECURITY_ENABLED_SETTINGS, null, null, null, null, null, null, null, null, null); + return compositeRolesStore.getXpackUserRole(); + } + + private Role getAsyncSearchUserRole() { + CompositeRolesStore compositeRolesStore = + buildCompositeRolesStore(SECURITY_ENABLED_SETTINGS, null, null, null, null, null, null, null, null, null); + return compositeRolesStore.getAsyncSearchUserRole(); + } + private CompositeRolesStore buildCompositeRolesStore(Settings settings, @Nullable FileRolesStore fileRolesStore, @Nullable NativeRolesStore nativeRolesStore, @@ -1402,7 +1499,7 @@ private CompositeRolesStore buildCompositeRolesStore(Settings settings, } return new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, reservedRolesStore, privilegeStore, Collections.emptyList(), new ThreadContext(settings), licenseState, cache, apiKeyService, - serviceAccountService, documentSubsetBitsetCache, roleConsumer); + serviceAccountService, documentSubsetBitsetCache, resolver, roleConsumer); } private DocumentSubsetBitsetCache buildBitsetCache() { @@ -1442,6 +1539,12 @@ public void writeTo(StreamOutput out) throws IOException { } } + private String getAuditLogName() { + final DateTime date = new DateTime().plusDays(randomIntBetween(1, 360)); + final IndexNameResolver.Rollover rollover = randomFrom(IndexNameResolver.Rollover.values()); + return IndexNameResolver.resolve(IndexAuditTrailField.INDEX_NAME_PREFIX, date, rollover); + } + private IndexAbstraction mockIndexAbstraction(String name) { IndexAbstraction mock = mock(IndexAbstraction.class); when(mock.getName()).thenReturn(name); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java index 4b33aa6f9551c..9ed397099b381 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java @@ -8,6 +8,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; +import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.MinimizationOperations; import org.apache.lucene.util.automaton.Operations; import org.elasticsearch.common.xcontent.ParseField; @@ -35,6 +36,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.RunAsPermission; import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; +import org.elasticsearch.xpack.core.security.support.Automatons; import java.io.BufferedWriter; import java.io.IOException; @@ -69,6 +71,8 @@ public class FileRolesStoreTests extends ESTestCase { + public Automaton reservedNamesAutomaton = Automatons.EMPTY; + @Override protected NamedXContentRegistry xContentRegistry() { return new NamedXContentRegistry(singletonList(new NamedXContentRegistry.Entry(QueryBuilder.class, @@ -85,7 +89,7 @@ public void testParseFile() throws Exception { RoleDescriptor descriptor = roles.get("role1"); assertNotNull(descriptor); - Role role = Role.builder(descriptor, null).build(); + Role role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role1" })); assertThat(role.cluster(), notNullValue()); @@ -113,7 +117,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role1.ab"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role1.ab" })); assertThat(role.cluster(), notNullValue()); @@ -125,7 +129,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role2"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role2" })); assertThat(role.cluster(), notNullValue()); @@ -136,7 +140,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role3"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role3" })); assertThat(role.cluster(), notNullValue()); @@ -160,7 +164,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role_run_as"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role_run_as" })); assertThat(role.cluster(), notNullValue()); @@ -173,7 +177,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role_run_as1"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role_run_as1" })); assertThat(role.cluster(), notNullValue()); @@ -186,7 +190,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role_fields"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role_fields" })); assertThat(role.cluster(), notNullValue()); @@ -208,7 +212,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role_query"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role_query" })); assertThat(role.cluster(), notNullValue()); @@ -229,7 +233,7 @@ public void testParseFile() throws Exception { descriptor = roles.get("role_query_fields"); assertNotNull(descriptor); - role = Role.builder(descriptor, null).build(); + role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role_query_fields" })); assertThat(role.cluster(), notNullValue()); @@ -389,7 +393,7 @@ public void testAutoReload() throws Exception { descriptors = store.roleDescriptors(Collections.singleton("role5")); assertThat(descriptors, notNullValue()); assertEquals(1, descriptors.size()); - Role role = Role.builder(descriptors.iterator().next(), null).build(); + Role role = Role.builder(descriptors.iterator().next(), null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "role5" })); assertThat(role.cluster().check("cluster:monitor/foo/bar", request, authentication), is(true)); @@ -476,7 +480,7 @@ public void testThatInvalidRoleDefinitions() throws Exception { assertThat(roles, hasKey("valid_role")); RoleDescriptor descriptor = roles.get("valid_role"); assertNotNull(descriptor); - Role role = Role.builder(descriptor, null).build(); + Role role = Role.builder(descriptor, null, reservedNamesAutomaton).build(); assertThat(role, notNullValue()); assertThat(role.names(), equalTo(new String[] { "valid_role" })); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/AsyncSearchUserTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/AsyncSearchUserTests.java deleted file mode 100644 index 3d8da744f6ec7..0000000000000 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/AsyncSearchUserTests.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -package org.elasticsearch.xpack.security.user; - -import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsAction; -import org.elasticsearch.action.admin.cluster.state.ClusterStateAction; -import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction; -import org.elasticsearch.action.delete.DeleteAction; -import org.elasticsearch.action.get.GetAction; -import org.elasticsearch.action.index.IndexAction; -import org.elasticsearch.action.search.SearchAction; -import org.elasticsearch.cluster.metadata.IndexAbstraction; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.transport.TransportRequest; -import org.elasticsearch.xpack.core.security.authc.Authentication; -import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; -import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; -import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchAction; -import org.hamcrest.Matchers; - -import java.util.Arrays; -import java.util.function.Predicate; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class AsyncSearchUserTests extends ESTestCase { - - public void testAsyncSearchUserCannotAccessNonRestrictedIndices() { - for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { - Predicate predicate = AsyncSearchUser.ROLE.indices().allowedIndicesMatcher(action); - IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { - assertThat(predicate.test(index), Matchers.is(false)); - } - index = mockIndexAbstraction("." + randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { - assertThat(predicate.test(index), Matchers.is(false)); - } - } - } - - public void testAsyncSearchUserCanAccessOnlyAsyncSearchRestrictedIndices() { - for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { - final Predicate predicate = AsyncSearchUser.ROLE.indices().allowedIndicesMatcher(action); - for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { - assertThat(predicate.test(mockIndexAbstraction(index)), Matchers.is(false)); - } - assertThat(predicate.test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 3))), - Matchers.is(true)); - } - } - - public void testAsyncSearchUserHasNoClusterPrivileges() { - for (String action : Arrays.asList(ClusterStateAction.NAME, GetWatchAction.NAME, ClusterStatsAction.NAME, NodesStatsAction.NAME)) { - assertThat(AsyncSearchUser.ROLE.cluster().check(action, mock(TransportRequest.class), mock(Authentication.class)), - Matchers.is(false)); - } - } - - private IndexAbstraction mockIndexAbstraction(String name) { - IndexAbstraction mock = mock(IndexAbstraction.class); - when(mock.getName()).thenReturn(name); - when(mock.getType()).thenReturn(randomFrom(IndexAbstraction.Type.CONCRETE_INDEX, - IndexAbstraction.Type.ALIAS, IndexAbstraction.Type.DATA_STREAM)); - return mock; - } -} diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java deleted file mode 100644 index e6d1516840f21..0000000000000 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -package org.elasticsearch.xpack.security.user; - -import org.elasticsearch.action.delete.DeleteAction; -import org.elasticsearch.action.get.GetAction; -import org.elasticsearch.action.index.IndexAction; -import org.elasticsearch.action.search.SearchAction; -import org.elasticsearch.action.update.UpdateAction; -import org.elasticsearch.cluster.metadata.IndexAbstraction; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.core.security.index.IndexAuditTrailField; -import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; -import org.elasticsearch.xpack.core.security.user.XPackUser; -import org.elasticsearch.xpack.security.audit.index.IndexNameResolver; -import org.hamcrest.Matchers; -import org.joda.time.DateTime; - -import java.util.Arrays; -import java.util.function.Predicate; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class XPackUserTests extends ESTestCase { - - public void testXPackUserCanAccessNonSecurityIndices() { - for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { - Predicate predicate = XPackUser.ROLE.indices().allowedIndicesMatcher(action); - IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { - assertThat(predicate.test(index), Matchers.is(true)); - } - index = mockIndexAbstraction("." + randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { - assertThat(predicate.test(index), Matchers.is(true)); - } - } - } - - public void testXPackUserCannotAccessRestrictedIndices() { - for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { - Predicate predicate = XPackUser.ROLE.indices().allowedIndicesMatcher(action); - for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { - assertThat(predicate.test(mockIndexAbstraction(index)), Matchers.is(false)); - } - assertThat(predicate.test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), - Matchers.is(false)); - } - } - - public void testXPackUserCanReadAuditTrail() { - final String action = randomFrom(GetAction.NAME, SearchAction.NAME); - final Predicate predicate = XPackUser.ROLE.indices().allowedIndicesMatcher(action); - assertThat(predicate.test(mockIndexAbstraction(getAuditLogName())), Matchers.is(true)); - } - - public void testXPackUserCannotWriteToAuditTrail() { - final String action = randomFrom(IndexAction.NAME, UpdateAction.NAME); - final Predicate predicate = XPackUser.ROLE.indices().allowedIndicesMatcher(action); - assertThat(predicate.test(mockIndexAbstraction(getAuditLogName())), Matchers.is(false)); - } - - private String getAuditLogName() { - final DateTime date = new DateTime().plusDays(randomIntBetween(1, 360)); - final IndexNameResolver.Rollover rollover = randomFrom(IndexNameResolver.Rollover.values()); - return IndexNameResolver.resolve(IndexAuditTrailField.INDEX_NAME_PREFIX, date, rollover); - } - - private IndexAbstraction mockIndexAbstraction(String name) { - IndexAbstraction mock = mock(IndexAbstraction.class); - when(mock.getName()).thenReturn(name); - when(mock.getType()).thenReturn(randomFrom(IndexAbstraction.Type.CONCRETE_INDEX, - IndexAbstraction.Type.ALIAS, IndexAbstraction.Type.DATA_STREAM)); - return mock; - } -} From b0832da97767bfdc50b7f2797a9ceb88b0db93e3 Mon Sep 17 00:00:00 2001 From: jaymode Date: Wed, 16 Jun 2021 14:30:39 -0600 Subject: [PATCH 02/29] restricted index updates --- .../authz/store/ReservedRolesStore.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index 214ec8685ae9c..2b017ba38d5d4 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -93,7 +93,7 @@ private static Map initializeReservedRoles() { RoleDescriptor.IndicesPrivileges.builder() .indices("*").privileges("monitor").allowRestrictedIndices(true).build(), RoleDescriptor.IndicesPrivileges.builder() - .indices(".kibana*").privileges("read").build() + .indices(".kibana*").privileges("read").allowRestrictedIndices(true).build() }, null, null, @@ -139,7 +139,7 @@ private static Map initializeReservedRoles() { }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() - .indices(".kibana*", ".reporting-*").privileges("all").build(), + .indices(".kibana*", ".reporting-*").privileges("all").allowRestrictedIndices(true).build(), RoleDescriptor.IndicesPrivileges.builder() .indices(".monitoring-*").privileges("read", "read_cross_cluster").build(), RoleDescriptor.IndicesPrivileges.builder() @@ -152,10 +152,10 @@ private static Map initializeReservedRoles() { .privileges("read", "write").build(), // APM agent configuration RoleDescriptor.IndicesPrivileges.builder() - .indices(".apm-agent-configuration").privileges("all").build(), + .indices(".apm-agent-configuration").privileges("all").allowRestrictedIndices(true).build(), // APM custom link index creation RoleDescriptor.IndicesPrivileges.builder() - .indices(".apm-custom-link").privileges("all").build(), + .indices(".apm-custom-link").privileges("all").allowRestrictedIndices(true).build(), // APM telemetry queries APM indices in kibana task runner RoleDescriptor.IndicesPrivileges.builder() .indices("apm-*") @@ -163,7 +163,7 @@ private static Map initializeReservedRoles() { // Data telemetry reads mappings, metadata and stats of indices RoleDescriptor.IndicesPrivileges.builder() .indices("*") - .privileges("view_index_metadata", "monitor").build(), + .privileges("view_index_metadata", "monitor").allowRestrictedIndices(true).build(), // Endpoint diagnostic information. Kibana reads from these indices to send telemetry RoleDescriptor.IndicesPrivileges.builder() .indices(".logs-endpoint.diagnostic.collection-*") @@ -172,7 +172,7 @@ private static Map initializeReservedRoles() { // Fleet Server indices. Kibana read and write to this indice to manage Elastic Agents RoleDescriptor.IndicesPrivileges.builder() .indices(".fleet*") - .privileges("all").build(), + .privileges("all").allowRestrictedIndices(true).build(), // Legacy "Alerts as data" index. Kibana user will create this index. // Kibana user will read / write to these indices RoleDescriptor.IndicesPrivileges.builder() @@ -271,6 +271,7 @@ private static Map initializeReservedRoles() { new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() .indices(".ml-anomalies*", ".ml-notifications*", ".ml-state*", ".ml-meta*", ".ml-stats-*") + .allowRestrictedIndices(true) .privileges("view_index_metadata", "read").build(), RoleDescriptor.IndicesPrivileges.builder().indices(".ml-annotations*") .privileges("view_index_metadata", "read", "write").build() @@ -330,12 +331,13 @@ private static Map initializeReservedRoles() { .put("watcher_admin", new RoleDescriptor("watcher_admin", new String[] { "manage_watcher" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX, TriggeredWatchStoreField.INDEX_NAME, - HistoryStoreField.INDEX_PREFIX + "*").privileges("read").build() }, + HistoryStoreField.INDEX_PREFIX + "*").allowRestrictedIndices(true).privileges("read").build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("watcher_user", new RoleDescriptor("watcher_user", new String[] { "monitor_watcher" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX) .privileges("read") + .allowRestrictedIndices(true) .build(), RoleDescriptor.IndicesPrivileges.builder().indices(HistoryStoreField.INDEX_PREFIX + "*") .privileges("read") @@ -343,7 +345,9 @@ private static Map initializeReservedRoles() { .put("logstash_admin", new RoleDescriptor("logstash_admin", new String[] {"manage_logstash_pipelines"}, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder().indices(".logstash*") - .privileges("create", "delete", "index", "manage", "read").build() }, + .privileges("create", "delete", "index", "manage", "read") + .allowRestrictedIndices(true) + .build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("rollup_user", new RoleDescriptor("rollup_user", new String[] { "monitor_rollup" }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) @@ -358,6 +362,7 @@ private static Map initializeReservedRoles() { .put("enrich_user", new RoleDescriptor("enrich_user", new String[]{ "manage_enrich", "manage_ingest_pipelines", "monitor" }, new RoleDescriptor.IndicesPrivileges[]{ RoleDescriptor.IndicesPrivileges.builder() .indices(".enrich-*") + .allowRestrictedIndices(true) .privileges("manage", "read", "write") .build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("viewer", buildViewerRoleDescriptor()) From 92bfa5f250f05f85c1832b2160cb82fe49734b65 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 11:21:56 -0600 Subject: [PATCH 03/29] admin role needs restricted index access for now --- client/rest-high-level/roles.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/client/rest-high-level/roles.yml b/client/rest-high-level/roles.yml index d3d0630f43058..22b4c60ef1bc4 100644 --- a/client/rest-high-level/roles.yml +++ b/client/rest-high-level/roles.yml @@ -3,6 +3,7 @@ admin: - all indices: - names: '*' + allow_restricted_indices: true privileges: - all run_as: [ '*' ] From a11ff2fcd2453da68ab5bd00641f224e4f6058a1 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 14:04:29 -0600 Subject: [PATCH 04/29] fix test failures --- .../core/async/AsyncTaskIndexService.java | 2 +- .../authz/permission/IndicesPermission.java | 23 +++--- .../xpack/core/security/user/XPackUser.java | 2 +- .../authz/AuthorizationServiceTests.java | 4 +- .../authz/AuthorizedIndicesTests.java | 17 ++-- .../authz/IndicesAndAliasesResolverTests.java | 30 ++++++- .../xpack/security/authz/RBACEngineTests.java | 34 ++++---- .../accesscontrol/IndicesPermissionTests.java | 46 +++++------ .../authz/store/CompositeRolesStoreTests.java | 13 +-- .../security/test/TestRestrictedIndices.java | 80 +++++++++++++++++++ 10 files changed, 180 insertions(+), 71 deletions(-) create mode 100644 x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/AsyncTaskIndexService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/AsyncTaskIndexService.java index 05bce4ec44d31..fcc414db78d92 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/AsyncTaskIndexService.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/async/AsyncTaskIndexService.java @@ -122,7 +122,7 @@ private static XContentBuilder mappings() { public static SystemIndexDescriptor getSystemIndexDescriptor() { return SystemIndexDescriptor.builder() - .setIndexPattern(XPackPlugin.ASYNC_RESULTS_INDEX) + .setIndexPattern(XPackPlugin.ASYNC_RESULTS_INDEX + "*") .setDescription("Async search results") .setPrimaryIndex(XPackPlugin.ASYNC_RESULTS_INDEX) .setMappings(mappings()) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index 52622a92a7e11..9ec2f5dd473b1 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -20,7 +20,6 @@ import org.elasticsearch.common.regex.Regex; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; -import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.support.StringMatcher; @@ -33,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; @@ -69,7 +69,7 @@ private static StringMatcher indexMatcher(Collection ordinaryIndices, Co matcher = StringMatcher.of(ordinaryIndices); if (restrictedNamesAutomaton != null) { CharacterRunAutomaton characterRunAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); - matcher = matcher.and(name -> characterRunAutomaton.run(name) == false); + matcher = matcher.and("", name -> characterRunAutomaton.run(name) == false); } if (restrictedIndices.isEmpty() == false) { matcher = StringMatcher.of(restrictedIndices).or(matcher); @@ -98,14 +98,17 @@ private Predicate buildIndexMatcherPredicateForAction(String a final boolean isMappingUpdateAction = isMappingUpdateAction(action); Automaton restrictedNamesAutomaton = null; for (final Group group : groups) { + if (restrictedNamesAutomaton == null) { + restrictedNamesAutomaton = group.restrictedNamesAutomaton; + } else { + assert restrictedNamesAutomaton == group.restrictedNamesAutomaton : "Groups have different restricted names automatons"; + } + if (group.actionMatcher.test(action)) { if (group.allowRestrictedIndices) { restrictedIndices.addAll(Arrays.asList(group.indices())); } else { ordinaryIndices.addAll(Arrays.asList(group.indices())); - if (restrictedNamesAutomaton == null) { - restrictedNamesAutomaton = group.restrictedNamesAutomaton; - } } } else if (isMappingUpdateAction && containsPrivilegeThatGrantsMappingUpdatesForBwc(group)) { // special BWC case for certain privileges: allow put mapping on indices and aliases (but not on data streams), even if @@ -114,9 +117,6 @@ private Predicate buildIndexMatcherPredicateForAction(String a grantMappingUpdatesOnRestrictedIndices.addAll(Arrays.asList(group.indices())); } else { grantMappingUpdatesOnIndices.addAll(Arrays.asList(group.indices())); - if (restrictedNamesAutomaton == null) { - restrictedNamesAutomaton = group.restrictedNamesAutomaton; - } } } } @@ -163,7 +163,8 @@ public ResourcePrivilegesMap checkResourcePrivileges(Set checkForIndexPa for (String forIndexPattern : checkForIndexPatterns) { Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern); if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) { - checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, RestrictedIndicesNames.NAMES_AUTOMATON); + Optional restrictedNamesAutomaton = Arrays.stream(groups).map(g -> g.restrictedNamesAutomaton).findFirst(); + checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, restrictedNamesAutomaton.orElse(Automatons.EMPTY)); } if (false == Operations.isEmpty(checkIndexAutomaton)) { Automaton allowedIndexPrivilegesAutomaton = null; @@ -341,7 +342,9 @@ private boolean isConcreteRestrictedIndex(String indexPattern) { if (Regex.isSimpleMatchPattern(indexPattern) || Automatons.isLuceneRegex(indexPattern)) { return false; } - return RestrictedIndicesNames.isRestricted(indexPattern); + CharacterRunAutomaton runAutomaton = + new CharacterRunAutomaton(Arrays.stream(groups).map(g -> g.restrictedNamesAutomaton).findFirst().orElse(Automatons.EMPTY)); + return runAutomaton.run(indexPattern); } private static boolean isMappingUpdateAction(String action) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java index b2501585b8408..0525155707996 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/XPackUser.java @@ -20,7 +20,7 @@ public class XPackUser extends User { public static final RoleDescriptor ROLE_DESCRIPTOR = new RoleDescriptor(ROLE_NAME, new String[] { "all" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() - .indices("/@&~(\\.security.*)/") + .indices("/@&~(\\.security.*)&~(\\.async-search.*)/") .privileges("all") .allowRestrictedIndices(true) .build(), diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index 073418054478b..331ae837f828a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -139,7 +139,6 @@ import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; -import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.ElasticUser; import org.elasticsearch.xpack.core.security.user.KibanaUser; @@ -191,6 +190,7 @@ import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7; import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS; import static org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.PRINCIPAL_ROLES_FIELD_NAME; +import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; @@ -274,7 +274,7 @@ public void setup() { callback.onResponse(Role.EMPTY); } else { CompositeRolesStore.buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, privilegesStore, - Automatons.EMPTY, ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) + RESTRICTED_INDICES_AUTOMATON, ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) ); } return Void.TYPE; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java index 621502688abc7..6a840ab06d33b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java @@ -28,13 +28,13 @@ import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; -import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; import java.util.List; import java.util.Set; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createTimestampField; +import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.not; @@ -77,7 +77,7 @@ public void testAuthorizedIndicesUserWithSomeRoles() { final PlainActionFuture future = new PlainActionFuture<>(); final Set descriptors = Sets.newHashSet(aStarRole, bRole); CompositeRolesStore.buildRoleFromDescriptors( - descriptors, new FieldPermissionsCache(Settings.EMPTY), null, Automatons.EMPTY, future); + descriptors, new FieldPermissionsCache(Settings.EMPTY), null, RESTRICTED_INDICES_AUTOMATON, future); Role roles = future.actionGet(); Set list = RBACEngine.resolveAuthorizedIndicesFromRole(roles, getRequestInfo(SearchAction.NAME), metadata.getIndicesLookup()); @@ -89,21 +89,22 @@ public void testAuthorizedIndicesUserWithSomeRoles() { } public void testAuthorizedIndicesUserWithSomeRolesEmptyMetadata() { - Role role = Role.builder(Automatons.EMPTY, "role").add(IndexPrivilege.ALL, "*").build(); + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role").add(IndexPrivilege.ALL, "*").build(); Set authorizedIndices = RBACEngine.resolveAuthorizedIndicesFromRole(role, getRequestInfo(SearchAction.NAME), Metadata.EMPTY_METADATA.getIndicesLookup()); assertTrue(authorizedIndices.isEmpty()); } public void testSecurityIndicesAreRemovedFromRegularUser() { - Role role = Role.builder(Automatons.EMPTY, "user_role").add(IndexPrivilege.ALL, "*").cluster(Set.of("all"), Set.of()).build(); + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "user_role") + .add(IndexPrivilege.ALL, "*").cluster(Set.of("all"), Set.of()).build(); Set authorizedIndices = RBACEngine.resolveAuthorizedIndicesFromRole(role, getRequestInfo(SearchAction.NAME), Metadata.EMPTY_METADATA.getIndicesLookup()); assertTrue(authorizedIndices.isEmpty()); } public void testSecurityIndicesAreRestrictedForDefaultRole() { - Role role = Role.builder(Automatons.EMPTY, randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) .add(IndexPrivilege.ALL, "*") .cluster(Set.of("all"), Set.of()) .build(); @@ -130,7 +131,7 @@ public void testSecurityIndicesAreRestrictedForDefaultRole() { } public void testSecurityIndicesAreNotRemovedFromUnrestrictedRole() { - Role role = Role.builder(Automatons.EMPTY, randomAlphaOfLength(8)) + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, randomAlphaOfLength(8)) .add(FieldPermissions.DEFAULT, null, IndexPrivilege.ALL, true, "*") .cluster(Set.of("all"), Set.of()) .build(); @@ -193,7 +194,7 @@ public void testDataStreamsAreNotIncludedInAuthorizedIndices() { final PlainActionFuture future = new PlainActionFuture<>(); final Set descriptors = Sets.newHashSet(aStarRole, bRole); CompositeRolesStore.buildRoleFromDescriptors( - descriptors, new FieldPermissionsCache(Settings.EMPTY), null, Automatons.EMPTY, future); + descriptors, new FieldPermissionsCache(Settings.EMPTY), null, RESTRICTED_INDICES_AUTOMATON, future); Role roles = future.actionGet(); Set list = RBACEngine.resolveAuthorizedIndicesFromRole(roles, getRequestInfo(SearchAction.NAME), metadata.getIndicesLookup()); @@ -239,7 +240,7 @@ public void testDataStreamsAreIncludedInAuthorizedIndices() { final PlainActionFuture future = new PlainActionFuture<>(); final Set descriptors = Sets.newHashSet(aStarRole, bRole); CompositeRolesStore.buildRoleFromDescriptors( - descriptors, new FieldPermissionsCache(Settings.EMPTY), null, Automatons.EMPTY, future); + descriptors, new FieldPermissionsCache(Settings.EMPTY), null, RESTRICTED_INDICES_AUTOMATON, future); Role roles = future.actionGet(); TransportRequest request = new ResolveIndexAction.Request(new String[]{"a*"}); AuthorizationEngine.RequestInfo requestInfo = new AuthorizationEngine.RequestInfo(null, request, SearchAction.NAME); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index d3a8b871de94d..f205e14084346 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -64,7 +64,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; -import org.elasticsearch.xpack.core.security.support.Automatons; +import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.security.user.XPackSecurityUser; import org.elasticsearch.xpack.core.security.user.XPackUser; @@ -90,6 +90,7 @@ import static org.elasticsearch.test.TestMatchers.throwableWithMessage; import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS; import static org.elasticsearch.xpack.security.authz.AuthorizedIndicesTests.getRequestInfo; +import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.arrayContaining; @@ -277,12 +278,35 @@ public void setup() { callback.onResponse(Role.EMPTY); } else { CompositeRolesStore.buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, null, - Automatons.EMPTY, ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) + RESTRICTED_INDICES_AUTOMATON, ActionListener.wrap(r -> callback.onResponse(r), callback::onFailure) ); } return Void.TYPE; }).when(rolesStore).roles(any(Set.class), any(ActionListener.class)); - doCallRealMethod().when(rolesStore).getRoles(any(User.class), any(Authentication.class), any(ActionListener.class)); + + doAnswer(i -> { + User user = (User) i.getArguments()[0]; + ActionListener listener = (ActionListener) i.getArguments()[2]; + if (XPackUser.is(user)) { + listener.onResponse(Role.builder(XPackUser.ROLE_DESCRIPTOR, fieldPermissionsCache, RESTRICTED_INDICES_AUTOMATON).build()); + return Void.TYPE; + } + if (XPackSecurityUser.is(user)) { + listener.onResponse(Role.builder( + ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR, + fieldPermissionsCache, + RESTRICTED_INDICES_AUTOMATON + ).build()); + return Void.TYPE; + } + if (AsyncSearchUser.is(user)) { + listener.onResponse( + Role.builder(AsyncSearchUser.ROLE_DESCRIPTOR, fieldPermissionsCache, RESTRICTED_INDICES_AUTOMATON).build()); + return Void.TYPE; + } + i.callRealMethod(); + return Void.TYPE; + }).when(rolesStore).getRoles(any(User.class), any(Authentication.class), any(ActionListener.class)); ClusterService clusterService = mock(ClusterService.class); when(clusterService.getClusterSettings()).thenReturn(new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java index df20647c97ad1..77c1557d21bea 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java @@ -63,7 +63,6 @@ import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.Privilege; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; -import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; @@ -86,6 +85,7 @@ import static java.util.Collections.emptyMap; import static org.elasticsearch.common.util.set.Sets.newHashSet; import static org.elasticsearch.xpack.security.authz.AuthorizedIndicesTests.getRequestInfo; +import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.emptyIterable; import static org.hamcrest.Matchers.equalTo; @@ -355,7 +355,7 @@ public void testNamedIndexPrivilegesMatchApplicableActions() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test1") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test1") .cluster(Collections.singleton("all"), Collections.emptyList()) .add(IndexPrivilege.WRITE, "academy") .build(); @@ -397,7 +397,7 @@ public void testMatchSubsetOfPrivileges() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test2") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test2") .cluster(Set.of("monitor"), Set.of()) .add(IndexPrivilege.INDEX, "academy") .add(IndexPrivilege.WRITE, "initiative") @@ -456,7 +456,7 @@ public void testMatchNothing() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test3") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test3") .cluster(Set.of("monitor"), Set.of()) .build(); RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); @@ -495,7 +495,7 @@ public void testWildcardHandling() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test3") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test3") .add(IndexPrivilege.ALL, "logstash-*", "foo?") .add(IndexPrivilege.READ, "abc*") .add(IndexPrivilege.WRITE, "*xyz") @@ -586,7 +586,7 @@ public void testCheckingIndexPermissionsDefinedOnDifferentPatterns() throws Exce User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test-write") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test-write") .add(IndexPrivilege.INDEX, "apache-*") .add(IndexPrivilege.DELETE, "apache-2016-*") .build(); @@ -614,7 +614,7 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { when(authentication.getUser()).thenReturn(user); final String patternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, randomIntBetween(2, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.length() - 2)); - Role role = Role.builder(Automatons.EMPTY, "role") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, patternPrefix + "*") .build(); RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); @@ -702,7 +702,7 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) .put("index", false).map()).build())); - role = Role.builder(Automatons.EMPTY, "role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, true, patternPrefix + "*") .build(); authzInfo = new RBACAuthorizationInfo(role, null); @@ -725,7 +725,7 @@ public void testCheckExplicitRestrictedIndexPermissions() throws Exception { when(authentication.getUser()).thenReturn(user); final boolean restrictedIndexPermission = randomBoolean(); final boolean restrictedMonitorPermission = randomBoolean(); - Role role = Role.builder(Automatons.EMPTY, "role") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, restrictedIndexPermission, ".sec*") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.MONITOR, restrictedMonitorPermission, ".security*") .build(); @@ -768,7 +768,7 @@ public void testCheckRestrictedIndexWildcardPermissions() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "role") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, ".sec*") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.MONITOR, true, ".security*") .build(); @@ -805,7 +805,7 @@ public void testCheckRestrictedIndexWildcardPermissions() throws Exception { .put("index", false).put("monitor", true).map()).build() )); - role = Role.builder(Automatons.EMPTY, "role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, true, ".sec*") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.MONITOR, false, ".security*") .build(); @@ -855,7 +855,7 @@ public void testCheckingApplicationPrivilegesOnDifferentApplicationsAndResources User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test-role") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test-role") .addApplicationPrivilege(app1Read, Collections.singleton("foo/*")) .addApplicationPrivilege(app1All, Collections.singleton("foo/bar/baz")) .addApplicationPrivilege(app2Read, Collections.singleton("foo/bar/*")) @@ -918,7 +918,7 @@ public void testCheckingApplicationPrivilegesWithComplexNames() throws Exception User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test-write") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test-write") .addApplicationPrivilege(priv1, Collections.singleton("user/*/name")) .build(); RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); @@ -953,7 +953,7 @@ public void testIsCompleteMatch() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test-write") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test-write") .cluster(Set.of("monitor"), Set.of()) .add(IndexPrivilege.READ, "read-*") .add(IndexPrivilege.ALL, "all-*") @@ -1005,7 +1005,7 @@ public void testIsCompleteMatch() throws Exception { public void testBuildUserPrivilegeResponse() { final ManageApplicationPrivileges manageApplicationPrivileges = new ManageApplicationPrivileges(Sets.newHashSet("app01", "app02")); final BytesArray query = new BytesArray("{\"term\":{\"public\":true}}"); - final Role role = Role.builder(Automatons.EMPTY, "test", "role") + final Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test", "role") .cluster(Sets.newHashSet("monitor", "manage_watcher"), Collections.singleton(manageApplicationPrivileges)) .add(IndexPrivilege.get(Sets.newHashSet("read", "write")), "index-1") .add(IndexPrivilege.ALL, "index-2", "index-3") @@ -1052,7 +1052,7 @@ public void testBackingIndicesAreIncludedForAuthorizedDataStreams() { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test1") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test1") .cluster(Collections.singleton("all"), Collections.emptyList()) .add(IndexPrivilege.READ, dataStreamName) .build(); @@ -1084,7 +1084,7 @@ public void testExplicitMappingUpdatesAreNotGrantedWithIngestPrivileges() { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - Role role = Role.builder(Automatons.EMPTY, "test1") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "test1") .cluster(Collections.emptySet(), Collections.emptyList()) .add(IndexPrivilege.CREATE, "my_*") .add(IndexPrivilege.WRITE, "my_data*") diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index d7b69f9442127..55e4264f9838c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; -import org.elasticsearch.xpack.core.security.support.Automatons; import java.io.IOException; import java.util.ArrayList; @@ -45,6 +44,7 @@ import java.util.stream.Collectors; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createTimestampField; +import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -68,7 +68,7 @@ public void testAuthorize() { // basics: Set query = Collections.singleton(new BytesArray("{}")); String[] fields = new String[]{"_field"}; - Role role = Role.builder(Automatons.EMPTY, "_role") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_index") .build(); IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), lookup, fieldPermissionsCache); @@ -80,7 +80,7 @@ public void testAuthorize() { assertThat(permissions.getIndexPermissions("_index").getDocumentPermissions().getQueries(), equalTo(query)); // no document level security: - role = Role.builder(Automatons.EMPTY, "_role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), null, IndexPrivilege.ALL, randomBoolean(), "_index") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), lookup, fieldPermissionsCache); @@ -91,7 +91,7 @@ public void testAuthorize() { assertThat(permissions.getIndexPermissions("_index").getDocumentPermissions().getQueries(), nullValue()); // no field level security: - role = Role.builder(Automatons.EMPTY, "_role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(), query, IndexPrivilege.ALL, randomBoolean(), "_index") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), lookup, fieldPermissionsCache); @@ -102,7 +102,7 @@ public void testAuthorize() { assertThat(permissions.getIndexPermissions("_index").getDocumentPermissions().getQueries(), equalTo(query)); // index group associated with an alias: - role = Role.builder(Automatons.EMPTY, "_role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_alias") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), lookup, fieldPermissionsCache); @@ -123,7 +123,7 @@ public void testAuthorize() { // match all fields String[] allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"}, new String[]{randomAlphaOfLengthBetween(1, 10), "*"}); - role = Role.builder(Automatons.EMPTY, "_role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(fieldPermissionDef(allFields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_alias") .build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), lookup, fieldPermissionsCache); @@ -153,7 +153,7 @@ public void testAuthorize() { Set fooQuery = Collections.singleton(new BytesArray("{foo}")); allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"}, new String[]{randomAlphaOfLengthBetween(1, 10), "*"}); - role = Role.builder(Automatons.EMPTY, "_role") + role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(fieldPermissionDef(allFields, null)), fooQuery, IndexPrivilege.ALL, randomBoolean(), "_alias") .add(new FieldPermissions(fieldPermissionDef(allFields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_alias") .build(); @@ -193,7 +193,7 @@ public void testAuthorizeMultipleGroupsMixedDls() { Set query = Collections.singleton(new BytesArray("{}")); String[] fields = new String[]{"_field"}; - Role role = Role.builder(Automatons.EMPTY, "_role") + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "_role") .add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, randomBoolean(), "_index") .add(new FieldPermissions(fieldPermissionDef(null, null)), null, IndexPrivilege.ALL, randomBoolean(), "*") .build(); @@ -248,9 +248,9 @@ public void testCorePermissionAuthorize() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), - Automatons.EMPTY, "a1"); + RESTRICTED_INDICES_AUTOMATON, "a1"); IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.READ, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), Automatons.EMPTY, + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a1"); IndicesPermission core = new IndicesPermission(group1, group2); Map authzMap = @@ -266,16 +266,16 @@ public void testCorePermissionAuthorize() { assertFalse(core.check("unknown")); // test with two indices - group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), Automatons.EMPTY, "a1"); + group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a1"); group2 = new IndicesPermission.Group(IndexPrivilege.ALL, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), Automatons.EMPTY, + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a1"); IndicesPermission.Group group3 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(fieldPermissionDef(new String[] { "*_field" }, new String[] { "denied_field" })), null, - randomBoolean(), Automatons.EMPTY, "a2"); + randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a2"); IndicesPermission.Group group4 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(fieldPermissionDef(new String[] { "*_field2" }, new String[] { "denied_field2" })), null, - randomBoolean(), Automatons.EMPTY, "a2"); + randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a2"); core = new IndicesPermission(group1, group2, group3, group4); authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), lookup, fieldPermissionsCache); assertFalse(authzMap.get("a1").getFieldPermissions().hasFieldLevelSecurity()); @@ -299,7 +299,7 @@ public void testErrorMessageIfIndexPatternIsTooComplex() { indices.add("*" + prefix + "*" + suffixBegin + "*"); } final ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, - () -> new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), Automatons.EMPTY, + () -> new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, indices.toArray(Strings.EMPTY_ARRAY))); assertThat(e.getMessage(), containsString(indices.get(0))); assertThat(e.getMessage(), containsString("too complex to evaluate")); @@ -322,7 +322,7 @@ public void testSecurityIndicesPermissions() { // allow_restricted_indices: false IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, - Automatons.EMPTY, "*"); + RESTRICTED_INDICES_AUTOMATON, "*"); Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache); @@ -330,7 +330,7 @@ public void testSecurityIndicesPermissions() { assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(false)); // allow_restricted_indices: true - group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, Automatons.EMPTY, "*"); + group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, RESTRICTED_INDICES_AUTOMATON, "*"); authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache); @@ -353,13 +353,13 @@ public void testAsyncSearchIndicesPermissions() { // allow_restricted_indices: false IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, - Automatons.EMPTY, "*"); + RESTRICTED_INDICES_AUTOMATON, "*"); Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(false)); // allow_restricted_indices: true - group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, Automatons.EMPTY, "*"); + group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, RESTRICTED_INDICES_AUTOMATON, "*"); authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true)); @@ -384,7 +384,7 @@ public void testAuthorizationForBackingIndices() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); SortedMap lookup = metadata.getIndicesLookup(); IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.READ, new FieldPermissions(), null, false, - Automatons.EMPTY, dataStreamName); + RESTRICTED_INDICES_AUTOMATON, dataStreamName); Map authzMap = new IndicesPermission(group).authorize( SearchAction.NAME, Sets.newHashSet(backingIndices.stream().map(im -> im.getIndex().getName()).collect(Collectors.toList())), @@ -395,7 +395,7 @@ public void testAuthorizationForBackingIndices() { assertThat(authzMap.get(im.getIndex().getName()).isGranted(), is(true)); } - group = new IndicesPermission.Group(IndexPrivilege.CREATE_DOC, new FieldPermissions(), null, false, Automatons.EMPTY, + group = new IndicesPermission.Group(IndexPrivilege.CREATE_DOC, new FieldPermissions(), null, false, RESTRICTED_INDICES_AUTOMATON, dataStreamName); authzMap = new IndicesPermission(group).authorize( randomFrom(PutMappingAction.NAME, AutoPutMappingAction.NAME), @@ -430,9 +430,9 @@ public void testAuthorizationForMappingUpdates() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.INDEX, new FieldPermissions(), null, randomBoolean(), - Automatons.EMPTY, "test*"); + RESTRICTED_INDICES_AUTOMATON, "test*"); IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.WRITE, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), Automatons.EMPTY, + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "test_write*"); IndicesPermission core = new IndicesPermission(group1, group2); Map authzMap = diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 939dc14e48e13..256be0314a6b9 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -36,7 +36,6 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.indices.TestIndexNameExpressionResolver; import org.elasticsearch.license.License.OperationMode; import org.elasticsearch.license.TestUtils.UpdatableLicenseState; import org.elasticsearch.license.XPackLicenseState; @@ -69,7 +68,6 @@ import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; import org.elasticsearch.xpack.core.security.index.IndexAuditTrailField; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; -import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.security.support.MetadataUtils; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; @@ -83,6 +81,7 @@ import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; +import org.elasticsearch.xpack.security.test.TestRestrictedIndices; import org.hamcrest.Matchers; import org.joda.time.DateTime; @@ -113,6 +112,7 @@ import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY; import static org.elasticsearch.xpack.security.authc.ApiKeyService.API_KEY_ID_KEY; import static org.elasticsearch.xpack.security.authc.ApiKeyServiceTests.Utils.createApiKeyAuthentication; +import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -140,7 +140,7 @@ public class CompositeRolesStoreTests extends ESTestCase { .put(XPackSettings.SECURITY_ENABLED.getKey(), true) .build(); - private final IndexNameExpressionResolver resolver = TestIndexNameExpressionResolver.newInstance(); + private final IndexNameExpressionResolver resolver = TestRestrictedIndices.RESOLVER; private final FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY); private final String concreteSecurityIndexName = randomFrom( RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6, RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7); @@ -565,7 +565,7 @@ public void testMergingRolesWithFls() { FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY); PlainActionFuture future = new PlainActionFuture<>(); CompositeRolesStore.buildRoleFromDescriptors( - Sets.newHashSet(flsRole, addsL1Fields), cache, null, Automatons.EMPTY, future); + Sets.newHashSet(flsRole, addsL1Fields), cache, null, RESTRICTED_INDICES_AUTOMATON, future); Role role = future.actionGet(); Metadata metadata = Metadata.builder() @@ -661,7 +661,8 @@ public ClusterPermission.Builder buildPermission(ClusterPermission.Builder build listener.onResponse(set); return null; }).when(privilegeStore).getPrivileges(any(Collection.class), any(Collection.class), any(ActionListener.class)); - CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(role1, role2), cache, privilegeStore, Automatons.EMPTY, future); + CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(role1, role2), cache, privilegeStore, RESTRICTED_INDICES_AUTOMATON, + future); Role role = future.actionGet(); assertThat(role.cluster().check(ClusterStateAction.NAME, randomFrom(request1, request2, request3), authentication), equalTo(true)); @@ -1378,7 +1379,7 @@ public void testXPackUserCanAccessNonSecurityIndices() { } } - public void testXPackUserCannotAccessRestrictedIndices() { + public void testXPackUserCannotAccessSecurityOrAsyncSearch() { for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java new file mode 100644 index 0000000000000..09bf4d3233621 --- /dev/null +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java @@ -0,0 +1,80 @@ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.test; + +import org.apache.lucene.util.automaton.Automaton; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.indices.SystemIndexDescriptor; +import org.elasticsearch.indices.SystemIndices; +import org.elasticsearch.indices.SystemIndices.Feature; +import org.elasticsearch.indices.TestIndexNameExpressionResolver; +import org.elasticsearch.xpack.core.XPackPlugin; +import org.elasticsearch.xpack.security.Security; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.Map; + +import static org.apache.lucene.util.LuceneTestCase.createTempDir; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.index.mapper.MapperService.SINGLE_MAPPING_NAME; +import static org.elasticsearch.xpack.core.ClientHelper.ASYNC_SEARCH_ORIGIN; + +public class TestRestrictedIndices { + + public static final Automaton RESTRICTED_INDICES_AUTOMATON; + public static final IndexNameExpressionResolver RESOLVER; + + static { + Security securityPlugin = new Security(Settings.EMPTY, createTempDir()); + SystemIndices systemIndices = new SystemIndices(Map.of( + securityPlugin.getClass().getSimpleName(), + new Feature(securityPlugin.getFeatureName(), securityPlugin.getFeatureDescription(), + securityPlugin.getSystemIndexDescriptors(Settings.EMPTY)), + "async-search-mock", + new Feature("async search mock", "fake async search for restricted indices", List.of( + SystemIndexDescriptor.builder() + .setIndexPattern(XPackPlugin.ASYNC_RESULTS_INDEX + "*") + .setDescription("Async search results") + .setPrimaryIndex(XPackPlugin.ASYNC_RESULTS_INDEX) + .setMappings(mockMappings()) + .setSettings(Settings.EMPTY) + .setVersionMetaKey("version") + .setOrigin(ASYNC_SEARCH_ORIGIN) + .build() + )))); + RESTRICTED_INDICES_AUTOMATON = systemIndices.getSystemNameAutomaton(); + RESOLVER = TestIndexNameExpressionResolver.newInstance(systemIndices); + } + + private TestRestrictedIndices() {} + + private static XContentBuilder mockMappings() { + try { + XContentBuilder builder = jsonBuilder() + .startObject() + .startObject(SINGLE_MAPPING_NAME) + .startObject("_meta") + .field("version", Version.CURRENT) + .endObject() + .field("dynamic", "strict") + .startObject("properties") + .endObject() + .endObject() + .endObject(); + return builder; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} From b12116bf8464899bfd960eae96f0b63ebfe02b87 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 14:07:01 -0600 Subject: [PATCH 05/29] fix test --- .../elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java index ecff51e9e7ec3..b7c18b87e451b 100644 --- a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java +++ b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java @@ -235,7 +235,8 @@ private static Map runSql( return true; } else { String warning = warnings.get(0); - return warning.startsWith("this request accesses system indices: ") == false; + return warning.startsWith("this request accesses system indices: ") == false && + warning.startsWith("this request accesses aliases with names reserved for system indices: ") == false; } }); } From 74bfe520614e906e9036e9579071a3d688f6d4c6 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 14:07:47 -0600 Subject: [PATCH 06/29] Revert "fix test" This reverts commit b12116bf8464899bfd960eae96f0b63ebfe02b87. --- .../elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java index b7c18b87e451b..ecff51e9e7ec3 100644 --- a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java +++ b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java @@ -235,8 +235,7 @@ private static Map runSql( return true; } else { String warning = warnings.get(0); - return warning.startsWith("this request accesses system indices: ") == false && - warning.startsWith("this request accesses aliases with names reserved for system indices: ") == false; + return warning.startsWith("this request accesses system indices: ") == false; } }); } From 69d6e6da6bf7b0ae3e5207faa895881e1770caf1 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 14:46:55 -0600 Subject: [PATCH 07/29] remove some of restrictedindicesnames --- .../index/RestrictedIndicesNames.java | 14 --- .../core/security/user/AsyncSearchUser.java | 3 +- .../authz/store/ReservedRolesStoreTests.java | 115 +++++++++--------- .../security/test/TestRestrictedIndices.java | 41 +++++-- .../authz/AuthorizationServiceTests.java | 2 +- .../authz/AuthorizedIndicesTests.java | 2 +- .../authz/IndicesAndAliasesResolverTests.java | 3 +- .../xpack/security/authz/RBACEngineTests.java | 11 +- .../accesscontrol/IndicesPermissionTests.java | 5 +- .../authz/store/CompositeRolesStoreTests.java | 20 +-- .../sql/qa/server/security/build.gradle | 1 + .../sql/qa/security/SqlSecurityTestCase.java | 7 +- 12 files changed, 123 insertions(+), 101 deletions(-) rename x-pack/plugin/{security/src/test/java/org/elasticsearch/xpack => core/src/test/java/org/elasticsearch/xpack/core}/security/test/TestRestrictedIndices.java (58%) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java index 6507a6140031a..b7e02a7d43577 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java @@ -7,11 +7,8 @@ package org.elasticsearch.xpack.core.security.index; -import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.xpack.core.security.support.Automatons; -import java.util.Arrays; import java.util.Collections; import java.util.Set; @@ -23,21 +20,10 @@ public final class RestrictedIndicesNames { public static final String INTERNAL_SECURITY_TOKENS_INDEX_7 = ".security-tokens-7"; public static final String SECURITY_TOKENS_ALIAS = ".security-tokens"; - // public for tests - public static final String ASYNC_SEARCH_PREFIX = ".async-search"; - private static final Automaton ASYNC_SEARCH_AUTOMATON = Automatons.patterns(ASYNC_SEARCH_PREFIX + "*"); - // public for tests public static final Set RESTRICTED_NAMES = Collections.unmodifiableSet(Sets.newHashSet(SECURITY_MAIN_ALIAS, INTERNAL_SECURITY_MAIN_INDEX_6, INTERNAL_SECURITY_MAIN_INDEX_7, INTERNAL_SECURITY_TOKENS_INDEX_7, SECURITY_TOKENS_ALIAS)); - public static boolean isRestricted(String concreteIndexName) { - return RESTRICTED_NAMES.contains(concreteIndexName) || concreteIndexName.startsWith(ASYNC_SEARCH_PREFIX); - } - - public static final Automaton NAMES_AUTOMATON = Automatons.unionAndMinimize(Arrays.asList(Automatons.patterns(RESTRICTED_NAMES), - ASYNC_SEARCH_AUTOMATON)); - private RestrictedIndicesNames() { } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java index a38ddaf7365a5..1fc3bc7d70f47 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.core.security.user; +import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.support.MetadataUtils; @@ -19,7 +20,7 @@ public class AsyncSearchUser extends User { new String[] { "cancel_task" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() - .indices(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + "*") + .indices(XPackPlugin.ASYNC_RESULTS_INDEX + "*") .privileges("all") .allowRestrictedIndices(true).build(), }, diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index e36603f8eaa5f..9ec57b75a8a4c 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -6,7 +6,6 @@ */ package org.elasticsearch.xpack.core.security.authz.store; -import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.remote.RemoteInfoAction; @@ -53,6 +52,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.TransportRequest; +import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.action.XPackInfoAction; import org.elasticsearch.xpack.core.ilm.action.DeleteLifecycleAction; import org.elasticsearch.xpack.core.ilm.action.GetLifecycleAction; @@ -188,6 +188,7 @@ import java.util.Map; import java.util.SortedMap; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -201,8 +202,6 @@ public class ReservedRolesStoreTests extends ESTestCase { private static final String READ_CROSS_CLUSTER_NAME = "internal:transport/proxy/indices:data/read/query"; - public Automaton reservedNamesAutomaton = Automatons.EMPTY; - public void testIsReserved() { assertThat(ReservedRolesStore.isReserved("kibana_system"), is(true)); assertThat(ReservedRolesStore.isReserved("superuser"), is(true)); @@ -289,10 +288,10 @@ public void testSnapshotUserRole() { mockIndexAbstraction(index)), is(true)); } assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test( - mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(true)); + mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(true)); assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(snapshotUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testIngestAdminRole() { @@ -303,7 +302,7 @@ public void testIngestAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role ingestAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role ingestAdminRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(ingestAdminRole.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(true)); assertThat(ingestAdminRole.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(true)); assertThat(ingestAdminRole.cluster().check(DeleteIndexTemplateAction.NAME, request, authentication), is(true)); @@ -322,7 +321,7 @@ public void testIngestAdminRole() { mockIndexAbstraction(randomAlphaOfLengthBetween(8, 24))), is(false)); assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(ingestAdminRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testKibanaSystemRole() { @@ -333,7 +332,7 @@ public void testKibanaSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role kibanaRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role kibanaRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(kibanaRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(kibanaRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(kibanaRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -549,7 +548,7 @@ public void testKibanaSystemRole() { assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(mockIndexAbstraction(index)), is(false)); assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(kibanaRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testKibanaAdminRole() { @@ -561,7 +560,7 @@ public void testKibanaAdminRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); - Role kibanaAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role kibanaAdminRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(kibanaAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(kibanaAdminRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(kibanaAdminRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -608,7 +607,7 @@ public void testKibanaUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role kibanaUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role kibanaUserRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(kibanaUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(kibanaUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(kibanaUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -637,7 +636,7 @@ public void testKibanaUserRole() { "*"), is(false)); assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(kibanaUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testMonitoringUserRole() { @@ -648,7 +647,7 @@ public void testMonitoringUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role monitoringUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role monitoringUserRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(monitoringUserRole.cluster().check(MainAction.NAME, request, authentication), is(true)); assertThat(monitoringUserRole.cluster().check(XPackInfoAction.NAME, request, authentication), is(true)); assertThat(monitoringUserRole.cluster().check(RemoteInfoAction.NAME, request, authentication), is(true)); @@ -691,7 +690,7 @@ public void testMonitoringUserRole() { assertThat(monitoringUserRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(mockIndexAbstraction(index)), is(true)); assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(monitoringUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(monitoringUserRole.application().grants( @@ -714,7 +713,7 @@ public void testRemoteMonitoringAgentRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role remoteMonitoringAgentRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role remoteMonitoringAgentRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(remoteMonitoringAgentRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringAgentRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringAgentRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -801,7 +800,7 @@ public void testRemoteMonitoringAgentRole() { .test(mockIndexAbstraction(metricbeatIndex)), is(false)); assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(remoteMonitoringAgentRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testRemoteMonitoringCollectorRole() { @@ -812,7 +811,7 @@ public void testRemoteMonitoringCollectorRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role remoteMonitoringCollectorRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role remoteMonitoringCollectorRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(remoteMonitoringCollectorRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -877,45 +876,45 @@ public void testRemoteMonitoringCollectorRole() { assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(true)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(true)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(RecoveryAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(RecoveryAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(true)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(true)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(true)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(true)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(SearchAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(SearchAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(false)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(GetAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(GetAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(false)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(DeleteAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(DeleteAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(false)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndexAction.NAME) .test(mockIndexAbstraction(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES))), is(false)); assertThat(remoteMonitoringCollectorRole.indices().allowedIndicesMatcher(IndexAction.NAME) - .test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), is(false)); + .test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), is(false)); assertMonitoringOnRestrictedIndices(remoteMonitoringCollectorRole); assertNoAccessAllowed(remoteMonitoringCollectorRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(remoteMonitoringCollectorRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(remoteMonitoringCollectorRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } private void assertMonitoringOnRestrictedIndices(Role role) { @@ -934,7 +933,7 @@ private void assertMonitoringOnRestrictedIndices(Role role) { final List indexMonitoringActionNamesList = Arrays.asList(IndicesStatsAction.NAME, IndicesSegmentsAction.NAME, GetSettingsAction.NAME, IndicesShardStoresAction.NAME, RecoveryAction.NAME); for (final String indexMonitoringActionName : indexMonitoringActionNamesList) { - String asyncSearchIndex = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2); + String asyncSearchIndex = XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2); final Map authzMap = role.indices().authorize(indexMonitoringActionName, Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS, asyncSearchIndex), metadata.getIndicesLookup(), fieldPermissionsCache); @@ -953,7 +952,7 @@ public void testReportingUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role reportingUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role reportingUserRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(reportingUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(reportingUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(reportingUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -988,7 +987,7 @@ public void testReportingUserRole() { assertThat(reportingUserRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(mockIndexAbstraction(index)), is(false)); assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(reportingUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testKibanaDashboardOnlyUserRole() { @@ -1000,7 +999,7 @@ public void testKibanaDashboardOnlyUserRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); - Role dashboardsOnlyUserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role dashboardsOnlyUserRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(dashboardsOnlyUserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(dashboardsOnlyUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(dashboardsOnlyUserRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -1026,7 +1025,7 @@ public void testKibanaDashboardOnlyUserRole() { new ApplicationPrivilege(applicationWithRandomIndex, "app-random-index", "all"), "*"), is(false)); assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(dashboardsOnlyUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testSuperuserRole() { @@ -1037,7 +1036,7 @@ public void testSuperuserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role superuserRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role superuserRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(superuserRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(superuserRole.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(true)); assertThat(superuserRole.cluster().check(PutUserAction.NAME, request, authentication), is(true)); @@ -1109,7 +1108,7 @@ public void testLogstashSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role logstashSystemRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role logstashSystemRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(logstashSystemRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(logstashSystemRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(logstashSystemRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -1128,7 +1127,7 @@ public void testLogstashSystemRole() { .test(mockIndexAbstraction(randomAlphaOfLengthBetween(8, 24))), is(false)); assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(logstashSystemRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testBeatsAdminRole() { @@ -1140,7 +1139,7 @@ public void testBeatsAdminRole() { assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - final Role beatsAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + final Role beatsAdminRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(beatsAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(beatsAdminRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); assertThat(beatsAdminRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false)); @@ -1169,7 +1168,7 @@ public void testBeatsAdminRole() { assertThat(beatsAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(mockIndexAbstraction(index)), is(true)); assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(beatsAdminRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testBeatsSystemRole() { @@ -1180,7 +1179,7 @@ public void testBeatsSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role beatsSystemRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role beatsSystemRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(beatsSystemRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(beatsSystemRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(beatsSystemRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -1205,7 +1204,7 @@ public void testBeatsSystemRole() { assertThat(beatsSystemRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(mockIndexAbstraction(index)), is(true)); assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(beatsSystemRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testAPMSystemRole() { @@ -1216,7 +1215,7 @@ public void testAPMSystemRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role APMSystemRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role APMSystemRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(APMSystemRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); assertThat(APMSystemRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); assertThat(APMSystemRole.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); @@ -1248,7 +1247,7 @@ public void testAPMSystemRole() { "indices:data/write/index:op_type/" + randomAlphaOfLengthBetween(3,5)).test(mockIndexAbstraction(index)), is(false)); assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(APMSystemRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testAPMUserRole() { @@ -1259,7 +1258,7 @@ public void testAPMUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false)); assertThat(role.runAs().check(randomAlphaOfLengthBetween(1, 12)), is(false)); @@ -1305,7 +1304,7 @@ public void testMachineLearningAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertRoleHasManageMl(role); assertThat(role.cluster().check(DelegatePkiAuthenticationAction.NAME, request, authentication), is(false)); @@ -1320,7 +1319,7 @@ public void testMachineLearningAdminRole() { assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants( @@ -1411,7 +1410,7 @@ public void testMachineLearningUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(role.cluster().check(CloseJobAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(DeleteCalendarAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(DeleteCalendarEventAction.NAME, request, authentication), is(false)); @@ -1474,7 +1473,7 @@ public void testMachineLearningUserRole() { assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); @@ -1508,7 +1507,7 @@ public void testTransformAdminRole() { assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); } - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true)); @@ -1527,7 +1526,7 @@ public void testTransformAdminRole() { assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); // internal use only assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants( @@ -1566,7 +1565,7 @@ public void testTransformUserRole() { assertThat(roleDescriptor.getMetadata(), not(hasEntry("_deprecated", true))); } - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(role.cluster().check(DeleteTransformAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(GetTransformAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetTransformStatsAction.NAME, request, authentication), is(true)); @@ -1585,7 +1584,7 @@ public void testTransformUserRole() { assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants( @@ -1614,7 +1613,7 @@ public void testWatcherAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(role.cluster().check(PutWatchAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(GetWatchAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(DeleteWatchAction.NAME, request, authentication), is(true)); @@ -1634,7 +1633,7 @@ public void testWatcherAdminRole() { } assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testWatcherUserRole() { @@ -1645,7 +1644,7 @@ public void testWatcherUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(role.cluster().check(PutWatchAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(GetWatchAction.NAME, request, authentication), is(true)); assertThat(role.cluster().check(DeleteWatchAction.NAME, request, authentication), is(false)); @@ -1667,7 +1666,7 @@ public void testWatcherUserRole() { } assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } public void testPredefinedViewerRole() { @@ -1678,7 +1677,7 @@ public void testPredefinedViewerRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); // No cluster privileges assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(role.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); @@ -1727,7 +1726,7 @@ public void testPredefinedEditorRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role role = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role role = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); // No cluster privileges assertThat(role.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); @@ -1809,7 +1808,7 @@ private void assertOnlyReadAllowed(Role role, String index) { assertThat(role.indices().allowedIndicesMatcher(BulkAction.NAME).test(mockIndexAbstraction(index)), is(false)); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); - assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); + assertNoAccessAllowed(role, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); } private void assertNoAccessAllowed(Role role, Collection indices) { @@ -1838,7 +1837,7 @@ public void testLogstashAdminRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role logstashAdminRole = Role.builder(roleDescriptor, null, reservedNamesAutomaton).build(); + Role logstashAdminRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(logstashAdminRole.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false)); assertThat(logstashAdminRole.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false)); assertThat(logstashAdminRole.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java similarity index 58% rename from x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java rename to x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java index 09bf4d3233621..ed771907f03af 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/test/TestRestrictedIndices.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java @@ -6,29 +6,32 @@ * 2.0. */ -package org.elasticsearch.xpack.security.test; +package org.elasticsearch.xpack.core.security.test; import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.indices.ExecutorNames; import org.elasticsearch.indices.SystemIndexDescriptor; import org.elasticsearch.indices.SystemIndices; import org.elasticsearch.indices.SystemIndices.Feature; import org.elasticsearch.indices.TestIndexNameExpressionResolver; import org.elasticsearch.xpack.core.XPackPlugin; -import org.elasticsearch.xpack.security.Security; +import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import java.io.IOException; import java.io.UncheckedIOException; import java.util.List; import java.util.Map; -import static org.apache.lucene.util.LuceneTestCase.createTempDir; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.mapper.MapperService.SINGLE_MAPPING_NAME; import static org.elasticsearch.xpack.core.ClientHelper.ASYNC_SEARCH_ORIGIN; +import static org.elasticsearch.xpack.core.ClientHelper.SECURITY_ORIGIN; +import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS; +import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_TOKENS_ALIAS; public class TestRestrictedIndices { @@ -36,11 +39,35 @@ public class TestRestrictedIndices { public static final IndexNameExpressionResolver RESOLVER; static { - Security securityPlugin = new Security(Settings.EMPTY, createTempDir()); SystemIndices systemIndices = new SystemIndices(Map.of( - securityPlugin.getClass().getSimpleName(), - new Feature(securityPlugin.getFeatureName(), securityPlugin.getFeatureDescription(), - securityPlugin.getSystemIndexDescriptors(Settings.EMPTY)), + "security-mock", + new Feature("security-mock", "fake security for test restricted indices", List.of( + SystemIndexDescriptor.builder() + // This can't just be `.security-*` because that would overlap with the tokens index pattern + .setIndexPattern(".security-[0-9]+") + .setPrimaryIndex(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7) + .setDescription("Contains Security configuration") + .setMappings(mockMappings()) + .setSettings(Settings.EMPTY) + .setAliasName(SECURITY_MAIN_ALIAS) + .setIndexFormat(7) + .setVersionMetaKey("security-version") + .setOrigin(SECURITY_ORIGIN) + .setThreadPools(ExecutorNames.CRITICAL_SYSTEM_INDEX_THREAD_POOLS) + .build(), + SystemIndexDescriptor.builder() + .setIndexPattern(".security-tokens-[0-9]+") + .setPrimaryIndex(RestrictedIndicesNames.INTERNAL_SECURITY_TOKENS_INDEX_7) + .setDescription("Contains auth token data") + .setMappings(mockMappings()) + .setSettings(Settings.EMPTY) + .setAliasName(SECURITY_TOKENS_ALIAS) + .setIndexFormat(7) + .setVersionMetaKey("security-version") + .setOrigin(SECURITY_ORIGIN) + .setThreadPools(ExecutorNames.CRITICAL_SYSTEM_INDEX_THREAD_POOLS) + .build() + )), "async-search-mock", new Feature("async search mock", "fake async search for restricted indices", List.of( SystemIndexDescriptor.builder() diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index 331ae837f828a..9c4ddcbd30b8c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -190,7 +190,7 @@ import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_7; import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS; import static org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.PRINCIPAL_ROLES_FIELD_NAME; -import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java index 6a840ab06d33b..fe720678f52ed 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java @@ -34,7 +34,7 @@ import java.util.Set; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createTimestampField; -import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.not; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index f205e14084346..9406021427878 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -90,7 +90,7 @@ import static org.elasticsearch.test.TestMatchers.throwableWithMessage; import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS; import static org.elasticsearch.xpack.security.authz.AuthorizedIndicesTests.getRequestInfo; -import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.arrayContaining; @@ -106,7 +106,6 @@ import static org.hamcrest.Matchers.oneOf; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java index 77c1557d21bea..ac50bedc68ba9 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java @@ -31,6 +31,7 @@ import org.elasticsearch.license.GetLicenseAction; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.TransportRequest; +import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.action.GetApiKeyAction; import org.elasticsearch.xpack.core.security.action.GetApiKeyRequest; import org.elasticsearch.xpack.core.security.action.user.AuthenticateAction; @@ -85,7 +86,7 @@ import static java.util.Collections.emptyMap; import static org.elasticsearch.common.util.set.Sets.newHashSet; import static org.elasticsearch.xpack.security.authz.AuthorizedIndicesTests.getRequestInfo; -import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.emptyIterable; import static org.hamcrest.Matchers.equalTo; @@ -612,8 +613,8 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); when(authentication.getUser()).thenReturn(user); - final String patternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, - randomIntBetween(2, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.length() - 2)); + final String patternPrefix = XPackPlugin.ASYNC_RESULTS_INDEX.substring(0, + randomIntBetween(2, XPackPlugin.ASYNC_RESULTS_INDEX.length() - 2)); Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, "role") .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, patternPrefix + "*") .build(); @@ -632,7 +633,7 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) .put("index", false).map()).build())); - String matchesPatternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, patternPrefix.length() + 1); + String matchesPatternPrefix = XPackPlugin.ASYNC_RESULTS_INDEX.substring(0, patternPrefix.length() + 1); response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() .indices(matchesPatternPrefix + "*") .allowRestrictedIndices(false) @@ -667,7 +668,7 @@ public void testCheckRestrictedIndexPatternPermission() throws Exception { .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) .put("index", true).map()).build())); - final String restrictedIndexMatchingWildcard = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2); + final String restrictedIndexMatchingWildcard = XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2); response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() .indices(restrictedIndexMatchingWildcard + "*") .allowRestrictedIndices(true) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index 55e4264f9838c..bc6fcca0319ce 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions; @@ -44,7 +45,7 @@ import java.util.stream.Collectors; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createTimestampField; -import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -340,7 +341,7 @@ public void testSecurityIndicesPermissions() { public void testAsyncSearchIndicesPermissions() { final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build(); - final String asyncSearchIndex = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2); + final String asyncSearchIndex = XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2); final Metadata metadata = new Metadata.Builder() .put(new IndexMetadata.Builder(asyncSearchIndex) .settings(indexSettings) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 256be0314a6b9..10acb0c4e3f55 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.security.authz.store; +import org.apache.lucene.util.automaton.CharacterRunAutomaton; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsAction; @@ -45,6 +46,7 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequest.Empty; +import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateAction; import org.elasticsearch.xpack.core.security.action.user.PutUserAction; @@ -81,7 +83,7 @@ import org.elasticsearch.xpack.security.authc.service.ServiceAccountService; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.SecurityIndexManager; -import org.elasticsearch.xpack.security.test.TestRestrictedIndices; +import org.elasticsearch.xpack.core.security.test.TestRestrictedIndices; import org.hamcrest.Matchers; import org.joda.time.DateTime; @@ -112,7 +114,7 @@ import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY; import static org.elasticsearch.xpack.security.authc.ApiKeyService.API_KEY_ID_KEY; import static org.elasticsearch.xpack.security.authc.ApiKeyServiceTests.Utils.createApiKeyAuthentication; -import static org.elasticsearch.xpack.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; +import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -1366,14 +1368,15 @@ Version.CURRENT, randomFrom(AuthenticationType.REALM, AuthenticationType.TOKEN, } public void testXPackUserCanAccessNonSecurityIndices() { + CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(RESTRICTED_INDICES_AUTOMATON); for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + if (false == restrictedAutomaton.run(index.getName())) { assertThat(predicate.test(index), Matchers.is(true)); } index = mockIndexAbstraction("." + randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + if (false == restrictedAutomaton.run(index.getName())) { assertThat(predicate.test(index), Matchers.is(true)); } } @@ -1385,7 +1388,7 @@ public void testXPackUserCannotAccessSecurityOrAsyncSearch() { for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { assertThat(predicate.test(mockIndexAbstraction(index)), Matchers.is(false)); } - assertThat(predicate.test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2))), + assertThat(predicate.test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2))), Matchers.is(false)); } } @@ -1403,14 +1406,15 @@ public void testXPackUserCannotWriteToAuditTrail() { } public void testAsyncSearchUserCannotAccessNonRestrictedIndices() { + CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(RESTRICTED_INDICES_AUTOMATON); for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { Predicate predicate = getAsyncSearchUserRole().indices().allowedIndicesMatcher(action); IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + if (false == restrictedAutomaton.run(index.getName())) { assertThat(predicate.test(index), Matchers.is(false)); } index = mockIndexAbstraction("." + randomAlphaOfLengthBetween(3, 12)); - if (false == RestrictedIndicesNames.isRestricted(index.getName())) { + if (false == restrictedAutomaton.run(index.getName())) { assertThat(predicate.test(index), Matchers.is(false)); } } @@ -1422,7 +1426,7 @@ public void testAsyncSearchUserCanAccessOnlyAsyncSearchRestrictedIndices() { for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { assertThat(predicate.test(mockIndexAbstraction(index)), Matchers.is(false)); } - assertThat(predicate.test(mockIndexAbstraction(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 3))), + assertThat(predicate.test(mockIndexAbstraction(XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 3))), Matchers.is(true)); } } diff --git a/x-pack/plugin/sql/qa/server/security/build.gradle b/x-pack/plugin/sql/qa/server/security/build.gradle index 375c654645955..4bcb765b02877 100644 --- a/x-pack/plugin/sql/qa/server/security/build.gradle +++ b/x-pack/plugin/sql/qa/server/security/build.gradle @@ -1,5 +1,6 @@ dependencies { testImplementation project(':x-pack:plugin:core') + testImplementation(testArtifact(project(xpackModule('core')))) } Project mainProject = project diff --git a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java index 5baadf96e1d48..cf0ba15ab1078 100644 --- a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java +++ b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.qa.security; import org.apache.lucene.util.SuppressForbidden; +import org.apache.lucene.util.automaton.CharacterRunAutomaton; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.SpecialPermission; import org.elasticsearch.action.admin.indices.get.GetIndexAction; @@ -20,7 +21,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.rest.ESRestTestCase; -import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; +import org.elasticsearch.xpack.core.security.test.TestRestrictedIndices; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.AfterClass; @@ -641,13 +642,15 @@ public void assertLogs() throws Exception { List castIndices = (ArrayList) log.get("indices"); indices = castIndices; if ("test_admin".equals(log.get("user.name"))) { + CharacterRunAutomaton restrictedAutomaton = + new CharacterRunAutomaton(TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON); /* * Sometimes we accidentally sneak access to the security tables. This is fine, * SQL drops them from the interface. So we might have access to them, but we * don't show them. */ indices = indices.stream() - .filter(idx -> false == RestrictedIndicesNames.isRestricted(idx)) + .filter(idx -> false == restrictedAutomaton.run(idx)) .collect(Collectors.toList()); } } From 98c7d581684f72c7459ad727a821a8b6eac5cd46 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 15:00:59 -0600 Subject: [PATCH 08/29] fixes --- .../core/security/user/AsyncSearchUser.java | 1 - .../authz/AuthorizedIndicesTests.java | 3 ++- .../accesscontrol/IndicesPermissionTests.java | 19 ++++++++++--------- .../sql/qa/security/SqlSecurityTestCase.java | 5 +++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java index 1fc3bc7d70f47..ec7e21faccf62 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/AsyncSearchUser.java @@ -8,7 +8,6 @@ import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.support.MetadataUtils; public class AsyncSearchUser extends User { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java index fe720678f52ed..75805b8dd6d88 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizedIndicesTests.java @@ -104,7 +104,8 @@ public void testSecurityIndicesAreRemovedFromRegularUser() { } public void testSecurityIndicesAreRestrictedForDefaultRole() { - Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) + Role role = Role.builder(RESTRICTED_INDICES_AUTOMATON, + randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) .add(IndexPrivilege.ALL, "*") .cluster(Set.of("all"), Set.of()) .build(); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index bc6fcca0319ce..48d27b0a0b931 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -251,8 +251,8 @@ public void testCorePermissionAuthorize() { IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a1"); IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.READ, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, - "a1"); + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), + RESTRICTED_INDICES_AUTOMATON, "a1"); IndicesPermission core = new IndicesPermission(group1, group2); Map authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), lookup, fieldPermissionsCache); @@ -267,10 +267,11 @@ public void testCorePermissionAuthorize() { assertFalse(core.check("unknown")); // test with two indices - group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a1"); + group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), + RESTRICTED_INDICES_AUTOMATON, "a1"); group2 = new IndicesPermission.Group(IndexPrivilege.ALL, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, - "a1"); + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), + RESTRICTED_INDICES_AUTOMATON, "a1"); IndicesPermission.Group group3 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(fieldPermissionDef(new String[] { "*_field" }, new String[] { "denied_field" })), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a2"); @@ -300,8 +301,8 @@ public void testErrorMessageIfIndexPatternIsTooComplex() { indices.add("*" + prefix + "*" + suffixBegin + "*"); } final ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, - () -> new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, - indices.toArray(Strings.EMPTY_ARRAY))); + () -> new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), + RESTRICTED_INDICES_AUTOMATON, indices.toArray(Strings.EMPTY_ARRAY))); assertThat(e.getMessage(), containsString(indices.get(0))); assertThat(e.getMessage(), containsString("too complex to evaluate")); } @@ -433,8 +434,8 @@ public void testAuthorizationForMappingUpdates() { IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.INDEX, new FieldPermissions(), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "test*"); IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.WRITE, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), RESTRICTED_INDICES_AUTOMATON, - "test_write*"); + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), + RESTRICTED_INDICES_AUTOMATON, "test_write*"); IndicesPermission core = new IndicesPermission(group1, group2); Map authzMap = core.authorize(PutMappingAction.NAME, Sets.newHashSet("test1", "test_write1"), lookup, fieldPermissionsCache); diff --git a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java index cf0ba15ab1078..f12196e0a215b 100644 --- a/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java +++ b/x-pack/plugin/sql/qa/server/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java @@ -642,8 +642,9 @@ public void assertLogs() throws Exception { List castIndices = (ArrayList) log.get("indices"); indices = castIndices; if ("test_admin".equals(log.get("user.name"))) { - CharacterRunAutomaton restrictedAutomaton = - new CharacterRunAutomaton(TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON); + CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton( + TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON + ); /* * Sometimes we accidentally sneak access to the security tables. This is fine, * SQL drops them from the interface. So we might have access to them, but we From c1069023f44d99ae1bd410cb54f00fb1341b6afe Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 15:23:45 -0600 Subject: [PATCH 09/29] fix bad version name --- .../core/security/authz/store/ReservedRolesStoreTests.java | 2 +- .../xpack/core/security/test/TestRestrictedIndices.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index 9ec57b75a8a4c..416a1d3dd023b 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -245,7 +245,7 @@ public void testSnapshotUserRole() { assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - Role snapshotUserRole = Role.builder(roleDescriptor, null, Automatons.EMPTY).build(); + Role snapshotUserRole = Role.builder(roleDescriptor, null, RESTRICTED_INDICES_AUTOMATON).build(); assertThat(snapshotUserRole.cluster().check(GetRepositoriesAction.NAME, request, authentication), is(true)); assertThat(snapshotUserRole.cluster().check(CreateSnapshotAction.NAME, request, authentication), is(true)); assertThat(snapshotUserRole.cluster().check(SnapshotsStatusAction.NAME, request, authentication), is(true)); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java index ed771907f03af..98a8991bb7228 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/test/TestRestrictedIndices.java @@ -51,7 +51,7 @@ public class TestRestrictedIndices { .setSettings(Settings.EMPTY) .setAliasName(SECURITY_MAIN_ALIAS) .setIndexFormat(7) - .setVersionMetaKey("security-version") + .setVersionMetaKey("version") .setOrigin(SECURITY_ORIGIN) .setThreadPools(ExecutorNames.CRITICAL_SYSTEM_INDEX_THREAD_POOLS) .build(), @@ -63,7 +63,7 @@ public class TestRestrictedIndices { .setSettings(Settings.EMPTY) .setAliasName(SECURITY_TOKENS_ALIAS) .setIndexFormat(7) - .setVersionMetaKey("security-version") + .setVersionMetaKey("version") .setOrigin(SECURITY_ORIGIN) .setThreadPools(ExecutorNames.CRITICAL_SYSTEM_INDEX_THREAD_POOLS) .build() From 1bca2a9d67e746c824e6a85a23d2447ef0d90f29 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 15:44:26 -0600 Subject: [PATCH 10/29] unused --- .../xpack/core/security/authz/store/ReservedRolesStoreTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index 416a1d3dd023b..d743c960fd05d 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -76,7 +76,6 @@ import org.elasticsearch.xpack.core.ml.action.EvaluateDataFrameAction; import org.elasticsearch.xpack.core.ml.action.ExplainDataFrameAnalyticsAction; import org.elasticsearch.xpack.core.ml.action.FinalizeJobExecutionAction; -import org.elasticsearch.xpack.core.security.support.Automatons; import org.elasticsearch.xpack.core.textstructure.action.FindStructureAction; import org.elasticsearch.xpack.core.ml.action.FlushJobAction; import org.elasticsearch.xpack.core.ml.action.ForecastJobAction; From 5ee3b1878379850eca4d3b6a808d733f02b86c73 Mon Sep 17 00:00:00 2001 From: jaymode Date: Thu, 17 Jun 2021 20:10:53 -0600 Subject: [PATCH 11/29] fix test cp --- x-pack/plugin/sql/qa/server/security/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/sql/qa/server/security/build.gradle b/x-pack/plugin/sql/qa/server/security/build.gradle index 4bcb765b02877..36dff99506560 100644 --- a/x-pack/plugin/sql/qa/server/security/build.gradle +++ b/x-pack/plugin/sql/qa/server/security/build.gradle @@ -25,6 +25,7 @@ subprojects { dependencies { testImplementation project(":x-pack:plugin:core") + testImplementation(testArtifact(project(xpackModule('core')))) testArtifacts project(path: mainProject.path, configuration: 'testArtifacts') } From 5c3f54a19985cd902c90069c03af57d36f63747d Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 27 Jul 2021 10:32:18 -0400 Subject: [PATCH 12/29] Improve automaton build stream --- .../java/org/elasticsearch/indices/SystemIndices.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java index c0fed2d0e92c4..bb9d2bd0cdfcd 100644 --- a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java +++ b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java @@ -332,12 +332,9 @@ private static Automaton featureToIndexAutomaton(Feature feature) { private static Automaton buildDataStreamAutomaton(Map descriptors) { Optional automaton = descriptors.values().stream() - .map(feature -> - feature.getDataStreamDescriptors().stream() - .map(SystemDataStreamDescriptor::getDataStreamName) - .map(dsName -> SystemIndexDescriptor.buildAutomaton(dsName, null)) - .reduce(Operations::union) - .orElse(EMPTY)) + .flatMap(feature -> feature.getDataStreamDescriptors().stream()) + .map(SystemDataStreamDescriptor::getDataStreamName) + .map(dsName -> SystemIndexDescriptor.buildAutomaton(dsName, null)) .reduce(Operations::union); return automaton.isPresent() ? MinimizationOperations.minimize(automaton.get(), Integer.MAX_VALUE) : EMPTY; From 7aa5ea033db1e2c48d9462ad51735f989131c66f Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 27 Jul 2021 10:39:28 -0400 Subject: [PATCH 13/29] Handle warnings in tests --- .../security/authz/IndicesAndAliasesResolverTests.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index 3c390b6c6c092..18bcde041e901 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -106,6 +106,7 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.oneOf; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anySetOf; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -124,7 +125,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { private String tomorrowSuffix; @Before - @SuppressWarnings("unchecked") +// @SuppressWarnings("unchecked") public void setup() { Settings settings = Settings.builder() .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) @@ -284,10 +285,11 @@ public void setup() { ); } return Void.TYPE; - }).when(rolesStore).roles(any(Set.class), anyActionListener()); + }).when(rolesStore).roles(anySetOf(String.class), anyActionListener()); doAnswer(i -> { User user = (User) i.getArguments()[0]; + @SuppressWarnings("unchecked") ActionListener listener = (ActionListener) i.getArguments()[2]; if (XPackUser.is(user)) { listener.onResponse(Role.builder(XPackUser.ROLE_DESCRIPTOR, fieldPermissionsCache, RESTRICTED_INDICES_AUTOMATON).build()); From 00a68c0aa9e07ed332ed1d70c6907004486dddf6 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 29 Jul 2021 10:51:00 -0400 Subject: [PATCH 14/29] Add some javadoc --- .../org/elasticsearch/indices/SystemIndices.java | 4 ++++ .../security/authz/permission/IndicesPermission.java | 12 ++++++++++++ .../xpack/core/security/authz/permission/Role.java | 7 +++++++ 3 files changed, 23 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java index bb9d2bd0cdfcd..40d8386cf4d25 100644 --- a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java +++ b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java @@ -196,6 +196,10 @@ public boolean isSystemIndexBackingDataStream(String name) { return systemDataStreamIndicesRunAutomaton.run(name); } + /** + * @return An {@link Automaton} that tests whether strings are names of system indices, aliases, or + * data streams. + */ public Automaton getSystemNameAutomaton() { return systemNameAutomaton; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index 9ec2f5dd473b1..b1195e6bb38ca 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -60,6 +60,18 @@ public IndicesPermission(Group... groups) { this.groups = groups; } + /** + * This function constructs an index matcher that can be used to find indices allowed by + * permissions groups. + * + * @param ordinaryIndices A list of ordinary indices. If this collection contains restricted indices, + * according to the restrictedNamesAutomaton, they will not be matched. + * @param restrictedIndices A list of restricted index names. All of these will be matched. + * @param restrictedNamesAutomaton An automaton that will match restricted indices. We use this to filter + * out restricted indices from the ordinaryIndices collection. + * @return A matcher that will match all non-restricted index names in the ordinaryIndices + * collection and all index names in the restrictedIndices collection. + */ private static StringMatcher indexMatcher(Collection ordinaryIndices, Collection restrictedIndices, Automaton restrictedNamesAutomaton) { StringMatcher matcher; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java index 6b3b5438f9d3c..bc2906293058a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java @@ -74,6 +74,13 @@ public RunAsPermission runAs() { return runAs; } + /** + * @param restrictedIndices An automaton that can determine whether a string names + * a restricted index. For simple unit tests, this can be + * {@link Automatons#EMPTY}. + * @param names Names of roles. + * @return A builder for a role + */ public static Builder builder(Automaton restrictedIndices, String... names) { return new Builder(restrictedIndices, names); } From 7c2c04b707eef99d8b915ad132ad3776b2a40237 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 29 Jul 2021 13:55:14 -0400 Subject: [PATCH 15/29] DRY by using automaton in constructing a predicate --- .../java/org/elasticsearch/indices/SystemIndices.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java index 40d8386cf4d25..a6778b8622c6d 100644 --- a/server/src/main/java/org/elasticsearch/indices/SystemIndices.java +++ b/server/src/main/java/org/elasticsearch/indices/SystemIndices.java @@ -42,7 +42,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -345,11 +344,8 @@ private static Automaton buildDataStreamAutomaton(Map descripto } private static Predicate buildDataStreamNamePredicate(Map descriptors) { - Set systemDataStreamNames = descriptors.values().stream() - .flatMap(feature -> feature.getDataStreamDescriptors().stream()) - .map(SystemDataStreamDescriptor::getDataStreamName) - .collect(Collectors.toUnmodifiableSet()); - return systemDataStreamNames::contains; + CharacterRunAutomaton characterRunAutomaton = new CharacterRunAutomaton(buildDataStreamAutomaton(descriptors)); + return characterRunAutomaton::run; } private static Automaton buildDataStreamBackingIndicesAutomaton(Map descriptors) { From 28379bac72ab57b88ea7f226c9407f5f3affe2db Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 29 Jul 2021 14:05:57 -0400 Subject: [PATCH 16/29] Refactor to avoid repeating code --- .../security/authz/permission/IndicesPermission.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index b1195e6bb38ca..655dac4ef5095 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -32,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; @@ -175,8 +174,7 @@ public ResourcePrivilegesMap checkResourcePrivileges(Set checkForIndexPa for (String forIndexPattern : checkForIndexPatterns) { Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern); if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) { - Optional restrictedNamesAutomaton = Arrays.stream(groups).map(g -> g.restrictedNamesAutomaton).findFirst(); - checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, restrictedNamesAutomaton.orElse(Automatons.EMPTY)); + checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, getRestrictedNamesAutomaton()); } if (false == Operations.isEmpty(checkIndexAutomaton)) { Automaton allowedIndexPrivilegesAutomaton = null; @@ -354,11 +352,14 @@ private boolean isConcreteRestrictedIndex(String indexPattern) { if (Regex.isSimpleMatchPattern(indexPattern) || Automatons.isLuceneRegex(indexPattern)) { return false; } - CharacterRunAutomaton runAutomaton = - new CharacterRunAutomaton(Arrays.stream(groups).map(g -> g.restrictedNamesAutomaton).findFirst().orElse(Automatons.EMPTY)); + CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(getRestrictedNamesAutomaton()); return runAutomaton.run(indexPattern); } + private Automaton getRestrictedNamesAutomaton() { + return Arrays.stream(groups).map(g -> g.restrictedNamesAutomaton).findFirst().orElse(Automatons.EMPTY); + } + private static boolean isMappingUpdateAction(String action) { return action.equals(PutMappingAction.NAME) || action.equals(AutoPutMappingAction.NAME); } From 7c7e99246dd553276cdbc103e6bb17c085fb20bd Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 29 Jul 2021 14:07:43 -0400 Subject: [PATCH 17/29] Rename test for accuracy --- .../xpack/security/authz/store/CompositeRolesStoreTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 54f9e643b5be3..5d2e057de609c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -1389,7 +1389,7 @@ Version.CURRENT, randomFrom(AuthenticationType.REALM, AuthenticationType.TOKEN, AuthenticationType.ANONYMOUS), Collections.emptyMap()); } - public void testXPackUserCanAccessNonSecurityIndices() { + public void testXPackUserCanAccessNonRestrictedIndices() { CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(RESTRICTED_INDICES_AUTOMATON); for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); From a7b4bab0e2adb3fe18ca9e41c84d9aa1edfe54a0 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 3 Aug 2021 20:15:29 -0400 Subject: [PATCH 18/29] Remove unused method --- .../cluster/metadata/IndexNameExpressionResolver.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index ac74ab911ce8e..cda2ce550608b 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -764,10 +764,6 @@ boolean isPatternMatchingAllIndices(Metadata metadata, String[] indicesOrAliases return false; } - public boolean isSystemName(String name) { - return systemIndices.isSystemName(name); - } - public SystemIndexAccessLevel getSystemIndexAccessLevel() { final SystemIndexAccessLevel accessLevel = systemIndices.getSystemIndexAccessLevel(threadContext); assert accessLevel != SystemIndexAccessLevel.BACKWARDS_COMPATIBLE_ONLY From 60c0b4af00d7da0f82fcfb159cf35874009097c3 Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Sun, 1 Aug 2021 13:14:28 +0300 Subject: [PATCH 19/29] Give IndicesPermissions an automaton reference It's not great that the IndicesPermission has to refer to one of the restrictedNamesAutomaton from one of its groups. Instead, we assign the role's restrictedNamesAutomaton to the individual groups as well as to the containing IndicesPermission. Author: Albert Zaharovits Date: Sun Aug 1 13:14:28 2021 +0300 --- .../authz/permission/IndicesPermission.java | 78 ++++++++++--------- .../core/security/authz/permission/Role.java | 18 ++--- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index 655dac4ef5095..fbe08f3256d7b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -13,11 +13,11 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; import org.elasticsearch.cluster.metadata.IndexAbstraction; import org.elasticsearch.cluster.metadata.IndexMetadata; -import org.elasticsearch.core.Nullable; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.logging.DeprecationCategory; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.core.Nullable; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.support.Automatons; @@ -35,6 +35,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; +import java.util.function.Supplier; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; @@ -47,15 +48,39 @@ public final class IndicesPermission { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(IndicesPermission.class); - public static final IndicesPermission NONE = new IndicesPermission(); + public static final IndicesPermission NONE = new IndicesPermission(Automatons.EMPTY, new Group[0]); private static final Set PRIVILEGE_NAME_SET_BWC_ALLOW_MAPPING_UPDATE = Set.of("create", "create_doc", "index", "write"); private final Map> allowedIndicesMatchersForAction = new ConcurrentHashMap<>(); + private final Automaton restrictedNamesAutomaton; private final Group[] groups; - public IndicesPermission(Group... groups) { + public static class Builder { + + Automaton restrictedNamesAutomaton; + List groups = new ArrayList<>(); + + Builder(Automaton restrictedNamesAutomaton) { + this.restrictedNamesAutomaton = restrictedNamesAutomaton; + } + + void addGroup(IndexPrivilege privilege, + FieldPermissions fieldPermissions, + @Nullable Set query, + boolean allowRestrictedIndices, + String... indices) { + groups.add(new Group(privilege, fieldPermissions, query, allowRestrictedIndices, restrictedNamesAutomaton, indices)); + } + + IndicesPermission build() { + return new IndicesPermission(restrictedNamesAutomaton, groups.toArray(new Group[0])); + } + } + + private IndicesPermission(Automaton restrictedNamesAutomaton, Group[] groups) { + this.restrictedNamesAutomaton = restrictedNamesAutomaton; this.groups = groups; } @@ -66,13 +91,10 @@ public IndicesPermission(Group... groups) { * @param ordinaryIndices A list of ordinary indices. If this collection contains restricted indices, * according to the restrictedNamesAutomaton, they will not be matched. * @param restrictedIndices A list of restricted index names. All of these will be matched. - * @param restrictedNamesAutomaton An automaton that will match restricted indices. We use this to filter - * out restricted indices from the ordinaryIndices collection. * @return A matcher that will match all non-restricted index names in the ordinaryIndices * collection and all index names in the restrictedIndices collection. */ - private static StringMatcher indexMatcher(Collection ordinaryIndices, Collection restrictedIndices, - Automaton restrictedNamesAutomaton) { + private StringMatcher indexMatcher(Collection ordinaryIndices, Collection restrictedIndices) { StringMatcher matcher; if (ordinaryIndices.isEmpty()) { matcher = StringMatcher.of(restrictedIndices); @@ -107,14 +129,7 @@ private Predicate buildIndexMatcherPredicateForAction(String a final Set grantMappingUpdatesOnIndices = new HashSet<>(); final Set grantMappingUpdatesOnRestrictedIndices = new HashSet<>(); final boolean isMappingUpdateAction = isMappingUpdateAction(action); - Automaton restrictedNamesAutomaton = null; for (final Group group : groups) { - if (restrictedNamesAutomaton == null) { - restrictedNamesAutomaton = group.restrictedNamesAutomaton; - } else { - assert restrictedNamesAutomaton == group.restrictedNamesAutomaton : "Groups have different restricted names automatons"; - } - if (group.actionMatcher.test(action)) { if (group.allowRestrictedIndices) { restrictedIndices.addAll(Arrays.asList(group.indices())); @@ -131,9 +146,8 @@ private Predicate buildIndexMatcherPredicateForAction(String a } } } - final StringMatcher nameMatcher = indexMatcher(ordinaryIndices, restrictedIndices, restrictedNamesAutomaton); - final StringMatcher bwcSpecialCaseMatcher = indexMatcher(grantMappingUpdatesOnIndices, - grantMappingUpdatesOnRestrictedIndices, restrictedNamesAutomaton); + final StringMatcher nameMatcher = indexMatcher(ordinaryIndices, restrictedIndices); + final StringMatcher bwcSpecialCaseMatcher = indexMatcher(grantMappingUpdatesOnIndices, grantMappingUpdatesOnRestrictedIndices); return indexAbstraction -> nameMatcher.test(indexAbstraction.getName()) || (indexAbstraction.getType() != IndexAbstraction.Type.DATA_STREAM && @@ -174,12 +188,12 @@ public ResourcePrivilegesMap checkResourcePrivileges(Set checkForIndexPa for (String forIndexPattern : checkForIndexPatterns) { Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern); if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) { - checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, getRestrictedNamesAutomaton()); + checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, restrictedNamesAutomaton); } if (false == Operations.isEmpty(checkIndexAutomaton)) { Automaton allowedIndexPrivilegesAutomaton = null; for (Group group : groups) { - final Automaton groupIndexAutomaton = predicateCache.computeIfAbsent(group, Group::buildIndexMatcherAutomaton); + final Automaton groupIndexAutomaton = predicateCache.computeIfAbsent(group, Group::getIndexMatcherAutomaton); if (Operations.subsetOf(checkIndexAutomaton, groupIndexAutomaton)) { if (allowedIndexPrivilegesAutomaton != null) { allowedIndexPrivilegesAutomaton = Automatons @@ -352,14 +366,10 @@ private boolean isConcreteRestrictedIndex(String indexPattern) { if (Regex.isSimpleMatchPattern(indexPattern) || Automatons.isLuceneRegex(indexPattern)) { return false; } - CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(getRestrictedNamesAutomaton()); + CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); return runAutomaton.run(indexPattern); } - private Automaton getRestrictedNamesAutomaton() { - return Arrays.stream(groups).map(g -> g.restrictedNamesAutomaton).findFirst().orElse(Automatons.EMPTY); - } - private static boolean isMappingUpdateAction(String action) { return action.equals(PutMappingAction.NAME) || action.equals(AutoPutMappingAction.NAME); } @@ -375,13 +385,13 @@ public static class Group { private final Predicate actionMatcher; private final String[] indices; private final Predicate indexNameMatcher; + private final Supplier indexNameAutomaton; private final FieldPermissions fieldPermissions; private final Set query; // by default certain restricted indices are exempted when granting privileges, as they should generally be hidden for ordinary // users. Setting this flag true eliminates the special status for the purpose of this permission - restricted indices still have // to be covered by the "indices" private final boolean allowRestrictedIndices; - private final Automaton restrictedNamesAutomaton; public Group(IndexPrivilege privilege, FieldPermissions fieldPermissions, @Nullable Set query, boolean allowRestrictedIndices, Automaton restrictedNamesAutomaton, String... indices) { @@ -390,13 +400,16 @@ public Group(IndexPrivilege privilege, FieldPermissions fieldPermissions, @Nulla this.actionMatcher = privilege.predicate(); this.indices = indices; this.allowRestrictedIndices = allowRestrictedIndices; - this.restrictedNamesAutomaton = Objects.requireNonNull(restrictedNamesAutomaton); + ConcurrentHashMap indexNameAutomatonMemo = new ConcurrentHashMap<>(1); if (allowRestrictedIndices) { this.indexNameMatcher = StringMatcher.of(indices); + this.indexNameAutomaton = () -> indexNameAutomatonMemo.computeIfAbsent(indices, k -> Automatons.patterns(indices)); } else { final CharacterRunAutomaton restrictedNamesRunAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); this.indexNameMatcher = StringMatcher.of(indices) - .and(name -> restrictedNamesRunAutomaton.run(name) == false); + .and(name -> restrictedNamesRunAutomaton.run(name) == false); + this.indexNameAutomaton = () -> indexNameAutomatonMemo.computeIfAbsent(indices, + k -> Automatons.minusAndMinimize(Automatons.patterns(indices), restrictedNamesAutomaton)); } this.fieldPermissions = Objects.requireNonNull(fieldPermissions); this.query = query; @@ -436,16 +449,9 @@ public boolean allowRestrictedIndices() { return allowRestrictedIndices; } - public Automaton buildIndexMatcherAutomaton() { - final Automaton indicesAutomaton = Automatons.patterns(indices); - if (allowRestrictedIndices) { - return indicesAutomaton; - } else { - return Automatons.minusAndMinimize(indicesAutomaton, restrictedNamesAutomaton); - } + public Automaton getIndexMatcherAutomaton() { + return indexNameAutomaton.get(); } - - } private static class DocumentLevelPermissions { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java index bc2906293058a..f00a16be30714 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java @@ -8,15 +8,14 @@ import org.apache.lucene.util.automaton.Automaton; import org.elasticsearch.cluster.metadata.IndexAbstraction; -import org.elasticsearch.core.Nullable; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.core.Tuple; import org.elasticsearch.common.util.set.Sets; +import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.Tuple; import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; -import org.elasticsearch.xpack.core.security.authz.permission.IndicesPermission.Group; import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor; import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege; @@ -266,11 +265,12 @@ public Role build() { if (groups.isEmpty()) { indices = IndicesPermission.NONE; } else { - Group[] groupsArray = new Group[groups.size()]; - for (int i = 0; i < groups.size(); i++) { - groupsArray[i] = groups.get(i).toGroup(restrictedNamesAutomaton); + IndicesPermission.Builder indicesBuilder = new IndicesPermission.Builder(restrictedNamesAutomaton); + for (IndicesPermissionGroupDefinition group : groups) { + indicesBuilder.addGroup(group.privilege, group.fieldPermissions, group.query, group.allowRestrictedIndices, + group.indices); } - indices = new IndicesPermission(groupsArray); + indices = indicesBuilder.build(); } final ApplicationPermission applicationPermission = applicationPrivs.isEmpty() ? ApplicationPermission.NONE : new ApplicationPermission(applicationPrivs); @@ -320,10 +320,6 @@ private IndicesPermissionGroupDefinition(IndexPrivilege privilege, this.allowRestrictedIndices = allowRestrictedIndices; this.indices = indices; } - - private IndicesPermission.Group toGroup(Automaton automaton) { - return new Group(privilege, fieldPermissions, query, allowRestrictedIndices, automaton, indices); - } } } From 02bb4e1a2fbff1f7369a848b4f77df1ed3a8e4d9 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 3 Aug 2021 22:46:55 -0400 Subject: [PATCH 20/29] Adjust tests for new builder --- .../authz/permission/IndicesPermission.java | 7 +- .../accesscontrol/IndicesPermissionTests.java | 107 +++++++++++------- 2 files changed, 69 insertions(+), 45 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index fbe08f3256d7b..b44d3ef29cf72 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -62,19 +62,20 @@ public static class Builder { Automaton restrictedNamesAutomaton; List groups = new ArrayList<>(); - Builder(Automaton restrictedNamesAutomaton) { + public Builder(Automaton restrictedNamesAutomaton) { this.restrictedNamesAutomaton = restrictedNamesAutomaton; } - void addGroup(IndexPrivilege privilege, + public Builder addGroup(IndexPrivilege privilege, FieldPermissions fieldPermissions, @Nullable Set query, boolean allowRestrictedIndices, String... indices) { groups.add(new Group(privilege, fieldPermissions, query, allowRestrictedIndices, restrictedNamesAutomaton, indices)); + return this; } - IndicesPermission build() { + public IndicesPermission build() { return new IndicesPermission(restrictedNamesAutomaton, groups.toArray(new Group[0])); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index 48d27b0a0b931..81f99d88afc43 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -248,12 +248,15 @@ public void testCorePermissionAuthorize() { SortedMap lookup = metadata.getIndicesLookup(); FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), - RESTRICTED_INDICES_AUTOMATON, "a1"); - IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.READ, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), - RESTRICTED_INDICES_AUTOMATON, "a1"); - IndicesPermission core = new IndicesPermission(group1, group2); + IndicesPermission core = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), "a1") + .addGroup( + IndexPrivilege.READ, + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), + null, + randomBoolean(), + "a1") + .build(); Map authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), lookup, fieldPermissionsCache); assertTrue(authzMap.get("a1").getFieldPermissions().grantsAccessTo("denied_field")); @@ -267,18 +270,27 @@ public void testCorePermissionAuthorize() { assertFalse(core.check("unknown")); // test with two indices - group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), - RESTRICTED_INDICES_AUTOMATON, "a1"); - group2 = new IndicesPermission.Group(IndexPrivilege.ALL, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), - RESTRICTED_INDICES_AUTOMATON, "a1"); - IndicesPermission.Group group3 = new IndicesPermission.Group(IndexPrivilege.ALL, - new FieldPermissions(fieldPermissionDef(new String[] { "*_field" }, new String[] { "denied_field" })), null, - randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a2"); - IndicesPermission.Group group4 = new IndicesPermission.Group(IndexPrivilege.ALL, - new FieldPermissions(fieldPermissionDef(new String[] { "*_field2" }, new String[] { "denied_field2" })), null, - randomBoolean(), RESTRICTED_INDICES_AUTOMATON, "a2"); - core = new IndicesPermission(group1, group2, group3, group4); + core = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.ALL, new FieldPermissions(), null, randomBoolean(), "a1") + .addGroup( + IndexPrivilege.ALL, + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), + null, + randomBoolean(), + "a1") + .addGroup( + IndexPrivilege.ALL, + new FieldPermissions(fieldPermissionDef(new String[] { "*_field" }, new String[] { "denied_field" })), + null, + randomBoolean(), + "a2") + .addGroup( + IndexPrivilege.ALL, + new FieldPermissions(fieldPermissionDef(new String[] { "*_field2" }, new String[] { "denied_field2" })), + null, + randomBoolean(), + "a2") + .build(); authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), lookup, fieldPermissionsCache); assertFalse(authzMap.get("a1").getFieldPermissions().hasFieldLevelSecurity()); assertFalse(authzMap.get("a2").getFieldPermissions().grantsAccessTo("denied_field2")); @@ -323,17 +335,20 @@ public void testSecurityIndicesPermissions() { SortedMap lookup = metadata.getIndicesLookup(); // allow_restricted_indices: false - IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, - RESTRICTED_INDICES_AUTOMATON, "*"); - Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, - Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, - fieldPermissionsCache); + IndicesPermission indicesPermission = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.ALL, new FieldPermissions(), null, false, "*") + .build(); + Map authzMap = indicesPermission.authorize(SearchAction.NAME, + Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, + fieldPermissionsCache); assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(false)); assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(false)); // allow_restricted_indices: true - group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, RESTRICTED_INDICES_AUTOMATON, "*"); - authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, + indicesPermission = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.ALL, new FieldPermissions(), null, true, "*") + .build(); + authzMap = indicesPermission.authorize(SearchAction.NAME, Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), lookup, fieldPermissionsCache); assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true)); @@ -354,15 +369,18 @@ public void testAsyncSearchIndicesPermissions() { SortedMap lookup = metadata.getIndicesLookup(); // allow_restricted_indices: false - IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, - RESTRICTED_INDICES_AUTOMATON, "*"); - Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, + IndicesPermission indicesPermission = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.ALL, new FieldPermissions(), null, false, "*") + .build(); + Map authzMap = indicesPermission.authorize(SearchAction.NAME, Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(false)); // allow_restricted_indices: true - group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, RESTRICTED_INDICES_AUTOMATON, "*"); - authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, + indicesPermission = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.ALL, new FieldPermissions(), null, true, "*") + .build(); + authzMap = indicesPermission.authorize(SearchAction.NAME, Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true)); } @@ -385,9 +403,10 @@ public void testAuthorizationForBackingIndices() { FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); SortedMap lookup = metadata.getIndicesLookup(); - IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.READ, new FieldPermissions(), null, false, - RESTRICTED_INDICES_AUTOMATON, dataStreamName); - Map authzMap = new IndicesPermission(group).authorize( + IndicesPermission indicesPermission = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.READ, new FieldPermissions(), null, false, dataStreamName) + .build(); + Map authzMap = indicesPermission.authorize( SearchAction.NAME, Sets.newHashSet(backingIndices.stream().map(im -> im.getIndex().getName()).collect(Collectors.toList())), lookup, @@ -397,9 +416,10 @@ public void testAuthorizationForBackingIndices() { assertThat(authzMap.get(im.getIndex().getName()).isGranted(), is(true)); } - group = new IndicesPermission.Group(IndexPrivilege.CREATE_DOC, new FieldPermissions(), null, false, RESTRICTED_INDICES_AUTOMATON, - dataStreamName); - authzMap = new IndicesPermission(group).authorize( + indicesPermission = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.CREATE_DOC, new FieldPermissions(), null, false, dataStreamName) + .build(); + authzMap = indicesPermission.authorize( randomFrom(PutMappingAction.NAME, AutoPutMappingAction.NAME), Sets.newHashSet(backingIndices.stream().map(im -> im.getIndex().getName()).collect(Collectors.toList())), lookup, @@ -431,12 +451,15 @@ public void testAuthorizationForMappingUpdates() { SortedMap lookup = metadata.build().getIndicesLookup(); FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.INDEX, new FieldPermissions(), null, randomBoolean(), - RESTRICTED_INDICES_AUTOMATON, "test*"); - IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.WRITE, - new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, randomBoolean(), - RESTRICTED_INDICES_AUTOMATON, "test_write*"); - IndicesPermission core = new IndicesPermission(group1, group2); + IndicesPermission core = new IndicesPermission.Builder(RESTRICTED_INDICES_AUTOMATON) + .addGroup(IndexPrivilege.INDEX, new FieldPermissions(), null, randomBoolean(), "test*") + .addGroup( + IndexPrivilege.WRITE, + new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), + null, + randomBoolean(), + "test_write*") + .build(); Map authzMap = core.authorize(PutMappingAction.NAME, Sets.newHashSet("test1", "test_write1"), lookup, fieldPermissionsCache); assertThat(authzMap.get("test1").isGranted(), is(true)); From f1964fa7e0ec4b073301324eb0d3374ba5ca6f80 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 3 Aug 2021 22:51:22 -0400 Subject: [PATCH 21/29] Precompute character run automaton --- .../core/security/authz/permission/IndicesPermission.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index b44d3ef29cf72..056cf7939964e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -56,6 +56,7 @@ public final class IndicesPermission { private final Automaton restrictedNamesAutomaton; private final Group[] groups; + private final CharacterRunAutomaton characterRunAutomaton; public static class Builder { @@ -82,6 +83,7 @@ public IndicesPermission build() { private IndicesPermission(Automaton restrictedNamesAutomaton, Group[] groups) { this.restrictedNamesAutomaton = restrictedNamesAutomaton; + this.characterRunAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); this.groups = groups; } @@ -367,8 +369,7 @@ private boolean isConcreteRestrictedIndex(String indexPattern) { if (Regex.isSimpleMatchPattern(indexPattern) || Automatons.isLuceneRegex(indexPattern)) { return false; } - CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(restrictedNamesAutomaton); - return runAutomaton.run(indexPattern); + return characterRunAutomaton.run(indexPattern); } private static boolean isMappingUpdateAction(String action) { From 0a1a1fe8b91952c558d90e3ff7f50f8993277736 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Wed, 4 Aug 2021 17:39:13 -0400 Subject: [PATCH 22/29] Add a few tests --- .../authz/store/CompositeRolesStoreTests.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 5d2e057de609c..bba207fe98be6 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -1460,6 +1460,27 @@ public void testAsyncSearchUserHasNoClusterPrivileges() { } } + // async search can't read/write audit trail + public void testAsyncSearchUserCannotReadAuditTrail() { + final String action = randomFrom(GetAction.NAME, SearchAction.NAME); + final Predicate predicate = getAsyncSearchUserRole().indices().allowedIndicesMatcher(action); + assertThat(predicate.test(mockIndexAbstraction(getAuditLogName())), Matchers.is(false)); + } + + public void testAsyncSearchUserCannotWriteToAuditTrail() { + final String action = randomFrom(IndexAction.NAME, UpdateAction.NAME); + final Predicate predicate = getAsyncSearchUserRole().indices().allowedIndicesMatcher(action); + assertThat(predicate.test(mockIndexAbstraction(getAuditLogName())), Matchers.is(false)); + } + + + public void testXpackUserHasClusterPrivileges() { + for (String action : Arrays.asList(ClusterStateAction.NAME, GetWatchAction.NAME, ClusterStatsAction.NAME, NodesStatsAction.NAME)) { + assertThat(getXPackUserRole().cluster().check(action, mock(TransportRequest.class), mock(Authentication.class)), + Matchers.is(true)); + } + } + private Role getXPackUserRole() { CompositeRolesStore compositeRolesStore = buildCompositeRolesStore(SECURITY_ENABLED_SETTINGS, null, null, null, null, null, null, null, null, null); From a322e1cdde964ed82af2d65169f7ebda27a7f1f6 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Wed, 11 Aug 2021 01:39:08 -0400 Subject: [PATCH 23/29] Remove unneeded restricted index access --- .../xpack/core/security/authz/store/ReservedRolesStore.java | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index e56809fa5e7e7..16407177d43e1 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -279,7 +279,6 @@ private static Map initializeReservedRoles() { new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() .indices(".ml-anomalies*", ".ml-notifications*", ".ml-state*", ".ml-meta*", ".ml-stats-*") - .allowRestrictedIndices(true) .privileges("view_index_metadata", "read").build(), RoleDescriptor.IndicesPrivileges.builder().indices(".ml-annotations*") .privileges("view_index_metadata", "read", "write").build() From 40de66b20dc52d207a9130ba7f7f9fb966d3d417 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Wed, 11 Aug 2021 11:03:18 -0400 Subject: [PATCH 24/29] Roll back changes to reserved roles to see what breaks in tests --- .../authz/store/ReservedRolesStore.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index 16407177d43e1..26956c2ad7582 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -93,7 +93,7 @@ private static Map initializeReservedRoles() { RoleDescriptor.IndicesPrivileges.builder() .indices("*").privileges("monitor").allowRestrictedIndices(true).build(), RoleDescriptor.IndicesPrivileges.builder() - .indices(".kibana*").privileges("read").allowRestrictedIndices(true).build() + .indices(".kibana*").privileges("read").build() }, null, null, @@ -139,7 +139,7 @@ private static Map initializeReservedRoles() { }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder() - .indices(".kibana*", ".reporting-*").privileges("all").allowRestrictedIndices(true).build(), + .indices(".kibana*", ".reporting-*").privileges("all").build(), RoleDescriptor.IndicesPrivileges.builder() .indices(".monitoring-*").privileges("read", "read_cross_cluster").build(), RoleDescriptor.IndicesPrivileges.builder() @@ -152,10 +152,10 @@ private static Map initializeReservedRoles() { .privileges("read", "write").build(), // APM agent configuration RoleDescriptor.IndicesPrivileges.builder() - .indices(".apm-agent-configuration").privileges("all").allowRestrictedIndices(true).build(), + .indices(".apm-agent-configuration").privileges("all").build(), // APM custom link index creation RoleDescriptor.IndicesPrivileges.builder() - .indices(".apm-custom-link").privileges("all").allowRestrictedIndices(true).build(), + .indices(".apm-custom-link").privileges("all").build(), // APM telemetry queries APM indices in kibana task runner RoleDescriptor.IndicesPrivileges.builder() .indices("apm-*") @@ -163,7 +163,7 @@ private static Map initializeReservedRoles() { // Data telemetry reads mappings, metadata and stats of indices RoleDescriptor.IndicesPrivileges.builder() .indices("*") - .privileges("view_index_metadata", "monitor").allowRestrictedIndices(true).build(), + .privileges("view_index_metadata", "monitor").build(), // Endpoint diagnostic information. Kibana reads from these indices to send telemetry RoleDescriptor.IndicesPrivileges.builder() .indices(".logs-endpoint.diagnostic.collection-*") @@ -172,7 +172,7 @@ private static Map initializeReservedRoles() { // Fleet Server indices. Kibana read and write to this indice to manage Elastic Agents RoleDescriptor.IndicesPrivileges.builder() .indices(".fleet*") - .privileges("all").allowRestrictedIndices(true).build(), + .privileges("all").build(), // Legacy "Alerts as data" index. Kibana user will create this index. // Kibana user will read / write to these indices RoleDescriptor.IndicesPrivileges.builder() @@ -338,13 +338,12 @@ private static Map initializeReservedRoles() { .put("watcher_admin", new RoleDescriptor("watcher_admin", new String[] { "manage_watcher" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX, TriggeredWatchStoreField.INDEX_NAME, - HistoryStoreField.INDEX_PREFIX + "*").allowRestrictedIndices(true).privileges("read").build() }, + HistoryStoreField.INDEX_PREFIX + "*").privileges("read").build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("watcher_user", new RoleDescriptor("watcher_user", new String[] { "monitor_watcher" }, new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder().indices(Watch.INDEX) .privileges("read") - .allowRestrictedIndices(true) .build(), RoleDescriptor.IndicesPrivileges.builder().indices(HistoryStoreField.INDEX_PREFIX + "*") .privileges("read") @@ -353,7 +352,6 @@ private static Map initializeReservedRoles() { new RoleDescriptor.IndicesPrivileges[] { RoleDescriptor.IndicesPrivileges.builder().indices(".logstash*") .privileges("create", "delete", "index", "manage", "read") - .allowRestrictedIndices(true) .build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("rollup_user", new RoleDescriptor("rollup_user", new String[] { "monitor_rollup" }, @@ -369,7 +367,6 @@ private static Map initializeReservedRoles() { .put("enrich_user", new RoleDescriptor("enrich_user", new String[]{ "manage_enrich", "manage_ingest_pipelines", "monitor" }, new RoleDescriptor.IndicesPrivileges[]{ RoleDescriptor.IndicesPrivileges.builder() .indices(".enrich-*") - .allowRestrictedIndices(true) .privileges("manage", "read", "write") .build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA)) .put("viewer", buildViewerRoleDescriptor()) From 6393c5bee02d1efa6e8f2ec765f7cb15f3d49d62 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 19 Aug 2021 10:29:23 -0400 Subject: [PATCH 25/29] Remove allow restricted indices from client test role --- client/rest-high-level/roles.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/client/rest-high-level/roles.yml b/client/rest-high-level/roles.yml index 22b4c60ef1bc4..d3d0630f43058 100644 --- a/client/rest-high-level/roles.yml +++ b/client/rest-high-level/roles.yml @@ -3,7 +3,6 @@ admin: - all indices: - names: '*' - allow_restricted_indices: true privileges: - all run_as: [ '*' ] From e6b686cfb2576489023cda9d24388055e6999804 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 19 Aug 2021 11:49:53 -0400 Subject: [PATCH 26/29] Try using an admin client for cleaning test state --- .../client/ESRestHighLevelClientTestCase.java | 19 +++++++++++++++++++ .../client/MachineLearningGetResultsIT.java | 2 +- .../client/MlTestStateCleaner.java | 9 ++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java index 700570934ecaa..956c4acba9963 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java @@ -22,6 +22,7 @@ import org.elasticsearch.client.cluster.RemoteInfoRequest; import org.elasticsearch.client.cluster.RemoteInfoResponse; import org.elasticsearch.client.indices.CreateIndexRequest; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.core.Booleans; import org.elasticsearch.core.CheckedRunnable; import org.elasticsearch.common.bytes.BytesReference; @@ -66,6 +67,7 @@ public abstract class ESRestHighLevelClientTestCase extends ESRestTestCase { protected static final String CONFLICT_PIPELINE_ID = "conflict_pipeline"; private static RestHighLevelClient restHighLevelClient; + private static RestHighLevelClient adminRestHighLevelClient; private static boolean async = Booleans.parseBoolean(System.getProperty("tests.rest.async", "false")); @Before @@ -74,18 +76,35 @@ public void initHighLevelClient() throws IOException { if (restHighLevelClient == null) { restHighLevelClient = new HighLevelClient(client()); } + if (adminRestHighLevelClient == null) { + adminRestHighLevelClient = new HighLevelClient(adminClient()); + } } @AfterClass public static void cleanupClient() throws IOException { IOUtils.close(restHighLevelClient); + IOUtils.close(adminRestHighLevelClient); restHighLevelClient = null; + adminRestHighLevelClient = null; } protected static RestHighLevelClient highLevelClient() { return restHighLevelClient; } + @Override + protected Settings restAdminSettings() { + String token = basicAuthHeaderValue("admin_user", new SecureString("admin-password".toCharArray())); + return Settings.builder() + .put(ThreadContext.PREFIX + ".Authorization", token) + .build(); + } + + protected static RestHighLevelClient adminHighLevelClient() { + return adminRestHighLevelClient; + } + /** * Executes the provided request using either the sync method or its async variant, both provided as functions */ diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java index 136e9f00a0c46..cad4a89a7a95d 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningGetResultsIT.java @@ -274,7 +274,7 @@ private void addModelSnapshotIndexRequests(BulkRequest bulkRequest) throws IOExc @After public void deleteJob() throws IOException { - new MlTestStateCleaner(logger, highLevelClient()).clearMlMetadata(); + new MlTestStateCleaner(logger, adminHighLevelClient()).clearMlMetadata(); } public void testGetModelSnapshots() throws IOException { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java index f7e42db2ee810..fb273fe1336f2 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java @@ -9,9 +9,11 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.elasticsearch.action.admin.cluster.snapshots.features.ResetFeatureStateResponse; import org.elasticsearch.action.ingest.DeletePipelineRequest; import org.elasticsearch.client.core.PageParams; import org.elasticsearch.client.feature.ResetFeaturesRequest; +import org.elasticsearch.client.feature.ResetFeaturesResponse; import org.elasticsearch.client.ml.GetTrainedModelsStatsRequest; import java.io.IOException; @@ -36,7 +38,12 @@ public MlTestStateCleaner(Logger logger, RestHighLevelClient client) { public void clearMlMetadata() throws IOException { deleteAllTrainedModelIngestPipelines(); // This resets all features, not just ML, but they should have been getting reset between tests anyway so it shouldn't matter - client.features().resetFeatures(new ResetFeaturesRequest(), RequestOptions.DEFAULT); + ResetFeaturesResponse response = client.features().resetFeatures(new ResetFeaturesRequest(), RequestOptions.DEFAULT); + for (ResetFeaturesResponse.ResetFeatureStateStatus status : response.getFeatureResetStatuses()) { + if (status.getStatus().equals("FAILURE")) { + logger.info("Reset state response: " + status.getException()); + } + } } @SuppressWarnings("unchecked") From 2e38e55febcfb15271c49094ace0d9eecec4c2ff Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 19 Aug 2021 12:52:53 -0400 Subject: [PATCH 27/29] checkstyle --- .../test/java/org/elasticsearch/client/MlTestStateCleaner.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java index fb273fe1336f2..6ca7bf05c31bc 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java @@ -9,7 +9,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.elasticsearch.action.admin.cluster.snapshots.features.ResetFeatureStateResponse; import org.elasticsearch.action.ingest.DeletePipelineRequest; import org.elasticsearch.client.core.PageParams; import org.elasticsearch.client.feature.ResetFeaturesRequest; From af12dc90a97d3179e1458cdb2ee59418d4dc714a Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 19 Aug 2021 15:53:21 -0400 Subject: [PATCH 28/29] Give admin client access to truststore settings --- .../java/org/elasticsearch/client/EnrollmentIT.java | 5 +++++ .../client/documentation/EnrollmentDocumentationIT.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java index 6940035fc72a5..c8cb40e054ff1 100644 --- a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java +++ b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java @@ -59,6 +59,11 @@ protected Settings restClientSettings() { .build(); } + @Override + protected Settings restAdminSettings() { + return restClientSettings(); + } + public void testEnrollNode() throws Exception { final NodeEnrollmentResponse nodeEnrollmentResponse = execute(highLevelClient().security()::enrollNode, highLevelClient().security()::enrollNodeAsync, RequestOptions.DEFAULT); diff --git a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java index 9511df7a4fd03..c8d3229883eb0 100644 --- a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java +++ b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java @@ -52,6 +52,11 @@ protected Settings restClientSettings() { .build(); } + @Override + protected Settings restAdminSettings() { + return restClientSettings(); + } + public void testNodeEnrollment() throws Exception { RestHighLevelClient client = highLevelClient(); From 0995be0085d791c62387a6dc1588664b0192fe2c Mon Sep 17 00:00:00 2001 From: William Brafford Date: Thu, 19 Aug 2021 17:44:39 -0400 Subject: [PATCH 29/29] Add explanatory comments and use warn-level logging for reset failures --- .../java/org/elasticsearch/client/EnrollmentIT.java | 6 ++++++ .../client/documentation/EnrollmentDocumentationIT.java | 6 ++++++ .../org/elasticsearch/client/MlTestStateCleaner.java | 9 ++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java index c8cb40e054ff1..7e4b8b37f1c6b 100644 --- a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java +++ b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/EnrollmentIT.java @@ -59,6 +59,12 @@ protected Settings restClientSettings() { .build(); } + /** + * Cleanup for these tests requires the admin client to have access to the + * truststore, so we use the same settings that we're using for the rest + * client in this test class. + * @return Settings for the admin client. + */ @Override protected Settings restAdminSettings() { return restClientSettings(); diff --git a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java index c8d3229883eb0..cbdf9ccfac712 100644 --- a/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java +++ b/client/rest-high-level/qa/ssl-enabled/src/javaRestTest/java/org/elasticsearch/client/documentation/EnrollmentDocumentationIT.java @@ -52,6 +52,12 @@ protected Settings restClientSettings() { .build(); } + /** + * Cleanup for these tests requires the admin client to have access to the + * truststore, so we use the same settings that we're using for the rest + * client in this test class. + * @return Settings for the admin client. + */ @Override protected Settings restAdminSettings() { return restClientSettings(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java index 6ca7bf05c31bc..5cbffb7157ca4 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/MlTestStateCleaner.java @@ -38,9 +38,12 @@ public void clearMlMetadata() throws IOException { deleteAllTrainedModelIngestPipelines(); // This resets all features, not just ML, but they should have been getting reset between tests anyway so it shouldn't matter ResetFeaturesResponse response = client.features().resetFeatures(new ResetFeaturesRequest(), RequestOptions.DEFAULT); - for (ResetFeaturesResponse.ResetFeatureStateStatus status : response.getFeatureResetStatuses()) { - if (status.getStatus().equals("FAILURE")) { - logger.info("Reset state response: " + status.getException()); + if (response.getFeatureResetStatuses().stream().anyMatch(status -> "FAILURE".equals(status.getStatus()))) { + logger.warn("Not all feature states could be reset while clearing ML Metadata:"); + for (ResetFeaturesResponse.ResetFeatureStateStatus status : response.getFeatureResetStatuses()) { + if (status.getStatus().equals("FAILURE")) { + logger.warn("Feature {} failed with response: {}", status.getFeatureName(), status.getException()); + } } } }