From 1675a930bed1f090c17fb7781e3f7199b6b2cfab Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Tue, 13 Jun 2023 16:27:43 -0400 Subject: [PATCH 01/13] Add Put Query Rule API call --- .../rest-api-spec/api/query_ruleset.put.json | 44 ++++++ .../xpack/core/XPackSettings.java | 2 +- .../privilege/ClusterPrivilegeResolver.java | 7 + x-pack/plugin/ent-search/qa/rest/build.gradle | 2 +- x-pack/plugin/ent-search/qa/rest/roles.yml | 1 + .../test/entsearch/200_query_ruleset_put.yml | 32 ++++ .../ent-search/src/main/java/module-info.java | 1 + .../xpack/application/EnterpriseSearch.java | 9 +- .../rules/action/PutQueryRulesetAction.java | 145 ++++++++++++++++++ .../action/RestPutQueryRulesetAction.java | 56 +++++++ .../TransportPutQueryRulesetAction.java | 36 +++++ ...yRulesetActionRequestSerializingTests.java | 30 ++++ ...RulesetActionResponseSerializingTests.java | 32 ++++ .../RestPutQueryRulesetActionTests.java | 59 +++++++ .../xpack/security/operator/Constants.java | 1 + 15 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json create mode 100644 x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml create mode 100644 x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java create mode 100644 x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetAction.java create mode 100644 x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/TransportPutQueryRulesetAction.java create mode 100644 x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionRequestSerializingTests.java create mode 100644 x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionResponseSerializingTests.java create mode 100644 x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetActionTests.java diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json b/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json new file mode 100644 index 0000000000000..37ebac0a8f8f1 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json @@ -0,0 +1,44 @@ +{ + "query_ruleset.put": { + "documentation": { + "url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/put-query-ruleset.html", + "description": "Creates or updates a query ruleset." + }, + "stability": "experimental", + "visibility": "public", + "headers": { + "accept": [ + "application/json" + ], + "content_type": [ + "application/json" + ] + }, + "url": { + "paths": [ + { + "path": "/_query_rules/{ruleset_id}", + "methods": [ + "PUT" + ], + "parts": { + "ruleset_id": { + "type": "string", + "description": "The unique identifier of the ruleset to be created or updated." + } + } + } + ] + }, + "params": { + "create": { + "type": "boolean", + "description": "If true, requires that a query_ruleset with the specified resource_id does not already exist. (default: false)" + } + }, + "body": { + "description": "The query ruleset configuration, including `rules`", + "required": true + } + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java index 31caf66d817fa..fc78266616a77 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java @@ -110,7 +110,7 @@ public Iterator> settings() { /** Setting for enabling or disabling query rules. Defaults to false. */ public static final Setting ENTERPRISE_SEARCH_QUERY_RULES_ENABLED = Setting.boolSetting( "xpack.ent_search.query_rules.enabled", - false, + true, Setting.Property.NodeScope ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index 9b9d69ba7461f..1cbaa78eb6855 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -154,6 +154,7 @@ public class ClusterPrivilegeResolver { private static final Set READ_SLM_PATTERN = Set.of(GetSnapshotLifecycleAction.NAME, GetStatusAction.NAME); private static final Set MANAGE_SEARCH_APPLICATION_PATTERN = Set.of("cluster:admin/xpack/application/search_application/*"); + private static final Set MANAGE_QUERY_RULES_PATTERN = Set.of("cluster:admin/xpack/query_rules/*"); private static final Set CROSS_CLUSTER_SEARCH_PATTERN = Set.of( RemoteClusterService.REMOTE_CLUSTER_HANDSHAKE_ACTION_NAME, @@ -277,6 +278,11 @@ public class ClusterPrivilegeResolver { "manage_search_application", MANAGE_SEARCH_APPLICATION_PATTERN ); + + public static final NamedClusterPrivilege MANAGE_QUERY_RULES = new ActionClusterPrivilege( + "manage_query_rules", + MANAGE_QUERY_RULES_PATTERN + ); public static final NamedClusterPrivilege MANAGE_BEHAVIORAL_ANALYTICS = new ActionClusterPrivilege( "manage_behavioral_analytics", MANAGE_BEHAVIORAL_ANALYTICS_PATTERN @@ -343,6 +349,7 @@ public class ClusterPrivilegeResolver { MANAGE_LOGSTASH_PIPELINES, CANCEL_TASK, MANAGE_SEARCH_APPLICATION, + MANAGE_QUERY_RULES, MANAGE_BEHAVIORAL_ANALYTICS, POST_BEHAVIORAL_ANALYTICS_EVENT, TcpTransport.isUntrustedRemoteClusterEnabled() ? CROSS_CLUSTER_SEARCH : null, diff --git a/x-pack/plugin/ent-search/qa/rest/build.gradle b/x-pack/plugin/ent-search/qa/rest/build.gradle index 3b809445e7b79..2be2ac72cf11e 100644 --- a/x-pack/plugin/ent-search/qa/rest/build.gradle +++ b/x-pack/plugin/ent-search/qa/rest/build.gradle @@ -7,7 +7,7 @@ dependencies { restResources { restApi { - include '_common', 'cluster', 'nodes', 'indices', 'index', 'search_application', 'xpack' + include '_common', 'cluster', 'nodes', 'indices', 'index', 'query_ruleset', 'search_application', 'xpack' } } diff --git a/x-pack/plugin/ent-search/qa/rest/roles.yml b/x-pack/plugin/ent-search/qa/rest/roles.yml index f46ec4ef96d40..25063f49904b5 100644 --- a/x-pack/plugin/ent-search/qa/rest/roles.yml +++ b/x-pack/plugin/ent-search/qa/rest/roles.yml @@ -2,6 +2,7 @@ admin: cluster: - manage_search_application - manage_behavioral_analytics + - manage_query_rules - monitor indices: - names: [ diff --git a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml new file mode 100644 index 0000000000000..c7c59d1f789c4 --- /dev/null +++ b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml @@ -0,0 +1,32 @@ + + +--- +"Create Query Ruleset": + - do: + query_ruleset.put: + ruleset_id: test-ruleset + body: + ruleset_id: test-ruleset + rules: + - rule_id: query-rule-id1 + type: pinned + criteria: + - type: exact + metadata: query_string + value: elastic + actions: + ids: + - 'id1' + - 'id2' + - rule_id: query-rule-id2 + type: pinned + criteria: + - type: exact + metadata: query_string + value: kibana + actions: + ids: + - 'id3' + - 'id4' + + - match: { result: "created" } diff --git a/x-pack/plugin/ent-search/src/main/java/module-info.java b/x-pack/plugin/ent-search/src/main/java/module-info.java index b58c76687cac4..077f511ec753a 100644 --- a/x-pack/plugin/ent-search/src/main/java/module-info.java +++ b/x-pack/plugin/ent-search/src/main/java/module-info.java @@ -30,4 +30,5 @@ exports org.elasticsearch.xpack.application.search; exports org.elasticsearch.xpack.application.search.action; + exports org.elasticsearch.xpack.application.rules.action; } diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index d260f2095631a..741b00b951015 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -52,6 +52,9 @@ import org.elasticsearch.xpack.application.analytics.action.TransportPutAnalyticsCollectionAction; import org.elasticsearch.xpack.application.analytics.ingest.AnalyticsEventIngestConfig; import org.elasticsearch.xpack.application.rules.QueryRulesIndexService; +import org.elasticsearch.xpack.application.rules.action.PutQueryRulesetAction; +import org.elasticsearch.xpack.application.rules.action.RestPutQueryRulesetAction; +import org.elasticsearch.xpack.application.rules.action.TransportPutQueryRulesetAction; import org.elasticsearch.xpack.application.search.SearchApplicationIndexService; import org.elasticsearch.xpack.application.search.action.DeleteSearchApplicationAction; import org.elasticsearch.xpack.application.search.action.GetSearchApplicationAction; @@ -90,6 +93,8 @@ public class EnterpriseSearch extends Plugin implements ActionPlugin, SystemInde public static final String BEHAVIORAL_ANALYTICS_API_ENDPOINT = APPLICATION_API_ENDPOINT + "/analytics"; + public static final String QUERY_RULES_API_ENDPOINT = "_query_rules"; + private static final Logger logger = LogManager.getLogger(EnterpriseSearch.class); public static final String FEATURE_NAME = "ent_search"; @@ -125,6 +130,7 @@ protected XPackLicenseState getLicenseState() { new ActionHandler<>(PutSearchApplicationAction.INSTANCE, TransportPutSearchApplicationAction.class), new ActionHandler<>(QuerySearchApplicationAction.INSTANCE, TransportQuerySearchApplicationAction.class), new ActionHandler<>(RenderSearchApplicationQueryAction.INSTANCE, TransportRenderSearchApplicationQueryAction.class), + new ActionHandler<>(PutQueryRulesetAction.INSTANCE, TransportPutQueryRulesetAction.class), usageAction, infoAction ); @@ -154,7 +160,8 @@ public List getRestHandlers( new RestGetAnalyticsCollectionAction(getLicenseState()), new RestDeleteAnalyticsCollectionAction(getLicenseState()), new RestPostAnalyticsEventAction(getLicenseState()), - new RestRenderSearchApplicationQueryAction(getLicenseState()) + new RestRenderSearchApplicationQueryAction(getLicenseState()), + new RestPutQueryRulesetAction(getLicenseState()) ); } diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java new file mode 100644 index 0000000000000..8f7efda4eca99 --- /dev/null +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.application.rules.action; + +import org.elasticsearch.action.ActionRequest; +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.action.ActionType; +import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.StatusToXContentObject; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentType; +import org.elasticsearch.xpack.application.rules.QueryRuleset; + +import java.io.IOException; +import java.util.Objects; + +public class PutQueryRulesetAction extends ActionType { + + public static final PutQueryRulesetAction INSTANCE = new PutQueryRulesetAction(); + public static final String NAME = "cluster:admin/xpack/query_rules/put"; + + public PutQueryRulesetAction() { + super(NAME, PutQueryRulesetAction.Response::new); + } + + public static class Request extends ActionRequest { + + private final QueryRuleset queryRuleset; + private final boolean create; + + public Request(StreamInput in) throws IOException { + super(in); + this.queryRuleset = new QueryRuleset(in); + this.create = in.readBoolean(); + } + + public Request(QueryRuleset queryRuleset, boolean create) { + this.queryRuleset = queryRuleset; + this.create = create; + } + + public Request(String rulesetId, boolean create, BytesReference content, XContentType contentType) { + this.queryRuleset = QueryRuleset.fromXContentBytes(rulesetId, content, contentType); + this.create = create; + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + + // TODO: validate query ruleset + + return validationException; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + queryRuleset.writeTo(out); + out.writeBoolean(create); + } + + public QueryRuleset queryRuleset() { + return queryRuleset; + } + + public boolean create() { + return create; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Request request = (Request) o; + return create == request.create && Objects.equals(queryRuleset, request.queryRuleset); + } + + @Override + public int hashCode() { + return Objects.hash(queryRuleset, create); + } + } + + public static class Response extends ActionResponse implements StatusToXContentObject { + + final DocWriteResponse.Result result; + + public Response(StreamInput in) throws IOException { + super(in); + result = DocWriteResponse.Result.readFrom(in); + } + + public Response(DocWriteResponse.Result result) { + this.result = result; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + this.result.writeTo(out); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("result", this.result.getLowercase()); + builder.endObject(); + return builder; + } + + @Override + public RestStatus status() { + return switch (result) { + case CREATED -> RestStatus.CREATED; + case NOT_FOUND -> RestStatus.NOT_FOUND; + default -> RestStatus.OK; + }; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Response that = (Response) o; + return Objects.equals(result, that.result); + } + + @Override + public int hashCode() { + return Objects.hash(result); + } + + } + +} diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetAction.java new file mode 100644 index 0000000000000..fcb22fcd22929 --- /dev/null +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetAction.java @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.application.rules.action; + +import org.elasticsearch.client.internal.node.NodeClient; +import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.Scope; +import org.elasticsearch.rest.ServerlessScope; +import org.elasticsearch.rest.action.RestToXContentListener; +import org.elasticsearch.xpack.application.EnterpriseSearch; +import org.elasticsearch.xpack.application.EnterpriseSearchBaseRestHandler; + +import java.io.IOException; +import java.util.List; + +import static org.elasticsearch.rest.RestRequest.Method.PUT; + +@ServerlessScope(Scope.PUBLIC) +public class RestPutQueryRulesetAction extends EnterpriseSearchBaseRestHandler { + public RestPutQueryRulesetAction(XPackLicenseState licenseState) { + super(licenseState); + } + + @Override + public String getName() { + return "query_ruleset_put_action"; + } + + @Override + public List routes() { + return List.of(new Route(PUT, "/" + EnterpriseSearch.QUERY_RULES_API_ENDPOINT + "/{ruleset_id}")); + } + + @Override + protected RestChannelConsumer innerPrepareRequest(RestRequest restRequest, NodeClient client) throws IOException { + PutQueryRulesetAction.Request request = new PutQueryRulesetAction.Request( + restRequest.param("ruleset_id"), + restRequest.paramAsBoolean("create", false), + restRequest.content(), + restRequest.getXContentType() + ); + return channel -> client.execute(PutQueryRulesetAction.INSTANCE, request, new RestToXContentListener<>(channel) { + @Override + protected RestStatus getStatus(PutQueryRulesetAction.Response response) { + return response.status(); + } + }); + } +} diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/TransportPutQueryRulesetAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/TransportPutQueryRulesetAction.java new file mode 100644 index 0000000000000..0bfaf544b0386 --- /dev/null +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/TransportPutQueryRulesetAction.java @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.application.rules.action; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.HandledTransportAction; +import org.elasticsearch.client.internal.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.tasks.Task; +import org.elasticsearch.transport.TransportService; +import org.elasticsearch.xpack.application.rules.QueryRulesIndexService; +import org.elasticsearch.xpack.application.rules.QueryRuleset; + +public class TransportPutQueryRulesetAction extends HandledTransportAction { + protected final QueryRulesIndexService systemIndexService; + + @Inject + public TransportPutQueryRulesetAction(TransportService transportService, ActionFilters actionFilters, Client client) { + super(PutQueryRulesetAction.NAME, transportService, actionFilters, PutQueryRulesetAction.Request::new); + this.systemIndexService = new QueryRulesIndexService(client); + } + + @Override + protected void doExecute(Task task, PutQueryRulesetAction.Request request, ActionListener listener) { + QueryRuleset queryRuleset = request.queryRuleset(); + boolean create = request.create(); + systemIndexService.putQueryRuleset(queryRuleset, create, listener.map(r -> new PutQueryRulesetAction.Response(r.getResult()))); + + } +} diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionRequestSerializingTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionRequestSerializingTests.java new file mode 100644 index 0000000000000..92f72a800d2bd --- /dev/null +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionRequestSerializingTests.java @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.application.rules.action; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.test.AbstractWireSerializingTestCase; +import org.elasticsearch.xpack.application.search.SearchApplicationTestUtils; + +public class PutQueryRulesetActionRequestSerializingTests extends AbstractWireSerializingTestCase { + + @Override + protected Writeable.Reader instanceReader() { + return PutQueryRulesetAction.Request::new; + } + + @Override + protected PutQueryRulesetAction.Request createTestInstance() { + return new PutQueryRulesetAction.Request(SearchApplicationTestUtils.randomQueryRuleset(), randomBoolean()); + } + + @Override + protected PutQueryRulesetAction.Request mutateInstance(PutQueryRulesetAction.Request instance) { + return randomValueOtherThan(instance, this::createTestInstance); + } +} diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionResponseSerializingTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionResponseSerializingTests.java new file mode 100644 index 0000000000000..b35380a70b636 --- /dev/null +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetActionResponseSerializingTests.java @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.application.rules.action; + +import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.test.AbstractWireSerializingTestCase; + +import java.io.IOException; + +public class PutQueryRulesetActionResponseSerializingTests extends AbstractWireSerializingTestCase { + + @Override + protected Writeable.Reader instanceReader() { + return PutQueryRulesetAction.Response::new; + } + + @Override + protected PutQueryRulesetAction.Response createTestInstance() { + return new PutQueryRulesetAction.Response(randomFrom(DocWriteResponse.Result.values())); + } + + @Override + protected PutQueryRulesetAction.Response mutateInstance(PutQueryRulesetAction.Response instance) throws IOException { + return randomValueOtherThan(instance, this::createTestInstance); + } +} diff --git a/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetActionTests.java b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetActionTests.java new file mode 100644 index 0000000000000..5bb006cc64513 --- /dev/null +++ b/x-pack/plugin/ent-search/src/test/java/org/elasticsearch/xpack/application/rules/action/RestPutQueryRulesetActionTests.java @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.application.rules.action; + +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.xcontent.NamedXContentRegistry; +import org.elasticsearch.xcontent.XContentType; +import org.elasticsearch.xpack.application.AbstractRestEnterpriseSearchActionTests; +import org.elasticsearch.xpack.application.EnterpriseSearchBaseRestHandler; + +import java.util.Map; + +public class RestPutQueryRulesetActionTests extends AbstractRestEnterpriseSearchActionTests { + public void testWithNonCompliantLicense() throws Exception { + checkLicenseForRequest( + new FakeRestRequest.Builder(NamedXContentRegistry.EMPTY).withMethod(RestRequest.Method.PUT) + .withParams(Map.of("ruleset_id", "ruleset-id")) + .withContent(new BytesArray(""" + { + "ruleset_id": "ruleset-id", + "rules": [ + { + "rule_id": "query-rule-id", + "type": "pinned", + "criteria": [ + { + "type": "exact", + "metadata": "query_string", + "value": "elastic" + } + ], + "actions": + { + "ids": [ + "id1", + "id2" + ] + } + } + ] + } + """), XContentType.JSON) + .build() + ); + } + + @Override + protected EnterpriseSearchBaseRestHandler getRestAction(XPackLicenseState licenseState) { + return new RestPutQueryRulesetAction(licenseState); + } +} diff --git a/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java b/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java index 042c884265d87..cb2c38bd2c224 100644 --- a/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java +++ b/x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java @@ -190,6 +190,7 @@ public class Constants { "cluster:admin/xpack/ml/upgrade_mode", "cluster:admin/xpack/monitoring/bulk", "cluster:admin/xpack/monitoring/migrate/alerts", + "cluster:admin/xpack/query_rules/put", "cluster:admin/xpack/rollup/delete", "cluster:admin/xpack/rollup/put", "cluster:admin/xpack/rollup/start", From 4fff2a55edb20f7300c9e9061aabe0410c7ac429 Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 08:28:44 -0400 Subject: [PATCH 02/13] Add more yaml tests --- .../test/entsearch/200_query_ruleset_put.yml | 80 +++++++++++++++++-- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml index c7c59d1f789c4..7b6a650b129b3 100644 --- a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml +++ b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml @@ -1,7 +1,7 @@ --- -"Create Query Ruleset": +'Create Query Ruleset': - do: query_ruleset.put: ruleset_id: test-ruleset @@ -25,8 +25,78 @@ metadata: query_string value: kibana actions: - ids: - - 'id3' - - 'id4' + docs: + - '_index': 'test-index1' + '_id': 'id3' + - '_index': 'test-index2' + '_id': 'id4' + + - match: { result: 'created' } + +--- +'Create Query Ruleset - Resource already exists': + - do: + query_ruleset.put: + create: true + ruleset_id: test-query-ruleset-recreating + body: + ruleset_id: 'test-query-ruleset-recreating' + rules: + rule_id: 'test-rule-1' + type: 'pinned' + criteria: + type: 'exact' + metadata: 'query_string' + value: 'elastic' + actions: + ids: + - 'id1' + + - match: { result: 'created' } + + - do: + catch: conflict + query_ruleset.put: + create: true + ruleset_id: test-query-ruleset-recreating + body: + ruleset_id: 'test-query-ruleset-recreating' + rules: + rule_id: 'test-rule-1' + type: 'pinned' + criteria: + type: 'exact' + metadata: 'query_string' + value: 'elastic' + actions: + ids: + - 'id2' + + - match: { error.type: 'version_conflict_engine_exception' } + +--- +'Create Query Ruleset - Insufficient privilege': + - skip: + features: headers + + - do: + catch: forbidden + headers: { Authorization: "Basic ZW50c2VhcmNoLXVzZXI6ZW50c2VhcmNoLXVzZXItcGFzc3dvcmQ=" } # user + query_ruleset.put: + ruleset_id: forbidden-query-ruleset + create: true + body: + ruleset_id: 'forbidden-query-ruleset' + rules: + rule_id: 'test-rule-1' + type: 'pinned' + criteria: + type: 'exact' + metadata: 'query_string' + value: 'elastic' + actions: + ids: + - 'id1' + - 'id2' - - match: { result: "created" } + - match: { error.type: 'security_exception' } From bf753b186208ec2d7a9b357827e879ffb4fa3b1d Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 09:34:48 -0400 Subject: [PATCH 03/13] Replace property with feature flag --- .../java/org/elasticsearch/test/cluster/FeatureFlag.java | 4 +++- .../java/org/elasticsearch/xpack/core/XPackSettings.java | 7 ------- .../rest-api-spec/test/entsearch/200_query_ruleset_put.yml | 2 +- .../elasticsearch/xpack/application/EnterpriseSearch.java | 6 +++--- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java index ef602257cab69..86e87af6d9714 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java @@ -18,7 +18,9 @@ public enum FeatureFlag { TIME_SERIES_MODE("es.index_mode_feature_flag_registered=true", Version.fromString("8.0.0"), null), NEW_RCS_MODE("es.untrusted_remote_cluster_feature_flag_registered=true", Version.fromString("8.5.0"), null), DLM_ENABLED("es.dlm_feature_flag_enabled=true", Version.fromString("8.8.0"), null), - SYNONYMS_ENABLED("es.synonyms_feature_flag_enabled=true", Version.fromString("8.9.0"), null); + SYNONYMS_ENABLED("es.synonyms_feature_flag_enabled=true", Version.fromString("8.9.0"), null), + + QUERY_RULES_ENABLED("es.query_rules_feature_flag_enabled=true", Version.fromString("8.9.0"), null); public final String systemProperty; public final Version from; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java index fc78266616a77..3f785eb6f2a04 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackSettings.java @@ -107,13 +107,6 @@ public Iterator> settings() { Setting.Property.NodeScope ); - /** Setting for enabling or disabling query rules. Defaults to false. */ - public static final Setting ENTERPRISE_SEARCH_QUERY_RULES_ENABLED = Setting.boolSetting( - "xpack.ent_search.query_rules.enabled", - true, - Setting.Property.NodeScope - ); - /** Setting for enabling or disabling auditing. Defaults to false. */ public static final Setting AUDIT_ENABLED = Setting.boolSetting( "xpack.security.audit.enabled", diff --git a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml index 7b6a650b129b3..0225735128b61 100644 --- a/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml +++ b/x-pack/plugin/ent-search/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/entsearch/200_query_ruleset_put.yml @@ -78,7 +78,7 @@ 'Create Query Ruleset - Insufficient privilege': - skip: features: headers - + - do: catch: forbidden headers: { Authorization: "Basic ZW50c2VhcmNoLXVzZXI6ZW50c2VhcmNoLXVzZXItcGFzc3dvcmQ=" } # user diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index 741b00b951015..883c09ebeaf9c 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.util.FeatureFlag; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.indices.SystemIndexDescriptor; @@ -101,11 +102,10 @@ public class EnterpriseSearch extends Plugin implements ActionPlugin, SystemInde private final boolean enabled; - private final boolean queryRulesEnabled; + private static final FeatureFlag QUERY_RULES_FEATURE_FLAG = new FeatureFlag("query_rules"); public EnterpriseSearch(Settings settings) { this.enabled = XPackSettings.ENTERPRISE_SEARCH_ENABLED.get(settings); - this.queryRulesEnabled = XPackSettings.ENTERPRISE_SEARCH_QUERY_RULES_ENABLED.get(settings); } protected XPackLicenseState getLicenseState() { @@ -199,7 +199,7 @@ public Collection createComponents( @Override public Collection getSystemIndexDescriptors(Settings settings) { - if (queryRulesEnabled) { + if (QUERY_RULES_FEATURE_FLAG.isEnabled()) { return Arrays.asList( SearchApplicationIndexService.getSystemIndexDescriptor(), QueryRulesIndexService.getSystemIndexDescriptor() From 7965506c6968ac5f914ec78a736d56c5de1aceef Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 09:40:09 -0400 Subject: [PATCH 04/13] Whitespace --- .../main/java/org/elasticsearch/test/cluster/FeatureFlag.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java index 86e87af6d9714..8c8fe355a60f0 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/FeatureFlag.java @@ -19,7 +19,6 @@ public enum FeatureFlag { NEW_RCS_MODE("es.untrusted_remote_cluster_feature_flag_registered=true", Version.fromString("8.5.0"), null), DLM_ENABLED("es.dlm_feature_flag_enabled=true", Version.fromString("8.8.0"), null), SYNONYMS_ENABLED("es.synonyms_feature_flag_enabled=true", Version.fromString("8.9.0"), null), - QUERY_RULES_ENABLED("es.query_rules_feature_flag_enabled=true", Version.fromString("8.9.0"), null); public final String systemProperty; From dcf24dc0e54cc25eee44d009e365c6351a405c36 Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 10:10:38 -0400 Subject: [PATCH 05/13] Fix FF handlers --- .../xpack/application/EnterpriseSearch.java | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index 883c09ebeaf9c..8d8294393bef8 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -54,7 +54,6 @@ import org.elasticsearch.xpack.application.analytics.ingest.AnalyticsEventIngestConfig; import org.elasticsearch.xpack.application.rules.QueryRulesIndexService; import org.elasticsearch.xpack.application.rules.action.PutQueryRulesetAction; -import org.elasticsearch.xpack.application.rules.action.RestPutQueryRulesetAction; import org.elasticsearch.xpack.application.rules.action.TransportPutQueryRulesetAction; import org.elasticsearch.xpack.application.search.SearchApplicationIndexService; import org.elasticsearch.xpack.application.search.action.DeleteSearchApplicationAction; @@ -80,6 +79,7 @@ import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction; import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -119,7 +119,8 @@ protected XPackLicenseState getLicenseState() { if (enabled == false) { return List.of(usageAction, infoAction); } - return List.of( + + final List> actionHandlers = new ArrayList<>(List.of( new ActionHandler<>(PutAnalyticsCollectionAction.INSTANCE, TransportPutAnalyticsCollectionAction.class), new ActionHandler<>(GetAnalyticsCollectionAction.INSTANCE, TransportGetAnalyticsCollectionAction.class), new ActionHandler<>(DeleteAnalyticsCollectionAction.INSTANCE, TransportDeleteAnalyticsCollectionAction.class), @@ -130,10 +131,17 @@ protected XPackLicenseState getLicenseState() { new ActionHandler<>(PutSearchApplicationAction.INSTANCE, TransportPutSearchApplicationAction.class), new ActionHandler<>(QuerySearchApplicationAction.INSTANCE, TransportQuerySearchApplicationAction.class), new ActionHandler<>(RenderSearchApplicationQueryAction.INSTANCE, TransportRenderSearchApplicationQueryAction.class), - new ActionHandler<>(PutQueryRulesetAction.INSTANCE, TransportPutQueryRulesetAction.class), usageAction, infoAction - ); + )); + + if (QUERY_RULES_FEATURE_FLAG.isEnabled()) { + actionHandlers.add( + new ActionHandler<>(PutQueryRulesetAction.INSTANCE, TransportPutQueryRulesetAction.class) + ); + } + + return actionHandlers; } @Override @@ -150,19 +158,27 @@ public List getRestHandlers( if (enabled == false) { return Collections.emptyList(); } - return List.of( - new RestGetSearchApplicationAction(getLicenseState()), - new RestListSearchApplicationAction(getLicenseState()), - new RestPutSearchApplicationAction(getLicenseState()), - new RestDeleteSearchApplicationAction(getLicenseState()), - new RestQuerySearchApplicationAction(getLicenseState()), + + final List restHandlers = new ArrayList<>(List.of( new RestPutAnalyticsCollectionAction(getLicenseState()), new RestGetAnalyticsCollectionAction(getLicenseState()), new RestDeleteAnalyticsCollectionAction(getLicenseState()), new RestPostAnalyticsEventAction(getLicenseState()), - new RestRenderSearchApplicationQueryAction(getLicenseState()), - new RestPutQueryRulesetAction(getLicenseState()) - ); + new RestDeleteSearchApplicationAction(getLicenseState()), + new RestGetSearchApplicationAction(getLicenseState()), + new RestListSearchApplicationAction(getLicenseState()), + new RestPutSearchApplicationAction(getLicenseState()), + new RestQuerySearchApplicationAction(getLicenseState()), + new RestRenderSearchApplicationQueryAction(getLicenseState()) + )); + + if (QUERY_RULES_FEATURE_FLAG.isEnabled()) { + restHandlers.add( + new RestPutSearchApplicationAction(getLicenseState()) + ); + } + + return restHandlers; } @Override From 48434232eed5a206cc7c6c29d9a3ea1606c36bfc Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 10:11:26 -0400 Subject: [PATCH 06/13] Fix test --- .../resources/rest-api-spec/test/privileges/11_builtin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml index e17d429e35248..7467d1760efc2 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml @@ -15,5 +15,5 @@ setup: # This is fragile - it needs to be updated every time we add a new cluster/index privilege # I would much prefer we could just check that specific entries are in the array, but we don't have # an assertion for that - - length: { "cluster" : 48 } + - length: { "cluster" : 49 } - length: { "index" : 22 } From 56a3be4db063849494257b9d53aa73c365d63514 Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 10:19:19 -0400 Subject: [PATCH 07/13] Spotless --- .../xpack/application/EnterpriseSearch.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index 8d8294393bef8..2b0a0e5c64e71 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -120,25 +120,25 @@ protected XPackLicenseState getLicenseState() { return List.of(usageAction, infoAction); } - final List> actionHandlers = new ArrayList<>(List.of( - new ActionHandler<>(PutAnalyticsCollectionAction.INSTANCE, TransportPutAnalyticsCollectionAction.class), - new ActionHandler<>(GetAnalyticsCollectionAction.INSTANCE, TransportGetAnalyticsCollectionAction.class), - new ActionHandler<>(DeleteAnalyticsCollectionAction.INSTANCE, TransportDeleteAnalyticsCollectionAction.class), - new ActionHandler<>(PostAnalyticsEventAction.INSTANCE, TransportPostAnalyticsEventAction.class), - new ActionHandler<>(DeleteSearchApplicationAction.INSTANCE, TransportDeleteSearchApplicationAction.class), - new ActionHandler<>(GetSearchApplicationAction.INSTANCE, TransportGetSearchApplicationAction.class), - new ActionHandler<>(ListSearchApplicationAction.INSTANCE, TransportListSearchApplicationAction.class), - new ActionHandler<>(PutSearchApplicationAction.INSTANCE, TransportPutSearchApplicationAction.class), - new ActionHandler<>(QuerySearchApplicationAction.INSTANCE, TransportQuerySearchApplicationAction.class), - new ActionHandler<>(RenderSearchApplicationQueryAction.INSTANCE, TransportRenderSearchApplicationQueryAction.class), - usageAction, - infoAction - )); + final List> actionHandlers = new ArrayList<>( + List.of( + new ActionHandler<>(PutAnalyticsCollectionAction.INSTANCE, TransportPutAnalyticsCollectionAction.class), + new ActionHandler<>(GetAnalyticsCollectionAction.INSTANCE, TransportGetAnalyticsCollectionAction.class), + new ActionHandler<>(DeleteAnalyticsCollectionAction.INSTANCE, TransportDeleteAnalyticsCollectionAction.class), + new ActionHandler<>(PostAnalyticsEventAction.INSTANCE, TransportPostAnalyticsEventAction.class), + new ActionHandler<>(DeleteSearchApplicationAction.INSTANCE, TransportDeleteSearchApplicationAction.class), + new ActionHandler<>(GetSearchApplicationAction.INSTANCE, TransportGetSearchApplicationAction.class), + new ActionHandler<>(ListSearchApplicationAction.INSTANCE, TransportListSearchApplicationAction.class), + new ActionHandler<>(PutSearchApplicationAction.INSTANCE, TransportPutSearchApplicationAction.class), + new ActionHandler<>(QuerySearchApplicationAction.INSTANCE, TransportQuerySearchApplicationAction.class), + new ActionHandler<>(RenderSearchApplicationQueryAction.INSTANCE, TransportRenderSearchApplicationQueryAction.class), + usageAction, + infoAction + ) + ); if (QUERY_RULES_FEATURE_FLAG.isEnabled()) { - actionHandlers.add( - new ActionHandler<>(PutQueryRulesetAction.INSTANCE, TransportPutQueryRulesetAction.class) - ); + actionHandlers.add(new ActionHandler<>(PutQueryRulesetAction.INSTANCE, TransportPutQueryRulesetAction.class)); } return actionHandlers; @@ -159,23 +159,23 @@ public List getRestHandlers( return Collections.emptyList(); } - final List restHandlers = new ArrayList<>(List.of( - new RestPutAnalyticsCollectionAction(getLicenseState()), - new RestGetAnalyticsCollectionAction(getLicenseState()), - new RestDeleteAnalyticsCollectionAction(getLicenseState()), - new RestPostAnalyticsEventAction(getLicenseState()), - new RestDeleteSearchApplicationAction(getLicenseState()), - new RestGetSearchApplicationAction(getLicenseState()), - new RestListSearchApplicationAction(getLicenseState()), - new RestPutSearchApplicationAction(getLicenseState()), - new RestQuerySearchApplicationAction(getLicenseState()), - new RestRenderSearchApplicationQueryAction(getLicenseState()) - )); + final List restHandlers = new ArrayList<>( + List.of( + new RestPutAnalyticsCollectionAction(getLicenseState()), + new RestGetAnalyticsCollectionAction(getLicenseState()), + new RestDeleteAnalyticsCollectionAction(getLicenseState()), + new RestPostAnalyticsEventAction(getLicenseState()), + new RestDeleteSearchApplicationAction(getLicenseState()), + new RestGetSearchApplicationAction(getLicenseState()), + new RestListSearchApplicationAction(getLicenseState()), + new RestPutSearchApplicationAction(getLicenseState()), + new RestQuerySearchApplicationAction(getLicenseState()), + new RestRenderSearchApplicationQueryAction(getLicenseState()) + ) + ); if (QUERY_RULES_FEATURE_FLAG.isEnabled()) { - restHandlers.add( - new RestPutSearchApplicationAction(getLicenseState()) - ); + restHandlers.add(new RestPutSearchApplicationAction(getLicenseState())); } return restHandlers; From 51220a3c0b5d768e43fc1c0b951ce9c2de999815 Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 10:30:26 -0400 Subject: [PATCH 08/13] Fat fingered refactor --- .../org/elasticsearch/xpack/application/EnterpriseSearch.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index 2b0a0e5c64e71..ef853138aabab 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -54,6 +54,7 @@ import org.elasticsearch.xpack.application.analytics.ingest.AnalyticsEventIngestConfig; import org.elasticsearch.xpack.application.rules.QueryRulesIndexService; import org.elasticsearch.xpack.application.rules.action.PutQueryRulesetAction; +import org.elasticsearch.xpack.application.rules.action.RestPutQueryRulesetAction; import org.elasticsearch.xpack.application.rules.action.TransportPutQueryRulesetAction; import org.elasticsearch.xpack.application.search.SearchApplicationIndexService; import org.elasticsearch.xpack.application.search.action.DeleteSearchApplicationAction; @@ -175,7 +176,7 @@ public List getRestHandlers( ); if (QUERY_RULES_FEATURE_FLAG.isEnabled()) { - restHandlers.add(new RestPutSearchApplicationAction(getLicenseState())); + restHandlers.add(new RestPutQueryRulesetAction(getLicenseState())); } return restHandlers; From c261a2047524bc76f83ad67c90962eba4c05fdc1 Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 16:18:06 -0400 Subject: [PATCH 09/13] Update rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json Co-authored-by: Carlos Delgado <6339205+carlosdelest@users.noreply.github.com> --- .../main/resources/rest-api-spec/api/query_ruleset.put.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json b/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json index 37ebac0a8f8f1..7967a9b90a707 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/query_ruleset.put.json @@ -5,7 +5,8 @@ "description": "Creates or updates a query ruleset." }, "stability": "experimental", - "visibility": "public", + "visibility": "feature_flag", + "feature_flag": "es.query_rules_feature_flag_enabled", "headers": { "accept": [ "application/json" From acb8b6771312ac605484fc520aa8ff82411e51ed Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 16:19:31 -0400 Subject: [PATCH 10/13] Update x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java Co-authored-by: Carlos Delgado <6339205+carlosdelest@users.noreply.github.com> --- .../org/elasticsearch/xpack/application/EnterpriseSearch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index ef853138aabab..4be0745e2364e 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -142,7 +142,7 @@ protected XPackLicenseState getLicenseState() { actionHandlers.add(new ActionHandler<>(PutQueryRulesetAction.INSTANCE, TransportPutQueryRulesetAction.class)); } - return actionHandlers; + return Collections.unmodifiableList(actionHandlers); } @Override From 403579e1aaf79c988b45929c5c1f2a351e53f3a9 Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Wed, 14 Jun 2023 16:19:39 -0400 Subject: [PATCH 11/13] Update x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java Co-authored-by: Carlos Delgado <6339205+carlosdelest@users.noreply.github.com> --- .../org/elasticsearch/xpack/application/EnterpriseSearch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java index 4be0745e2364e..63f98f8aec61c 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/EnterpriseSearch.java @@ -179,7 +179,7 @@ public List getRestHandlers( restHandlers.add(new RestPutQueryRulesetAction(getLicenseState())); } - return restHandlers; + return Collections.unmodifiableList(restHandlers); } @Override From 8b9670a91e94e14fb45cb72027f79953e36fde7f Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Thu, 15 Jun 2023 09:44:57 -0400 Subject: [PATCH 12/13] PR Feedback --- .../authz/privilege/ClusterPrivilegeResolver.java | 6 ------ x-pack/plugin/ent-search/qa/rest/roles.yml | 2 +- .../rules/action/PutQueryRulesetAction.java | 14 +++++++++++++- .../rest-api-spec/test/privileges/11_builtin.yml | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index 1cbaa78eb6855..d3c4c5abfe526 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -154,7 +154,6 @@ public class ClusterPrivilegeResolver { private static final Set READ_SLM_PATTERN = Set.of(GetSnapshotLifecycleAction.NAME, GetStatusAction.NAME); private static final Set MANAGE_SEARCH_APPLICATION_PATTERN = Set.of("cluster:admin/xpack/application/search_application/*"); - private static final Set MANAGE_QUERY_RULES_PATTERN = Set.of("cluster:admin/xpack/query_rules/*"); private static final Set CROSS_CLUSTER_SEARCH_PATTERN = Set.of( RemoteClusterService.REMOTE_CLUSTER_HANDSHAKE_ACTION_NAME, @@ -279,10 +278,6 @@ public class ClusterPrivilegeResolver { MANAGE_SEARCH_APPLICATION_PATTERN ); - public static final NamedClusterPrivilege MANAGE_QUERY_RULES = new ActionClusterPrivilege( - "manage_query_rules", - MANAGE_QUERY_RULES_PATTERN - ); public static final NamedClusterPrivilege MANAGE_BEHAVIORAL_ANALYTICS = new ActionClusterPrivilege( "manage_behavioral_analytics", MANAGE_BEHAVIORAL_ANALYTICS_PATTERN @@ -349,7 +344,6 @@ public class ClusterPrivilegeResolver { MANAGE_LOGSTASH_PIPELINES, CANCEL_TASK, MANAGE_SEARCH_APPLICATION, - MANAGE_QUERY_RULES, MANAGE_BEHAVIORAL_ANALYTICS, POST_BEHAVIORAL_ANALYTICS_EVENT, TcpTransport.isUntrustedRemoteClusterEnabled() ? CROSS_CLUSTER_SEARCH : null, diff --git a/x-pack/plugin/ent-search/qa/rest/roles.yml b/x-pack/plugin/ent-search/qa/rest/roles.yml index 25063f49904b5..4ab49432c70b0 100644 --- a/x-pack/plugin/ent-search/qa/rest/roles.yml +++ b/x-pack/plugin/ent-search/qa/rest/roles.yml @@ -2,7 +2,7 @@ admin: cluster: - manage_search_application - manage_behavioral_analytics - - manage_query_rules + - manage - monitor indices: - names: [ diff --git a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java index 8f7efda4eca99..eac7a5705bb60 100644 --- a/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java +++ b/x-pack/plugin/ent-search/src/main/java/org/elasticsearch/xpack/application/rules/action/PutQueryRulesetAction.java @@ -12,6 +12,7 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionType; import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -19,11 +20,15 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.XContentType; +import org.elasticsearch.xpack.application.rules.QueryRule; import org.elasticsearch.xpack.application.rules.QueryRuleset; import java.io.IOException; +import java.util.List; import java.util.Objects; +import static org.elasticsearch.action.ValidateActions.addValidationError; + public class PutQueryRulesetAction extends ActionType { public static final PutQueryRulesetAction INSTANCE = new PutQueryRulesetAction(); @@ -58,7 +63,14 @@ public Request(String rulesetId, boolean create, BytesReference content, XConten public ActionRequestValidationException validate() { ActionRequestValidationException validationException = null; - // TODO: validate query ruleset + if (Strings.isNullOrEmpty(queryRuleset.id())) { + validationException = addValidationError("ruleset_id cannot be null or empty", validationException); + } + + List rules = queryRuleset.rules(); + if (rules == null || rules.isEmpty()) { + validationException = addValidationError("rules cannot be null or empty", validationException); + } return validationException; } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml index 7467d1760efc2..e17d429e35248 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/privileges/11_builtin.yml @@ -15,5 +15,5 @@ setup: # This is fragile - it needs to be updated every time we add a new cluster/index privilege # I would much prefer we could just check that specific entries are in the array, but we don't have # an assertion for that - - length: { "cluster" : 49 } + - length: { "cluster" : 48 } - length: { "index" : 22 } From 7b468922709c9097a4a3e9e0da55497a4b8c1ded Mon Sep 17 00:00:00 2001 From: Kathleen DeRusso Date: Thu, 15 Jun 2023 09:55:04 -0400 Subject: [PATCH 13/13] Remove whitespace --- .../core/security/authz/privilege/ClusterPrivilegeResolver.java | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java index d3c4c5abfe526..9b9d69ba7461f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilegeResolver.java @@ -277,7 +277,6 @@ public class ClusterPrivilegeResolver { "manage_search_application", MANAGE_SEARCH_APPLICATION_PATTERN ); - public static final NamedClusterPrivilege MANAGE_BEHAVIORAL_ANALYTICS = new ActionClusterPrivilege( "manage_behavioral_analytics", MANAGE_BEHAVIORAL_ANALYTICS_PATTERN