From 02682ff4ec2df83bb8c68c1291f430bc2075da18 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Fri, 9 Jan 2015 11:04:22 +0100 Subject: [PATCH] Indices resolution: special treatment for IndicesAliasesRequest IndicesAliasesRequest needs to be authorized against both indices and aliases. That means that the following request curl -XPOST 'http://localhost:9200/_aliases' -d ' { "actions" : [ { "add" : { "index" : "test1", "alias" : "alias1" } } ] }' requires now indices:admin/aliases privileges for both test1 and alias1. Added manage_aliases shortcut privilege that points to indices:admin/aliases. Also, IndicesAliasesRequest used to not support replacing its indices (request doesn't implement IndicesRequest.Replaceable in es core). That can be worked around as well through the special treatment that we are introducing in shield for this specific request. Given that it is a composite action, every single operation has now its wildcards replaced with authorized indices (supported among aliases as well in case of remove operations). If any of the operation ends up relating to no indices after wildcards expansion, the whole request fails. The DefaultIndicesResolver#explodeWildcards method, which used to expand wildcards as es core would do it, is not needed anymore, as all of the requests that support wildcards have now their indices properly replaced. Added also special authorization pass for create index, if the request body contains aliases. The index can only be created if the current user has permission to create the index and to create the aliases that are part of the same request. Closes elastic/elasticsearch#112 Closes elastic/elasticsearch#557 Closes elastic/elasticsearch#529 Original commit: elastic/x-pack-elasticsearch@d7201e8a8b3eb9531ae2a7cc5eca1b9930da4b6c --- .../create/CreateIndexRequestHelper.java | 24 ++ .../authz/InternalAuthorizationService.java | 36 +- .../elasticsearch/shield/authz/Privilege.java | 3 +- .../DefaultIndicesResolver.java | 148 ++++--- .../shield/VersionCompatibilityTests.java | 5 + .../shield/authz/IndexAliasesTests.java | 371 ++++++++++++++++++ .../shield/authz/PrivilegeTests.java | 2 +- .../DefaultIndicesResolverTests.java | 216 ++++++++-- 8 files changed, 723 insertions(+), 82 deletions(-) create mode 100644 src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestHelper.java create mode 100644 src/test/java/org/elasticsearch/shield/authz/IndexAliasesTests.java diff --git a/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestHelper.java b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestHelper.java new file mode 100644 index 00000000000..466eae4db47 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestHelper.java @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.action.admin.indices.create; + +import org.elasticsearch.action.admin.indices.alias.Alias; + +import java.util.Set; + +/* + * Helper needed to retrieve aliases from a CreateIndexRequest, as the corresponding getter has package private visibility + * TODO Remove this class as soon as es core 1.5.0 is out + */ +public final class CreateIndexRequestHelper { + + private CreateIndexRequestHelper() { + } + + public static Set aliases(CreateIndexRequest createIndexRequest) { + return createIndexRequest.aliases(); + } +} diff --git a/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java b/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java index ea7a8316de5..7ee34f87196 100644 --- a/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java +++ b/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java @@ -10,11 +10,15 @@ import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest; import org.elasticsearch.action.search.ClearScrollAction; import org.elasticsearch.action.search.SearchScrollAction; +import org.elasticsearch.action.admin.indices.alias.Alias; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequestHelper; import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.base.Predicate; import org.elasticsearch.common.base.Predicates; import org.elasticsearch.common.collect.ImmutableList; +import org.elasticsearch.common.collect.Sets; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; @@ -145,22 +149,44 @@ public class InternalAuthorizationService extends AbstractComponent implements A assert !indexNames.isEmpty() || request instanceof AnalyzeRequest : "no indices request other than the analyze api has optional indices thus the resolved indices must not be empty"; + if (!authorizeIndices(action, indexNames, permission.indices())) { + throw denial(user, action, request); + } + + //if we are creating an index we need to authorize potential aliases created at the same time + if (Privilege.Index.CREATE_INDEX.predicate().apply(action)) { + assert request instanceof CreateIndexRequest; + Set aliases = CreateIndexRequestHelper.aliases((CreateIndexRequest) request); + if (!aliases.isEmpty()) { + Set aliasesAndIndices = Sets.newHashSet(indexNames); + for (Alias alias : aliases) { + aliasesAndIndices.add(alias.name()); + } + if (!authorizeIndices("indices:admin/aliases", aliasesAndIndices, permission.indices())) { + throw denial(user, "indices:admin/aliases", request); + } + } + } + + grant(user, action, request); + } + + private boolean authorizeIndices(String action, Set requestIndices, Permission.Indices permission) { // now... every index that is associated with the request, must be granted // by at least one indices permission group - for (String index : indexNames) { + for (String index : requestIndices) { boolean granted = false; - for (Permission.Indices.Group group : permission.indices()) { + for (Permission.Indices.Group group : permission) { if (group.check(action, index)) { granted = true; break; } } if (!granted) { - throw denial(user, action, request); + return false; } } - - grant(user, action, request); + return true; } private Permission.Global permission(User user) { diff --git a/src/main/java/org/elasticsearch/shield/authz/Privilege.java b/src/main/java/org/elasticsearch/shield/authz/Privilege.java index aba9d388059..9ac53ef7cd6 100644 --- a/src/main/java/org/elasticsearch/shield/authz/Privilege.java +++ b/src/main/java/org/elasticsearch/shield/authz/Privilege.java @@ -108,6 +108,7 @@ public abstract class Privilege

> { public static final Index ALL = new Index(Name.ALL, "indices:*"); public static final Index MANAGE = new Index("manage", "indices:monitor/*", "indices:admin/*"); public static final Index CREATE_INDEX = new Index("create_index", "indices:admin/create"); + public static final Index MANAGE_ALIASES = new Index("manage_aliases", "indices:admin/aliases"); public static final Index MONITOR = new Index("monitor", "indices:monitor/*"); public static final Index DATA_ACCESS = new Index("data_access", "indices:data/*"); public static final Index CRUD = new Index("crud", "indices:data/write/*", "indices:data/read/*"); @@ -119,7 +120,7 @@ public abstract class Privilege

> { public static final Index WRITE = new Index("write", "indices:data/write/*"); private static final Index[] values = new Index[] { - NONE, ALL, MANAGE, CREATE_INDEX, MONITOR, DATA_ACCESS, CRUD, READ, SEARCH, GET, INDEX, DELETE, WRITE + NONE, ALL, MANAGE, CREATE_INDEX, MANAGE_ALIASES, MONITOR, DATA_ACCESS, CRUD, READ, SEARCH, GET, INDEX, DELETE, WRITE }; public static final Predicate ACTION_MATCHER = Privilege.Index.ALL.predicate(); diff --git a/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolver.java b/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolver.java index de8eaac7ea5..73534f967d0 100644 --- a/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolver.java +++ b/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolver.java @@ -9,10 +9,11 @@ import org.elasticsearch.action.CompositeIndicesRequest; import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.cluster.metadata.AliasAction; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.collect.ImmutableList; -import org.elasticsearch.common.collect.Sets; +import org.elasticsearch.common.collect.*; +import org.elasticsearch.common.hppc.ObjectLookupContainer; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.index.Index; import org.elasticsearch.indices.IndexMissingException; @@ -65,56 +66,86 @@ public class DefaultIndicesResolver implements IndicesResolver if (indicesRequest.indicesOptions().expandWildcardsOpen() || indicesRequest.indicesOptions().expandWildcardsClosed()) { if (indicesRequest instanceof IndicesRequest.Replaceable) { ImmutableList authorizedIndices = authzService.authorizedIndicesAndAliases(user, action); - List indices = replaceWildcardsWithAuthorizedIndices(indicesRequest, metaData, authorizedIndices); - //ignore the IndicesOptions#allowNoIndices and just throw exception if the wildcards expansion to authorized - //indices resulted in no indices. This is important as we always need to replace wildcards for security reason, - //to make sure that the operation is executed on the indices that we authorized it to execute on. - //If we can't replace because we got an empty set, we can only throw exception. - //Downside of this is that a single item exception is going to make fail the composite request that holds it as a whole. - if (indices == null || indices.isEmpty()) { - if (MetaData.isAllIndices(indicesRequest.indices())) { - throw new IndexMissingException(new Index(MetaData.ALL)); - } - throw new IndexMissingException(new Index(Arrays.toString(indicesRequest.indices()))); - } + List indices = replaceWildcardsWithAuthorizedIndices(indicesRequest.indices(), indicesRequest.indicesOptions(), metaData, authorizedIndices); ((IndicesRequest.Replaceable) indicesRequest).indices(indices.toArray(new String[indices.size()])); return Sets.newHashSet(indices); } - if (containsWildcards(indicesRequest)) { - //used for requests that support wildcards but don't allow to replace their indices (e.g. IndicesAliasesRequest) - //potentially insecure as cluster state may change hence we may end up resolving to different indices on different nodes - assert indicesRequest instanceof IndicesAliasesRequest - : "IndicesAliasesRequest is the only request known to support wildcards that doesn't support replacing its indices"; - return Sets.newHashSet(explodeWildcards(indicesRequest, metaData)); - } + assert indicesRequest instanceof IndicesAliasesRequest || !containsWildcards(indicesRequest) : + "IndicesAliasesRequest is the only external request known to support wildcards that doesn't support replacing its indices"; + //NOTE: shard level requests do support wildcards (as they hold the original indices options) but don't support replacing their indices. //That is fine though because they never contain wildcards, as they get replaced as part of the authorization of their - //corresponding parent request on the coordinating node. Hence wildcards don't get replaced nor exploded for shard level requests. + //corresponding parent request on the coordinating node. Hence wildcards don't need to get replaced nor exploded for shard level requests. } + + if (indicesRequest instanceof IndicesAliasesRequest) { + //special treatment for IndicesAliasesRequest since we need to extract indices from indices() as well as aliases() + //Also, we need to replace wildcards in both with authorized indices and/or aliases (IndicesAliasesRequest doesn't implement Replaceable) + IndicesAliasesRequest request = (IndicesAliasesRequest) indicesRequest; + + ImmutableList authorizedIndices = authzService.authorizedIndicesAndAliases(user, action); + Set finalIndices = Sets.newHashSet(); + + List authorizedAliases = null; + + for (IndicesAliasesRequest.AliasActions aliasActions : request.getAliasActions()) { + //replace indices with authorized ones if needed + if (indicesRequest.indicesOptions().expandWildcardsOpen() || indicesRequest.indicesOptions().expandWildcardsClosed()) { + //Note: the indices that the alias operation maps to might end up containing aliases, since authorized indices can also be aliases. + //This is fine as es core resolves them to concrete indices anyway before executing the actual operation. + //Also es core already allows to specify aliases among indices, they will just be resolved (alias to alias is not supported). + //e.g. index: foo* gets resolved in core to anything that matches the expression, aliases included, hence their corresponding indices. + List indices = replaceWildcardsWithAuthorizedIndices(aliasActions.indices(), indicesRequest.indicesOptions(), metaData, authorizedIndices); + aliasActions.indices(indices.toArray(new String[indices.size()])); + } + Collections.addAll(finalIndices, aliasActions.indices()); + + //replace aliases with authorized ones if needed + if (aliasActions.actionType() == AliasAction.Type.REMOVE) { + //lazily initialize a list of all the authorized aliases (filtering concrete indices out) + if (authorizedAliases == null) { + authorizedAliases = Lists.newArrayList(); + ObjectLookupContainer existingAliases = metaData.aliases().keys(); + for (String authorizedIndex : authorizedIndices) { + if (existingAliases.contains(authorizedIndex)) { + authorizedAliases.add(authorizedIndex); + } + } + } + + List finalAliases = Lists.newArrayList(); + for (String aliasPattern : aliasActions.aliases()) { + if (aliasPattern.equals(MetaData.ALL)) { + finalAliases.addAll(authorizedAliases); + } else if (Regex.isSimpleMatchPattern(aliasPattern)) { + for (String authorizedAlias : authorizedAliases) { + if (Regex.simpleMatch(aliasPattern, authorizedAlias)) { + finalAliases.add(authorizedAlias); + } + } + } else { + finalAliases.add(aliasPattern); + } + } + + //throw exception if the wildcards expansion to authorized aliases resulted in no indices. + // This is important as we always need to replace wildcards for security reason, + //to make sure that the operation is executed on the aliases that we authorized it to execute on. + //If we can't replace because we got an empty set, we can only throw exception. + if (finalAliases.isEmpty()) { + throw new IndexMissingException(new Index(Arrays.toString(aliasActions.aliases()))); + } + aliasActions.aliases(finalAliases.toArray(new String[finalAliases.size()])); + } + Collections.addAll(finalIndices, aliasActions.aliases()); + } + return finalIndices; + } + return Sets.newHashSet(indicesRequest.indices()); } - /* - * Explodes wildcards based on default core behaviour. Used for IndicesAliasesRequest only as it doesn't support - * replacing its indices. It will go away once that gets fixed. - */ - private String[] explodeWildcards(IndicesRequest indicesRequest, MetaData metaData) { - //note that "_all" will map to concrete indices only, as the same happens in core - //which is different from "*" as the latter expands to all indices and aliases - if (MetaData.isAllIndices(indicesRequest.indices())) { - if (indicesRequest.indicesOptions().expandWildcardsOpen() && indicesRequest.indicesOptions().expandWildcardsClosed()) { - return metaData.concreteAllIndices(); - } - if (indicesRequest.indicesOptions().expandWildcardsOpen()) { - return metaData.concreteAllOpenIndices(); - } - return metaData.concreteAllClosedIndices(); - - } - return metaData.convertFromWildcards(indicesRequest.indices(), indicesRequest.indicesOptions()); - } - private boolean containsWildcards(IndicesRequest indicesRequest) { if (MetaData.isAllIndices(indicesRequest.indices())) { return true; @@ -127,22 +158,22 @@ public class DefaultIndicesResolver implements IndicesResolver return false; } - private List replaceWildcardsWithAuthorizedIndices(IndicesRequest indicesRequest, MetaData metaData, List authorizedIndices) { + private List replaceWildcardsWithAuthorizedIndices(String[] indices, IndicesOptions indicesOptions, MetaData metaData, List authorizedIndices) { - if (MetaData.isAllIndices(indicesRequest.indices())) { + if (MetaData.isAllIndices(indices)) { List visibleIndices = new ArrayList<>(); for (String authorizedIndex : authorizedIndices) { - if (isIndexVisible(authorizedIndex, indicesRequest.indicesOptions(), metaData)) { + if (isIndexVisible(authorizedIndex, indicesOptions, metaData)) { visibleIndices.add(authorizedIndex); } } - return visibleIndices; + return throwExceptionIfNoIndicesWereResolved(indices, visibleIndices); } //the order matters when it comes to + and - (see MetaData#convertFromWildcards) List finalIndices = new ArrayList<>(); - for (int i = 0; i < indicesRequest.indices().length; i++) { - String index = indicesRequest.indices()[i]; + for (int i = 0; i < indices.length; i++) { + String index = indices[i]; String aliasOrIndex; boolean minus = false; if (index.charAt(0) == '+') { @@ -152,7 +183,7 @@ public class DefaultIndicesResolver implements IndicesResolver //mimic the MetaData#convertFromWilcards behaviour with "-index" syntax //but instead of adding all the indices, add only the ones that the user is authorized for for (String authorizedIndex : authorizedIndices) { - if (isIndexVisible(authorizedIndex, indicesRequest.indicesOptions(), metaData)) { + if (isIndexVisible(authorizedIndex, indicesOptions, metaData)) { finalIndices.add(authorizedIndex); } } @@ -169,7 +200,7 @@ public class DefaultIndicesResolver implements IndicesResolver if (minus) { finalIndices.remove(authorizedIndex); } else { - if (isIndexVisible(authorizedIndex, indicesRequest.indicesOptions(), metaData)) { + if (isIndexVisible(authorizedIndex, indicesOptions, metaData)) { finalIndices.add(authorizedIndex); } } @@ -188,7 +219,22 @@ public class DefaultIndicesResolver implements IndicesResolver } } - return finalIndices; + return throwExceptionIfNoIndicesWereResolved(indices, finalIndices); + } + + private List throwExceptionIfNoIndicesWereResolved(String[] originalIndices, List resolvedIndices) { + //ignore the IndicesOptions#allowNoIndices and just throw exception if the wildcards expansion to authorized + //indices resulted in no indices. This is important as we always need to replace wildcards for security reason, + //to make sure that the operation is executed on the indices that we authorized it to execute on. + //If we can't replace because we got an empty set, we can only throw exception. + //Downside of this is that a single item exception is going to make fail the composite request that holds it as a whole. + if (resolvedIndices == null || resolvedIndices.isEmpty()) { + if (MetaData.isAllIndices(originalIndices)) { + throw new IndexMissingException(new Index(MetaData.ALL)); + } + throw new IndexMissingException(new Index(Arrays.toString(originalIndices))); + } + return resolvedIndices; } private static boolean isIndexVisible(String index, IndicesOptions indicesOptions, MetaData metaData) { diff --git a/src/test/java/org/elasticsearch/shield/VersionCompatibilityTests.java b/src/test/java/org/elasticsearch/shield/VersionCompatibilityTests.java index 26c3c95d72e..8dea9d5f25c 100644 --- a/src/test/java/org/elasticsearch/shield/VersionCompatibilityTests.java +++ b/src/test/java/org/elasticsearch/shield/VersionCompatibilityTests.java @@ -41,5 +41,10 @@ public class VersionCompatibilityTests extends ElasticsearchTestCase { * see https://github.com/elasticsearch/elasticsearch/pull/9134 {@link org.elasticsearch.shield.transport.netty.SecuredMessageChannelHandler} */ assertThat("Cleanup SecuredMessageChannelHandler class and remove needless code, fixed in es core 1.5", Version.CURRENT.onOrBefore(Version.V_1_4_2), is(true)); + + /** + * see https://github.com/elasticsearch/elasticsearch/pull/9273 {@link org.elasticsearch.action.admin.indices.create.CreateIndexRequestHelper} + */ + assertThat("Remove CreateIndexRequestHelper class, fixed in es core 1.5", Version.CURRENT.onOrBefore(Version.V_1_4_2), is(true)); } } diff --git a/src/test/java/org/elasticsearch/shield/authz/IndexAliasesTests.java b/src/test/java/org/elasticsearch/shield/authz/IndexAliasesTests.java new file mode 100644 index 00000000000..2e3a1fc2800 --- /dev/null +++ b/src/test/java/org/elasticsearch/shield/authz/IndexAliasesTests.java @@ -0,0 +1,371 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.shield.authz; + +import org.elasticsearch.action.admin.indices.alias.Alias; +import org.elasticsearch.indices.IndexMissingException; +import org.elasticsearch.shield.authc.support.SecuredString; +import org.elasticsearch.test.ShieldIntegrationTest; +import org.junit.Test; + +import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER; +import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; +import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.CoreMatchers.containsString; + +@ClusterScope(scope = Scope.SUITE) +public class IndexAliasesTests extends ShieldIntegrationTest { + + @Override + protected String configUsers() { + return super.configUsers() + + "create_only:{plain}test123\n" + + "create_test_aliases_test:{plain}test123\n" + + "create_test_aliases_alias:{plain}test123\n" + + "create_test_aliases_test_alias:{plain}test123\n" + + "aliases_only:{plain}test123\n"; + } + + @Override + protected String configUsersRoles() { + return super.configUsersRoles() + + "create_only:create_only\n" + + "create_test_aliases_test:create_test_aliases_test\n" + + "create_test_aliases_alias:create_test_aliases_alias\n" + + "create_test_aliases_test_alias:create_test_aliases_test_alias\n" + + "aliases_only:aliases_only\n"; + } + + @Override + protected String configRoles() { + return super.configRoles() + "\n" + + //role that has create index only privileges + "create_only:\n" + + " indices:\n" + + " '*': create_index\n" + + //role that has create index and managa aliases on test_*, not enough to manage aliases outside of test_* namespace + "create_test_aliases_test:\n" + + " indices:\n" + + " 'test_*': create_index,manage_aliases\n" + + //role that has create index on test_* and manage aliases on alias_*, can't create aliases pointing to test_* though + "create_test_aliases_alias:\n" + + " indices:\n" + + " 'test_*': create_index\n" + + " 'alias_*': manage_aliases\n" + + //role that has create index on test_* and manage_aliases on both alias_* and test_* + "create_test_aliases_test_alias:\n" + + " indices:\n" + + " 'test_*': create_index\n" + + " 'alias_*,test_*': manage_aliases\n" + + //role that has manage_aliases only on both test_* and alias_* + "aliases_only:\n" + + " indices:\n" + + " 'alias_*,test_*': manage_aliases\n"; + } + + @Test + public void testCreateIndexThenAliasesCreateOnlyPermission() { + //user has create permission only: allows to create indices, manage_aliases is required to add/remove aliases + assertAcked(client().admin().indices().prepareCreate("test_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray())))); + + try { + client().admin().indices().prepareAliases().addAlias("test_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray()))).get(); + fail("add alias should have failed due to missing manage_aliases privileges"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_only]")); + } + + try { + client().admin().indices().prepareAliases().addAlias("test_*", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray()))).get(); + fail("add alias should have failed due to missing manage_aliases privileges"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[test_*]")); + } + } + + @Test + public void testCreateIndexAndAliasesCreateOnlyPermission() { + //user has create permission only: allows to create indices, manage_aliases is required to add aliases although they are part of the same create index request + try { + client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_2")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray()))).get(); + fail("create index should have failed due to missing manage_aliases privileges"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_only]")); + } + } + + @Test + public void testDeleteAliasesCreateOnlyPermission() { + //user has create permission only: allows to create indices, manage_aliases is required to add/remove aliases + try { + client().admin().indices().prepareAliases().removeAlias("test_1", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_only]")); + } + + try { + client().admin().indices().prepareAliases().removeAlias("test_1", "alias_*") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[alias_*")); + } + + try { + client().admin().indices().prepareAliases().removeAlias("test_1", "_all") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[_all]")); + } + } + + @Test + public void testCreateIndexThenAliasesCreateAndAliasesPermission() { + //user has create and manage_aliases permission on test_*. manage_aliases is required to add/remove aliases on both aliases and indices + assertAcked(client().admin().indices().prepareCreate("test_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareAliases().addAlias("test_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareAliases().addAlias("test_*", "test_alias_2") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + + try { + //fails: user doesn't have manage_aliases on alias_1 + client().admin().indices().prepareAliases().addAlias("test_1", "alias_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray()))).get(); + fail("add alias should have failed due to missing manage_aliases privileges on alias_1"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_test]")); + } + } + + @Test + public void testCreateIndexAndAliasesCreateAndAliasesPermission() { + //user has create and manage_aliases permission on test_*. manage_aliases is required to add/remove aliases on both aliases and indices + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_alias")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + + try { + //fails: user doesn't have manage_aliases on alias_1 + client().admin().indices().prepareCreate("test_2").addAlias(new Alias("test_alias")).addAlias(new Alias("alias_2")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray()))).get(); + fail("create index should have failed due to missing manage_aliases privileges on alias_2"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_test]")); + } + } + + @Test + public void testDeleteAliasesCreateAndAliasesPermission() { + //user has create and manage_aliases permission on test_*. manage_aliases is required to add/remove aliases on both aliases and indices + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_alias_1")).addAlias(new Alias("test_alias_2")) + .addAlias(new Alias("test_alias_3")).addAlias(new Alias("test_alias_4")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareAliases().removeAlias("test_1", "test_alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareAliases().removeAlias("test_*", "test_alias_2") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + //ok: user has manage_aliases on test_* + assertAcked(client().admin().indices().prepareAliases().removeAlias("test_1", "test_alias_*") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray())))); + + try { + //fails: all aliases have been deleted, no existing aliases match test_alias_* + client().admin().indices().prepareAliases().removeAlias("test_1", "test_alias_*") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to no existing matching aliases to expand test_alias_* to"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[test_alias_*]")); + } + + try { + //fails: all aliases have been deleted, no existing aliases match _all + client().admin().indices().prepareAliases().removeAlias("test_1", "_all") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to no existing matching aliases to expand _all to"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[_all]")); + } + + try { + //fails: user doesn't have manage_aliases on alias_1 + client().admin().indices().prepareAliases().removeAlias("test_1", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges on alias_1"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_test]")); + } + } + + @Test + public void testCreateIndexThenAliasesCreateAndAliasesPermission2() { + //user has create permission on test_* and manage_aliases permission on alias_*. manage_aliases is required to add/remove aliases on both aliases and indices + assertAcked(client().admin().indices().prepareCreate("test_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray())))); + + try { + //fails: user doesn't have manage aliases on test_1 + client().admin().indices().prepareAliases().addAlias("test_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("add alias should have failed due to missing manage_aliases privileges on test_alias and test_1"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_alias]")); + } + + try { + //fails: user doesn't have manage aliases on test_1 + client().admin().indices().prepareAliases().addAlias("test_1", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("add alias should have failed due to missing manage_aliases privileges on test_1"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_alias]")); + } + + try { + //fails: user doesn't have manage aliases on test_*, no matching indices to replace wildcards + client().admin().indices().prepareAliases().addAlias("test_*", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("add alias should have failed due to missing manage_aliases privileges on test_1"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[test_*]")); + } + } + + @Test + public void testCreateIndexAndAliasesCreateAndAliasesPermission2() { + //user has create permission on test_* and manage_aliases permission on alias_*. manage_aliases is required to add/remove aliases on both aliases and indices + try { + //fails: user doesn't have manage_aliases on test_1, create index is rejected as a whole + client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_alias")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("create index should have failed due to missing manage_aliases privileges on test_1 and test_alias"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_alias]")); + } + + try { + //fails: user doesn't have manage_aliases on test_*, create index is rejected as a whole + client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_alias")).addAlias(new Alias("alias_1")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("create index should have failed due to missing manage_aliases privileges on test_1 and test_alias"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_alias]")); + } + } + + @Test + public void testDeleteAliasesCreateAndAliasesPermission2() { + //user has create permission on test_* and manage_aliases permission on alias_*. manage_aliases is required to add/remove aliases on both aliases and indices + try { + //fails: user doesn't have manage_aliases on test_1 + client().admin().indices().prepareAliases().removeAlias("test_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges on test_alias and test_1"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_alias]")); + } + + try { + //fails: user doesn't have manage_aliases on test_1 + client().admin().indices().prepareAliases().removeAlias("test_1", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges on test_1"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_alias]")); + } + + try { + //fails: user doesn't have manage_aliases on test_*, wildcards can't get replaced + client().admin().indices().prepareAliases().removeAlias("test_*", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_alias", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges on test_*"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[test_*]")); + } + } + + @Test + public void testCreateIndexThenAliasesCreateAndAliasesPermission3() { + //user has create permission on test_* and manage_aliases permission on test_*,alias_*. All good. + assertAcked(client().admin().indices().prepareCreate("test_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + assertAcked(client().admin().indices().prepareAliases().addAlias("test_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + assertAcked(client().admin().indices().prepareAliases().addAlias("test_1", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + assertAcked(client().admin().indices().prepareAliases().addAlias("test_*", "alias_2") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + } + + @Test + public void testCreateIndexAndAliasesCreateAndAliasesPermission3() { + //user has create permission on test_* and manage_aliases permission on test_*,alias_*. All good. + assertAcked(client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_alias")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + assertAcked(client().admin().indices().prepareCreate("test_2").addAlias(new Alias("test_alias_2")).addAlias(new Alias("alias_2")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + } + + @Test + public void testDeleteAliasesCreateAndAliasesPermission3() { + //user has create permission on test_* and manage_aliases permission on test_*,alias_*. All good. + assertAcked(client().admin().indices().prepareCreate("test_1").addAlias(new Alias("test_alias")).addAlias(new Alias("alias_1")) + .addAlias(new Alias("alias_2")).addAlias(new Alias("alias_3")) + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + try { + //fails: user doesn't have manage_aliases privilege on non_authorized + client().admin().indices().prepareAliases().removeAlias("test_1", "non_authorized").removeAlias("test_1", "test_alias") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to missing manage_aliases privileges on non_authorized"); + } catch(AuthorizationException e) { + assertThat(e.getMessage(), containsString("action [indices:admin/aliases] is unauthorized for user [create_test_aliases_test_alias]")); + } + + assertAcked(client().admin().indices().prepareAliases().removeAlias("test_1", "alias_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + assertAcked(client().admin().indices().prepareAliases().removeAlias("test_*", "_all") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray())))); + + + try { + //fails: all aliases have been deleted, _all can't be resolved to any existing authorized aliases + client().admin().indices().prepareAliases().removeAlias("test_1", "_all") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_test_aliases_test_alias", new SecuredString("test123".toCharArray()))).get(); + fail("remove alias should have failed due to no existing aliases matching _all"); + } catch(IndexMissingException e) { + assertThat(e.getMessage(), containsString("[_all]")); + } + } + + @Test(expected = AuthorizationException.class) + public void testCreateIndexAliasesOnlyPermission() { + client().admin().indices().prepareCreate("test_1") + .putHeader(BASIC_AUTH_HEADER, basicAuthHeaderValue("aliases_only", new SecuredString("test123".toCharArray()))).get(); + } +} diff --git a/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java b/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java index a96480c8876..8b586f05a80 100644 --- a/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java +++ b/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java @@ -91,7 +91,7 @@ public class PrivilegeTests extends ElasticsearchTestCase { } @Test - public void testCluster_InvalidNaem() throws Exception { + public void testCluster_InvalidName() throws Exception { thrown.expect(ElasticsearchIllegalArgumentException.class); Privilege.Name actionName = new Privilege.Name("foobar"); Privilege.Cluster.get(actionName); diff --git a/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java b/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java index 1d67952e095..9413b8cc207 100644 --- a/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java +++ b/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java @@ -19,6 +19,7 @@ import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.Requests; +import org.elasticsearch.cluster.metadata.AliasAction; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; @@ -33,8 +34,7 @@ import org.junit.Test; import java.util.Set; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -70,6 +70,7 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { when(authzService.authorizedIndicesAndAliases(user, DeleteIndexAction.NAME)).thenReturn(ImmutableList.copyOf(authorizedIndices)); when(authzService.authorizedIndicesAndAliases(user, DeleteByQueryAction.NAME)).thenReturn(ImmutableList.copyOf(authorizedIndices)); userNoIndices = new User.Simple("test", "test"); + when(authzService.authorizedIndicesAndAliases(userNoIndices, IndicesAliasesAction.NAME)).thenReturn(ImmutableList.of()); when(authzService.authorizedIndicesAndAliases(userNoIndices, SearchAction.NAME)).thenReturn(ImmutableList.of()); when(authzService.authorizedIndicesAndAliases(userNoIndices, MultiSearchAction.NAME)).thenReturn(ImmutableList.of()); @@ -84,7 +85,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -96,7 +96,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -108,7 +107,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -120,7 +118,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -132,7 +129,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"barbaz", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -144,7 +140,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"barbaz", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -156,7 +151,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -168,7 +162,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "bar-closed"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -180,7 +173,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "barbaz"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -192,7 +184,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] replacedIndices = new String[]{"bar", "bar-closed", "barbaz"}; assertThat(indices.size(), equalTo(replacedIndices.length)); assertThat(indices, hasItems(replacedIndices)); - assertThat(request.indices().length, equalTo(replacedIndices.length)); assertThat(request.indices(), arrayContaining(replacedIndices)); } @@ -238,27 +229,206 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { } @Test - public void testResolveAllIndicesRequestNonReplaceable() { + public void testResolveIndicesAliasesRequest() { IndicesAliasesRequest request = new IndicesAliasesRequest(); - request.addAlias("alias", "_all"); + request.addAlias("alias1", "foo", "foofoo"); + request.addAlias("alias2", "foo", "foobar"); Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); - //we do expand _all (to concrete indices only!) but we don't replace them with authorized indices - String[] expectedIndices = new String[]{"foofoo", "foo", "foobar", "bar", "bar2"}; + //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)); - assertThat(request.indices(), equalTo(new String[]{"_all"})); + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foo", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("alias1")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("foo", "foobar")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("alias2")); } @Test - public void testResolveWildcardsIndicesRequestNonReplaceable() { + public void testResolveIndicesAliasesRequestExistingAlias() { IndicesAliasesRequest request = new IndicesAliasesRequest(); - request.addAlias("alias", "*"); + request.addAlias("alias1", "foo", "foofoo"); + request.addAlias("foofoobar", "foo", "foobar"); Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); - //we do explode wildcards but we don't replace them with authorized indices - String[] expectedIndices = new String[]{"foofoobar", "foobar", "barbaz", "foo", "foofoo", "bar", "bar2"}; + //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)); - assertThat(request.indices(), equalTo(new String[]{"*"})); + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foo", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("alias1")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("foo", "foobar")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("foofoobar")); + } + + @Test + public void testResolveIndicesAliasesRequestMissingIndex() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAlias("alias1", "foo", "foofoo"); + request.addAlias("alias2", "missing"); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //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)); + assertThat(indices, hasItems(expectedIndices)); + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foo", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("alias1")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("missing")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("alias2")); + } + + @Test + public void testResolveWildcardsIndicesAliasesRequest() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAlias("alias1", "foo*"); + request.addAlias("alias2", "bar*"); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //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", "alias2", "foofoo", "foofoobar", "bar"}; + assertThat(indices.size(), equalTo(expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); + //wildcards get replaced on each single action + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foofoobar", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("alias1")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("bar")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("alias2")); + } + + @Test(expected = IndexMissingException.class) + public void testResolveWildcardsIndicesAliasesRequestNoMatchingIndices() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAlias("alias1", "foo*"); + request.addAlias("alias2", "bar*"); + request.addAlias("alias3", "non_matching_*"); + //if a single operation contains wildcards and ends up being resolved to no indices, it makes the whole request fail + defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + } + + @Test + public void testResolveAllIndicesAliasesRequest() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAlias("alias1", "_all"); + request.addAlias("alias2", "_all"); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //the union of all resolved indices and aliases gets returned + String[] expectedIndices = new String[]{"bar", "foofoobar", "foofoo", "alias1", "alias2"}; + assertThat(indices.size(), equalTo(expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); + String[] replacedIndices = new String[]{"bar", "foofoobar", "foofoo"}; + //_all gets replaced with all indices that user is authorized for, on each single action + assertThat(request.getAliasActions().get(0).indices(), arrayContaining(replacedIndices)); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("alias1")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining(replacedIndices)); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("alias2")); + } + + @Test(expected = IndexMissingException.class) + public void testResolveAllIndicesAliasesRequestNoAuthorizedIndices() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAlias("alias1", "_all"); + //current user is not authorized for any index, _all resolves to no indices, the request fails + defaultIndicesResolver.resolve(userNoIndices, IndicesAliasesAction.NAME, request, metaData); + } + + @Test(expected = IndexMissingException.class) + public void testResolveWildcardsIndicesAliasesRequestNoAuthorizedIndices() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAlias("alias1", "foo*"); + //current user is not authorized for any index, foo* resolves to no indices, the request fails + defaultIndicesResolver.resolve(userNoIndices, IndicesAliasesAction.NAME, request, metaData); + } + + @Test + public void testResolveIndicesAliasesRequestDeleteActions() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAliasAction(AliasAction.newRemoveAliasAction("foo", "foofoobar")); + request.addAliasAction(AliasAction.newRemoveAliasAction("foofoo", "barbaz")); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //the union of all indices and aliases gets returned + String[] expectedIndices = new String[]{"foo", "foofoobar", "foofoo", "barbaz"}; + assertThat(indices.size(), equalTo(expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("foofoobar")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("foofoo")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("barbaz")); + } + + @Test + public void testResolveIndicesAliasesRequestDeleteActionsMissingIndex() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAliasAction(AliasAction.newRemoveAliasAction("foo", "foofoobar")); + request.addAliasAction(AliasAction.newRemoveAliasAction("missing_index", "missing_alias")); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //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)); + assertThat(indices, hasItems(expectedIndices)); + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("foofoobar")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("missing_index")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("missing_alias")); + } + + @Test + public void testResolveWildcardsIndicesAliasesRequestDeleteActions() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAliasAction(AliasAction.newRemoveAliasAction("foo*", "foofoobar")); + request.addAliasAction(AliasAction.newRemoveAliasAction("bar*", "barbaz")); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //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)); + assertThat(indices, hasItems(expectedIndices)); + //wildcards get replaced within each single action + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foofoobar", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("foofoobar")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("bar")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("barbaz")); + } + + @Test + public void testResolveAliasesWildcardsIndicesAliasesRequestDeleteActions() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAliasAction(AliasAction.newRemoveAliasAction("*", "foo*")); + request.addAliasAction(AliasAction.newRemoveAliasAction("*bar", "foo*")); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //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)); + //alias foofoobar on both sides, that's fine, es core would do the same, same as above + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("bar", "foofoobar", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("foofoobar")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("bar", "foofoobar")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("foofoobar")); + } + + @Test(expected = IndexMissingException.class) + public void testResolveAliasesWildcardsIndicesAliasesRequestDeleteActionsNoAuthorizedIndices() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAliasAction(AliasAction.newRemoveAliasAction("foo*", "foo*")); + //no authorized aliases match bar*, hence this action fails and makes the whole request fail + request.addAliasAction(AliasAction.newRemoveAliasAction("*bar", "bar*")); + defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + } + + @Test + public void testResolveWildcardsIndicesAliasesRequestAddAndDeleteActions() { + IndicesAliasesRequest request = new IndicesAliasesRequest(); + request.addAliasAction(AliasAction.newRemoveAliasAction("foo*", "foofoobar")); + request.addAliasAction(AliasAction.newAddAliasAction("bar*", "foofoobar")); + Set indices = defaultIndicesResolver.resolve(user, IndicesAliasesAction.NAME, request, metaData); + //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)); + //every single action has its indices replaced with matching (authorized) ones + assertThat(request.getAliasActions().get(0).indices(), arrayContaining("foofoobar", "foofoo")); + assertThat(request.getAliasActions().get(0).aliases(), arrayContaining("foofoobar")); + assertThat(request.getAliasActions().get(1).indices(), arrayContaining("bar")); + assertThat(request.getAliasActions().get(1).aliases(), arrayContaining("foofoobar")); } //msearch is a CompositeIndicesRequest whose items (SearchRequests) implement IndicesRequest.Replaceable, wildcards will get replaced @@ -379,7 +549,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foofoo", "foofoo-closed"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); - assertThat(request.indices().length, equalTo(expectedIndices.length)); assertThat(request.indices(), arrayContaining(expectedIndices)); } @@ -390,7 +559,6 @@ public class DefaultIndicesResolverTests extends ElasticsearchTestCase { String[] expectedIndices = new String[]{"bar", "foofoobar", "foofoo"}; assertThat(indices.size(), equalTo(expectedIndices.length)); assertThat(indices, hasItems(expectedIndices)); - assertThat(request.indices().length, equalTo(expectedIndices.length)); assertThat(request.indices(), arrayContaining(expectedIndices)); }