Browse Source

Support `match` for the Query API Key API (#104594)

This adds support for the `match` query type to the Query API key Information API.
Note that since string values associated to API Keys are mapped as `keywords`,
a `match` query with no analyzer parameter is effectively equivalent to a `term` query
for such fields (e.g. `name`, `username`, `realm_name`).

Relates: #101691
Albert Zaharovits 1 year ago
parent
commit
111a69d15f

+ 5 - 0
docs/changelog/104594.yaml

@@ -0,0 +1,5 @@
+pr: 104594
+summary: Support of `match` for the Query API Key API
+area: Authentication
+type: enhancement
+issues: []

+ 11 - 3
docs/reference/rest-api/security/query-api-key.asciidoc

@@ -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]
 ====

+ 11 - 0
x-pack/plugin/security/qa/security-basic/src/javaRestTest/java/org/elasticsearch/xpack/security/QueryApiKeyIT.java

@@ -63,10 +63,21 @@ public class QueryApiKeyIT extends SecurityInBasicRestTestCase {
             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,
             """

+ 43 - 6
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/ApiKeyBoolQueryBuilder.java

@@ -14,6 +14,7 @@ import org.elasticsearch.index.query.ExistsQueryBuilder;
 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;
@@ -111,7 +112,8 @@ public class ApiKeyBoolQueryBuilder extends BoolQueryBuilder {
         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);
@@ -124,28 +126,63 @@ public class ApiKeyBoolQueryBuilder extends BoolQueryBuilder {
         } 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");

+ 192 - 21
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/support/ApiKeyBoolQueryBuilderTests.java

@@ -7,13 +7,16 @@
 
 package org.elasticsearch.xpack.security.support;
 
+import org.elasticsearch.common.unit.Fuzziness;
 import org.elasticsearch.index.query.AbstractQueryBuilder;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.DistanceFeatureQueryBuilder;
 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.MultiTermQueryBuilder;
+import org.elasticsearch.index.query.Operator;
 import org.elasticsearch.index.query.PrefixQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -24,10 +27,12 @@ import org.elasticsearch.index.query.SpanQueryBuilder;
 import org.elasticsearch.index.query.TermQueryBuilder;
 import org.elasticsearch.index.query.TermsQueryBuilder;
 import org.elasticsearch.index.query.WildcardQueryBuilder;
+import org.elasticsearch.index.query.ZeroTermsQueryOption;
 import org.elasticsearch.indices.TermsLookup;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.core.security.authc.Authentication;
+import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
 import org.elasticsearch.xpack.core.security.authc.AuthenticationTests;
 import org.elasticsearch.xpack.core.security.authc.RealmConfig;
 import org.elasticsearch.xpack.core.security.user.User;
@@ -47,11 +52,13 @@ import static org.elasticsearch.xpack.security.support.ApiKeyFieldNameTranslator
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.emptyIterable;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.iterableWithSize;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -60,17 +67,144 @@ import static org.mockito.Mockito.verify;
 public class ApiKeyBoolQueryBuilderTests extends ESTestCase {
 
     public void testBuildFromSimpleQuery() {
-        final Authentication authentication = randomBoolean() ? AuthenticationTests.randomAuthentication(null, null) : null;
-        final QueryBuilder q1 = randomSimpleQuery("name");
-        final List<String> queryFields = new ArrayList<>();
-        final ApiKeyBoolQueryBuilder apiKeyQb1 = ApiKeyBoolQueryBuilder.build(q1, queryFields::add, authentication);
-        assertQueryFields(queryFields, q1, authentication);
-        assertCommonFilterQueries(apiKeyQb1, authentication);
-        final List<QueryBuilder> mustQueries = apiKeyQb1.must();
+        {
+            QueryBuilder qb = randomSimpleQuery("name");
+            List<String> queryFields = new ArrayList<>();
+            ApiKeyBoolQueryBuilder apiKeyQb = ApiKeyBoolQueryBuilder.build(qb, queryFields::add, null);
+            assertQueryFields(queryFields, qb, null);
+            assertCommonFilterQueries(apiKeyQb, null);
+            List<QueryBuilder> mustQueries = apiKeyQb.must();
+            assertThat(mustQueries, hasSize(1));
+            assertThat(mustQueries.get(0), equalTo(qb));
+            assertThat(apiKeyQb.should(), emptyIterable());
+            assertThat(apiKeyQb.mustNot(), emptyIterable());
+        }
+        {
+            Authentication authentication = AuthenticationTests.randomAuthentication(null, null);
+            QueryBuilder qb = randomSimpleQuery("name");
+            List<String> queryFields = new ArrayList<>();
+            ApiKeyBoolQueryBuilder apiKeyQb = ApiKeyBoolQueryBuilder.build(qb, queryFields::add, authentication);
+            assertQueryFields(queryFields, qb, authentication);
+            assertCommonFilterQueries(apiKeyQb, authentication);
+            List<QueryBuilder> mustQueries = apiKeyQb.must();
+            assertThat(mustQueries, hasSize(1));
+            assertThat(mustQueries.get(0), equalTo(qb));
+            assertThat(apiKeyQb.should(), emptyIterable());
+            assertThat(apiKeyQb.mustNot(), emptyIterable());
+        }
+        {
+            String apiKeyId = randomUUID();
+            Authentication authentication = AuthenticationTests.randomApiKeyAuthentication(AuthenticationTests.randomUser(), apiKeyId);
+            QueryBuilder qb = randomSimpleQuery("name");
+            List<String> queryFields = new ArrayList<>();
+            ApiKeyBoolQueryBuilder apiKeyQb = ApiKeyBoolQueryBuilder.build(qb, queryFields::add, authentication);
+            assertQueryFields(queryFields, qb, authentication);
+            assertCommonFilterQueries(apiKeyQb, authentication);
+            List<QueryBuilder> mustQueries = apiKeyQb.must();
+            assertThat(mustQueries, hasSize(1));
+            assertThat(mustQueries.get(0), equalTo(qb));
+            assertThat(apiKeyQb.should(), emptyIterable());
+            assertThat(apiKeyQb.mustNot(), emptyIterable());
+        }
+    }
+
+    public void testPrefixQueryBuilderPropertiesArePreserved() {
+        Authentication authentication = randomFrom(
+            AuthenticationTests.randomApiKeyAuthentication(AuthenticationTests.randomUser(), randomUUID()),
+            AuthenticationTests.randomAuthentication(null, null),
+            null
+        );
+        String fieldName = randomValidFieldName();
+        PrefixQueryBuilder prefixQueryBuilder = QueryBuilders.prefixQuery(fieldName, randomAlphaOfLengthBetween(0, 4));
+        if (randomBoolean()) {
+            prefixQueryBuilder.boost(Math.abs(randomFloat()));
+        }
+        if (randomBoolean()) {
+            prefixQueryBuilder.caseInsensitive(randomBoolean());
+        }
+        if (randomBoolean()) {
+            prefixQueryBuilder.rewrite(randomAlphaOfLengthBetween(0, 4));
+        }
+        List<String> queryFields = new ArrayList<>();
+        ApiKeyBoolQueryBuilder apiKeyMatchQueryBuilder = ApiKeyBoolQueryBuilder.build(prefixQueryBuilder, queryFields::add, authentication);
+        assertThat(queryFields, hasItem(ApiKeyFieldNameTranslators.translate(fieldName)));
+        List<QueryBuilder> mustQueries = apiKeyMatchQueryBuilder.must();
         assertThat(mustQueries, hasSize(1));
-        assertThat(mustQueries.get(0), equalTo(q1));
-        assertTrue(apiKeyQb1.should().isEmpty());
-        assertTrue(apiKeyQb1.mustNot().isEmpty());
+        assertThat(mustQueries.get(0), instanceOf(PrefixQueryBuilder.class));
+        PrefixQueryBuilder prefixQueryBuilder2 = (PrefixQueryBuilder) mustQueries.get(0);
+        assertThat(prefixQueryBuilder2.fieldName(), is(ApiKeyFieldNameTranslators.translate(prefixQueryBuilder.fieldName())));
+        assertThat(prefixQueryBuilder2.value(), is(prefixQueryBuilder.value()));
+        assertThat(prefixQueryBuilder2.boost(), is(prefixQueryBuilder.boost()));
+        assertThat(prefixQueryBuilder2.caseInsensitive(), is(prefixQueryBuilder.caseInsensitive()));
+        assertThat(prefixQueryBuilder2.rewrite(), is(prefixQueryBuilder.rewrite()));
+    }
+
+    public void testMatchQueryBuilderPropertiesArePreserved() {
+        // the match query has many properties, that all must be preserved after limiting for API Key docs only
+        Authentication authentication = randomFrom(
+            AuthenticationTests.randomApiKeyAuthentication(AuthenticationTests.randomUser(), randomUUID()),
+            AuthenticationTests.randomAuthentication(null, null),
+            null
+        );
+        String fieldName = randomValidFieldName();
+        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(fieldName, new Object());
+        if (randomBoolean()) {
+            matchQueryBuilder.boost(Math.abs(randomFloat()));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.operator(randomFrom(Operator.OR, Operator.AND));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.analyzer(randomAlphaOfLength(4));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.fuzziness(randomFrom(Fuzziness.ZERO, Fuzziness.ONE, Fuzziness.TWO, Fuzziness.AUTO));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.minimumShouldMatch(randomAlphaOfLength(4));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.fuzzyRewrite(randomAlphaOfLength(4));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.zeroTermsQuery(randomFrom(ZeroTermsQueryOption.NONE, ZeroTermsQueryOption.ALL, ZeroTermsQueryOption.NULL));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.prefixLength(randomNonNegativeInt());
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.maxExpansions(randomIntBetween(1, 100));
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.fuzzyTranspositions(randomBoolean());
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.lenient(randomBoolean());
+        }
+        if (randomBoolean()) {
+            matchQueryBuilder.autoGenerateSynonymsPhraseQuery(randomBoolean());
+        }
+        List<String> queryFields = new ArrayList<>();
+        ApiKeyBoolQueryBuilder apiKeyMatchQueryBuilder = ApiKeyBoolQueryBuilder.build(matchQueryBuilder, queryFields::add, authentication);
+        assertThat(queryFields, hasItem(ApiKeyFieldNameTranslators.translate(fieldName)));
+        List<QueryBuilder> mustQueries = apiKeyMatchQueryBuilder.must();
+        assertThat(mustQueries, hasSize(1));
+        assertThat(mustQueries.get(0), instanceOf(MatchQueryBuilder.class));
+        MatchQueryBuilder matchQueryBuilder2 = (MatchQueryBuilder) mustQueries.get(0);
+        assertThat(matchQueryBuilder2.fieldName(), is(ApiKeyFieldNameTranslators.translate(matchQueryBuilder.fieldName())));
+        assertThat(matchQueryBuilder2.value(), is(matchQueryBuilder.value()));
+        assertThat(matchQueryBuilder2.operator(), is(matchQueryBuilder.operator()));
+        assertThat(matchQueryBuilder2.analyzer(), is(matchQueryBuilder.analyzer()));
+        assertThat(matchQueryBuilder2.fuzziness(), is(matchQueryBuilder.fuzziness()));
+        assertThat(matchQueryBuilder2.minimumShouldMatch(), is(matchQueryBuilder.minimumShouldMatch()));
+        assertThat(matchQueryBuilder2.fuzzyRewrite(), is(matchQueryBuilder.fuzzyRewrite()));
+        assertThat(matchQueryBuilder2.zeroTermsQuery(), is(matchQueryBuilder.zeroTermsQuery()));
+        assertThat(matchQueryBuilder2.prefixLength(), is(matchQueryBuilder.prefixLength()));
+        assertThat(matchQueryBuilder2.maxExpansions(), is(matchQueryBuilder.maxExpansions()));
+        assertThat(matchQueryBuilder2.fuzzyTranspositions(), is(matchQueryBuilder.fuzzyTranspositions()));
+        assertThat(matchQueryBuilder2.lenient(), is(matchQueryBuilder.lenient()));
+        assertThat(matchQueryBuilder2.autoGenerateSynonymsPhraseQuery(), is(matchQueryBuilder.autoGenerateSynonymsPhraseQuery()));
+        assertThat(matchQueryBuilder2.boost(), is(matchQueryBuilder.boost()));
     }
 
     public void testQueryForDomainAuthentication() {
@@ -405,7 +539,6 @@ public class ApiKeyBoolQueryBuilderTests extends ESTestCase {
         final Authentication authentication = randomBoolean() ? AuthenticationTests.randomAuthentication(null, null) : null;
 
         final AbstractQueryBuilder<? extends AbstractQueryBuilder<?>> q1 = randomFrom(
-            QueryBuilders.matchQuery(randomAlphaOfLength(5), randomAlphaOfLength(5)),
             QueryBuilders.constantScoreQuery(mock(QueryBuilder.class)),
             QueryBuilders.boostingQuery(mock(QueryBuilder.class), mock(QueryBuilder.class)),
             QueryBuilders.queryStringQuery("q=a:42"),
@@ -760,20 +893,38 @@ public class ApiKeyBoolQueryBuilderTests extends ESTestCase {
         if (authentication == null) {
             return;
         }
-        assertTrue(
-            tqb.stream()
-                .anyMatch(
-                    q -> q.equals(QueryBuilders.termQuery("creator.principal", authentication.getEffectiveSubject().getUser().principal()))
+        if (authentication.isApiKey()) {
+            List<IdsQueryBuilder> idsQueryBuilders = qb.filter()
+                .stream()
+                .filter(q -> q.getClass() == IdsQueryBuilder.class)
+                .map(q -> (IdsQueryBuilder) q)
+                .toList();
+            assertThat(idsQueryBuilders, iterableWithSize(1));
+            assertThat(
+                idsQueryBuilders.get(0),
+                equalTo(
+                    QueryBuilders.idsQuery()
+                        .addIds((String) authentication.getAuthenticatingSubject().getMetadata().get(AuthenticationField.API_KEY_ID_KEY))
                 )
-        );
-        assertTrue(
-            tqb.stream()
-                .anyMatch(q -> q.equals(QueryBuilders.termQuery("creator.realm", ApiKeyService.getCreatorRealmName(authentication))))
-        );
+            );
+        } else {
+            assertTrue(
+                tqb.stream()
+                    .anyMatch(
+                        q -> q.equals(
+                            QueryBuilders.termQuery("creator.principal", authentication.getEffectiveSubject().getUser().principal())
+                        )
+                    )
+            );
+            assertTrue(
+                tqb.stream()
+                    .anyMatch(q -> q.equals(QueryBuilders.termQuery("creator.realm", ApiKeyService.getCreatorRealmName(authentication))))
+            );
+        }
     }
 
     private QueryBuilder randomSimpleQuery(String fieldName) {
-        return switch (randomIntBetween(0, 8)) {
+        return switch (randomIntBetween(0, 9)) {
             case 0 -> QueryBuilders.termQuery(fieldName, randomAlphaOfLengthBetween(3, 8));
             case 1 -> QueryBuilders.termsQuery(fieldName, randomArray(1, 3, String[]::new, () -> randomAlphaOfLengthBetween(3, 8)));
             case 2 -> QueryBuilders.idsQuery().addIds(randomArray(1, 3, String[]::new, () -> randomAlphaOfLength(22)));
@@ -788,6 +939,11 @@ public class ApiKeyBoolQueryBuilderTests extends ESTestCase {
                 .field(fieldName)
                 .lenient(randomBoolean())
                 .analyzeWildcard(randomBoolean());
+            case 9 -> QueryBuilders.matchQuery(fieldName, randomAlphaOfLengthBetween(3, 8))
+                .operator(randomFrom(Operator.OR, Operator.AND))
+                .lenient(randomBoolean())
+                .maxExpansions(randomIntBetween(1, 100))
+                .analyzer(randomFrom(randomAlphaOfLength(4), null));
             default -> throw new IllegalStateException("illegal switch case");
         };
     }
@@ -802,4 +958,19 @@ public class ApiKeyBoolQueryBuilderTests extends ESTestCase {
             assertThat(actualQueryFields, hasItem("creator.realm"));
         }
     }
+
+    private static String randomValidFieldName() {
+        return randomFrom(
+            "username",
+            "realm_name",
+            "name",
+            "type",
+            "creation",
+            "expiration",
+            "invalidated",
+            "invalidation",
+            "metadata",
+            "metadata.what.ever"
+        );
+    }
 }