Skip to content

Commit 75665ab

Browse files
albertzaharovitskcm
authored andcommitted
Empty GetAliases authorization fix (#34444)
This fixes a bug about aliases authorization. That is, a user might see aliases which he is not authorized to see. This manifests when the user is not authorized to see any aliases and the `GetAlias` request is empty which normally is a marking that all aliases are requested. In this case, no aliases should be returned, but due to this bug, all aliases will have been returned.
1 parent 1fcfb84 commit 75665ab

File tree

6 files changed

+57
-53
lines changed

6 files changed

+57
-53
lines changed

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

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -249,53 +249,45 @@ public SortedMap<String, AliasOrIndex> getAliasAndIndexLookup() {
249249
}
250250

251251
/**
252-
* Finds the specific index aliases that point to the specified concrete indices or match partially with the indices via wildcards.
252+
* Finds the specific index aliases that point to the requested concrete indices directly
253+
* or that match with the indices via wildcards.
253254
*
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
255+
* @param concreteIndices The concrete indices that the aliases must point to in order to be returned.
256+
* @return A map of index name to the list of aliases metadata. If a concrete index does not have matching
257+
* aliases then the result will <b>not</b> include the index's key.
257258
*/
258-
public ImmutableOpenMap<String, List<AliasMetaData>> findAllAliases(String[] concreteIndices) {
259-
return findAliases(Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY, concreteIndices);
259+
public ImmutableOpenMap<String, List<AliasMetaData>> findAllAliases(final String[] concreteIndices) {
260+
return findAliases(Strings.EMPTY_ARRAY, concreteIndices);
260261
}
261262

262263
/**
263-
* Finds the specific index aliases that match with the specified aliases directly or partially via wildcards and
264-
* that point to the specified concrete indices or match partially with the indices via wildcards.
264+
* Finds the specific index aliases that match with the specified aliases directly or partially via wildcards, and
265+
* that point to the specified concrete indices (directly or matching indices via wildcards).
265266
*
266267
* @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
268+
* @param concreteIndices The concrete indices that the aliases must point to in order to be returned.
269+
* @return A map of index name to the list of aliases metadata. If a concrete index does not have matching
270+
* aliases then the result will <b>not</b> include the index's key.
270271
*/
271-
public ImmutableOpenMap<String, List<AliasMetaData>> findAliases(final AliasesRequest aliasesRequest, String[] concreteIndices) {
272-
return findAliases(aliasesRequest.getOriginalAliases(), aliasesRequest.aliases(), concreteIndices);
272+
public ImmutableOpenMap<String, List<AliasMetaData>> findAliases(final AliasesRequest aliasesRequest, final String[] concreteIndices) {
273+
return findAliases(aliasesRequest.aliases(), concreteIndices);
273274
}
274275

275276
/**
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.
277+
* Finds the specific index aliases that match with the specified aliases directly or partially via wildcards, and
278+
* that point to the specified concrete indices (directly or matching indices via wildcards).
278279
*
279-
* @param aliases The aliases to look for
280-
* @param originalAliases The original aliases that the user originally requested
281-
* @param concreteIndices The concrete indexes the index aliases must point to order to be returned.
282-
* @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
283-
* present for that index
280+
* @param aliases The aliases to look for. Might contain include or exclude wildcards.
281+
* @param concreteIndices The concrete indices that the aliases must point to in order to be returned
282+
* @return A map of index name to the list of aliases metadata. If a concrete index does not have matching
283+
* aliases then the result will <b>not</b> include the index's key.
284284
*/
285-
private ImmutableOpenMap<String, List<AliasMetaData>> findAliases(String[] originalAliases, String[] aliases,
286-
String[] concreteIndices) {
285+
private ImmutableOpenMap<String, List<AliasMetaData>> findAliases(final String[] aliases, final String[] concreteIndices) {
287286
assert aliases != null;
288-
assert originalAliases != null;
289287
assert concreteIndices != null;
290288
if (concreteIndices.length == 0) {
291289
return ImmutableOpenMap.of();
292290
}
293-
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-
299291
String[] patterns = new String[aliases.length];
300292
boolean[] include = new boolean[aliases.length];
301293
for (int i = 0; i < aliases.length; i++) {
@@ -331,7 +323,6 @@ private ImmutableOpenMap<String, List<AliasMetaData>> findAliases(String[] origi
331323
filteredValues.add(value);
332324
}
333325
}
334-
335326
if (filteredValues.isEmpty() == false) {
336327
// Make the list order deterministic
337328
CollectionUtil.timSort(filteredValues, Comparator.comparing(AliasMetaData::alias));

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.elasticsearch.index.mapper.RoutingFieldMapper;
5252
import org.elasticsearch.index.shard.IndexEventListener;
5353
import org.elasticsearch.indices.IndicesService;
54+
import org.elasticsearch.indices.InvalidAliasNameException;
5455
import org.elasticsearch.test.ESTestCase;
5556
import org.hamcrest.Matchers;
5657
import org.mockito.ArgumentCaptor;
@@ -82,7 +83,7 @@
8283
public class IndexCreationTaskTests extends ESTestCase {
8384

8485
private final IndicesService indicesService = mock(IndicesService.class);
85-
private final AliasValidator aliasValidator = mock(AliasValidator.class);
86+
private final AliasValidator aliasValidator = new AliasValidator(Settings.EMPTY);
8687
private final NamedXContentRegistry xContentRegistry = mock(NamedXContentRegistry.class);
8788
private final CreateIndexClusterStateUpdateRequest request = mock(CreateIndexClusterStateUpdateRequest.class);
8889
private final Logger logger = mock(Logger.class);
@@ -149,6 +150,12 @@ public void testApplyDataFromRequest() throws Exception {
149150
assertThat(getMappingsFromResponse(), Matchers.hasKey("mapping1"));
150151
}
151152

153+
public void testInvalidAliasName() throws Exception {
154+
final String[] invalidAliasNames = new String[] { "-alias1", "+alias2", "_alias3", "a#lias", "al:ias", ".", ".." };
155+
setupRequestAlias(new Alias(randomFrom(invalidAliasNames)));
156+
expectThrows(InvalidAliasNameException.class, this::executeTask);
157+
}
158+
152159
public void testRequestDataHavePriorityOverTemplateData() throws Exception {
153160
final CompressedXContent tplMapping = createMapping("text");
154161
final CompressedXContent reqMapping = createMapping("keyword");

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,21 @@ public void testFindAliases() {
6666
assertThat(aliases.size(), equalTo(0));
6767
}
6868
{
69+
final GetAliasesRequest request;
70+
if (randomBoolean()) {
71+
request = new GetAliasesRequest();
72+
} else {
73+
request = new GetAliasesRequest(randomFrom("alias1", "alias2"));
74+
// replacing with empty aliases behaves as if aliases were unspecified at request building
75+
request.replaceAliases(Strings.EMPTY_ARRAY);
76+
}
6977
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAliases(new GetAliasesRequest(), new String[]{"index"});
7078
assertThat(aliases.size(), equalTo(1));
7179
List<AliasMetaData> aliasMetaDataList = aliases.get("index");
7280
assertThat(aliasMetaDataList.size(), equalTo(2));
7381
assertThat(aliasMetaDataList.get(0).alias(), equalTo("alias1"));
7482
assertThat(aliasMetaDataList.get(1).alias(), equalTo("alias2"));
7583
}
76-
{
77-
GetAliasesRequest getAliasesRequest = new GetAliasesRequest("alias1");
78-
getAliasesRequest.replaceAliases(Strings.EMPTY_ARRAY);
79-
ImmutableOpenMap<String, List<AliasMetaData>> aliases = metaData.findAliases(getAliasesRequest, new String[]{"index"});
80-
assertThat(aliases.size(), equalTo(0));
81-
}
8284
{
8385
ImmutableOpenMap<String, List<AliasMetaData>> aliases =
8486
metaData.findAliases(new GetAliasesRequest("alias*"), new String[]{"index"});

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646

4747
class IndicesAndAliasesResolver {
4848

49-
//`*,-*` what we replace indices with if we need Elasticsearch to return empty responses without throwing exception
50-
private static final String[] NO_INDICES_ARRAY = new String[] { "*", "-*" };
51-
static final List<String> NO_INDICES_LIST = Arrays.asList(NO_INDICES_ARRAY);
49+
//`*,-*` what we replace indices and aliases with if we need Elasticsearch to return empty responses without throwing exception
50+
static final String[] NO_INDICES_OR_ALIASES_ARRAY = new String[] { "*", "-*" };
51+
static final List<String> NO_INDICES_OR_ALIASES_LIST = Arrays.asList(NO_INDICES_OR_ALIASES_ARRAY);
5252

5353
private final IndexNameExpressionResolver nameExpressionResolver;
5454
private final RemoteClusterResolver remoteClusterResolver;
@@ -165,7 +165,7 @@ ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData
165165
//this is how we tell es core to return an empty response, we can let the request through being sure
166166
//that the '-*' wildcard expression will be resolved to no indices. We can't let empty indices through
167167
//as that would be resolved to _all by es core.
168-
replaceable.indices(NO_INDICES_ARRAY);
168+
replaceable.indices(NO_INDICES_OR_ALIASES_ARRAY);
169169
indicesReplacedWithNoIndices = true;
170170
resolvedIndicesBuilder.addLocal(NO_INDEX_PLACEHOLDER);
171171
} else {
@@ -176,8 +176,6 @@ ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData
176176
}
177177
} else {
178178
if (containsWildcards(indicesRequest)) {
179-
//an alias can still contain '*' in its name as of 5.0. Such aliases cannot be referred to when using
180-
//the security plugin, otherwise the following exception gets thrown
181179
throw new IllegalStateException("There are no external requests known to support wildcards that don't support replacing " +
182180
"their indices");
183181
}
@@ -198,8 +196,6 @@ ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData
198196
if (aliasesRequest.expandAliasesWildcards()) {
199197
List<String> aliases = replaceWildcardsWithAuthorizedAliases(aliasesRequest.aliases(),
200198
loadAuthorizedAliases(authorizedIndices.get(), metaData));
201-
//it may be that we replace aliases with an empty array, in case there are no authorized aliases for the action.
202-
//MetaData#findAliases will return nothing when some alias was originally requested, which was replaced with empty.
203199
aliasesRequest.replaceAliases(aliases.toArray(new String[aliases.size()]));
204200
}
205201
if (indicesReplacedWithNoIndices) {
@@ -213,6 +209,13 @@ ResolvedIndices resolveIndicesAndAliases(IndicesRequest indicesRequest, MetaData
213209
} else {
214210
resolvedIndicesBuilder.addLocal(aliasesRequest.aliases());
215211
}
212+
// if no aliases are authorized, then fill in an expression that
213+
// MetaData#findAliases evaluates to the empty alias list. You cannot put
214+
// "nothing" (the empty list) explicitly because this is resolved by es core to
215+
// _all
216+
if (aliasesRequest.aliases().length == 0) {
217+
aliasesRequest.replaceAliases(NO_INDICES_OR_ALIASES_ARRAY);
218+
}
216219
}
217220
return resolvedIndicesBuilder.build();
218221
}

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ public void testNonXPackUserCannotExecuteOperationAgainstSecurityIndex() {
818818
final SearchRequest searchRequest = new SearchRequest("_all");
819819
authorize(authentication, SearchAction.NAME, searchRequest);
820820
assertEquals(2, searchRequest.indices().length);
821-
assertEquals(IndicesAndAliasesResolver.NO_INDICES_LIST, Arrays.asList(searchRequest.indices()));
821+
assertEquals(IndicesAndAliasesResolver.NO_INDICES_OR_ALIASES_LIST, Arrays.asList(searchRequest.indices()));
822822
}
823823

824824
public void testGrantedNonXPackUserCanExecuteMonitoringOperationsAgainstSecurityIndex() {

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -778,11 +778,11 @@ public void testResolveAllAliasesWildcardsIndicesAliasesRequestDeleteActions() {
778778
public void testResolveAliasesWildcardsIndicesAliasesRequestDeleteActionsNoAuthorizedIndices() {
779779
IndicesAliasesRequest request = new IndicesAliasesRequest();
780780
request.addAliasAction(AliasActions.remove().index("foo*").alias("foo*"));
781-
//no authorized aliases match bar*, hence aliases are replaced with empty string for that action
781+
//no authorized aliases match bar*, hence aliases are replaced with no-aliases-expression for that action
782782
request.addAliasAction(AliasActions.remove().index("*bar").alias("bar*"));
783783
resolveIndices(request, buildAuthorizedIndices(user, IndicesAliasesAction.NAME));
784784
assertThat(request.getAliasActions().get(0).aliases(), arrayContainingInAnyOrder("foofoobar", "foobarfoo"));
785-
assertThat(request.getAliasActions().get(1).aliases().length, equalTo(0));
785+
assertThat(request.getAliasActions().get(1).aliases(), arrayContaining(IndicesAndAliasesResolver.NO_INDICES_OR_ALIASES_ARRAY));
786786
}
787787

788788
public void testResolveWildcardsIndicesAliasesRequestAddAndDeleteActions() {
@@ -1084,11 +1084,11 @@ public void testResolveAliasesWildcardsGetAliasesRequest() {
10841084

10851085
public void testResolveAliasesWildcardsGetAliasesRequestNoAuthorizedIndices() {
10861086
GetAliasesRequest request = new GetAliasesRequest();
1087-
//no authorized aliases match bar*, hence aliases are replaced with empty array
1087+
//no authorized aliases match bar*, hence aliases are replaced with the no-aliases-expression
10881088
request.aliases("bar*");
10891089
request.indices("*bar");
10901090
resolveIndices(request, buildAuthorizedIndices(user, GetAliasesAction.NAME));
1091-
assertThat(request.aliases().length, equalTo(0));
1091+
assertThat(request.aliases(), arrayContaining(IndicesAndAliasesResolver.NO_INDICES_OR_ALIASES_ARRAY));
10921092
}
10931093

10941094
public void testResolveAliasesExclusionWildcardsGetAliasesRequest() {
@@ -1112,10 +1112,11 @@ public void testResolveAliasesAllGetAliasesRequestNoAuthorizedIndices() {
11121112
request.aliases("_all");
11131113
}
11141114
request.indices("non_existing");
1115-
//current user is not authorized for any index, foo* resolves to no indices, aliases are replaced with empty array
1115+
//current user is not authorized for any index, aliases are replaced with the no-aliases-expression
11161116
ResolvedIndices resolvedIndices = resolveIndices(request, buildAuthorizedIndices(userNoIndices, GetAliasesAction.NAME));
11171117
assertThat(resolvedIndices.getLocal(), contains("non_existing"));
1118-
assertThat(request.aliases().length, equalTo(0));
1118+
assertThat(Arrays.asList(request.indices()), contains("non_existing"));
1119+
assertThat(request.aliases(), arrayContaining(IndicesAndAliasesResolver.NO_INDICES_OR_ALIASES_ARRAY));
11191120
}
11201121

11211122
/**
@@ -1372,7 +1373,7 @@ private static void assertNoIndices(IndicesRequest.Replaceable request, Resolved
13721373
final List<String> localIndices = resolvedIndices.getLocal();
13731374
assertEquals(1, localIndices.size());
13741375
assertEquals(IndicesAndAliasesResolverField.NO_INDEX_PLACEHOLDER, localIndices.iterator().next());
1375-
assertEquals(IndicesAndAliasesResolver.NO_INDICES_LIST, Arrays.asList(request.indices()));
1376+
assertEquals(IndicesAndAliasesResolver.NO_INDICES_OR_ALIASES_LIST, Arrays.asList(request.indices()));
13761377
assertEquals(0, resolvedIndices.getRemote().size());
13771378
}
13781379

0 commit comments

Comments
 (0)