Fix Get Alias API handling of hidden indices with visible aliases (#53147)

This commit changes the Get Aliases API to include hidden indices by
default - this is slightly different from other APIs, but is necessary
to make this API work intuitively.
This commit is contained in:
Gordon Brown 2020-03-09 16:16:29 -06:00 committed by GitHub
parent 2bb4b96a7f
commit 1cb0a4399d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 160 additions and 15 deletions

View File

@ -33,7 +33,7 @@ public class GetAliasesRequest extends MasterNodeReadRequest<GetAliasesRequest>
private String[] indices = Strings.EMPTY_ARRAY; private String[] indices = Strings.EMPTY_ARRAY;
private String[] aliases = Strings.EMPTY_ARRAY; private String[] aliases = Strings.EMPTY_ARRAY;
private IndicesOptions indicesOptions = IndicesOptions.strictExpand(); private IndicesOptions indicesOptions = IndicesOptions.strictExpandHidden();
private String[] originalAliases = Strings.EMPTY_ARRAY; private String[] originalAliases = Strings.EMPTY_ARRAY;
public GetAliasesRequest(String... aliases) { public GetAliasesRequest(String... aliases) {

View File

@ -124,6 +124,9 @@ public class IndicesOptions implements ToXContentFragment {
EnumSet.of(WildcardStates.OPEN, WildcardStates.CLOSED, WildcardStates.HIDDEN)); EnumSet.of(WildcardStates.OPEN, WildcardStates.CLOSED, WildcardStates.HIDDEN));
public static final IndicesOptions STRICT_EXPAND_OPEN_CLOSED = public static final IndicesOptions STRICT_EXPAND_OPEN_CLOSED =
new IndicesOptions(EnumSet.of(Option.ALLOW_NO_INDICES), EnumSet.of(WildcardStates.OPEN, WildcardStates.CLOSED)); new IndicesOptions(EnumSet.of(Option.ALLOW_NO_INDICES), EnumSet.of(WildcardStates.OPEN, WildcardStates.CLOSED));
public static final IndicesOptions STRICT_EXPAND_OPEN_CLOSED_HIDDEN =
new IndicesOptions(EnumSet.of(Option.ALLOW_NO_INDICES),
EnumSet.of(WildcardStates.OPEN, WildcardStates.CLOSED, WildcardStates.HIDDEN));
public static final IndicesOptions STRICT_EXPAND_OPEN_FORBID_CLOSED = public static final IndicesOptions STRICT_EXPAND_OPEN_FORBID_CLOSED =
new IndicesOptions(EnumSet.of(Option.ALLOW_NO_INDICES, Option.FORBID_CLOSED_INDICES), EnumSet.of(WildcardStates.OPEN)); new IndicesOptions(EnumSet.of(Option.ALLOW_NO_INDICES, Option.FORBID_CLOSED_INDICES), EnumSet.of(WildcardStates.OPEN));
public static final IndicesOptions STRICT_EXPAND_OPEN_HIDDEN_FORBID_CLOSED = public static final IndicesOptions STRICT_EXPAND_OPEN_HIDDEN_FORBID_CLOSED =
@ -412,6 +415,14 @@ public class IndicesOptions implements ToXContentFragment {
return STRICT_EXPAND_OPEN_CLOSED; return STRICT_EXPAND_OPEN_CLOSED;
} }
/**
* @return indices option that requires every specified index to exist, expands wildcards to both open and closed indices, includes
* hidden indices, and allows that no indices are resolved from wildcard expressions (not returning an error).
*/
public static IndicesOptions strictExpandHidden() {
return STRICT_EXPAND_OPEN_CLOSED_HIDDEN;
}
/** /**
* @return indices option that requires each specified index or alias to exist, doesn't expand wildcards and * @return indices option that requires each specified index or alias to exist, doesn't expand wildcards and
* throws error if any of the aliases resolves to multiple indices * throws error if any of the aliases resolves to multiple indices

View File

@ -19,6 +19,9 @@
package org.elasticsearch.index; package org.elasticsearch.index;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
@ -36,7 +39,10 @@ import java.util.Map;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
public class HiddenIndexIT extends ESIntegTestCase { public class HiddenIndexIT extends ESIntegTestCase {
@ -135,4 +141,63 @@ public class HiddenIndexIT extends ESIntegTestCase {
GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings("my_hidden_pattern1").get(); GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings("my_hidden_pattern1").get();
assertThat(getSettingsResponse.getSetting("my_hidden_pattern1", "index.hidden"), is("true")); assertThat(getSettingsResponse.getSetting("my_hidden_pattern1", "index.hidden"), is("true"));
} }
public void testAliasesForHiddenIndices() {
final String hiddenIndex = "hidden-index";
final String visibleAlias = "alias-visible";
final String hiddenAlias = "alias-hidden";
final String dotHiddenAlias = ".alias-hidden";
assertAcked(client().admin().indices().prepareCreate(hiddenIndex)
.setSettings(Settings.builder().put("index.hidden", true).build())
.get());
assertAcked(admin().indices().prepareAliases()
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index(hiddenIndex).alias(visibleAlias)));
// The index should be returned here when queried by name or by wildcard because the alias is visible
final GetAliasesRequestBuilder req = client().admin().indices().prepareGetAliases(visibleAlias);
GetAliasesResponse response = req.get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), equalTo(visibleAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), nullValue());
response = client().admin().indices().prepareGetAliases("alias*").get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), equalTo(visibleAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), nullValue());
// Now try with a hidden alias
assertAcked(admin().indices().prepareAliases()
.addAliasAction(IndicesAliasesRequest.AliasActions.remove().index(hiddenIndex).alias(visibleAlias))
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index(hiddenIndex).alias(hiddenAlias).isHidden(true)));
// Querying by name directly should get the right result
response = client().admin().indices().prepareGetAliases(hiddenAlias).get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), equalTo(hiddenAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), equalTo(true));
// querying by wildcard should get the right result because the indices options include hidden by default
response = client().admin().indices().prepareGetAliases("alias*").get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), equalTo(hiddenAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), equalTo(true));
// But we should get no results if we specify indices options that don't include hidden
response = client().admin().indices().prepareGetAliases("alias*")
.setIndicesOptions(IndicesOptions.strictExpandOpen()).get();
assertThat(response.getAliases().get(hiddenIndex), nullValue());
// Now try with a hidden alias that starts with a dot
assertAcked(admin().indices().prepareAliases()
.addAliasAction(IndicesAliasesRequest.AliasActions.remove().index(hiddenIndex).alias(hiddenAlias))
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index(hiddenIndex).alias(dotHiddenAlias).isHidden(true)));
// Check that querying by dot-prefixed pattern returns the alias
response = client().admin().indices().prepareGetAliases(".alias*").get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), equalTo(dotHiddenAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), equalTo(true));
}
} }

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.security.authz;
import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequestBuilder; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
@ -14,9 +15,11 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException; import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException;
import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.test.SecurityIntegTestCase;
import org.hamcrest.Matchers;
import org.junit.Before; import org.junit.Before;
import java.util.Collections; import java.util.Collections;
@ -28,6 +31,8 @@ import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswo
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
public class IndexAliasesTests extends SecurityIntegTestCase { public class IndexAliasesTests extends SecurityIntegTestCase {
@ -590,6 +595,61 @@ public class IndexAliasesTests extends SecurityIntegTestCase {
assertAliases(client().admin().indices().prepareGetAliases().setAliases("*"), "bogus_index_1", "bogus_alias_1", "bogus_alias_2"); assertAliases(client().admin().indices().prepareGetAliases().setAliases("*"), "bogus_index_1", "bogus_alias_1", "bogus_alias_2");
} }
public void testAliasesForHiddenIndices() {
final String hiddenIndex = "test_hidden";
final String visibleAlias = "alias_visible";
final String hiddenAlias = "alias_hidden";
final Map<String, String> createHeaders = Collections.singletonMap(
BASIC_AUTH_HEADER, basicAuthHeaderValue("all_on_test", new SecureString("test123".toCharArray())));
final Client createClient = client(createHeaders);
final Map<String, String> aliasHeaders = Collections.singletonMap(
BASIC_AUTH_HEADER, basicAuthHeaderValue("aliases_only", new SecureString("test123".toCharArray())));
final Client aliasesClient = client(aliasHeaders);
assertAcked(createClient.admin().indices().prepareCreate(hiddenIndex)
.setSettings(Settings.builder().put("index.hidden", true).build())
.get());
assertAcked(aliasesClient.admin().indices().prepareAliases()
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index(hiddenIndex).alias(visibleAlias)));
// The index should be returned here when queried by name or by wildcard because the alias is visible
final GetAliasesRequestBuilder req = aliasesClient.admin().indices().prepareGetAliases(visibleAlias);
GetAliasesResponse response = req.get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), Matchers.equalTo(visibleAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), nullValue());
response = client().admin().indices().prepareGetAliases("alias*").get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), Matchers.equalTo(visibleAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), nullValue());
// Now try with a hidden alias
assertAcked(aliasesClient.admin().indices().prepareAliases()
.addAliasAction(IndicesAliasesRequest.AliasActions.remove().index(hiddenIndex).alias(visibleAlias))
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index(hiddenIndex).alias(hiddenAlias).isHidden(true)));
// Querying by name directly should get the right result
response = aliasesClient.admin().indices().prepareGetAliases(hiddenAlias).get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), Matchers.equalTo(hiddenAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), Matchers.equalTo(true));
// querying by wildcard should get the right result because the indices options include hidden by default
response = aliasesClient.admin().indices().prepareGetAliases("alias*").get();
assertThat(response.getAliases().get(hiddenIndex), hasSize(1));
assertThat(response.getAliases().get(hiddenIndex).get(0).alias(), Matchers.equalTo(hiddenAlias));
assertThat(response.getAliases().get(hiddenIndex).get(0).isHidden(), Matchers.equalTo(true));
// But we should get no results if we specify indices options that don't include hidden
response = aliasesClient.admin().indices().prepareGetAliases("alias*")
.setIndicesOptions(IndicesOptions.strictExpandOpen()).get();
assertThat(response.getAliases().get(hiddenIndex), nullValue());
}
private static Client client(final Map<String, String> headers) { private static Client client(final Map<String, String> headers) {
// it should not matter what client we send the request to, but let's pin all requests to a specific node // it should not matter what client we send the request to, but let's pin all requests to a specific node
final Client client; final Client client;

View File

@ -985,11 +985,12 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
request.aliases("alias1"); request.aliases("alias1");
final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME);
List<String> indices = resolveIndices(request, authorizedIndices).getLocal(); List<String> indices = resolveIndices(request, authorizedIndices).getLocal();
//the union of all resolved indices and aliases gets returned //the union of all resolved indices and aliases gets returned, including hidden indices as Get Aliases includes hidden by default
String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "alias1"}; String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "alias1",
assertThat(indices.size(), equalTo(expectedIndices.length)); "hidden-open", "hidden-closed", ".hidden-open", ".hidden-closed"};
assertThat(indices, hasItems(expectedIndices)); assertSameValues(indices, expectedIndices);
String[] replacedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed"}; String[] replacedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "hidden-open",
"hidden-closed", ".hidden-open", ".hidden-closed"};
//_all gets replaced with all indices that user is authorized for //_all gets replaced with all indices that user is authorized for
assertThat(request.indices(), arrayContainingInAnyOrder(replacedIndices)); assertThat(request.indices(), arrayContainingInAnyOrder(replacedIndices));
assertThat(request.aliases(), arrayContainingInAnyOrder("alias1")); assertThat(request.aliases(), arrayContainingInAnyOrder("alias1"));
@ -1065,8 +1066,9 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
} }
final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME);
List<String> indices = resolveIndices(request, authorizedIndices).getLocal(); List<String> indices = resolveIndices(request, authorizedIndices).getLocal();
//the union of all resolved indices and aliases gets returned //the union of all resolved indices and aliases gets returned, including hidden indices as Get Aliases includes hidden by default
String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed"}; String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "hidden-open",
"hidden-closed", ".hidden-open", ".hidden-closed"};
assertSameValues(indices, expectedIndices); assertSameValues(indices, expectedIndices);
//_all gets replaced with all indices that user is authorized for //_all gets replaced with all indices that user is authorized for
assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices)); assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices));
@ -1080,11 +1082,14 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
} }
final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME);
List<String> indices = resolveIndices(request, authorizedIndices).getLocal(); List<String> indices = resolveIndices(request, authorizedIndices).getLocal();
//the union of all resolved indices and aliases gets returned //the union of all resolved indices and aliases gets returned, including hidden indices as Get Aliases includes hidden by default
String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "explicit"}; String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "explicit",
"hidden-open", "hidden-closed", ".hidden-open", ".hidden-closed"};
logger.info("indices: {}", indices);
assertSameValues(indices, expectedIndices); assertSameValues(indices, expectedIndices);
//_all gets replaced with all indices that user is authorized for //_all gets replaced with all indices that user is authorized for
assertThat(request.indices(), arrayContainingInAnyOrder("bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed")); assertThat(request.indices(), arrayContainingInAnyOrder("bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed",
"hidden-open", "hidden-closed", ".hidden-open", ".hidden-closed"));
assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar", "foobarfoo", "explicit")); assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar", "foobarfoo", "explicit"));
} }
@ -1095,8 +1100,9 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
} }
final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME); final List<String> authorizedIndices = buildAuthorizedIndices(user, GetAliasesAction.NAME);
List<String> indices = resolveIndices(request, authorizedIndices).getLocal(); List<String> indices = resolveIndices(request, authorizedIndices).getLocal();
//the union of all resolved indices and aliases gets returned //the union of all resolved indices and aliases gets returned, including hidden indices as Get Aliases includes hidden by default
String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed"}; String[] expectedIndices = new String[]{"bar", "bar-closed", "foofoobar", "foobarfoo", "foofoo", "foofoo-closed", "hidden-open",
"hidden-closed", ".hidden-open", ".hidden-closed"};
assertSameValues(indices, expectedIndices); assertSameValues(indices, expectedIndices);
//_all gets replaced with all indices that user is authorized for //_all gets replaced with all indices that user is authorized for
assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices)); assertThat(request.indices(), arrayContainingInAnyOrder(expectedIndices));
@ -1136,10 +1142,13 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
//union of all resolved indices and aliases gets returned, based on what user is authorized for //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 //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) //the same and resolve those aliases to their corresponding concrete indices (which we let core do)
String[] expectedIndices = new String[]{"bar", "bar-closed", "foobarfoo", "foofoo", "foofoo-closed", "foofoobar"}; //also includes hidden indices as Get Aliases includes hidden by default
String[] expectedIndices = new String[]{"bar", "bar-closed", "foobarfoo", "foofoo", "foofoo-closed", "foofoobar", "hidden-open",
"hidden-closed", ".hidden-open", ".hidden-closed"};
assertSameValues(indices, expectedIndices); assertSameValues(indices, expectedIndices);
//alias foofoobar on both sides, that's fine, es core would do the same, same as above //alias foofoobar on both sides, that's fine, es core would do the same, same as above
assertThat(request.indices(), arrayContainingInAnyOrder("bar", "bar-closed", "foobarfoo", "foofoo", "foofoo-closed", "foofoobar")); assertThat(request.indices(), arrayContainingInAnyOrder("bar", "bar-closed", "foobarfoo", "foofoo", "foofoo-closed", "foofoobar",
"hidden-open", "hidden-closed", ".hidden-open", ".hidden-closed"));
assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar")); assertThat(request.aliases(), arrayContainingInAnyOrder("foofoobar"));
} }