From a73be456ec596c8f96a8c8071581d21ed619475a Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Mon, 26 Jun 2017 12:30:31 +1000 Subject: [PATCH] [Security] Don't apply local restrictions on remote cluster search (elastic/x-pack-elasticsearch#1609) This change removes all local security checks against remote cluster names. Any user is allowed to attempt a cross-cluster search, and it is the responsibility of the remote cluster to authorise the search (or not). This includes support for remote searches even if you have _no_ local search privileges. Original commit: elastic/x-pack-elasticsearch@1620c3a8fa0409b0f21cf82994076efcbcdcf1fc --- .../security/authz/AuthorizationService.java | 49 ++- .../authz/IndicesAndAliasesResolver.java | 222 +++++++++--- .../authz/AuthorizationServiceTests.java | 91 ++++- .../authz/IndicesAndAliasesResolverTests.java | 338 ++++++++++-------- 4 files changed, 470 insertions(+), 230 deletions(-) diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java index ba753e6d3a8..a9af008f2c8 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java @@ -5,6 +5,13 @@ */ package org.elasticsearch.xpack.security.authz; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.CompositeIndicesRequest; @@ -22,7 +29,6 @@ import org.elasticsearch.action.search.SearchScrollAction; import org.elasticsearch.action.search.SearchTransportService; import org.elasticsearch.action.support.replication.TransportReplicationAction.ConcreteShardRequest; import org.elasticsearch.action.termvectors.MultiTermVectorsAction; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.AbstractComponent; @@ -44,6 +50,7 @@ import org.elasticsearch.xpack.security.authc.Authentication; import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler; import org.elasticsearch.xpack.security.authc.esnative.NativeRealm; import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; +import org.elasticsearch.xpack.security.authz.IndicesAndAliasesResolver.ResolvedIndices; import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.security.authz.permission.ClusterPermission; import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache; @@ -58,13 +65,6 @@ import org.elasticsearch.xpack.security.user.SystemUser; import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.user.XPackUser; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; - import static org.elasticsearch.xpack.security.Security.setting; import static org.elasticsearch.xpack.security.support.Exceptions.authorizationError; @@ -250,24 +250,37 @@ public class AuthorizationService extends AbstractComponent { } } - if (permission.indices().check(action) == false) { + final boolean allowsRemoteIndices = request instanceof IndicesRequest + && IndicesAndAliasesResolver.allowsRemoteIndices((IndicesRequest) request); + + // If this request does not allow remote indices + // then the user must have permission to perform this action on at least 1 local index + if (allowsRemoteIndices == false && permission.indices().check(action) == false) { throw denial(authentication, action, request); } - MetaData metaData = clusterService.state().metaData(); - AuthorizedIndices authorizedIndices = new AuthorizedIndices(authentication.getUser(), permission, action, metaData); - Set indexNames = resolveIndexNames(authentication, action, request, metaData, authorizedIndices); - assert !indexNames.isEmpty() : "every indices request needs to have its indices set thus the resolved indices must not be empty"; + final MetaData metaData = clusterService.state().metaData(); + final AuthorizedIndices authorizedIndices = new AuthorizedIndices(authentication.getUser(), permission, action, metaData); + final ResolvedIndices resolvedIndices = resolveIndexNames(authentication, action, request, metaData, authorizedIndices); + assert !resolvedIndices.isEmpty() + : "every indices request needs to have its indices set thus the resolved indices must not be empty"; + + // If this request does reference any remote indices + // then the user must have permission to perform this action on at least 1 local index + if (resolvedIndices.getRemote().isEmpty() && permission.indices().check(action) == false) { + throw denial(authentication, action, request); + } //all wildcard expressions have been resolved and only the security plugin could have set '-*' here. //'-*' matches no indices so we allow the request to go through, which will yield an empty response - if (indexNames.size() == 1 && indexNames.contains(IndicesAndAliasesResolver.NO_INDEX_PLACEHOLDER)) { + if (resolvedIndices.isNoIndicesPlaceholder()) { setIndicesAccessControl(IndicesAccessControl.ALLOW_NO_INDICES); grant(authentication, action, request); return; } - IndicesAccessControl indicesAccessControl = permission.authorize(action, indexNames, metaData, fieldPermissionsCache); + final Set localIndices = new HashSet<>(resolvedIndices.getLocal()); + IndicesAccessControl indicesAccessControl = permission.authorize(action, localIndices, metaData, fieldPermissionsCache); if (!indicesAccessControl.isGranted()) { throw denial(authentication, action, request); } else if (indicesAccessControl.getIndexPermissions(SecurityLifecycleService.SECURITY_INDEX_NAME) != null @@ -289,7 +302,7 @@ public class AuthorizationService extends AbstractComponent { assert request instanceof CreateIndexRequest; Set aliases = ((CreateIndexRequest) request).aliases(); if (!aliases.isEmpty()) { - Set aliasesAndIndices = Sets.newHashSet(indexNames); + Set aliasesAndIndices = Sets.newHashSet(localIndices); for (Alias alias : aliases) { aliasesAndIndices.add(alias.name()); } @@ -305,8 +318,8 @@ public class AuthorizationService extends AbstractComponent { grant(authentication, action, originalRequest); } - private Set resolveIndexNames(Authentication authentication, String action, TransportRequest request, MetaData metaData, - AuthorizedIndices authorizedIndices) { + private ResolvedIndices resolveIndexNames(Authentication authentication, String action, TransportRequest request, MetaData metaData, + AuthorizedIndices authorizedIndices) { try { return indicesAndAliasesResolver.resolve(request, metaData, authorizedIndices); } catch (Exception e) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java index 1e31460f1e5..75827c36285 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java @@ -5,28 +5,6 @@ */ package org.elasticsearch.xpack.security.authz; -import org.elasticsearch.action.AliasesRequest; -import org.elasticsearch.action.IndicesRequest; -import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; -import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; -import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest; -import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; -import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest; -import org.elasticsearch.transport.RemoteClusterAware; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.cluster.metadata.AliasOrIndex; -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.common.collect.Tuple; -import org.elasticsearch.common.regex.Regex; -import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.IndexNotFoundException; -import org.elasticsearch.transport.TransportRequest; - import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; @@ -39,13 +17,34 @@ import java.util.SortedMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; +import org.elasticsearch.action.AliasesRequest; +import org.elasticsearch.action.IndicesRequest; +import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; +import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest; +import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; +import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.cluster.metadata.AliasOrIndex; +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.common.regex.Regex; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexNotFoundException; +import org.elasticsearch.transport.RemoteClusterAware; +import org.elasticsearch.transport.TransportRequest; + public class IndicesAndAliasesResolver { //placeholder used in the security plugin to indicate that the request is authorized knowing that it will yield an empty response public static final String NO_INDEX_PLACEHOLDER = "-*"; - private static final Set NO_INDEX_PLACEHOLDER_SET = Collections.singleton(NO_INDEX_PLACEHOLDER); + private static final ResolvedIndices NO_INDEX_PLACEHOLDER_RESOLVED = ResolvedIndices.local(NO_INDEX_PLACEHOLDER); //`*,-*` what we replace indices with if we need Elasticsearch to return empty responses without throwing exception - private static final String[] NO_INDICES_ARRAY = new String[] {"*", "-*"}; + private static final String[] NO_INDICES_ARRAY = new String[] { "*", "-*" }; static final List NO_INDICES_LIST = Arrays.asList(NO_INDICES_ARRAY); private final IndexNameExpressionResolver nameExpressionResolver; @@ -56,12 +55,41 @@ public class IndicesAndAliasesResolver { this.remoteClusterResolver = new RemoteClusterResolver(settings, clusterService.getClusterSettings()); } - public Set resolve(TransportRequest request, MetaData metaData, AuthorizedIndices authorizedIndices) { + /** + * Resolves, and if necessary updates, the list of index names in the provided request in accordance with the user's + * authorizedIndices. + *

+ * Wildcards are expanded at this phase to ensure that all security and execution decisions are made against a fixed set of index names + * that is consistent and does not change during the life of the request. + *

+ *

+ * If the provided request is of a type that {@link #allowsRemoteIndices(IndicesRequest) allows remote indices}, + * then the index names will be categorized into those that refer to {@link ResolvedIndices#getLocal() local indices}, and those that + * refer to {@link ResolvedIndices#getRemote() remote indices}. This categorization follows the standard + * {@link RemoteClusterAware#buildRemoteIndexName(String, String) remote index-name format} and also respects the currently defined + * {@link RemoteClusterAware#getRemoteClusterNames() remote clusters}. + *


+ * Thus an index name N will considered to be remote if-and-only-if all of the following are true + *
    + *
  • request supports remote indices
  • + *
  • + * N is in the format cluster:index. + * It is allowable for cluster and index to contain wildcards, but the separator (:) must be explicit. + *
  • + *
  • cluster matches one or more remote cluster names that are registered within this cluster.
  • + *
+ * In which case, any wildcards in the cluster portion of the name will be expanded and the resulting remote-index-name(s) will + * be added to the remote index list. + *
+ * Otherwise, N will be added to the local index list. + */ + + public ResolvedIndices resolve(TransportRequest request, MetaData metaData, AuthorizedIndices authorizedIndices) { if (request instanceof IndicesAliasesRequest) { - Set indices = new HashSet<>(); + ResolvedIndices indices = ResolvedIndices.empty(); IndicesAliasesRequest indicesAliasesRequest = (IndicesAliasesRequest) request; for (IndicesRequest indicesRequest : indicesAliasesRequest.getAliasActions()) { - indices.addAll(resolveIndicesAndAliases(indicesRequest, metaData, authorizedIndices)); + indices = ResolvedIndices.add(indices, resolveIndicesAndAliases(indicesRequest, metaData, authorizedIndices)); } return indices; } @@ -73,9 +101,10 @@ public class IndicesAndAliasesResolver { return resolveIndicesAndAliases((IndicesRequest) request, metaData, authorizedIndices); } - private Set resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData metaData, AuthorizedIndices authorizedIndices) { + private ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData metaData, + AuthorizedIndices authorizedIndices) { boolean indicesReplacedWithNoIndices = false; - final Set indices; + final ResolvedIndices indices; if (indicesRequest instanceof PutMappingRequest && ((PutMappingRequest) indicesRequest).getConcreteIndex() != null) { /* * This is a special case since PutMappingRequests from dynamic mapping updates have a concrete index @@ -84,7 +113,7 @@ public class IndicesAndAliasesResolver { */ assert indicesRequest.indices() == null || indicesRequest.indices().length == 0 : "indices are: " + Arrays.toString(indicesRequest.indices()); // Arrays.toString() can handle null values - all good - return Collections.singleton(((PutMappingRequest) indicesRequest).getConcreteIndex().getName()); + return ResolvedIndices.local(((PutMappingRequest) indicesRequest).getConcreteIndex().getName()); } else if (indicesRequest instanceof IndicesRequest.Replaceable) { IndicesRequest.Replaceable replaceable = (IndicesRequest.Replaceable) indicesRequest; final boolean replaceWildcards = indicesRequest.indicesOptions().expandWildcardsOpen() @@ -97,49 +126,48 @@ public class IndicesAndAliasesResolver { indicesOptions.expandWildcardsOpen(), indicesOptions.expandWildcardsClosed()); } - List replacedIndices = new ArrayList<>(); + ResolvedIndices result = ResolvedIndices.empty(); // check for all and return list of authorized indices if (IndexNameExpressionResolver.isAllIndices(indicesList(indicesRequest.indices()))) { if (replaceWildcards) { for (String authorizedIndex : authorizedIndices.get()) { if (isIndexVisible(authorizedIndex, indicesOptions, metaData)) { - replacedIndices.add(authorizedIndex); + result = ResolvedIndices.add(result, ResolvedIndices.local(authorizedIndex)); } } } // if we cannot replace wildcards the indices list stays empty. Same if there are no authorized indices. // we honour allow_no_indices like es core does. } else { - String[] localIndexNames = indicesRequest.indices(); - List remoteIndices = Collections.emptyList(); + final ResolvedIndices split; if (allowsRemoteIndices(indicesRequest)) { - final Tuple, List> split = remoteClusterResolver.splitLocalAndRemoteIndexNames(localIndexNames); - localIndexNames = split.v1().toArray(new String[split.v1().size()]); - remoteIndices = split.v2(); + split = remoteClusterResolver.splitLocalAndRemoteIndexNames(indicesRequest.indices()); + } else { + split = ResolvedIndices.local(indicesRequest.indices()); } - replacedIndices = replaceWildcardsWithAuthorizedIndices(localIndexNames, indicesOptions, metaData, authorizedIndices.get(), - replaceWildcards); + List replaced = replaceWildcardsWithAuthorizedIndices(split.getLocal(), indicesOptions, metaData, + authorizedIndices.get(), replaceWildcards); if (indicesOptions.ignoreUnavailable()) { //out of all the explicit names (expanded from wildcards and original ones that were left untouched) //remove all the ones that the current user is not authorized for and ignore them - replacedIndices = replacedIndices.stream().filter(authorizedIndices.get()::contains).collect(Collectors.toList()); + replaced = replaced.stream().filter(authorizedIndices.get()::contains).collect(Collectors.toList()); } - replacedIndices.addAll(remoteIndices); + result = new ResolvedIndices(new ArrayList<>(replaced), split.getRemote()); } - if (replacedIndices.isEmpty()) { + if (result.isEmpty()) { if (indicesOptions.allowNoIndices()) { //this is how we tell es core to return an empty response, we can let the request through being sure //that the '-*' wildcard expression will be resolved to no indices. We can't let empty indices through //as that would be resolved to _all by es core. replaceable.indices(NO_INDICES_ARRAY); indicesReplacedWithNoIndices = true; - indices = NO_INDEX_PLACEHOLDER_SET; + indices = NO_INDEX_PLACEHOLDER_RESOLVED; } else { throw new IndexNotFoundException(Arrays.toString(indicesRequest.indices())); } } else { - replaceable.indices(replacedIndices.toArray(new String[replacedIndices.size()])); - indices = new HashSet<>(replacedIndices); + replaceable.indices(result.toArray()); + indices = result; } } else { if (containsWildcards(indicesRequest)) { @@ -157,7 +185,7 @@ public class IndicesAndAliasesResolver { for (String name : indicesRequest.indices()) { resolvedNames.add(nameExpressionResolver.resolveDateMathExpression(name)); } - indices = new HashSet<>(resolvedNames); + indices = new ResolvedIndices(resolvedNames, new ArrayList<>()); } if (indicesRequest instanceof AliasesRequest) { @@ -178,14 +206,14 @@ public class IndicesAndAliasesResolver { //if we replaced the indices with '-*' we shouldn't be adding the aliases to the list otherwise the request will //not get authorized. Leave only '-*' and ignore the rest, result will anyway be empty. } else { - Collections.addAll(indices, aliasesRequest.aliases()); + return ResolvedIndices.add(indices, ResolvedIndices.local(aliasesRequest.aliases())); } } - return Collections.unmodifiableSet(indices); + return indices; } - private static boolean allowsRemoteIndices(IndicesRequest indicesRequest) { - return indicesRequest instanceof SearchRequest || indicesRequest instanceof FieldCapabilitiesRequest; + public static boolean allowsRemoteIndices(IndicesRequest request) { + return request instanceof SearchRequest || request instanceof FieldCapabilitiesRequest; } private List loadAuthorizedAliases(List authorizedIndices, MetaData metaData) { @@ -249,7 +277,7 @@ public class IndicesAndAliasesResolver { } //TODO Investigate reusing code from vanilla es to resolve index names and wildcards - private List replaceWildcardsWithAuthorizedIndices(String[] indices, IndicesOptions indicesOptions, MetaData metaData, + private List replaceWildcardsWithAuthorizedIndices(Iterable indices, IndicesOptions indicesOptions, MetaData metaData, List authorizedIndices, boolean replaceWildcards) { //the order matters when it comes to exclusions List finalIndices = new ArrayList<>(); @@ -374,13 +402,101 @@ public class IndicesAndAliasesResolver { } } - Tuple, List> splitLocalAndRemoteIndexNames(String ... indices) { + ResolvedIndices splitLocalAndRemoteIndexNames(String... indices) { final Map> map = super.groupClusterIndices(indices, exists -> false); final List local = map.remove(LOCAL_CLUSTER_GROUP_KEY); final List remote = map.entrySet().stream() .flatMap(e -> e.getValue().stream().map(v -> e.getKey() + REMOTE_CLUSTER_INDEX_SEPARATOR + v)) .collect(Collectors.toList()); - return new Tuple<>(local == null ? Collections.emptyList() : local, remote); + return new ResolvedIndices(local == null ? Collections.emptyList() : local, remote); + } + } + + /** + * Stores a collection of index names separated into "local" and "remote". + * This allows the resolution and categorization to take place exactly once per-request. + */ + public static class ResolvedIndices { + private final List local; + private final List remote; + + ResolvedIndices(List local, List remote) { + this.local = local; + this.remote = remote; + } + + /** + * Constructs a new instance of this class where both the {@link #getLocal() local} and {@link #getRemote() remote} index lists + * are empty. + */ + private static ResolvedIndices empty() { + return new ResolvedIndices(Collections.emptyList(), Collections.emptyList()); + } + + /** + * Constructs a new instance of this class where both the {@link #getLocal() local} index list is populated with names + * and the {@link #getRemote() remote} index list is empty. + */ + private static ResolvedIndices local(String... names) { + return new ResolvedIndices(Arrays.asList(names), Collections.emptyList()); + } + + /** + * Returns the collection of index names that have been stored as "local" indices. + * This is a List because order may be important. For example [ "a*" , "-a1" ] is interpreted differently + * to [ "-a1", "a*" ]. As a consequence, this list may contain duplicates. + */ + public List getLocal() { + return Collections.unmodifiableList(local); + } + + /** + * Returns the collection of index names that have been stored as "remote" indices. + */ + public List getRemote() { + return Collections.unmodifiableList(remote); + } + + /** + * @return true if both the {@link #getLocal() local} and {@link #getRemote() remote} index lists are empty. + */ + public boolean isEmpty() { + return local.isEmpty() && remote.isEmpty(); + } + + /** + * @return true if the {@link #getRemote() remote} index lists is empty, and the local index list contains the + * {@link IndicesAndAliasesResolver#NO_INDEX_PLACEHOLDER no-index-placeholder} and nothing else. + */ + public boolean isNoIndicesPlaceholder() { + return remote.isEmpty() && local.size() == 1 && local.contains(IndicesAndAliasesResolver.NO_INDEX_PLACEHOLDER); + } + + private String[] toArray() { + final String[] array = new String[local.size() + remote.size()]; + int i = 0; + for (String index : local) { + array[i++] = index; + } + for (String index : remote) { + array[i++] = index; + } + return array; + } + + /** + * Returns a new ResolvedIndices contains the {@link #getLocal() local} and {@link #getRemote() remote} + * index lists from b appended to the corresponding lists in a. + */ + private static ResolvedIndices add(ResolvedIndices a, ResolvedIndices b) { + List local = new ArrayList<>(a.local.size() + b.local.size()); + local.addAll(a.local); + local.addAll(b.local); + + List remote = new ArrayList<>(a.remote.size() + b.remote.size()); + remote.addAll(a.remote); + remote.addAll(b.remote); + return new ResolvedIndices(local, remote); } } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index cd9b7d94fc1..50a800f7a78 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -5,6 +5,14 @@ */ package org.elasticsearch.xpack.security.authz; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; @@ -20,6 +28,8 @@ import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.create.CreateIndexAction; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsAction; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest; import org.elasticsearch.action.admin.indices.get.GetIndexAction; @@ -74,6 +84,7 @@ import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.IndexNotFoundException; @@ -113,14 +124,6 @@ import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.user.XPackUser; import org.junit.Before; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException; import static org.elasticsearch.test.SecurityTestsUtils.assertThrowsAuthorizationException; import static org.elasticsearch.test.SecurityTestsUtils.assertThrowsAuthorizationExceptionRunAs; @@ -154,13 +157,16 @@ public class AuthorizationServiceTests extends ESTestCase { public void setup() { rolesStore = mock(CompositeRolesStore.class); clusterService = mock(ClusterService.class); - final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + final Settings settings = Settings.builder() + .put("search.remote.other_cluster.seeds", "localhost:9999") + .build(); + final ClusterSettings clusterSettings = new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); when(clusterService.getClusterSettings()).thenReturn(clusterSettings); auditTrail = mock(AuditTrailService.class); - threadContext = new ThreadContext(Settings.EMPTY); + threadContext = new ThreadContext(settings); threadPool = mock(ThreadPool.class); when(threadPool.getThreadContext()).thenReturn(threadContext); - final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); + final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(settings); doAnswer((i) -> { ActionListener callback = (ActionListener) i.getArguments()[2]; @@ -182,8 +188,8 @@ public class AuthorizationServiceTests extends ESTestCase { } return Void.TYPE; }).when(rolesStore).roles(any(Set.class), any(FieldPermissionsCache.class), any(ActionListener.class)); - authorizationService = new AuthorizationService(Settings.EMPTY, rolesStore, clusterService, - auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY)); + authorizationService = new AuthorizationService(settings, rolesStore, clusterService, + auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(settings)); } private void authorize(Authentication authentication, String action, TransportRequest request) { @@ -246,6 +252,65 @@ public class AuthorizationServiceTests extends ESTestCase { verifyNoMoreInteractions(auditTrail); } + public void testUserWithNoRolesCanPerformRemoteSearch() { + SearchRequest request = new SearchRequest(); + request.indices("other_cluster:index1", "*_cluster:index2", "other_cluster:other_*"); + User user = new User("test user"); + mockEmptyMetaData(); + authorize(createAuthentication(user), SearchAction.NAME, request); + verify(auditTrail).accessGranted(user, SearchAction.NAME, request); + verifyNoMoreInteractions(auditTrail); + } + + /** + * This test mimics {@link #testUserWithNoRolesCanPerformRemoteSearch()} except that + * while the referenced index _looks_ like a remote index, the remote cluster name has not + * been defined, so it is actually a local index and access should be denied + */ + public void testUserWithNoRolesCannotPerformLocalSearch() { + SearchRequest request = new SearchRequest(); + request.indices("no_such_cluster:index"); + User user = new User("test user"); + mockEmptyMetaData(); + assertThrowsAuthorizationException( + () -> authorize(createAuthentication(user), SearchAction.NAME, request), + SearchAction.NAME, "test user"); + verify(auditTrail).accessDenied(user, SearchAction.NAME, request); + verifyNoMoreInteractions(auditTrail); + } + + /** + * This test mimics {@link #testUserWithNoRolesCannotPerformLocalSearch()} but includes + * both local and remote indices, including wildcards + */ + public void testUserWithNoRolesCanPerformMultiClusterSearch() { + SearchRequest request = new SearchRequest(); + request.indices("local_index", "wildcard_*", "other_cluster:remote_index", "*:foo?"); + User user = new User("test user"); + mockEmptyMetaData(); + assertThrowsAuthorizationException( + () -> authorize(createAuthentication(user), SearchAction.NAME, request), + SearchAction.NAME, "test user"); + verify(auditTrail).accessDenied(user, SearchAction.NAME, request); + verifyNoMoreInteractions(auditTrail); + } + + /** + * Verifies that the behaviour tested in {@link #testUserWithNoRolesCanPerformRemoteSearch} + * does not work for requests that are not remote-index-capable. + */ + public void testRemoteIndicesOnlyWorkWithApplicableRequestTypes() { + DeleteIndexRequest request = new DeleteIndexRequest(); + request.indices("other_cluster:index1", "other_cluster:index2"); + User user = new User("test user"); + mockEmptyMetaData(); + assertThrowsAuthorizationException( + () -> authorize(createAuthentication(user), DeleteIndexAction.NAME, request), + DeleteIndexAction.NAME, "test user"); + verify(auditTrail).accessDenied(user, DeleteIndexAction.NAME, request); + verifyNoMoreInteractions(auditTrail); + } + public void testUnknownRoleCausesDenial() { TransportRequest request = new SearchRequest(); User user = new User("test user", "non-existent-role"); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index 83203d67abc..3cdff6ac1be 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -5,6 +5,13 @@ */ package org.elasticsearch.xpack.security.authz; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.IndicesRequest; @@ -51,6 +58,7 @@ import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.xpack.security.SecurityLifecycleService; import org.elasticsearch.xpack.security.audit.AuditTrailService; import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler; +import org.elasticsearch.xpack.security.authz.IndicesAndAliasesResolver.ResolvedIndices; import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges; import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.security.authz.permission.Role; @@ -62,16 +70,11 @@ import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.user.XPackUser; import org.junit.Before; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.emptyIterable; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; @@ -179,7 +182,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { //aliases with names starting with '-' or '+' can be created up to version 5.x and can be around in 6.x ShardSearchTransportRequest request = mock(ShardSearchTransportRequest.class); when(request.indices()).thenReturn(new String[]{"-index10", "-index20", "+index30"}); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)) + .getLocal(); String[] expectedIndices = new String[]{"-index10", "-index20", "+index30"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); @@ -189,7 +193,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { ShardSearchTransportRequest request = mock(ShardSearchTransportRequest.class); when(request.indices()).thenReturn(new String[]{"index*"}); IllegalStateException illegalStateException = expectThrows(IllegalStateException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)) + .getLocal()); assertEquals("There are no external requests known to support wildcards that don't support replacing their indices", illegalStateException.getMessage()); } @@ -206,14 +211,16 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { } } IllegalStateException illegalStateException = expectThrows(IllegalStateException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)) + .getLocal()); assertEquals("There are no external requests known to support wildcards that don't support replacing their indices", illegalStateException.getMessage()); } public void testExplicitDashIndices() { SearchRequest request = new SearchRequest("-index10", "-index20"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"-index10", "-index20"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -228,7 +235,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { } else { request = new SearchRequest("*", "--index20"); } - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"-index10", "-index11", "-index21"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -238,7 +246,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testExplicitMixedWildcardDashIndices() { SearchRequest request = new SearchRequest("-index21", "-does_not_exist", "-index1*", "--index11"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"-index10", "-index21", "-does_not_exist"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -249,7 +258,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testDashIndicesNoExpandWildcard() { SearchRequest request = new SearchRequest("-index1*", "--index11"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), false, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"-index1*", "--index11"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -260,7 +270,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testDashIndicesMinus() { SearchRequest request = new SearchRequest("-index10", "-index11", "--index11", "-index20"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"-index10", "-index11", "--index11", "-index20"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -272,13 +283,13 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { SearchRequest request = new SearchRequest("+bar"); request.indicesOptions(IndicesOptions.fromOptions(true, false, randomBoolean(), randomBoolean())); expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME))); } public void testDashNotExistingIndex() { SearchRequest request = new SearchRequest("-does_not_exist"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(userDashIndices, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"-does_not_exist"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -289,7 +300,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveEmptyIndicesExpandWilcardsOpenAndClosed() { SearchRequest request = new SearchRequest(); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -300,7 +311,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveEmptyIndicesExpandWilcardsOpen() { SearchRequest request = new SearchRequest(); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -311,7 +322,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveAllExpandWilcardsOpenAndClosed() { SearchRequest request = new SearchRequest("_all"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -322,7 +333,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveAllExpandWilcardsOpen() { SearchRequest request = new SearchRequest("_all"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -333,7 +344,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsStrictExpand() { SearchRequest request = new SearchRequest("barbaz", "foofoo*"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"barbaz", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -344,7 +355,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsExpandOpenAndClosedIgnoreUnavailable() { SearchRequest request = new SearchRequest("barbaz", "foofoo*"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -355,7 +366,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsStrictExpandOpen() { SearchRequest request = new SearchRequest("barbaz", "foofoo*"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"barbaz", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -366,7 +377,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsLenientExpandOpen() { SearchRequest request = new SearchRequest("barbaz", "foofoo*"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -377,7 +388,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsMinusExpandWilcardsOpen() { SearchRequest request = new SearchRequest("*", "-foofoo*"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -388,7 +399,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsMinusExpandWilcardsOpenAndClosed() { SearchRequest request = new SearchRequest("*", "-foofoo*"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "bar-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -399,7 +410,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsExclusionsExpandWilcardsOpenStrict() { SearchRequest request = new SearchRequest("*", "-foofoo*", "barbaz", "foob*"); request.indicesOptions(IndicesOptions.fromOptions(false, true, true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "barbaz"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -410,7 +421,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsPlusAndMinusExpandWilcardsOpenIgnoreUnavailable() { SearchRequest request = new SearchRequest("*", "-foofoo*", "+barbaz", "+foob*"); request.indicesOptions(IndicesOptions.fromOptions(true, true, true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -421,7 +432,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsExclusionExpandWilcardsOpenAndClosedStrict() { SearchRequest request = new SearchRequest("*", "-foofoo*", "barbaz"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "bar-closed", "barbaz"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -432,7 +443,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveWildcardsExclusionExpandWilcardsOpenAndClosedIgnoreUnavailable() { SearchRequest request = new SearchRequest("*", "-foofoo*", "barbaz"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), true, true)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar", "bar-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -443,21 +454,21 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveNonMatchingIndicesAllowNoIndices() { SearchRequest request = new SearchRequest("missing*"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())); - assertNoIndices(request, defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + assertNoIndices(request, resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); } public void testResolveNonMatchingIndicesDisallowNoIndices() { SearchRequest request = new SearchRequest("missing*"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean())); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); assertEquals("no such index", e.getMessage()); } public void testResolveExplicitIndicesStrict() { SearchRequest request = new SearchRequest("missing", "bar", "barbaz"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"missing", "bar", "barbaz"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -468,7 +479,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveExplicitIndicesIgnoreUnavailable() { SearchRequest request = new SearchRequest("missing", "bar", "barbaz"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), randomBoolean(), randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] replacedIndices = new String[]{"bar"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(request.indices().length, equalTo(replacedIndices.length)); @@ -479,22 +490,22 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveNoAuthorizedIndicesAllowNoIndices() { SearchRequest request = new SearchRequest(); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())); - assertNoIndices(request, defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(userNoIndices, SearchAction.NAME))); + assertNoIndices(request, resolveIndices(request, + buildAuthorizedIndices(userNoIndices, SearchAction.NAME))); } public void testResolveNoAuthorizedIndicesDisallowNoIndices() { SearchRequest request = new SearchRequest(); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), false, true, randomBoolean())); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userNoIndices, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userNoIndices, SearchAction.NAME))); assertEquals("no such index", e.getMessage()); } public void testResolveMissingIndexStrict() { SearchRequest request = new SearchRequest("bar*", "missing"); request.indicesOptions(IndicesOptions.fromOptions(false, true, true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"bar", "missing"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -505,7 +516,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveMissingIndexIgnoreUnavailable() { SearchRequest request = new SearchRequest("bar*", "missing"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), true, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"bar"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(request.indices().length, equalTo(expectedIndices.length)); @@ -516,7 +527,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveNonMatchingIndicesAndExplicit() { SearchRequest request = new SearchRequest("missing*", "bar"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"bar"}; assertThat(indices.toArray(new String[indices.size()]), equalTo(expectedIndices)); assertThat(request.indices(), equalTo(expectedIndices)); @@ -525,7 +536,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveNoExpandStrict() { SearchRequest request = new SearchRequest("missing*"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), false, false)); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); String[] expectedIndices = new String[]{"missing*"}; assertThat(indices.toArray(new String[indices.size()]), equalTo(expectedIndices)); assertThat(request.indices(), equalTo(expectedIndices)); @@ -534,43 +545,47 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveNoExpandIgnoreUnavailable() { SearchRequest request = new SearchRequest("missing*"); request.indicesOptions(IndicesOptions.fromOptions(true, true, false, false)); - assertNoIndices(request, defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + assertNoIndices(request, resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); } public void testSearchWithRemoteIndex() { SearchRequest request = new SearchRequest("remote:indexName"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean())); - final Set resolved = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); - assertThat(resolved, containsInAnyOrder("remote:indexName")); + final ResolvedIndices resolved = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)); + assertThat(resolved.getLocal(), emptyIterable()); + assertThat(resolved.getRemote(), containsInAnyOrder("remote:indexName")); assertThat(request.indices(), arrayContaining("remote:indexName")); } public void testSearchWithRemoteAndLocalIndices() { SearchRequest request = new SearchRequest("remote:indexName", "bar", "bar2"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), randomBoolean(), randomBoolean())); - final Set resolved = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); - assertThat(resolved, containsInAnyOrder("remote:indexName", "bar")); + final ResolvedIndices resolved = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)); + assertThat(resolved.getLocal(), containsInAnyOrder("bar")); + assertThat(resolved.getRemote(), containsInAnyOrder("remote:indexName")); assertThat(request.indices(), arrayContainingInAnyOrder("remote:indexName", "bar")); } public void testSearchWithRemoteAndLocalWildcards() { SearchRequest request = new SearchRequest("*:foo", "r*:bar*", "remote:baz*", "bar*", "foofoo"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, false)); - final Set resolved = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); - final String[] expectedIndices = { "remote:foo", "other_remote:foo", "remote:bar*", "remote:baz*", "bar", "foofoo" }; - assertThat(resolved, containsInAnyOrder(expectedIndices)); - assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, SearchAction.NAME); + final ResolvedIndices resolved = resolveIndices(request, authorizedIndices); + assertThat(resolved.getRemote(), containsInAnyOrder("remote:foo", "other_remote:foo", "remote:bar*", "remote:baz*")); + assertThat(resolved.getLocal(), containsInAnyOrder("bar", "foofoo")); + assertThat(request.indices(), + arrayContainingInAnyOrder("remote:foo", "other_remote:foo", "remote:bar*", "remote:baz*", "bar", "foofoo")); } public void testResolveIndicesAliasesRequest() { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("alias1").indices("foo", "foofoo")); request.addAliasAction(AliasActions.add().alias("alias2").indices("foo", "foobar")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)).getLocal(); //the union of all indices and aliases gets returned String[] expectedIndices = new String[]{"alias1", "alias2", "foo", "foofoo", "foobar"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); assertThat(request.getAliasActions().get(0).indices(), arrayContainingInAnyOrder("foo", "foofoo")); assertThat(request.getAliasActions().get(0).aliases(), arrayContainingInAnyOrder("alias1")); assertThat(request.getAliasActions().get(1).indices(), arrayContainingInAnyOrder("foo", "foobar")); @@ -581,11 +596,11 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("alias1").indices("foo", "foofoo")); request.addAliasAction(AliasActions.add().alias("foofoobar").indices("foo", "foobar")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)).getLocal(); //the union of all indices and aliases gets returned, foofoobar is an existing alias but that doesn't make any difference String[] expectedIndices = new String[]{"alias1", "foofoobar", "foo", "foofoo", "foobar"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); assertThat(request.getAliasActions().get(0).indices(), arrayContainingInAnyOrder("foo", "foofoo")); assertThat(request.getAliasActions().get(0).aliases(), arrayContainingInAnyOrder("alias1")); assertThat(request.getAliasActions().get(1).indices(), arrayContainingInAnyOrder("foo", "foobar")); @@ -596,7 +611,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("alias1").indices("foo", "foofoo")); request.addAliasAction(AliasActions.add().alias("alias2").index("missing")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)).getLocal(); //the union of all indices and aliases gets returned, missing is not an existing index/alias but that doesn't make any difference String[] expectedIndices = new String[]{"alias1", "alias2", "foo", "foofoo", "missing"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -611,7 +627,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("foo-alias").index("foo*")); request.addAliasAction(AliasActions.add().alias("alias2").index("bar*")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)).getLocal(); //the union of all resolved indices and aliases gets returned, based on indices and aliases that user is authorized for String[] expectedIndices = new String[]{"foo-alias", "alias2", "foofoo", "bar"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -630,18 +647,18 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.addAliasAction(AliasActions.add().alias("alias3").index("non_matching_*")); //if a single operation contains wildcards and ends up being resolved to no indices, it makes the whole request fail expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME))); } public void testResolveAllIndicesAliasesRequest() { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("alias1").index("_all")); request.addAliasAction(AliasActions.add().alias("alias2").index("_all")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)).getLocal(); //the union of all resolved indices and aliases gets returned String[] expectedIndices = new String[]{"bar", "foofoo", "alias1", "alias2"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); String[] replacedIndices = new String[]{"bar", "foofoo"}; //_all gets replaced with all indices that user is authorized for, on each single action assertThat(request.getAliasActions().get(0).indices(), arrayContainingInAnyOrder(replacedIndices)); @@ -654,23 +671,24 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("alias1").index("_all")); //current user is not authorized for any index, _all resolves to no indices, the request fails - expectThrows(IndexNotFoundException.class, () -> defaultIndicesResolver.resolve( - request, metaData, buildAuthorizedIndices(userNoIndices, IndicesAliasesAction.NAME))); + expectThrows(IndexNotFoundException.class, () -> + resolveIndices(request, buildAuthorizedIndices(userNoIndices, IndicesAliasesAction.NAME))); } public void testResolveWildcardsIndicesAliasesRequestNoAuthorizedIndices() { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.add().alias("alias1").index("foo*")); //current user is not authorized for any index, foo* resolves to no indices, the request fails - expectThrows(IndexNotFoundException.class, () -> defaultIndicesResolver.resolve( - request, metaData, buildAuthorizedIndices(userNoIndices, IndicesAliasesAction.NAME))); + expectThrows(IndexNotFoundException.class, () -> resolveIndices( + request, buildAuthorizedIndices(userNoIndices, IndicesAliasesAction.NAME))); } public void testResolveIndicesAliasesRequestDeleteActions() { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.remove().index("foo").alias("foofoobar")); request.addAliasAction(AliasActions.remove().index("foofoo").alias("barbaz")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, IndicesAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all indices and aliases gets returned String[] expectedIndices = new String[]{"foo", "foofoobar", "foofoo", "barbaz"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -685,7 +703,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.remove().index("foo").alias("foofoobar")); request.addAliasAction(AliasActions.remove().index("missing_index").alias("missing_alias")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, IndicesAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all indices and aliases gets returned, doesn't matter is some of them don't exist String[] expectedIndices = new String[]{"foo", "foofoobar", "missing_index", "missing_alias"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -700,7 +719,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.remove().index("foo*").alias("foofoobar")); request.addAliasAction(AliasActions.remove().index("bar*").alias("barbaz")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, IndicesAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //union of all resolved indices and aliases gets returned, based on what user is authorized for String[] expectedIndices = new String[]{"foofoobar", "foofoo", "bar", "barbaz"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -716,13 +736,13 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.remove().index("*").alias("foo*")); request.addAliasAction(AliasActions.remove().index("*bar").alias("foo*")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, IndicesAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //union of all resolved indices and aliases gets returned, based on what user is authorized for //note that the index side will end up containing matching aliases too, which is fine, as es core would do //the same and resolve those aliases to their corresponding concrete indices (which we let core do) String[] expectedIndices = new String[]{"bar", "foofoobar", "foofoo"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //alias foofoobar on both sides, that's fine, es core would do the same, same as above assertThat(request.getAliasActions().get(0).indices(), arrayContainingInAnyOrder("bar", "foofoo")); assertThat(request.getAliasActions().get(0).aliases(), arrayContainingInAnyOrder("foofoobar")); @@ -734,13 +754,13 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.remove().index("*").alias("_all")); request.addAliasAction(AliasActions.remove().index("_all").aliases("_all", "explicit")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, IndicesAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //union of all resolved indices and aliases gets returned, based on what user is authorized for //note that the index side will end up containing matching aliases too, which is fine, as es core would do //the same and resolve those aliases to their corresponding concrete indices (which we let core do) String[] expectedIndices = new String[]{"bar", "foofoobar", "foofoo", "explicit"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //alias foofoobar on both sides, that's fine, es core would do the same, same as above assertThat(request.getAliasActions().get(0).indices(), arrayContainingInAnyOrder("bar", "foofoo")); assertThat(request.getAliasActions().get(0).aliases(), arrayContainingInAnyOrder("foofoobar")); @@ -753,19 +773,19 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.addAliasAction(AliasActions.remove().index("foo*").alias("foo*")); //no authorized aliases match bar*, hence this action fails and makes the whole request fail request.addAliasAction(AliasActions.remove().index("*bar").alias("bar*")); - expectThrows(IndexNotFoundException.class, () -> defaultIndicesResolver.resolve( - request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME))); + expectThrows(IndexNotFoundException.class, () -> resolveIndices( + request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME))); } public void testResolveWildcardsIndicesAliasesRequestAddAndDeleteActions() { IndicesAliasesRequest request = new IndicesAliasesRequest(); request.addAliasAction(AliasActions.remove().index("foo*").alias("foofoobar")); request.addAliasAction(AliasActions.add().index("bar*").alias("foofoobar")); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, IndicesAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //union of all resolved indices and aliases gets returned, based on what user is authorized for String[] expectedIndices = new String[]{"foofoobar", "foofoo", "bar"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //every single action has its indices replaced with matching (authorized) ones assertThat(request.getAliasActions().get(0).indices(), arrayContainingInAnyOrder("foofoo")); assertThat(request.getAliasActions().get(0).aliases(), arrayContainingInAnyOrder("foofoobar")); @@ -776,7 +796,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveGetAliasesRequestStrict() { GetAliasesRequest request = new GetAliasesRequest("alias1").indices("foo", "foofoo"); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all indices and aliases gets returned String[] expectedIndices = new String[]{"alias1", "foo", "foofoo"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -788,7 +809,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testResolveGetAliasesRequestIgnoreUnavailable() { GetAliasesRequest request = new GetAliasesRequest("alias1").indices("foo", "foofoo"); request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), randomBoolean(), randomBoolean())); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); String[] expectedIndices = new String[]{"alias1", "foofoo"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); @@ -801,7 +823,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), true, randomBoolean())); request.indices("missing"); request.aliases("alias2"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all indices and aliases gets returned, missing is not an existing index/alias but that doesn't make any difference String[] expectedIndices = new String[]{"alias2", "missing"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -816,7 +839,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indices("missing"); request.aliases("alias2"); IndexNotFoundException exception = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME)).getLocal()); assertEquals("no such index", exception.getMessage()); } @@ -825,7 +848,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(true, true, randomBoolean(), randomBoolean())); request.indices("missing"); request.aliases("alias2"); - assertNoIndices(request, defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME))); + assertNoIndices(request, resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME))); } public void testGetAliasesRequestMissingIndexStrict() { @@ -833,7 +856,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); request.indices("missing"); request.aliases("alias2"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); String[] expectedIndices = new String[]{"alias2", "missing"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); @@ -846,7 +870,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), true, true)); request.aliases("alias1"); request.indices("foo*"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned, based on indices and aliases that user is authorized for String[] expectedIndices = new String[]{"alias1", "foofoo", "foofoo-closed", "foofoobar"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -861,7 +886,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), true, false)); request.aliases("alias1"); request.indices("foo*"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned, based on indices and aliases that user is authorized for String[] expectedIndices = new String[]{"alias1", "foofoo", "foofoobar"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -876,7 +902,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(true, randomBoolean(), true, false)); request.aliases("alias1"); request.indices("foo*", "bar", "missing"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned, based on indices and aliases that user is authorized for String[] expectedIndices = new String[]{"alias1", "foofoo", "foofoobar", "bar"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -892,7 +919,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.aliases("alias3"); request.indices("non_matching_*"); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME)).getLocal()); assertEquals("no such index", e.getMessage()); } @@ -901,7 +928,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())); request.aliases("alias3"); request.indices("non_matching_*"); - assertNoIndices(request, defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME))); + assertNoIndices(request, resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME))); } public void testResolveAllGetAliasesRequest() { @@ -911,7 +938,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indices("_all"); } request.aliases("alias1"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed", "alias1"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -931,7 +959,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indices("_all"); } request.aliases("alias1"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned String[] expectedIndices = new String[]{"bar", "foofoobar", "foofoo", "alias1"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -947,8 +976,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())); request.aliases("alias1"); request.indices("_all"); - assertNoIndices(request, defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); + assertNoIndices(request, resolveIndices(request, + buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); } public void testAllGetAliasesRequestNoAuthorizedIndicesDisallowNoIndices() { @@ -957,7 +986,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.aliases("alias1"); request.indices("_all"); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); assertEquals("no such index", e.getMessage()); } @@ -966,8 +995,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.aliases("alias1"); request.indices("foo*"); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())); - assertNoIndices(request, defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); + assertNoIndices(request, resolveIndices(request, + buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); } public void testWildcardsGetAliasesRequestNoAuthorizedIndicesDisallowNoIndices() { @@ -977,7 +1006,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indices("foo*"); //current user is not authorized for any index, foo* resolves to no indices, the request fails IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); assertEquals("no such index", e.getMessage()); } @@ -989,11 +1018,11 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { if (randomBoolean()) { request.indices("_all"); } - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //_all gets replaced with all indices that user is authorized for assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices)); assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar")); @@ -1004,11 +1033,11 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { if (randomBoolean()) { request.indices("_all"); } - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed", "explicit"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //_all gets replaced with all indices that user is authorized for assertThat(request.indices(), arrayContainingInAnyOrder("bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed")); assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar", "explicit")); @@ -1019,11 +1048,11 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { if (randomBoolean()) { request.indices("_all"); } - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //the union of all resolved indices and aliases gets returned String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //_all gets replaced with all indices that user is authorized for assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices)); assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar", "foofoobar")); @@ -1033,13 +1062,13 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { GetAliasesRequest request = new GetAliasesRequest(); request.indices("*bar"); request.aliases("foo*"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); //union of all resolved indices and aliases gets returned, based on what user is authorized for //note that the index side will end up containing matching aliases too, which is fine, as es core would do //the same and resolve those aliases to their corresponding concrete indices (which we let core do) String[] expectedIndices = new String[]{"bar", "foofoobar"}; - assertThat(indices.size(), equalTo(expectedIndices.length)); - assertThat(indices, hasItems(expectedIndices)); + assertSameValues(indices, expectedIndices); //alias foofoobar on both sides, that's fine, es core would do the same, same as above assertThat(request.indices(), arrayContainingInAnyOrder("bar", "foofoobar")); assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar")); @@ -1051,7 +1080,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.aliases("bar*"); request.indices("*bar"); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME))); assertEquals("no such index", e.getMessage()); } @@ -1063,7 +1092,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { request.indices("non_existing"); //current user is not authorized for any index, foo* resolves to no indices, the request fails IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME))); assertEquals("no such index", e.getMessage()); } @@ -1077,8 +1106,11 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { new Tuple<>(new SearchRequest("remote:foo").indicesOptions(options), SearchAction.NAME), new Tuple<>(new FieldCapabilitiesRequest().indices("remote:foo").indicesOptions(options), FieldCapabilitiesAction.NAME) ); - final Set resolved = defaultIndicesResolver.resolve(tuple.v1(), metaData, buildAuthorizedIndices(user, tuple.v2())); - assertThat(resolved, containsInAnyOrder("remote:foo")); + final TransportRequest request = tuple.v1(); + ResolvedIndices resolved = resolveIndices(request, buildAuthorizedIndices(user, tuple.v2())); + assertThat(resolved.getRemote(), containsInAnyOrder("remote:foo")); + assertThat(resolved.getLocal(), emptyIterable()); + assertThat(((IndicesRequest) request).indices(), arrayContaining("remote:foo")); } /** @@ -1092,7 +1124,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { new Tuple<>(new PutMappingRequest("remote:foo").indicesOptions(options), PutMappingAction.NAME) ); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(tuple.v1(), metaData, buildAuthorizedIndices(user, tuple.v2()))); + () -> resolveIndices(tuple.v1(), buildAuthorizedIndices(user, tuple.v2())).getLocal()); assertEquals("no such index", e.getMessage()); } @@ -1103,21 +1135,22 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { new Tuple<>(new DeleteIndexRequest("*:*").indicesOptions(options), DeleteIndexAction.NAME), new Tuple<>(new PutMappingRequest("*:*").indicesOptions(options), PutMappingAction.NAME) ); - final Set resolved = defaultIndicesResolver.resolve(tuple.v1(), metaData, buildAuthorizedIndices(user, tuple.v2())); + final ResolvedIndices resolved = resolveIndices(tuple.v1(), buildAuthorizedIndices(user, tuple.v2())); assertNoIndices((IndicesRequest.Replaceable) tuple.v1(), resolved); } public void testCompositeIndicesRequestIsNotSupported() { TransportRequest request = randomFrom(new MultiSearchRequest(), new MultiGetRequest(), new MultiTermVectorsRequest(), new BulkRequest()); - expectThrows(IllegalStateException.class, () -> defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(user, MultiSearchAction.NAME))); + expectThrows(IllegalStateException.class, () -> resolveIndices(request, + buildAuthorizedIndices(user, MultiSearchAction.NAME))); } public void testResolveAdminAction() { + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(user, DeleteIndexAction.NAME); { RefreshRequest request = new RefreshRequest("*"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, DeleteIndexAction.NAME)); + List indices = resolveIndices(request, authorizedIndices).getLocal(); String[] expectedIndices = new String[]{"bar", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); @@ -1125,7 +1158,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { } { DeleteIndexRequest request = new DeleteIndexRequest("*"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, DeleteIndexAction.NAME)); + List indices = resolveIndices(request, authorizedIndices).getLocal(); String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); @@ -1138,35 +1171,35 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { //indices exists api never throws exception due to missing indices, but only returns false instead. { IndicesExistsRequest request = new IndicesExistsRequest(); - assertNoIndices(request, defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(userNoIndices, IndicesExistsAction.NAME))); + assertNoIndices(request, resolveIndices(request, + buildAuthorizedIndices(userNoIndices, IndicesExistsAction.NAME))); } { IndicesExistsRequest request = new IndicesExistsRequest("does_not_exist"); - assertNoIndices(request, defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(user, IndicesExistsAction.NAME))); + assertNoIndices(request, resolveIndices(request, + buildAuthorizedIndices(user, IndicesExistsAction.NAME))); } { IndicesExistsRequest request = new IndicesExistsRequest("does_not_exist_*"); - assertNoIndices(request, defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(user, IndicesExistsAction.NAME))); + assertNoIndices(request, resolveIndices(request, + buildAuthorizedIndices(user, IndicesExistsAction.NAME))); } } public void testXPackUserHasAccessToSecurityIndex() { SearchRequest request = new SearchRequest(); { - Set indices = defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(XPackUser.INSTANCE, SearchAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(XPackUser.INSTANCE, SearchAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); assertThat(indices, hasItem(SecurityLifecycleService.SECURITY_INDEX_NAME)); } { IndicesAliasesRequest aliasesRequest = new IndicesAliasesRequest(); aliasesRequest.addAliasAction(AliasActions.add().alias("security_alias").index(SecurityLifecycleService.SECURITY_INDEX_NAME)); - Set indices = defaultIndicesResolver.resolve(aliasesRequest, - metaData, buildAuthorizedIndices(XPackUser.INSTANCE, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(XPackUser.INSTANCE, IndicesAliasesAction.NAME); + List indices = resolveIndices(aliasesRequest, authorizedIndices).getLocal(); assertThat(indices, hasItem(SecurityLifecycleService.SECURITY_INDEX_NAME)); } } @@ -1178,16 +1211,16 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { { SearchRequest request = new SearchRequest(); - Set indices = defaultIndicesResolver.resolve(request, - metaData, buildAuthorizedIndices(allAccessUser, SearchAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(allAccessUser, SearchAction.NAME); + List indices = resolveIndices(request, authorizedIndices).getLocal(); assertThat(indices, not(hasItem(SecurityLifecycleService.SECURITY_INDEX_NAME))); } { IndicesAliasesRequest aliasesRequest = new IndicesAliasesRequest(); aliasesRequest.addAliasAction(AliasActions.add().alias("security_alias1").index("*")); - Set indices = defaultIndicesResolver.resolve(aliasesRequest, - metaData, buildAuthorizedIndices(allAccessUser, IndicesAliasesAction.NAME)); + final AuthorizedIndices authorizedIndices = buildAuthorizedIndices(allAccessUser, IndicesAliasesAction.NAME); + List indices = resolveIndices(aliasesRequest, authorizedIndices).getLocal(); assertThat(indices, not(hasItem(SecurityLifecycleService.SECURITY_INDEX_NAME))); } } @@ -1195,14 +1228,14 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testUnauthorizedDateMathExpressionIgnoreUnavailable() { SearchRequest request = new SearchRequest(""); request.indicesOptions(IndicesOptions.fromOptions(true, true, randomBoolean(), randomBoolean())); - assertNoIndices(request, defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + assertNoIndices(request, resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); } public void testUnauthorizedDateMathExpressionIgnoreUnavailableDisallowNoIndices() { SearchRequest request = new SearchRequest(""); request.indicesOptions(IndicesOptions.fromOptions(true, false, randomBoolean(), randomBoolean())); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); assertEquals("no such index" , e.getMessage()); } @@ -1210,7 +1243,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { SearchRequest request = new SearchRequest(""); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); assertEquals("no such index" , e.getMessage()); } @@ -1227,7 +1260,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { final boolean expandIndicesOpen = Regex.isSimpleMatchPattern(pattern) ? true : randomBoolean(); request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), expandIndicesOpen, randomBoolean())); } - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME)); + List indices = resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME)).getLocal(); assertThat(indices.size(), equalTo(1)); assertThat(request.indices()[0], equalTo(dateTimeIndex)); } @@ -1235,14 +1268,14 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { public void testMissingDateMathExpressionIgnoreUnavailable() { SearchRequest request = new SearchRequest(""); request.indicesOptions(IndicesOptions.fromOptions(true, true, randomBoolean(), randomBoolean())); - assertNoIndices(request, defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + assertNoIndices(request, resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); } public void testMissingDateMathExpressionIgnoreUnavailableDisallowNoIndices() { SearchRequest request = new SearchRequest(""); request.indicesOptions(IndicesOptions.fromOptions(true, false, randomBoolean(), randomBoolean())); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); assertEquals("no such index" , e.getMessage()); } @@ -1250,7 +1283,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { SearchRequest request = new SearchRequest(""); request.indicesOptions(IndicesOptions.fromOptions(false, randomBoolean(), randomBoolean(), randomBoolean())); IndexNotFoundException e = expectThrows(IndexNotFoundException.class, - () -> defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, SearchAction.NAME))); + () -> resolveIndices(request, buildAuthorizedIndices(user, SearchAction.NAME))); assertEquals("no such index" , e.getMessage()); } @@ -1261,7 +1294,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { roleMap.put("role", new RoleDescriptor("role", null, new IndicesPrivileges[] { IndicesPrivileges.builder().indices(authorizedIndices).privileges("all").build() }, null)); GetAliasesRequest request = new GetAliasesRequest("").indices("foo", "foofoo"); - Set indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME)); + List indices = + resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME)).getLocal(); //the union of all indices and aliases gets returned String[] expectedIndices = new String[]{"", "foo", "foofoo"}; assertThat(indices.size(), equalTo(expectedIndices.length)); @@ -1272,6 +1306,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { // TODO with the removal of DeleteByQuery is there another way to test resolving a write action? + private AuthorizedIndices buildAuthorizedIndices(User user, String action) { PlainActionFuture rolesListener = new PlainActionFuture<>(); authzService.roles(user, rolesListener); @@ -1284,9 +1319,20 @@ public class IndicesAndAliasesResolverTests extends ESTestCase { .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)); } - private static void assertNoIndices(IndicesRequest.Replaceable request, Set resolvedIndices) { - assertEquals(1, resolvedIndices.size()); - assertEquals(IndicesAndAliasesResolver.NO_INDEX_PLACEHOLDER, resolvedIndices.iterator().next()); + private ResolvedIndices resolveIndices(TransportRequest request, AuthorizedIndices authorizedIndices) { + return defaultIndicesResolver.resolve(request, this.metaData, authorizedIndices); + } + + private static void assertNoIndices(IndicesRequest.Replaceable request, ResolvedIndices resolvedIndices) { + final List localIndices = resolvedIndices.getLocal(); + assertEquals(1, localIndices.size()); + assertEquals(IndicesAndAliasesResolver.NO_INDEX_PLACEHOLDER, localIndices.iterator().next()); assertEquals(IndicesAndAliasesResolver.NO_INDICES_LIST, Arrays.asList(request.indices())); + assertEquals(0, resolvedIndices.getRemote().size()); + } + + private void assertSameValues(List indices, String[] expectedIndices) { + assertThat(indices.stream().distinct().count(), equalTo((long)expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); } }