Skip to content

Local index resolution records resolved expressions#135081

Merged
elasticsearchmachine merged 34 commits intoelastic:mainfrom
n1v0lg:record-local-index-resolution-results
Sep 24, 2025
Merged

Local index resolution records resolved expressions#135081
elasticsearchmachine merged 34 commits intoelastic:mainfrom
n1v0lg:record-local-index-resolution-results

Conversation

@n1v0lg
Copy link
Copy Markdown
Contributor

@n1v0lg n1v0lg commented Sep 19, 2025

This PR extends index resolution logic to capture the resolution mapping from original to resolution result via ResolvedIndexExpressions (introduced in #134783). This lets consumers such as cross-project search that require full resolution context to access it via the IndicesRequest.Replaceable interface.

To maximize code re-use, the PR updates the existing resolveIndexAbstractions method to always return a ResolvedIndexExpressions instance. The result is only recorded on the request if a boolean flag is set to avoid unnecessary memory overhead in contexts where storing the expressions is not necessary. In a (much bigger) future refactor, we plan to move away from storing a raw indices list and always use ResolvedIndexExpressions as the source of truth for indices resources accessed by a request. Once that's complete, we will move to always storing the full resolution result.

This PR does not include the following, which we'll address in immediate follow ups:

  • Integrate the change into the wider implementation of cross-project search index expressions rewriting and error handling
  • Store resolution for _all index expression handling
  • Record full authorization exceptions on authorization failures
  • Record resolution for remote expressions (e.g., p:logs -> p1:logs)

Relates: ES-12690

@n1v0lg n1v0lg self-assigned this Sep 19, 2025
@n1v0lg n1v0lg added >non-issue :Security/Security Security issues without another label labels Sep 19, 2025
@n1v0lg
Copy link
Copy Markdown
Contributor Author

n1v0lg commented Sep 21, 2025

@elasticmachine update branch plz

@elasticmachine
Copy link
Copy Markdown
Collaborator

There are no new commits on the base branch.

public List<String> resolveIndexAbstractions(
Iterable<String> indices,
public ResolvedIndexExpressions resolveIndexAbstractions(
List<String> indices,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's always a list in our codebase so I don't see a good reason to use the overly broad Iterable interface.

} else if (indicesOptions.ignoreUnavailable() == false || isAuthorized.test(indexAbstraction, selector)) {
resolvedExpressionsBuilder.excludeAll(resolvedIndices);
} else {
boolean authorized = isAuthorized.test(indexAbstraction, selector);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This has a performance implication:

Previously, we would only call isAuthorized if ignoreUnavailable = true. Now, we always run the authorization check. Since it's a single resource check I think it's acceptable overhead to introduce.

We could avoid this overhead by:

  1. When ignoreUnavailable = false, mark the result as SUCCESS without checking isAuthorized and letting downstream authorization code throw, or
  2. Using ResolvedIndexExpressions in downstream authorization code to skip the corresponding isAuthorized there

My preference is to proceed without the above (or include them in a follow up) in favor of simplicity and intuitive behavior of resolveIndexAbstractions.

this.auditTrailService = auditTrailService;
this.restrictedIndices = restrictedIndices;
this.indicesAndAliasesResolver = new IndicesAndAliasesResolver(settings, linkedProjectConfigService, resolver);
this.indicesAndAliasesResolver = new IndicesAndAliasesResolver(settings, linkedProjectConfigService, resolver, false);
Copy link
Copy Markdown
Contributor Author

@n1v0lg n1v0lg Sep 23, 2025

Choose a reason for hiding this comment

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

For now, don't record resolved index expressions for any request. In the next PRs that bring in end-to-end CPS index expression handling, we'll turn index resolution recording on if CPS is enabled.

assertThat(
actual.expressions(),
contains(
resolvedIndexExpression("-index10", Set.of("-index10"), SUCCESS),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

:shudder: it turns out one can create indices prefixed with - in older versions of ES...

*/
public record LocalExpressions(
List<String> expressions,
Set<String> expressions,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Converting this to a set for efficient exclusion handling (see here)

* A collection of {@link ResolvedIndexExpression}.
*/
public record ResolvedIndexExpressions(Map<String, ResolvedIndexExpression> expressions) {}
public record ResolvedIndexExpressions(List<ResolvedIndexExpression> expressions) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Converting to a list because of some edge-case behavior around duplicate index expressions:

See the two tests.

I'm fairly confident we can ultimately optimize this back to a map for efficient lookups but I want to change as little behavior as possible in the first round of changes so ensuring that duplicate index expressions are processed exactly as before (there may be some weird exclusion cases where we need the duplicates).

@n1v0lg n1v0lg marked this pull request as ready for review September 23, 2025 11:10
@elasticsearchmachine elasticsearchmachine added the Team:Security Meta label for security team label Sep 23, 2025
@elasticsearchmachine
Copy link
Copy Markdown
Collaborator

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

Copy link
Copy Markdown
Member

@piergm piergm left a comment

Choose a reason for hiding this comment

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

Very clean! LGTM!

@n1v0lg n1v0lg added the auto-merge-without-approval Automatically merge pull request when CI checks pass (NB doesn't wait for reviews!) label Sep 24, 2025
@elasticsearchmachine elasticsearchmachine merged commit a77d358 into elastic:main Sep 24, 2025
40 checks passed
@n1v0lg n1v0lg deleted the record-local-index-resolution-results branch September 24, 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 Team:Security Meta label for security team v9.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants