Skip to content

Commit d7b3784

Browse files
authored
Project routing should not impact error handling for index resolution (elastic#139177)
Project routing defines the world for which the flat-world index resolution is applied. Therefore it should not impact the error handling which is decided by whether the expression is qualified or unqualified. For example, both `logs` and `logs` + `_alias:*` are successful if logs is found on any project, while `*:logs` with or without project routing requires it to be found on all projects.
1 parent 6b8b471 commit d7b3784

File tree

3 files changed

+189
-58
lines changed

3 files changed

+189
-58
lines changed

server/src/main/java/org/elasticsearch/search/crossproject/CrossProjectIndexExpressionsRewriter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ public static IndexRewriteResult rewriteIndexExpression(
117117
rewrittenExpression = rewriteUnqualifiedExpression(indexExpression, originProjectAlias, allProjectAliases);
118118
logger.debug("Rewrote unqualified expression [{}] to [{}]", indexExpression, rewrittenExpression);
119119
}
120+
// Empty rewritten expressions should have been thrown earlier
121+
assert false == rewrittenExpression.isEmpty() : "rewritten index expression must not be empty";
120122
return rewrittenExpression;
121123
}
122124

@@ -221,5 +223,9 @@ public record IndexRewriteResult(@Nullable String localExpression, Set<String> r
221223
public IndexRewriteResult(String localExpression) {
222224
this(localExpression, Set.of());
223225
}
226+
227+
public boolean isEmpty() {
228+
return localExpression == null && remoteExpressions.isEmpty();
229+
}
224230
}
225231
}

server/src/main/java/org/elasticsearch/search/crossproject/CrossProjectIndexResolutionValidator.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ public static ElasticsearchException validate(
9797
String originalExpression = localResolvedIndices.original();
9898
logger.debug("Checking replaced expression for original expression [{}]", originalExpression);
9999

100-
// Check if this is a qualified resource (project:index pattern) or has project routing
101-
boolean isQualifiedExpression = hasProjectRouting || RemoteClusterAware.isRemoteIndexName(originalExpression);
100+
// Check if this is a qualified resource (project:index pattern)
101+
boolean isQualifiedExpression = RemoteClusterAware.isRemoteIndexName(originalExpression);
102102

103103
Set<String> remoteExpressions = localResolvedIndices.remoteExpressions();
104104
ResolvedIndexExpression.LocalExpressions localExpressions = localResolvedIndices.localExpressions();
@@ -129,11 +129,16 @@ public static ElasticsearchException validate(
129129
originalExpression,
130130
indicesOptions
131131
);
132-
if (localException == null) {
132+
if (localException == null && localExpressions != ResolvedIndexExpression.LocalExpressions.NONE) {
133133
// found locally, continue to next expression
134134
continue;
135135
}
136-
boolean isUnauthorized = localException instanceof ElasticsearchSecurityException;
136+
ElasticsearchSecurityException unauthorizedException = null;
137+
if (localException instanceof ElasticsearchSecurityException securityException) {
138+
unauthorizedException = securityException;
139+
}
140+
assert localExpressions != ResolvedIndexExpression.LocalExpressions.NONE || false == remoteExpressions.isEmpty()
141+
: "both local expression and remote expressions are empty which should have errored earlier at index rewriting time";
137142
boolean foundFlat = false;
138143
// checking if flat expression matched remotely
139144
for (String remoteExpression : remoteExpressions) {
@@ -150,17 +155,26 @@ public static ElasticsearchException validate(
150155
foundFlat = true;
151156
break;
152157
}
153-
if (false == isUnauthorized && exception instanceof ElasticsearchSecurityException) {
154-
isUnauthorized = true;
158+
if (unauthorizedException == null && exception instanceof ElasticsearchSecurityException securityException) {
159+
unauthorizedException = securityException;
155160
}
156161
}
157162
if (foundFlat) {
158163
continue;
159164
}
160-
if (isUnauthorized) {
165+
// Prefer reporting 403 over 404
166+
if (unauthorizedException != null) {
167+
return unauthorizedException;
168+
}
169+
// Prefer reporting 404 on origin over linked projects
170+
if (localException != null) {
171+
assert localException instanceof IndexNotFoundException
172+
: "Expected local exception to be IndexNotFoundException, but found: " + localException;
161173
return localException;
174+
} else {
175+
assert false == remoteExpressions.isEmpty() : "expected remote expressions to be non-empty";
176+
return new IndexNotFoundException(remoteExpressions.iterator().next());
162177
}
163-
return new IndexNotFoundException(originalExpression);
164178
}
165179
}
166180
// if we didn't throw before it means that we can proceed with the request

0 commit comments

Comments
 (0)