diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java index e92bc8ecf11..c54d929a375 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java @@ -12,6 +12,7 @@ import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.search.ClearScrollAction; import org.elasticsearch.action.search.SearchScrollAction; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.AliasOrIndex; @@ -79,13 +80,14 @@ public class InternalAuthorizationService extends AbstractComponent implements A @Inject public InternalAuthorizationService(Settings settings, RolesStore rolesStore, ClusterService clusterService, - AuditTrail auditTrail, AuthenticationFailureHandler authcFailureHandler, ThreadPool threadPool) { + AuditTrail auditTrail, AuthenticationFailureHandler authcFailureHandler, + ThreadPool threadPool, IndexNameExpressionResolver nameExpressionResolver) { super(settings); this.rolesStore = rolesStore; this.clusterService = clusterService; this.auditTrail = auditTrail; - this.indicesAndAliasesResolvers = new IndicesAndAliasesResolver[]{ - new DefaultIndicesAndAliasesResolver(this) + this.indicesAndAliasesResolvers = new IndicesAndAliasesResolver[] { + new DefaultIndicesAndAliasesResolver(this, nameExpressionResolver) }; this.authcFailureHandler = authcFailureHandler; this.threadContext = threadPool.getThreadContext(); diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesAndAliasesResolver.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesAndAliasesResolver.java index 2732d7c72de..9f575cafd9f 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesAndAliasesResolver.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesAndAliasesResolver.java @@ -7,7 +7,9 @@ package org.elasticsearch.shield.authz.indicesresolver; import org.elasticsearch.action.AliasesRequest; import org.elasticsearch.action.CompositeIndicesRequest; +import org.elasticsearch.action.DocumentRequest; import org.elasticsearch.action.IndicesRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.metadata.AliasOrIndex; @@ -35,9 +37,11 @@ import java.util.SortedMap; public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolver { private final AuthorizationService authzService; + private final IndexNameExpressionResolver nameExpressionResolver; - public DefaultIndicesAndAliasesResolver(AuthorizationService authzService) { + public DefaultIndicesAndAliasesResolver(AuthorizationService authzService, IndexNameExpressionResolver nameExpressionResolver) { this.authzService = authzService; + this.nameExpressionResolver = nameExpressionResolver; } @Override @@ -80,25 +84,30 @@ public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolv indices = Collections.singleton(((PutMappingRequest) indicesRequest).getConcreteIndex().getName()); assert indicesRequest.indices() == null || indicesRequest.indices().length == 0 : "indices are: " + Arrays.toString(indicesRequest.indices()); // Arrays.toString() can handle null values - all good - } else { - if (indicesRequest.indicesOptions().expandWildcardsOpen() || indicesRequest.indicesOptions().expandWildcardsClosed()) { - if (indicesRequest instanceof IndicesRequest.Replaceable) { - List authorizedIndices = replaceWildcardsWithAuthorizedIndices(indicesRequest.indices(), - indicesRequest.indicesOptions(), - metaData, authzService.authorizedIndicesAndAliases(user, action)); - ((IndicesRequest.Replaceable) indicesRequest).indices(authorizedIndices.toArray(new String[authorizedIndices.size()])); - } else { - assert !containsWildcards(indicesRequest) : - "There are no external requests known to support wildcards that don't support replacing their 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 need to get replaced nor exploded for - // shard level requests. - } - } + } else if (indicesRequest instanceof IndicesRequest.Replaceable) { + IndicesRequest.Replaceable replaceable = (IndicesRequest.Replaceable) indicesRequest; + final boolean replaceWildcards = indicesRequest.indicesOptions().expandWildcardsOpen() + || indicesRequest.indicesOptions().expandWildcardsClosed(); + List authorizedIndices = replaceWildcardsWithAuthorizedIndices(indicesRequest.indices(), + indicesRequest.indicesOptions(), + metaData, + authzService.authorizedIndicesAndAliases(user, action), + replaceWildcards); + replaceable.indices(authorizedIndices.toArray(new String[authorizedIndices.size()])); indices = Sets.newHashSet(indicesRequest.indices()); + } else { + assert !containsWildcards(indicesRequest) : + "There are no external requests known to support wildcards that don't support replacing their 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 need to get replaced nor exploded for + // shard level requests. + List resolvedNames = new ArrayList<>(); + for (String name : indicesRequest.indices()) { + resolvedNames.add(nameExpressionResolver.resolveDateMathExpression(name)); + } + indices = Sets.newHashSet(resolvedNames); } if (indicesRequest instanceof AliasesRequest) { @@ -177,10 +186,14 @@ public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolv } private List replaceWildcardsWithAuthorizedIndices(String[] indices, IndicesOptions indicesOptions, MetaData metaData, - List authorizedIndices) { + List authorizedIndices, boolean replaceWildcards) { // check for all and return list of authorized indices if (IndexNameExpressionResolver.isAllIndices(indicesList(indices))) { + if (replaceWildcards == false) { + // if we cannot replace wildcards, then we should not set all indices + return throwExceptionIfNoIndicesWereResolved(indices, null); + } List visibleIndices = new ArrayList<>(); for (String authorizedIndex : authorizedIndices) { if (isIndexVisible(authorizedIndex, indicesOptions, metaData)) { @@ -214,7 +227,7 @@ public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolv aliasOrIndex = index; } - if (Regex.isSimpleMatchPattern(aliasOrIndex)) { + if (replaceWildcards && Regex.isSimpleMatchPattern(aliasOrIndex)) { for (String authorizedIndex : authorizedIndices) { if (Regex.simpleMatch(aliasOrIndex, authorizedIndex)) { if (minus) { @@ -227,15 +240,32 @@ public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolv } } } else { - //MetaData#convertFromWildcards checks if the index exists here and throws IndexNotFoundException if not (based on - // ignore_unavailable). - //Do nothing as if the index is missing but the user is not authorized to it an AuthorizationException will be thrown. - //If the index is missing and the user is authorized to it, core will throw IndexNotFoundException later on. - //There is no problem with deferring this as we are dealing with an explicit name, not with wildcards. - if (minus) { - finalIndices.remove(aliasOrIndex); + // we always need to check for date math expressions + String dateMathName = nameExpressionResolver.resolveDateMathExpression(aliasOrIndex); + // we can use != here to compare strings since the name expression resolver returns the same instance, but add an assert + // to ensure we catch this if it changes + if (dateMathName != aliasOrIndex) { + assert dateMathName.equals(aliasOrIndex) == false; + if (authorizedIndices.contains(dateMathName)) { + if (minus) { + finalIndices.remove(dateMathName); + } else { + if (isIndexVisible(dateMathName, indicesOptions, metaData, true)) { + finalIndices.add(dateMathName); + } + } + } } else { - finalIndices.add(aliasOrIndex); + //MetaData#convertFromWildcards checks if the index exists here and throws IndexNotFoundException if not (based on + // ignore_unavailable). + //Do nothing as if the index is missing but the user is not authorized to it an AuthorizationException will be thrown. + //If the index is missing and the user is authorized to it, core will throw IndexNotFoundException later on. + //There is no problem with deferring this as we are dealing with an explicit name, not with wildcards. + if (minus) { + finalIndices.remove(aliasOrIndex); + } else { + finalIndices.add(aliasOrIndex); + } } } } @@ -258,6 +288,10 @@ public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolv } private static boolean isIndexVisible(String index, IndicesOptions indicesOptions, MetaData metaData) { + return isIndexVisible(index, indicesOptions, metaData, false); + } + + private static boolean isIndexVisible(String index, IndicesOptions indicesOptions, MetaData metaData, boolean dateMathExpression) { if (metaData.hasConcreteIndex(index)) { IndexMetaData indexMetaData = metaData.index(index); if (indexMetaData == null) { @@ -265,10 +299,10 @@ public class DefaultIndicesAndAliasesResolver implements IndicesAndAliasesResolv //complicated to support those options with aliases pointing to multiple indices... return true; } - if (indexMetaData.getState() == IndexMetaData.State.CLOSE && indicesOptions.expandWildcardsClosed()) { + if (indexMetaData.getState() == IndexMetaData.State.CLOSE && (indicesOptions.expandWildcardsClosed() || dateMathExpression)) { return true; } - if (indexMetaData.getState() == IndexMetaData.State.OPEN && indicesOptions.expandWildcardsOpen()) { + if (indexMetaData.getState() == IndexMetaData.State.OPEN && (indicesOptions.expandWildcardsOpen() || dateMathExpression)) { return true; } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/DateMathExpressionIntegTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/DateMathExpressionIntegTests.java new file mode 100644 index 00000000000..5b626b3c2f8 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/DateMathExpressionIntegTests.java @@ -0,0 +1,109 @@ +/* + * 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.integration; + +import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.get.MultiGetResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.MultiSearchResponse; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.update.UpdateResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.shield.authc.support.Hasher; +import org.elasticsearch.shield.authc.support.SecuredString; +import org.elasticsearch.test.ShieldIntegTestCase; + +import java.util.Collections; + +import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class DateMathExpressionIntegTests extends ShieldIntegTestCase { + + protected static final SecuredString USERS_PASSWD = new SecuredString("change_me".toCharArray()); + protected static final String USERS_PASSWD_HASHED = new String(Hasher.BCRYPT.hash(USERS_PASSWD)); + + @Override + protected String configUsers() { + return super.configUsers() + + "user1:" + USERS_PASSWD_HASHED + "\n"; + } + + @Override + protected String configUsersRoles() { + return super.configUsersRoles() + + "role1:user1\n"; + } + + @Override + protected String configRoles() { + return super.configRoles() + + "\nrole1:\n" + + " cluster: [ none ]\n" + + " indices:\n" + + " - names: 'datemath-*'\n" + + " privileges: [ ALL ]\n"; + } + + public void testDateMathExpressionsCanBeAuthorized() throws Exception { + final String expression = ""; + final String expectedIndexName = new IndexNameExpressionResolver(Settings.EMPTY).resolveDateMathExpression(expression); + final boolean refeshOnOperation = randomBoolean(); + Client client = client().filterWithHeader(Collections.singletonMap("Authorization", basicAuthHeaderValue("user1", USERS_PASSWD))); + + if (randomBoolean()) { + CreateIndexResponse response = client.admin().indices().prepareCreate(expression).get(); + assertThat(response.isAcknowledged(), is(true)); + } + IndexResponse response = client.prepareIndex(expression, "type").setSource("foo", "bar").setRefresh(refeshOnOperation).get(); + + assertThat(response.isCreated(), is(true)); + assertThat(response.getIndex(), containsString(expectedIndexName)); + + if (refeshOnOperation == false) { + client.admin().indices().prepareRefresh(expression).get(); + } + SearchResponse searchResponse = client.prepareSearch(expression) + .setQuery(QueryBuilders.matchAllQuery()) + .get(); + assertThat(searchResponse.getHits().getTotalHits(), is(1L)); + + MultiSearchResponse multiSearchResponse = client.prepareMultiSearch() + .add(client.prepareSearch(expression).setQuery(QueryBuilders.matchAllQuery()).request()) + .get(); + assertThat(multiSearchResponse.getResponses()[0].getResponse().getHits().getTotalHits(), is(1L)); + + UpdateResponse updateResponse = client.prepareUpdate(expression, "type", response.getId()) + .setDoc("new", "field") + .setRefresh(refeshOnOperation) + .get(); + assertThat(updateResponse.isCreated(), is(false)); + + if (refeshOnOperation == false) { + client.admin().indices().prepareRefresh(expression).get(); + } + GetResponse getResponse = client.prepareGet(expression, "type", response.getId()).setFetchSource(true).get(); + assertThat(getResponse.isExists(), is(true)); + assertThat(getResponse.getSourceAsMap().get("foo").toString(), is("bar")); + assertThat(getResponse.getSourceAsMap().get("new").toString(), is("field")); + + // multi get doesn't support expressions - this is probably a bug + MultiGetResponse multiGetResponse = client.prepareMultiGet() + .add(expression, "type", response.getId()) + .get(); + assertThat(multiGetResponse.getResponses()[0].getFailure().getMessage(), is("no such index")); + + DeleteIndexResponse deleteIndexResponse = client.admin().indices().prepareDelete(expression).get(); + assertThat(deleteIndexResponse.isAcknowledged(), is(true)); + } + +} diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java index d3a69df1381..69b07d08634 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java @@ -45,6 +45,7 @@ import org.elasticsearch.action.termvectors.TermVectorsAction; import org.elasticsearch.action.termvectors.TermVectorsRequest; import org.elasticsearch.action.update.UpdateAction; import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.AliasMetaData; @@ -82,6 +83,8 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.AdditionalAnswers.returnsFirstArg; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -105,8 +108,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase { threadPool = mock(ThreadPool.class); when(threadPool.getThreadContext()).thenReturn(threadContext); + IndexNameExpressionResolver nameExpressionResolver = mock(IndexNameExpressionResolver.class); + when(nameExpressionResolver.resolveDateMathExpression(any(String.class))).thenAnswer(returnsFirstArg()); internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, - auditTrail, new DefaultAuthenticationFailureHandler(), threadPool); + auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, nameExpressionResolver); } @After @@ -349,8 +354,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = new IndicesExistsRequest("b"); ClusterState state = mock(ClusterState.class); AnonymousUser.initialize(Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "a_all").build()); + IndexNameExpressionResolver nameExpressionResolver = mock(IndexNameExpressionResolver.class); + when(nameExpressionResolver.resolveDateMathExpression(any(String.class))).thenAnswer(returnsFirstArg()); internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail, - new DefaultAuthenticationFailureHandler(), threadPool); + new DefaultAuthenticationFailureHandler(), threadPool, nameExpressionResolver); when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build()); when(clusterService.state()).thenReturn(state); @@ -377,9 +384,11 @@ public class InternalAuthorizationServiceTests extends ESTestCase { .put(InternalAuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false) .build()); User anonymousUser = AnonymousUser.INSTANCE; + IndexNameExpressionResolver nameExpressionResolver = mock(IndexNameExpressionResolver.class); + when(nameExpressionResolver.resolveDateMathExpression(any(String.class))).thenAnswer(returnsFirstArg()); internalAuthorizationService = new InternalAuthorizationService( Settings.builder().put(InternalAuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false).build(), - rolesStore, clusterService, auditTrail, new DefaultAuthenticationFailureHandler(), threadPool); + rolesStore, clusterService, auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, nameExpressionResolver); when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build()); when(clusterService.state()).thenReturn(state); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java index c3c3e40e8cd..62680cce34f 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/indicesresolver/DefaultIndicesResolverTests.java @@ -20,6 +20,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.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.AliasAction; @@ -45,6 +46,7 @@ import org.junit.Before; import java.util.Set; +import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; @@ -61,6 +63,7 @@ public class DefaultIndicesResolverTests extends ESTestCase { private RolesStore rolesStore; private MetaData metaData; private DefaultIndicesAndAliasesResolver defaultIndicesResolver; + private IndexNameExpressionResolver indexNameExpressionResolver; @Before public void setup() { @@ -70,6 +73,7 @@ public class DefaultIndicesResolverTests extends ESTestCase { .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomIntBetween(0, 1)) .build(); + indexNameExpressionResolver = new IndexNameExpressionResolver(Settings.EMPTY); MetaData.Builder mdBuilder = MetaData.builder() .put(indexBuilder("foo").putAlias(AliasMetaData.builder("foofoobar")).settings(settings)) .put(indexBuilder("foobar").putAlias(AliasMetaData.builder("foofoobar")).settings(settings)) @@ -81,6 +85,7 @@ public class DefaultIndicesResolverTests extends ESTestCase { .put(indexBuilder("bar").settings(settings)) .put(indexBuilder("bar-closed").state(IndexMetaData.State.CLOSE).settings(settings)) .put(indexBuilder("bar2").settings(settings)) + .put(indexBuilder(indexNameExpressionResolver.resolveDateMathExpression("")).settings(settings)) .put(indexBuilder(ShieldTemplateService.SECURITY_INDEX_NAME).settings(settings)); metaData = mdBuilder.build(); @@ -97,8 +102,8 @@ public class DefaultIndicesResolverTests extends ESTestCase { when(state.metaData()).thenReturn(metaData); InternalAuthorizationService authzService = new InternalAuthorizationService(settings, rolesStore, clusterService, - mock(AuditTrail.class), new DefaultAuthenticationFailureHandler(), mock(ThreadPool.class)); - defaultIndicesResolver = new DefaultIndicesAndAliasesResolver(authzService); + mock(AuditTrail.class), new DefaultAuthenticationFailureHandler(), mock(ThreadPool.class), indexNameExpressionResolver); + defaultIndicesResolver = new DefaultIndicesAndAliasesResolver(authzService, indexNameExpressionResolver); } public void testResolveEmptyIndicesExpandWilcardsOpenAndClosed() { @@ -840,6 +845,82 @@ public class DefaultIndicesResolverTests extends ESTestCase { assertThat(indices, not(hasItem(ShieldTemplateService.SECURITY_INDEX_NAME))); } + public void testResolvingDateExpression() { + // the user isn't authorized so resolution should fail + SearchRequest request = new SearchRequest(""); + if (randomBoolean()) { + request.indicesOptions(IndicesOptions.strictSingleIndexNoExpandForbidClosed()); + } + try { + defaultIndicesResolver.resolve(user, SearchAction.NAME, request, metaData); + fail("user is not authorized to see this index"); + } catch (IndexNotFoundException e) { + assertThat(e.getMessage(), is("no such index")); + } + + // make the user authorized + String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed", + indexNameExpressionResolver.resolveDateMathExpression("")}; + when(rolesStore.role("role")).thenReturn(Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build()); + + Set indices = defaultIndicesResolver.resolve(user, SearchAction.NAME, request, metaData); + assertThat(indices.size(), equalTo(1)); + assertThat(request.indices()[0], equalTo(indexNameExpressionResolver.resolveDateMathExpression(""))); + } + + public void testMissingDateExpression() { + SearchRequest request = new SearchRequest(""); + try { + defaultIndicesResolver.resolve(user, SearchAction.NAME, request, metaData); + fail("index should not exist"); + } catch (IndexNotFoundException e) { + assertThat(e.getMessage(), is("no such index")); + } + } + + public void testAliasDateMathExpressionNotSupported() { + // make the user authorized + String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed", + indexNameExpressionResolver.resolveDateMathExpression("")}; + when(rolesStore.role("role")).thenReturn(Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build()); + GetAliasesRequest request = new GetAliasesRequest("").indices("foo", "foofoo"); + Set indices = defaultIndicesResolver.resolve(user, GetAliasesAction.NAME, request, metaData); + //the union of all indices and aliases gets returned + String[] expectedIndices = new String[]{"", "foo", "foofoo"}; + assertThat(indices.size(), equalTo(expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); + assertThat(request.indices(), arrayContainingInAnyOrder("foo", "foofoo")); + assertThat(request.aliases(), arrayContainingInAnyOrder("")); + } + + public void testCompositeRequestsCanResolveExpressions() { + // make the user authorized + String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed", + indexNameExpressionResolver.resolveDateMathExpression("")}; + when(rolesStore.role("role")).thenReturn(Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build()); + MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); + multiSearchRequest.add(new SearchRequest("")); + multiSearchRequest.add(new SearchRequest("bar")); + Set indices = defaultIndicesResolver.resolve(user, MultiSearchAction.NAME, multiSearchRequest, metaData); + + String resolvedName = indexNameExpressionResolver.resolveDateMathExpression(""); + String[] expectedIndices = new String[]{resolvedName, "bar"}; + assertThat(indices.size(), equalTo(expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); + assertThat(multiSearchRequest.requests().get(0).indices(), arrayContaining(resolvedName)); + assertThat(multiSearchRequest.requests().get(1).indices(), arrayContaining("bar")); + + // multi get doesn't support replacing indices but we should authorize the proper indices + MultiGetRequest multiGetRequest = new MultiGetRequest(); + multiGetRequest.add("", "type", "1"); + multiGetRequest.add("bar", "type", "1"); + indices = defaultIndicesResolver.resolve(user, MultiGetAction.NAME, multiGetRequest, metaData); + assertThat(indices.size(), equalTo(expectedIndices.length)); + assertThat(indices, hasItems(expectedIndices)); + assertThat(multiGetRequest.getItems().get(0).indices(), arrayContaining("")); + assertThat(multiGetRequest.getItems().get(1).indices(), arrayContaining("bar")); + } + // TODO with the removal of DeleteByQuery is there another way to test resolving a write action? private static IndexMetaData.Builder indexBuilder(String index) {