Skip to content

Commit 94a75b1

Browse files
committed
Remove aliases resolution limitations when security is enabled
Resolving wildcards in aliases expression is challenging as we may end up with no aliases to replace the original expression with, but if we replace with an empty array that means _all which is quite the opposite. Now that we support and serialize the original requested aliases, whenever aliases are replaced we will be able to know what was initially requested. MetaData#findAliases can then be updated to not return anything in case it gets empty aliases, but the original aliases were not empty. That means that empty aliases are interpreted as _all only if they were originally requested that way. Relates to elastic#31516
1 parent e46ed73 commit 94a75b1

File tree

12 files changed

+164
-67
lines changed

12 files changed

+164
-67
lines changed

server/src/main/java/org/elasticsearch/action/AliasesRequest.java

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public interface AliasesRequest extends IndicesRequest.Replaceable {
3232
*/
3333
String[] aliases();
3434

35+
String[] getOriginalAliases();
36+
3537
/**
3638
* Replaces current aliases with the provided aliases.
3739
*

server/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequest.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ private static ObjectParser<AliasActions, Void> parser(String name, Supplier<Ali
214214
private final AliasActions.Type type;
215215
private String[] indices;
216216
private String[] aliases = Strings.EMPTY_ARRAY;
217+
private String[] originalAliases = Strings.EMPTY_ARRAY;
217218
private String filter;
218219
private String routing;
219220
private String indexRouting;
@@ -237,6 +238,7 @@ public AliasActions(StreamInput in) throws IOException {
237238
indexRouting = in.readOptionalString();
238239
if (in.getVersion().onOrAfter(Version.V_6_4_0)) {
239240
writeIndex = in.readOptionalBoolean();
241+
originalAliases = in.readStringArray();
240242
}
241243
}
242244

@@ -251,6 +253,7 @@ public void writeTo(StreamOutput out) throws IOException {
251253
out.writeOptionalString(indexRouting);
252254
if (out.getVersion().onOrAfter(Version.V_6_4_0)) {
253255
out.writeOptionalBoolean(writeIndex);
256+
out.writeStringArray(originalAliases);
254257
}
255258
}
256259

@@ -315,6 +318,7 @@ public AliasActions aliases(String... aliases) {
315318
}
316319
}
317320
this.aliases = aliases;
321+
this.originalAliases = aliases;
318322
return this;
319323
}
320324

@@ -329,6 +333,7 @@ public AliasActions alias(String alias) {
329333
throw new IllegalArgumentException("[alias] can't be empty string");
330334
}
331335
this.aliases = new String[] {alias};
336+
this.originalAliases = aliases;
332337
return this;
333338
}
334339

@@ -432,6 +437,11 @@ public void replaceAliases(String... aliases) {
432437
this.aliases = aliases;
433438
}
434439

440+
@Override
441+
public String[] getOriginalAliases() {
442+
return originalAliases;
443+
}
444+
435445
@Override
436446
public boolean expandAliasesWildcards() {
437447
//remove operations support wildcards among aliases, add operations don't
@@ -579,7 +589,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
579589
}, AliasActions.PARSER, new ParseField("actions"));
580590
}
581591

582-
public static IndicesAliasesRequest fromXContent(XContentParser parser) throws IOException {
592+
public static IndicesAliasesRequest fromXContent(XContentParser parser) {
583593
return PARSER.apply(parser, null);
584594
}
585595
}

server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ protected void masterOperation(final IndicesAliasesRequest request, final Cluste
9595
Set<String> aliases = new HashSet<>();
9696
for (AliasActions action : actions) {
9797
String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request.indicesOptions(), action.indices());
98-
Collections.addAll(aliases, action.aliases());
98+
Collections.addAll(aliases, action.getOriginalAliases());
9999
for (String index : concreteIndices) {
100100
switch (action.actionType()) {
101101
case ADD:
@@ -142,7 +142,7 @@ private static String[] concreteAliases(AliasActions action, MetaData metaData,
142142
if (action.expandAliasesWildcards()) {
143143
//for DELETE we expand the aliases
144144
String[] indexAsArray = {concreteIndex};
145-
ImmutableOpenMap<String, List<AliasMetaData>> aliasMetaData = metaData.findAliases(action.aliases(), indexAsArray);
145+
ImmutableOpenMap<String, List<AliasMetaData>> aliasMetaData = metaData.findAliases(action, indexAsArray);
146146
List<String> finalAliases = new ArrayList<>();
147147
for (ObjectCursor<List<AliasMetaData>> curAliases : aliasMetaData.values()) {
148148
for (AliasMetaData aliasMeta: curAliases.value) {

server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected GetAliasesResponse newResponse() {
6363
@Override
6464
protected void masterOperation(GetAliasesRequest request, ClusterState state, ActionListener<GetAliasesResponse> listener) {
6565
String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request);
66-
ImmutableOpenMap<String, List<AliasMetaData>> aliases = state.metaData().findAliases(request.aliases(), concreteIndices);
66+
ImmutableOpenMap<String, List<AliasMetaData>> aliases = state.metaData().findAliases(request, concreteIndices);
6767
listener.onResponse(new GetAliasesResponse(postProcess(request, concreteIndices, aliases)));
6868
}
6969

server/src/main/java/org/elasticsearch/action/admin/indices/get/TransportGetIndexAction.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,14 @@
3232
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
3333
import org.elasticsearch.cluster.metadata.MappingMetaData;
3434
import org.elasticsearch.cluster.service.ClusterService;
35-
import org.elasticsearch.common.Strings;
3635
import org.elasticsearch.common.collect.ImmutableOpenMap;
3736
import org.elasticsearch.common.inject.Inject;
37+
import org.elasticsearch.common.settings.IndexScopedSettings;
3838
import org.elasticsearch.common.settings.Settings;
3939
import org.elasticsearch.common.settings.SettingsFilter;
4040
import org.elasticsearch.indices.IndicesService;
4141
import org.elasticsearch.threadpool.ThreadPool;
4242
import org.elasticsearch.transport.TransportService;
43-
import org.elasticsearch.common.settings.IndexScopedSettings;
4443

4544
import java.io.IOException;
4645
import java.util.List;
@@ -110,7 +109,7 @@ protected void doMasterOperation(final GetIndexRequest request, String[] concret
110109
break;
111110
case ALIASES:
112111
if (!doneAliases) {
113-
aliasesResult = state.metaData().findAliases(Strings.EMPTY_ARRAY, concreteIndices);
112+
aliasesResult = state.metaData().findAllAliases(concreteIndices);
114113
doneAliases = true;
115114
}
116115
break;

server/src/main/java/org/elasticsearch/cluster/metadata/MetaData.java

+35-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
2525
import org.apache.logging.log4j.Logger;
2626
import org.apache.lucene.util.CollectionUtil;
27+
import org.elasticsearch.action.AliasesRequest;
2728
import org.elasticsearch.cluster.ClusterState;
2829
import org.elasticsearch.cluster.ClusterState.FeatureAware;
2930
import org.elasticsearch.cluster.Diff;
@@ -247,22 +248,54 @@ public SortedMap<String, AliasOrIndex> getAliasAndIndexLookup() {
247248
return aliasAndIndexLookup;
248249
}
249250

251+
/**
252+
* Finds the specific index aliases that point to the specified concrete indices or match partially with the indices via wildcards.
253+
*
254+
* @param concreteIndices The concrete indexes the index aliases must point to order to be returned.
255+
* @return a map of index to a list of alias metadata, the list corresponding to a concrete index will be empty if no aliases are
256+
* present for that index
257+
*/
258+
public ImmutableOpenMap<String, List<AliasMetaData>> findAllAliases(String[] concreteIndices) {
259+
return findAliases(Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY, concreteIndices);
260+
}
261+
250262
/**
251263
* Finds the specific index aliases that match with the specified aliases directly or partially via wildcards and
252264
* that point to the specified concrete indices or match partially with the indices via wildcards.
253265
*
254-
* @param aliases The names of the index aliases to find
266+
* @param aliasesRequest The request to find aliases for
267+
* @param concreteIndices The concrete indexes the index aliases must point to order to be returned.
268+
* @return a map of index to a list of alias metadata, the list corresponding to a concrete index will be empty if no aliases are
269+
* present for that index
270+
*/
271+
public ImmutableOpenMap<String, List<AliasMetaData>> findAliases(final AliasesRequest aliasesRequest, String[] concreteIndices) {
272+
return findAliases(aliasesRequest.getOriginalAliases(), aliasesRequest.aliases(), concreteIndices);
273+
}
274+
275+
/**
276+
* Finds the specific index aliases that match with the specified aliases directly or partially via wildcards and
277+
* that point to the specified concrete indices or match partially with the indices via wildcards.
278+
*
279+
* @param aliases The aliases to look for
280+
* @param originalAliases The original aliases that the user originally requested
255281
* @param concreteIndices The concrete indexes the index aliases must point to order to be returned.
256282
* @return a map of index to a list of alias metadata, the list corresponding to a concrete index will be empty if no aliases are
257283
* present for that index
258284
*/
259-
public ImmutableOpenMap<String, List<AliasMetaData>> findAliases(final String[] aliases, String[] concreteIndices) {
285+
private ImmutableOpenMap<String, List<AliasMetaData>> findAliases(String[] originalAliases, String[] aliases,
286+
String[] concreteIndices) {
260287
assert aliases != null;
288+
assert originalAliases != null;
261289
assert concreteIndices != null;
262290
if (concreteIndices.length == 0) {
263291
return ImmutableOpenMap.of();
264292
}
265293

294+
//if aliases were provided but they got replaced with empty aliases, return empty map
295+
if (originalAliases.length > 0 && aliases.length == 0) {
296+
return ImmutableOpenMap.of();
297+
}
298+
266299
boolean matchAllAliases = matchAllAliases(aliases);
267300
ImmutableOpenMap.Builder<String, List<AliasMetaData>> mapBuilder = ImmutableOpenMap.builder();
268301
for (String index : concreteIndices) {

server/src/test/java/org/elasticsearch/cluster/metadata/MetaDataTests.java

+59
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.cluster.metadata;
2121

2222
import org.elasticsearch.Version;
23+
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
2324
import org.elasticsearch.cluster.ClusterModule;
2425
import org.elasticsearch.common.Strings;
2526
import org.elasticsearch.common.UUIDs;
@@ -41,6 +42,7 @@
4142
import java.io.IOException;
4243
import java.util.HashMap;
4344
import java.util.HashSet;
45+
import java.util.List;
4446
import java.util.Map;
4547
import java.util.Set;
4648

@@ -50,6 +52,63 @@
5052

5153
public class MetaDataTests extends ESTestCase {
5254

55+
public void testFindAliases() {
56+
MetaData metaData = MetaData.builder().put(IndexMetaData.builder("index")
57+
.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))
58+
.numberOfShards(1)
59+
.numberOfReplicas(0)
60+
.putAlias(AliasMetaData.builder("alias1").build())
61+
.putAlias(AliasMetaData.builder("alias2").build())).build();
62+
63+
{
64+
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAliases(new GetAliasesRequest(), Strings.EMPTY_ARRAY);
65+
assertThat(aliases.size(), equalTo(0));
66+
}
67+
{
68+
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAliases(new GetAliasesRequest(), new String[]{"index"});
69+
assertThat(aliases.size(), equalTo(1));
70+
List<AliasMetaData> aliasMetaDataList = aliases.get("index");
71+
assertThat(aliasMetaDataList.size(), equalTo(2));
72+
assertThat(aliasMetaDataList.get(0).alias(), equalTo("alias1"));
73+
assertThat(aliasMetaDataList.get(1).alias(), equalTo("alias2"));
74+
}
75+
{
76+
GetAliasesRequest getAliasesRequest = new GetAliasesRequest("alias1");
77+
getAliasesRequest.replaceAliases(Strings.EMPTY_ARRAY);
78+
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAliases(getAliasesRequest, new String[]{"index"});
79+
assertThat(aliases.size(), equalTo(0));
80+
}
81+
{
82+
ImmutableOpenMap<String, List<AliasMetaData>> aliases =
83+
metaData.findAliases(new GetAliasesRequest("alias*"), new String[]{"index"});
84+
assertThat(aliases.size(), equalTo(1));
85+
List<AliasMetaData> aliasMetaDataList = aliases.get("index");
86+
assertThat(aliasMetaDataList.size(), equalTo(2));
87+
assertThat(aliasMetaDataList.get(0).alias(), equalTo("alias1"));
88+
assertThat(aliasMetaDataList.get(1).alias(), equalTo("alias2"));
89+
}
90+
{
91+
ImmutableOpenMap<String, List<AliasMetaData>> aliases =
92+
metaData.findAliases(new GetAliasesRequest("alias1"), new String[]{"index"});
93+
assertThat(aliases.size(), equalTo(1));
94+
List<AliasMetaData> aliasMetaDataList = aliases.get("index");
95+
assertThat(aliasMetaDataList.size(), equalTo(1));
96+
assertThat(aliasMetaDataList.get(0).alias(), equalTo("alias1"));
97+
}
98+
{
99+
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAllAliases(new String[]{"index"});
100+
assertThat(aliases.size(), equalTo(1));
101+
List<AliasMetaData> aliasMetaDataList = aliases.get("index");
102+
assertThat(aliasMetaDataList.size(), equalTo(2));
103+
assertThat(aliasMetaDataList.get(0).alias(), equalTo("alias1"));
104+
assertThat(aliasMetaDataList.get(1).alias(), equalTo("alias2"));
105+
}
106+
{
107+
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAllAliases(Strings.EMPTY_ARRAY);
108+
assertThat(aliases.size(), equalTo(0));
109+
}
110+
}
111+
53112
public void testIndexAndAliasWithSameName() {
54113
IndexMetaData.Builder builder = IndexMetaData.builder("index")
55114
.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))

x-pack/docs/en/security/limitations.asciidoc

-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ with {security} enabled.
1919
Elasticsearch clusters with {security} enabled apply the `/_all` wildcard, and
2020
all other wildcards, to the indices that the current user has privileges for, not
2121
the set of all indices on the cluster.
22-
While creating or retrieving aliases by providing wildcard expressions for alias names, if there are no existing authorized aliases
23-
that match the wildcard expression provided an IndexNotFoundException is returned.
2422

2523
[float]
2624
=== Multi Document APIs

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolver.java

+4-16
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
2121
import org.elasticsearch.cluster.metadata.MetaData;
2222
import org.elasticsearch.cluster.service.ClusterService;
23-
import org.elasticsearch.common.Strings;
2423
import org.elasticsearch.common.collect.ImmutableOpenMap;
2524
import org.elasticsearch.common.regex.Regex;
2625
import org.elasticsearch.common.settings.ClusterSettings;
@@ -200,6 +199,8 @@ ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData
200199
if (aliasesRequest.expandAliasesWildcards()) {
201200
List<String> aliases = replaceWildcardsWithAuthorizedAliases(aliasesRequest.aliases(),
202201
loadAuthorizedAliases(authorizedIndices.get(), metaData));
202+
//it may be that we replace aliases with an empty array, in case there are no authorized aliases for the action.
203+
//MetaData#findAliases will return nothing when some alias was originally requested, which was replaced with empty.
203204
aliasesRequest.replaceAliases(aliases.toArray(new String[aliases.size()]));
204205
}
205206
if (indicesReplacedWithNoIndices) {
@@ -240,8 +241,7 @@ static String getPutMappingIndexOrAlias(PutMappingRequest request, AuthorizedInd
240241
} else {
241242
// the user is not authorized to put mappings for this index, but could have been
242243
// authorized for a write using an alias that triggered a dynamic mapping update
243-
ImmutableOpenMap<String, List<AliasMetaData>> foundAliases =
244-
metaData.findAliases(Strings.EMPTY_ARRAY, new String[] { concreteIndexName });
244+
ImmutableOpenMap<String, List<AliasMetaData>> foundAliases = metaData.findAllAliases(new String[] { concreteIndexName });
245245
List<AliasMetaData> aliasMetaData = foundAliases.get(concreteIndexName);
246246
if (aliasMetaData != null) {
247247
Optional<String> foundAlias = aliasMetaData.stream()
@@ -279,14 +279,12 @@ private List<String> replaceWildcardsWithAuthorizedAliases(String[] aliases, Lis
279279
List<String> finalAliases = new ArrayList<>();
280280

281281
//IndicesAliasesRequest doesn't support empty aliases (validation fails) but GetAliasesRequest does (in which case empty means _all)
282-
boolean matchAllAliases = aliases.length == 0;
283-
if (matchAllAliases) {
282+
if (aliases.length == 0) {
284283
finalAliases.addAll(authorizedAliases);
285284
}
286285

287286
for (String aliasPattern : aliases) {
288287
if (aliasPattern.equals(MetaData.ALL)) {
289-
matchAllAliases = true;
290288
finalAliases.addAll(authorizedAliases);
291289
} else if (Regex.isSimpleMatchPattern(aliasPattern)) {
292290
for (String authorizedAlias : authorizedAliases) {
@@ -298,16 +296,6 @@ private List<String> replaceWildcardsWithAuthorizedAliases(String[] aliases, Lis
298296
finalAliases.add(aliasPattern);
299297
}
300298
}
301-
302-
//Throw exception if the wildcards expansion to authorized aliases resulted in no indices.
303-
//We always need to replace wildcards for security reasons, to make sure that the operation is executed on the aliases that we
304-
//authorized it to execute on. Empty set gets converted to _all by es core though, and unlike with indices, here we don't have
305-
//a special expression to replace empty set with, which gives us the guarantee that nothing will be returned.
306-
//This is because existing aliases can contain all kinds of special characters, they are only validated since 5.1.
307-
if (finalAliases.isEmpty()) {
308-
String indexName = matchAllAliases ? MetaData.ALL : Arrays.toString(aliases);
309-
throw new IndexNotFoundException(indexName);
310-
}
311299
return finalAliases;
312300
}
313301

0 commit comments

Comments
 (0)