Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search API keys with a subset of Query DSL #73705

Closed
wants to merge 5 commits into from

Conversation

ywangd
Copy link
Member

@ywangd ywangd commented Jun 3, 2021

This PR aims to provide a more feature rich search experience for API keys. It allows a subset of Query DSL to be specified for fields that are explicilty allowed. This change enables the followings:

  • Arbitrary logic relationships (AND, OR etc)
  • Search the user provided metadata
  • Range query for date fields (creation_time for now)
  • Support bool, term, terms, prefix, wildcard and range query types

@ywangd ywangd added >enhancement :Security/Security Security issues without another label v8.0.0 v7.14.0 labels Jun 3, 2021
@sethmlarson sethmlarson added the Team:Clients Meta label for clients team label Jun 3, 2021
@ywangd
Copy link
Member Author

ywangd commented Jun 3, 2021

Notes for reviewers

This purpose of this draft PR is to demostrate the overall approach. It will be polished (or even rewritten) if the overall approach is agreed.

I went back and forth with different ideas and settled down with the following two design decisions:

  1. The search query is processed and rewritten at elasticsearch's QueryBuilder level instead of Lucene's Query level.
  2. Fields like username, realm_name, id, owner are kept outside of the Query DSL.

The main reason for first decision is simplicity. Since we have tight control of the supported query types and fields, it is sufficient to process them at the query DSL level. Processing at Lucene level is not necessary and technically very challenging because it is outside of security domain and is hard to connect them together.

The second decision was made mainly to work with the manage_own_api_key privilege, which needs to know values of these field from the GetApiKeyRequest. It may be technically possible to extract the values from the Query DSL. But I think it is not worth the complexity. For example, the username, realm_name pair can be specified at any nested level of either must or filter clauses. It is also possible to specify them in a should clause and minimum_should_match: 2. They can also be specified in either a term query or a single item terms query or a wildcard query without wildcard. To count for all these different combinations is an overkill in my opinion. In addition, the owner parameter does not have an equivalent in Query DSL anyway. So we need at least keep it separate. Another argument for keeping these fields separate is that they would resemble existing APIs better and offer similarity. We might need to discuss and rethink how manage_own_api_key work in future, but it should be of its own piece of work. For this PR, I prefer to keep it focus on just the search part and maintain other existing semantics.

Another issue is what the API endpoint should be. This PR works by add a request body to GET /_security/api_key. However, it is not recommended to have a GET API endpoint with request body without also provide an equivalent POST version. In fact, YAML test error out if it finds such an API. But POST /_security/api_key is already taken for creating API keys. So it seems we might after all need a new API for the new search function, something like GET /_security/_search/api_key?

PS: This test class is a quick way to see what queries are supported (or not) for API keys.

@tvernum
Copy link
Contributor

tvernum commented Jun 10, 2021

This is interesting, and I think it suggests that this is a path we can make work.
Has anyone from the search area looked at it? I wonder whether there's other (better) ways of doing it.

I have lots of ideas about specifics (endpoint naming, existing parameters, etc) but they're details we can work through once we're happy we have a clear direction.

@ywangd
Copy link
Member Author

ywangd commented Jun 10, 2021

Has anyone from the search area looked at it? I wonder whether there's other (better) ways of doing it.

Not yet. I can message Jim to ask who can help out in the search area team.

Copy link
Contributor

@jimczi jimczi left a comment

Choose a reason for hiding this comment

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

I left one comment regarding the setter for the allowed field names but the approach looks good to me.

this.allowedFieldNames = allowedFieldNames;
}

public SearchExecutionContext withAllowedFieldNames(Predicate<String> allowedFieldNames) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The context object has some mutable variables that we should preserve in the mutation. So instead of creating a new object we could add a setter for the allowed field names like we do for setAllowUnmappedFields ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wasn't sure between using setter or constructor. Thanks for the tip. I changed it to use setter.

}

static boolean isFieldNameAllowed(String fieldName) {
if (Set.of("doc_type", "name", "api_key_invalidated", "creation_time", "expiration_time").contains(fieldName)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this can be extracted in a static variable ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep it was done in a rush. Extracted now.

private ApiKeySearchQueryBuilder() {}

public static BoolQueryBuilder build(GetApiKeyRequest getApiKeyRequest, Authentication authentication) {
BoolQueryBuilder finalQuery;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not building an ApiKeyBoolQueryBuilder directly ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. No need to keep both ApiKeySearchQueryBuidler and ApiKeyBoolQueryBuilder. I consolidated everything into ApiKeyBoolQueryBuilder. Thanks!

@ywangd
Copy link
Member Author

ywangd commented Jul 14, 2021

Closing this draft PR in favor of #75335.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>enhancement :Security/Security Security issues without another label Team:Clients Meta label for clients team v7.15.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants