Skip to content

Conversation

n1v0lg
Copy link
Contributor

@n1v0lg n1v0lg commented Sep 24, 2025

This PR implements cross-project search index resolution and ports the resolve index API to use it.

Relates: ES-12690

Copy link
Contributor

@slobodanadamovic slobodanadamovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Authorization changes LGTM 👍
(left one question and suggestion which should not require another round of review)

// we will always store them
if (recordResolvedIndexExpressions) {
assert indicesRequest.indices() != null : "indices() cannot be null when resolving non-all-index expressions";
if (crossProjectModeDecider.resolvesCrossProject(replaceable)
Copy link
Contributor Author

@n1v0lg n1v0lg Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this entire block can go into a new class CrossProjectIndicesResolver.

That class could then have the following methods, combining CrossProjectModeDecider, CrossProjectIndexExpressionsRewriter, and CrossProjectIndexResolutionValidator into one coherent class:

class CrossProjectIndicesResolver {
  boolean crossProjectEnabled() { ... }

  boolean resolvesCrossProject(Indices.Replaceable request) { ... }

  ResolvedIndices resolve(Indices.Replaceable request) { ... }

  ElasticsearchException validate(
    IndicesOptions indicesOptions,
    ResolvedIndexExpressions localResolvedExpressions,
    Map<String, ResolvedIndexExpressions> remoteResolvedExpressions
  ) { ... }
}

I'd rather do that refactor in a follow up though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is somewhat related to @slobodanadamovic 's comment in other place about having IndicesAndAliasesResolver to be pluggable for CPS. I can see this being a more packaged and self-contained solution. But yeah let's defer it for the time being.

@n1v0lg n1v0lg marked this pull request as ready for review October 5, 2025 19:38
@elasticsearchmachine elasticsearchmachine added the Team:Security Meta label for security team label Oct 5, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Thanks for the iterations!

this.remoteClusterResolver = new RemoteClusterResolver(settings, linkedProjectConfigService);
this.recordResolvedIndexExpressions = recordResolvedIndexExpressions;
this.crossProjectModeDecider = crossProjectModeDecider;
this.recordResolvedIndexExpressions = crossProjectModeDecider.crossProjectEnabled();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this field separately now that we have crossProjectModeDecider?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, no sorry we don't need it, just forgot to remove it

// we will always store them
if (recordResolvedIndexExpressions) {
assert indicesRequest.indices() != null : "indices() cannot be null when resolving non-all-index expressions";
if (crossProjectModeDecider.resolvesCrossProject(replaceable)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is somewhat related to @slobodanadamovic 's comment in other place about having IndicesAndAliasesResolver to be pluggable for CPS. I can see this being a more packaged and self-contained solution. But yeah let's defer it for the time being.

Comment on lines 393 to 400
// we need an early return here, instead of relying on the outer none expression logic since the outer handling will
// prematurely throw an IndexNotFound exception if the resolved indices are empty and allow_no_indices is false.
if (resolvedIndicesBuilder.isEmpty()) {
setNoneExpression(replaceable, resolvedIndicesBuilder);
} else {
replaceable.indices(resolvedIndicesBuilder.build().toArray());
}
return resolvedIndicesBuilder.build();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this? It seems that we can have

if (indicesOptions.allowNoIndices() || crossProjectModeDecider.crossProjectEnabled()) 

at line 427 and it should work?

Copy link
Contributor Author

@n1v0lg n1v0lg Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but crossProjectModeDecider.resolvesCrossProject(replaceable) instead -- otherwise, we don't correctly handle the case where CPS is enabled but we are handling a local request that did not get CPS resolved (e.g., because it does not allowCrossProject).

Copy link
Member

@ywangd ywangd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments while reviewing the serveless PR.

Comment on lines 667 to 668
// we still need to call response validation for local results, since qualified expressions like `_origin:index` or
// `<alias-pattern-matching-origin-only>:index` get deferred validation, also
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentense is not completed. Also, in this case, TargetProjects#linkedProjects should be empty? If that's the case, we might be able to short circuit it in IndicesAndAliasesResolver and avoid going into the application code. Something to look into in future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah it's just very poorly phrase -- ", also" should be ", too" or ", as well" -- let me wordsmith it a bit

If that's the case, we might be able to short circuit it in IndicesAndAliasesResolver and avoid going into the application code.

Yup -- very much something to look into as an optimization once more of the core pieces are in place

I'll log a Jira for it

import java.util.Set;
import java.util.stream.Collectors;

public record TargetProjects(@Nullable ProjectRoutingInfo originProject, List<ProjectRoutingInfo> linkedProjects) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nullability confused me a bit. It might worth adding a comment to say (IIUC) that null originProject is really a placeholder and only possible when a request is not eligible for CPS expansion after checking all three layers. It does not mean the user has no access to the origin project. In fact, we use a non-null ProjectRoutingInfo for CPS request even when the user has no access to the origin project, the locally accessible indices are resolved separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed this sync: originProject can be null if the project was excluded by project routing. I added Javadoc to explain this in more detail.

@n1v0lg n1v0lg added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label Oct 6, 2025
@elasticsearchmachine elasticsearchmachine merged commit b9f878e into elastic:main Oct 6, 2025
40 checks passed
@n1v0lg n1v0lg deleted the cps-flat-world branch October 6, 2025 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) >non-issue :Security/Security Security issues without another label serverless-linked Added by automation, don't add manually Team:Security Meta label for security team v9.3.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants