Skip to content

Commit 2d69cfb

Browse files
richard-dennehyKubik42
authored andcommitted
resolve indices for prefixed _all expressions (elastic#137330)
Follow up to elastic#135425 As identified, expressions such as `*:_all` currently don't resolve correctly (it appears that it currently tries to authorize the user against the literal index "_all"). This PR fixes the resolution of prefixed `_all` expressions. Relates: ES-13213
1 parent 11b4c3a commit 2d69cfb

File tree

2 files changed

+123
-7
lines changed

2 files changed

+123
-7
lines changed

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,12 @@ ResolvedIndices resolveIndicesAndAliases(
340340
String allIndicesPatternSelector = null;
341341
if (indicesRequest.indices() != null && indicesRequest.indices().length > 0) {
342342
// Always parse selectors, but do so lazily so that we don't spend a lot of time splitting strings each resolution
343-
isAllIndices = IndexNameExpressionResolver.isAllIndices(
344-
indicesList(indicesRequest.indices()),
345-
(expr) -> IndexNameExpressionResolver.splitSelectorExpression(expr).v1()
346-
);
343+
isAllIndices = IndexNameExpressionResolver.isAllIndices(indicesList(indicesRequest.indices()), (expr) -> {
344+
var unprefixed = crossProjectModeDecider.resolvesCrossProject(replaceable)
345+
? RemoteClusterAware.splitIndexName(expr)[1]
346+
: expr;
347+
return IndexNameExpressionResolver.splitSelectorExpression(unprefixed).v1();
348+
});
347349
if (isAllIndices) {
348350
// This parses the single all-indices expression for a second time in this conditional branch, but this is better than
349351
// parsing a potentially big list of indices on every request.
@@ -394,12 +396,13 @@ ResolvedIndices resolveIndicesAndAliases(
394396
replaceable.getProjectRouting(),
395397
authorizedProjects
396398
);
397-
remoteIndices = CrossProjectIndexExpressionsRewriter.rewriteIndexExpression(
399+
final var rewritten = CrossProjectIndexExpressionsRewriter.rewriteIndexExpression(
398400
indexExpression,
399401
resolvedProjects.originProjectAlias(),
400402
resolvedProjects.allProjectAliases()
401-
).remoteExpressions();
402-
if (resolvedProjects.originProject() == null) {
403+
);
404+
remoteIndices = rewritten.remoteExpressions();
405+
if (resolvedProjects.originProject() == null || rewritten.localExpression() == null) {
403406
shouldExcludeLocalResolution = true;
404407
}
405408
}

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

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113

114114
import static org.elasticsearch.action.ResolvedIndexExpression.LocalIndexResolutionResult.CONCRETE_RESOURCE_NOT_VISIBLE;
115115
import static org.elasticsearch.action.ResolvedIndexExpression.LocalIndexResolutionResult.CONCRETE_RESOURCE_UNAUTHORIZED;
116+
import static org.elasticsearch.action.ResolvedIndexExpression.LocalIndexResolutionResult.NONE;
116117
import static org.elasticsearch.action.ResolvedIndexExpression.LocalIndexResolutionResult.SUCCESS;
117118
import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.newInstance;
118119
import static org.elasticsearch.test.ActionListenerUtils.anyActionListener;
@@ -2956,6 +2957,118 @@ public void testCrossProjectSearchSelectorsNotAllowed() {
29562957
assertThat(exception.getMessage(), equalTo("Selectors are not currently supported but was found in the expression [_all::data]"));
29572958
}
29582959

2960+
public void testResolveAllWithWildcardRemotePrefix() {
2961+
when(crossProjectModeDecider.resolvesCrossProject(any(IndicesRequest.Replaceable.class))).thenReturn(true);
2962+
2963+
var request = new SearchRequest().indices("*:_all");
2964+
request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true));
2965+
var resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliases(
2966+
"indices:/" + randomAlphaOfLength(8),
2967+
request,
2968+
projectMetadata,
2969+
buildAuthorizedIndices(user, TransportSearchAction.TYPE.name()),
2970+
new TargetProjects(
2971+
createRandomProjectWithAlias("local"),
2972+
List.of(createRandomProjectWithAlias("P1"), createRandomProjectWithAlias("P2"), createRandomProjectWithAlias("P3"))
2973+
)
2974+
);
2975+
2976+
var expectedIndices = new String[] { "bar", "foobarfoo", "bar-closed", "foofoobar", "foofoo-closed", "foofoo" };
2977+
2978+
assertThat(resolvedIndices.getLocal(), contains(expectedIndices));
2979+
assertThat(resolvedIndices.getRemote(), containsInAnyOrder("P1:_all", "P2:_all", "P3:_all"));
2980+
2981+
final var resolved = request.getResolvedIndexExpressions();
2982+
assertThat(resolved, is(notNullValue()));
2983+
assertThat(
2984+
resolved.expressions(),
2985+
contains(resolvedIndexExpression("*:_all", Set.of(expectedIndices), SUCCESS, Set.of("P1:_all", "P2:_all", "P3:_all")))
2986+
);
2987+
}
2988+
2989+
public void testResolveAllWithLocalPrefix() {
2990+
when(crossProjectModeDecider.resolvesCrossProject(any(IndicesRequest.Replaceable.class))).thenReturn(true);
2991+
2992+
var expression = randomBoolean() ? "local:_all" : "_origin:_all";
2993+
var request = new SearchRequest().indices(expression);
2994+
request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true));
2995+
var resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliases(
2996+
"indices:/" + randomAlphaOfLength(8),
2997+
request,
2998+
projectMetadata,
2999+
buildAuthorizedIndices(user, TransportSearchAction.TYPE.name()),
3000+
new TargetProjects(
3001+
createRandomProjectWithAlias("local"),
3002+
List.of(createRandomProjectWithAlias("P1"), createRandomProjectWithAlias("P2"), createRandomProjectWithAlias("P3"))
3003+
)
3004+
);
3005+
3006+
var expectedIndices = new String[] { "bar", "foobarfoo", "bar-closed", "foofoobar", "foofoo-closed", "foofoo" };
3007+
3008+
assertThat(resolvedIndices.getLocal(), contains(expectedIndices));
3009+
assertThat(resolvedIndices.getRemote(), is(empty()));
3010+
3011+
final var resolved = request.getResolvedIndexExpressions();
3012+
assertThat(resolved, is(notNullValue()));
3013+
assertThat(resolved.expressions(), contains(resolvedIndexExpression(expression, Set.of(expectedIndices), SUCCESS, Set.of())));
3014+
}
3015+
3016+
public void testResolveAllWithRemotePrefix() {
3017+
when(crossProjectModeDecider.resolvesCrossProject(any(IndicesRequest.Replaceable.class))).thenReturn(true);
3018+
3019+
var request = new SearchRequest().indices("P*:_all");
3020+
request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true));
3021+
var resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliases(
3022+
"indices:/" + randomAlphaOfLength(8),
3023+
request,
3024+
projectMetadata,
3025+
buildAuthorizedIndices(user, TransportSearchAction.TYPE.name()),
3026+
new TargetProjects(
3027+
createRandomProjectWithAlias("local"),
3028+
List.of(createRandomProjectWithAlias("P1"), createRandomProjectWithAlias("P2"), createRandomProjectWithAlias("P3"))
3029+
)
3030+
);
3031+
3032+
assertThat(resolvedIndices.getLocal(), is(empty()));
3033+
assertThat(resolvedIndices.getRemote(), contains("P1:_all", "P2:_all", "P3:_all"));
3034+
3035+
final var resolved = request.getResolvedIndexExpressions();
3036+
assertThat(resolved, is(notNullValue()));
3037+
assertThat(
3038+
resolved.expressions(),
3039+
contains(resolvedIndexExpression("P*:_all", Set.of(), NONE, Set.of("P1:_all", "P2:_all", "P3:_all")))
3040+
);
3041+
}
3042+
3043+
public void testResolveIndexWithRemotePrefix() {
3044+
when(crossProjectModeDecider.resolvesCrossProject(any(IndicesRequest.Replaceable.class))).thenReturn(true);
3045+
3046+
var request = new SearchRequest().indices("*:bar");
3047+
request.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), true, true));
3048+
var resolvedIndices = defaultIndicesResolver.resolveIndicesAndAliases(
3049+
"indices:/" + randomAlphaOfLength(8),
3050+
request,
3051+
projectMetadata,
3052+
buildAuthorizedIndices(user, TransportSearchAction.TYPE.name()),
3053+
new TargetProjects(
3054+
createRandomProjectWithAlias("local"),
3055+
List.of(createRandomProjectWithAlias("P1"), createRandomProjectWithAlias("P2"), createRandomProjectWithAlias("P3"))
3056+
)
3057+
);
3058+
3059+
var expectedIndices = new String[] { "bar" };
3060+
3061+
assertThat(resolvedIndices.getLocal(), contains(expectedIndices));
3062+
assertThat(resolvedIndices.getRemote(), containsInAnyOrder("P1:bar", "P2:bar", "P3:bar"));
3063+
3064+
final var resolved = request.getResolvedIndexExpressions();
3065+
assertThat(resolved, is(notNullValue()));
3066+
assertThat(
3067+
resolved.expressions(),
3068+
contains(resolvedIndexExpression("*:bar", Set.of(expectedIndices), SUCCESS, Set.of("P1:bar", "P2:bar", "P3:bar")))
3069+
);
3070+
}
3071+
29593072
private void assertIndicesMatch(IndicesRequest.Replaceable request, String expression, List<String> indices, String[] expectedIndices) {
29603073
assertThat(indices, hasSize(expectedIndices.length));
29613074
assertThat(request.indices().length, equalTo(expectedIndices.length));

0 commit comments

Comments
 (0)