Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/104594.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 104594
summary: Support of `match` for the Query API Key API
area: Authentication
type: enhancement
issues: []
14 changes: 11 additions & 3 deletions docs/reference/rest-api/security/query-api-key.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,20 @@ You can specify the following parameters in the request body:
(Optional, string) A <<query-dsl,query>> to filter which API keys to return.
The query supports a subset of query types, including
<<query-dsl-match-all-query,`match_all`>>, <<query-dsl-bool-query,`bool`>>,
<<query-dsl-term-query,`term`>>, <<query-dsl-terms-query,`terms`>>, <<query-dsl-ids-query,`ids`>>,
<<query-dsl-prefix-query,`prefix`>>, <<query-dsl-wildcard-query,`wildcard`>>, <<query-dsl-exists-query,`exists`>>,
<<query-dsl-range-query,`range`>>, and <<query-dsl-simple-query-string-query,`simple query string`>>
<<query-dsl-term-query,`term`>>, <<query-dsl-terms-query,`terms`>>,
<<query-dsl-match-query,`match`>>, <<query-dsl-ids-query,`ids`>>,
<<query-dsl-prefix-query,`prefix`>>, <<query-dsl-wildcard-query,`wildcard`>>,
<<query-dsl-exists-query,`exists`>>, <<query-dsl-range-query,`range`>>,
and <<query-dsl-simple-query-string-query,`simple query string`>>
+
You can query the following public values associated with an API key.
+
NOTE: The queryable string values associated with API keys are internally mapped as <<keyword,`keywords`>>.
Consequently, if no <<analysis-analyzers,`analyzer`>> parameter is specified for a
<<query-dsl-match-query,`match`>> query, then the provided match query string is interpreted as
a single keyword value. Such a <<query-dsl-match-query,`match`>> query is hence equivalent to a
<<query-dsl-term-query,`term`>> query.
+
.Valid values for `query`
[%collapsible%open]
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,21 @@ public void testQuery() throws IOException {
apiKeys.forEach(k -> assertThat(k, not(hasKey("_sort"))));
});

assertQuery(API_KEY_ADMIN_AUTH_HEADER, """
{ "query": { "match": {"name": {"query": "my-ingest-key-1 my-org/alert-key-1", "analyzer": "whitespace"} } } }""", apiKeys -> {
assertThat(apiKeys.size(), equalTo(2));
assertThat(apiKeys.get(0).get("name"), oneOf("my-ingest-key-1", "my-org/alert-key-1"));
assertThat(apiKeys.get(1).get("name"), oneOf("my-ingest-key-1", "my-org/alert-key-1"));
apiKeys.forEach(k -> assertThat(k, not(hasKey("_sort"))));
});

// An empty request body means search for all keys
assertQuery(API_KEY_ADMIN_AUTH_HEADER, randomBoolean() ? "" : """
{"query":{"match_all":{}}}""", apiKeys -> assertThat(apiKeys.size(), equalTo(6)));

assertQuery(API_KEY_ADMIN_AUTH_HEADER, randomBoolean() ? "" : """
{ "query": { "match": {"type": "rest"} } }""", apiKeys -> assertThat(apiKeys.size(), equalTo(6)));

assertQuery(
API_KEY_ADMIN_AUTH_HEADER,
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.MatchNoneQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.PrefixQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
Expand Down Expand Up @@ -111,7 +112,8 @@ private static QueryBuilder doProcess(QueryBuilder qb, Consumer<String> fieldNam
if (qb instanceof final BoolQueryBuilder query) {
final BoolQueryBuilder newQuery = QueryBuilders.boolQuery()
.minimumShouldMatch(query.minimumShouldMatch())
.adjustPureNegative(query.adjustPureNegative());
.adjustPureNegative(query.adjustPureNegative())
.boost(query.boost());
query.must().stream().map(q -> ApiKeyBoolQueryBuilder.doProcess(q, fieldNameVisitor)).forEach(newQuery::must);
query.should().stream().map(q -> ApiKeyBoolQueryBuilder.doProcess(q, fieldNameVisitor)).forEach(newQuery::should);
query.mustNot().stream().map(q -> ApiKeyBoolQueryBuilder.doProcess(q, fieldNameVisitor)).forEach(newQuery::mustNot);
Expand All @@ -124,28 +126,63 @@ private static QueryBuilder doProcess(QueryBuilder qb, Consumer<String> fieldNam
} else if (qb instanceof final TermQueryBuilder query) {
final String translatedFieldName = ApiKeyFieldNameTranslators.translate(query.fieldName());
fieldNameVisitor.accept(translatedFieldName);
return QueryBuilders.termQuery(translatedFieldName, query.value()).caseInsensitive(query.caseInsensitive());
return QueryBuilders.termQuery(translatedFieldName, query.value())
.caseInsensitive(query.caseInsensitive())
.boost(query.boost());
} else if (qb instanceof final ExistsQueryBuilder query) {
final String translatedFieldName = ApiKeyFieldNameTranslators.translate(query.fieldName());
fieldNameVisitor.accept(translatedFieldName);
return QueryBuilders.existsQuery(translatedFieldName);
return QueryBuilders.existsQuery(translatedFieldName).boost(query.boost());
} else if (qb instanceof final TermsQueryBuilder query) {
if (query.termsLookup() != null) {
throw new IllegalArgumentException("terms query with terms lookup is not supported for API Key query");
}
final String translatedFieldName = ApiKeyFieldNameTranslators.translate(query.fieldName());
fieldNameVisitor.accept(translatedFieldName);
return QueryBuilders.termsQuery(translatedFieldName, query.getValues());
return QueryBuilders.termsQuery(translatedFieldName, query.getValues()).boost(query.boost());
} else if (qb instanceof final PrefixQueryBuilder query) {
final String translatedFieldName = ApiKeyFieldNameTranslators.translate(query.fieldName());
fieldNameVisitor.accept(translatedFieldName);
return QueryBuilders.prefixQuery(translatedFieldName, query.value()).caseInsensitive(query.caseInsensitive());
return QueryBuilders.prefixQuery(translatedFieldName, query.value())
.caseInsensitive(query.caseInsensitive())
.rewrite(query.rewrite())
.boost(query.boost());
} else if (qb instanceof final WildcardQueryBuilder query) {
final String translatedFieldName = ApiKeyFieldNameTranslators.translate(query.fieldName());
fieldNameVisitor.accept(translatedFieldName);
return QueryBuilders.wildcardQuery(translatedFieldName, query.value())
.caseInsensitive(query.caseInsensitive())
.rewrite(query.rewrite());
.rewrite(query.rewrite())
.boost(query.boost());
} else if (qb instanceof final MatchQueryBuilder query) {
final String translatedFieldName = ApiKeyFieldNameTranslators.translate(query.fieldName());
fieldNameVisitor.accept(translatedFieldName);
final MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(translatedFieldName, query.value());
if (query.operator() != null) {
matchQueryBuilder.operator(query.operator());
}
if (query.analyzer() != null) {
matchQueryBuilder.analyzer(query.analyzer());
}
if (query.fuzziness() != null) {
matchQueryBuilder.fuzziness(query.fuzziness());
}
if (query.minimumShouldMatch() != null) {
matchQueryBuilder.minimumShouldMatch(query.minimumShouldMatch());
}
if (query.fuzzyRewrite() != null) {
matchQueryBuilder.fuzzyRewrite(query.fuzzyRewrite());
}
if (query.zeroTermsQuery() != null) {
matchQueryBuilder.zeroTermsQuery(query.zeroTermsQuery());
}
matchQueryBuilder.prefixLength(query.prefixLength())
.maxExpansions(query.maxExpansions())
.fuzzyTranspositions(query.fuzzyTranspositions())
.lenient(query.lenient())
.autoGenerateSynonymsPhraseQuery(query.autoGenerateSynonymsPhraseQuery())
.boost(query.boost());
return matchQueryBuilder;
} else if (qb instanceof final RangeQueryBuilder query) {
if (query.relation() != null) {
throw new IllegalArgumentException("range query with relation is not supported for API Key query");
Expand Down
Loading