From cd964f1229b1fdc919677768dae22cf1c05fa3e2 Mon Sep 17 00:00:00 2001 From: Tiago Vila Verde Date: Mon, 16 Sep 2024 17:15:10 +0200 Subject: [PATCH] [Security Solution][Entity Analytics] APIs for Entity Store engine (#191986) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces the following API routes for setting up Entity Store "engines":
Initialise Engine | POST /api/entity_store/engines//init -- | -- Start Engine | POST /api/entity_store/engines//start Stop Engine | POST /api/entity_store/engines//stop Delete Engine | DELETE /api/entity_store/engines/ Get engine | GET  /api/entity_store/engines/ List Engines | GET /api/entity_store/engines
The PR includes the following: - Adding the `EntityManager` plugin (see elastic/obs-entities) as a dependency of the Security Solution - The OpenAPI schemas for the new routes - The actual Kibana side endpoints - A `Saved Object` to track the installed engines - A new `EntityStoreDataClient` - A new feature flag `entityStoreEngineRoutesEnabled` ### How to test 1. Add some host/user data * Easiest is to use [elastic/security-data-generator](https://github.com/elastic/security-documents-generator) 2. Make sure to add `entityStoreEngineRoutesEnabled` under `xpack.securitySolution.enableExperimental` in your `kibana.dev.yml` 3. In kibana dev tools or your terminal, call the `INIT` route for either `user` or `host`. 4. You should now see 2 transforms in kibana. Make sure to re-trigger them if needed so they process the documents. 5. Check that new entities have been observed by querying the new entities index via: * `GET .entities.v1.latest.ea*/_search` 6. Check the other endpoints are working (`START`, `STOP`, `LIST`, etc) 7. Calling `DELETE` should remove the transforms Implements https://github.com/elastic/security-team/issues/10230 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .../current_fields.json | 6 + .../current_mappings.json | 17 ++ .../check_registered_types.test.ts | 1 + .../group3/type_registrations.test.ts | 1 + .../entity_store/common.gen.ts | 38 ++++ .../entity_store/common.schema.yaml | 37 ++++ .../entity_store/engine/delete.gen.ts | 43 ++++ .../entity_store/engine/delete.schema.yaml | 37 ++++ .../entity_store/engine/get.gen.ts | 33 +++ .../entity_store/engine/get.schema.yaml | 25 +++ .../entity_store/engine/init.gen.ts | 38 ++++ .../entity_store/engine/init.schema.yaml | 39 ++++ .../entity_store/engine/list.gen.ts | 25 +++ .../entity_store/engine/list.schema.yaml | 25 +++ .../entity_store/engine/start.gen.ts | 33 +++ .../entity_store/engine/start.schema.yaml | 31 +++ .../entity_store/engine/stats.gen.ts | 39 ++++ .../entity_store/engine/stats.schema.yaml | 41 ++++ .../entity_store/engine/stop.gen.ts | 33 +++ .../entity_store/engine/stop.schema.yaml | 30 +++ .../common/api/quickstart_client.gen.ts | 134 ++++++++++++ .../common/experimental_features.ts | 5 + ...alytics_api_2023_10_31.bundled.schema.yaml | 205 ++++++++++++++++++ ...alytics_api_2023_10_31.bundled.schema.yaml | 205 ++++++++++++++++++ x-pack/plugins/security_solution/kibana.jsonc | 5 +- .../routes/__mocks__/request_context.ts | 3 + .../entity_store/constants.ts | 21 ++ .../entity_store/definition.ts | 56 +++++ .../entity_store_data_client.mock.ts | 20 ++ .../entity_store/entity_store_data_client.ts | 120 ++++++++++ .../entity_store/routes/delete.ts | 64 ++++++ .../entity_store/routes/get.ts | 62 ++++++ .../entity_store/routes/index.ts | 8 + .../entity_store/routes/init.ts | 65 ++++++ .../entity_store/routes/list.ts | 57 +++++ .../routes/register_entity_store_routes.ts | 23 ++ .../entity_store/routes/start.ts | 59 +++++ .../entity_store/routes/stats.ts | 58 +++++ .../entity_store/routes/stop.ts | 59 +++++ .../saved_object/engine_descriptor.ts | 76 +++++++ .../saved_object/engine_descriptor_type.ts | 36 +++ .../entity_store/saved_object/index.ts | 8 + .../entity_store/utils/utils.ts | 33 +++ .../register_entity_analytics_routes.ts | 4 + .../server/request_context_factory.ts | 18 ++ .../security_solution/server/saved_objects.ts | 2 + .../plugins/security_solution/server/types.ts | 2 + .../plugins/security_solution/tsconfig.json | 2 + .../services/security_solution_api.gen.ts | 83 +++++++ .../platform_security/authorization.ts | 34 +++ 50 files changed, 2097 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.schema.yaml create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/definition.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.mock.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/delete.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/get.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/index.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/init.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/list.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/register_entity_store_routes.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/start.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stats.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor_type.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/index.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/utils/utils.ts diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 0447ba6a226dd..ec14f4519d344 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -312,6 +312,12 @@ "entity-discovery-api-key": [ "apiKey" ], + "entity-engine-status": [ + "filter", + "indexPattern", + "status", + "type" + ], "epm-packages": [ "additional_spaces_installed_kibana", "es_index_patterns", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index bda2270001bd9..2bdf6e75ad1cb 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1057,6 +1057,23 @@ } } }, + "entity-engine-status": { + "dynamic": false, + "properties": { + "filter": { + "type": "keyword" + }, + "indexPattern": { + "type": "keyword" + }, + "status": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, "epm-packages": { "properties": { "additional_spaces_installed_kibana": { diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 92de0c925951b..170cfa5958782 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -93,6 +93,7 @@ describe('checking migration metadata changes on all registered SO types', () => "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", "entity-definition": "61be3e95966045122b55e181bb39658b1dc9bbe9", "entity-discovery-api-key": "c267a65c69171d1804362155c1378365f5acef88", + "entity-engine-status": "0738aa1a06d3361911740f8f166071ea43a00927", "epm-packages": "8042d4a1522f6c4e6f5486e791b3ffe3a22f88fd", "epm-packages-assets": "7a3e58efd9a14191d0d1a00b8aaed30a145fd0b1", "event-annotation-group": "715ba867d8c68f3c9438052210ea1c30a9362582", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index 4320b0eb689d9..e95a82e63d0ff 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -124,6 +124,7 @@ const previouslyRegisteredTypes = [ 'security-rule', 'security-solution-signals-migration', 'risk-engine-configuration', + 'entity-engine-status', 'server', 'siem-detection-engine-rule-actions', 'siem-detection-engine-rule-execution-info', diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts new file mode 100644 index 0000000000000..e5f8c631fcbae --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.gen.ts @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Entity Store Common Schema + * version: 1 + */ + +import { z } from '@kbn/zod'; + +export type EntityType = z.infer; +export const EntityType = z.enum(['user', 'host']); +export type EntityTypeEnum = typeof EntityType.enum; +export const EntityTypeEnum = EntityType.enum; + +export type IndexPattern = z.infer; +export const IndexPattern = z.string(); + +export type EngineStatus = z.infer; +export const EngineStatus = z.enum(['installing', 'started', 'stopped']); +export type EngineStatusEnum = typeof EngineStatus.enum; +export const EngineStatusEnum = EngineStatus.enum; + +export type EngineDescriptor = z.infer; +export const EngineDescriptor = z.object({ + type: EntityType.optional(), + indexPattern: IndexPattern.optional(), + status: EngineStatus.optional(), + filter: z.string().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml new file mode 100644 index 0000000000000..dc17ad6193ee5 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/common.schema.yaml @@ -0,0 +1,37 @@ +openapi: 3.0.0 +info: + title: Entity Store Common Schema + description: Common schema for Entity Store + version: '1' +paths: {} +components: + schemas: + + EntityType: + type: string + enum: + - user + - host + + EngineDescriptor: + type: object + properties: + type: + $ref: '#/components/schemas/EntityType' + indexPattern: + $ref: '#/components/schemas/IndexPattern' + status: + $ref: '#/components/schemas/EngineStatus' + filter: + type: string + + EngineStatus: + type: string + enum: + - installing + - started + - stopped + + IndexPattern: + type: string + \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.gen.ts new file mode 100644 index 0000000000000..34acf2a802076 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.gen.ts @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Delete the entity store engine + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; +import { BooleanFromString } from '@kbn/zod-helpers'; + +import { EntityType } from '../common.gen'; + +export type DeleteEntityStoreRequestQuery = z.infer; +export const DeleteEntityStoreRequestQuery = z.object({ + /** + * Control flag to also delete the entity data. + */ + data: BooleanFromString.optional(), +}); +export type DeleteEntityStoreRequestQueryInput = z.input; + +export type DeleteEntityStoreRequestParams = z.infer; +export const DeleteEntityStoreRequestParams = z.object({ + /** + * The entity type of the store (either 'user' or 'host'). + */ + entityType: EntityType, +}); +export type DeleteEntityStoreRequestParamsInput = z.input; + +export type DeleteEntityStoreResponse = z.infer; +export const DeleteEntityStoreResponse = z.object({ + deleted: z.boolean().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.schema.yaml new file mode 100644 index 0000000000000..c766d9895c5fa --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/delete.schema.yaml @@ -0,0 +1,37 @@ +openapi: 3.0.0 + +info: + title: Delete the entity store engine + version: '2023-10-31' +paths: + /api/entity_store/engines/{entityType}: + delete: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: DeleteEntityStore + summary: Delete the Entity Store engine + parameters: + - name: entityType + in: path + required: true + schema: + $ref: '../common.schema.yaml#/components/schemas/EntityType' + description: The entity type of the store (either 'user' or 'host'). + + - name: data + in: query + required: false + schema: + type: boolean + description: Control flag to also delete the entity data. + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + deleted: + type: boolean + \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.gen.ts new file mode 100644 index 0000000000000..44f6f45844fc1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.gen.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get Entity Store engine + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EntityType, EngineDescriptor } from '../common.gen'; + +export type GetEntityStoreEngineRequestParams = z.infer; +export const GetEntityStoreEngineRequestParams = z.object({ + /** + * The entity type of the store (either 'user' or 'host'). + */ + entityType: EntityType, +}); +export type GetEntityStoreEngineRequestParamsInput = z.input< + typeof GetEntityStoreEngineRequestParams +>; + +export type GetEntityStoreEngineResponse = z.infer; +export const GetEntityStoreEngineResponse = EngineDescriptor; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.schema.yaml new file mode 100644 index 0000000000000..d65a5906e54d9 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/get.schema.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + title: Get Entity Store engine + version: '2023-10-31' +paths: + /api/entity_store/engines/{entityType}: + get: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: GetEntityStoreEngine + summary: Get the Entity Store engine + parameters: + - name: entityType + in: path + required: true + schema: + $ref: '../common.schema.yaml#/components/schemas/EntityType' + description: The entity type of the store (either 'user' or 'host'). + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../common.schema.yaml#/components/schemas/EngineDescriptor' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.gen.ts new file mode 100644 index 0000000000000..07f32f4cb7144 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.gen.ts @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Init Entity Store types + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EntityType, IndexPattern, EngineDescriptor } from '../common.gen'; + +export type InitEntityStoreRequestParams = z.infer; +export const InitEntityStoreRequestParams = z.object({ + /** + * The entity type of the store (either 'user' or 'host'). + */ + entityType: EntityType, +}); +export type InitEntityStoreRequestParamsInput = z.input; + +export type InitEntityStoreRequestBody = z.infer; +export const InitEntityStoreRequestBody = z.object({ + indexPattern: IndexPattern.optional(), + filter: z.string().optional(), +}); +export type InitEntityStoreRequestBodyInput = z.input; + +export type InitEntityStoreResponse = z.infer; +export const InitEntityStoreResponse = EngineDescriptor; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.schema.yaml new file mode 100644 index 0000000000000..8e826d57ce40a --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/init.schema.yaml @@ -0,0 +1,39 @@ +openapi: 3.0.0 + +info: + title: Init Entity Store types + version: '2023-10-31' +paths: + /api/entity_store/engines/{entityType}/init: + post: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: InitEntityStore + summary: Initialize the Entity Store + parameters: + - name: entityType + in: path + required: true + schema: + $ref: '../common.schema.yaml#/components/schemas/EntityType' + description: The entity type of the store (either 'user' or 'host'). + requestBody: + description: Schema for the engine initialization + required: true + content: + application/json: + schema: + type: object + properties: + indexPattern: + $ref: '../common.schema.yaml#/components/schemas/IndexPattern' + filter: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../common.schema.yaml#/components/schemas/EngineDescriptor' + diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.gen.ts new file mode 100644 index 0000000000000..926549a329a4b --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.gen.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: List Entity Store engines + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EngineDescriptor } from '../common.gen'; + +export type ListEntityStoreEnginesResponse = z.infer; +export const ListEntityStoreEnginesResponse = z.object({ + count: z.number().int().optional(), + engines: z.array(EngineDescriptor).optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.schema.yaml new file mode 100644 index 0000000000000..efad1a4380352 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/list.schema.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + title: List Entity Store engines + version: '2023-10-31' +paths: + /api/entity_store/engines: + get: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: ListEntityStoreEngines + summary: List the Entity Store engines + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + count: + type: integer + engines: + type: array + items: + $ref: '../common.schema.yaml#/components/schemas/EngineDescriptor' \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.gen.ts new file mode 100644 index 0000000000000..b8e94d00551c0 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.gen.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Start the entity store engine + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EntityType } from '../common.gen'; + +export type StartEntityStoreRequestParams = z.infer; +export const StartEntityStoreRequestParams = z.object({ + /** + * The entity type of the store (either 'user' or 'host'). + */ + entityType: EntityType, +}); +export type StartEntityStoreRequestParamsInput = z.input; + +export type StartEntityStoreResponse = z.infer; +export const StartEntityStoreResponse = z.object({ + started: z.boolean().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.schema.yaml new file mode 100644 index 0000000000000..5c048bf3d973c --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/start.schema.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 + +info: + title: Start the entity store engine + version: '2023-10-31' +paths: + /api/entity_store/engines/{entityType}/start: + post: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: StartEntityStore + summary: Start the Entity Store engine + parameters: + - name: entityType + in: path + required: true + schema: + $ref: '../common.schema.yaml#/components/schemas/EntityType' + description: The entity type of the store (either 'user' or 'host'). + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + started: + type: boolean + + \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.gen.ts new file mode 100644 index 0000000000000..631399faebc96 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.gen.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get the entity store engine stats + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EntityType, IndexPattern, EngineStatus } from '../common.gen'; + +export type GetEntityStoreStatsRequestParams = z.infer; +export const GetEntityStoreStatsRequestParams = z.object({ + /** + * The entity type of the store (either 'user' or 'host'). + */ + entityType: EntityType, +}); +export type GetEntityStoreStatsRequestParamsInput = z.input< + typeof GetEntityStoreStatsRequestParams +>; + +export type GetEntityStoreStatsResponse = z.infer; +export const GetEntityStoreStatsResponse = z.object({ + type: EntityType.optional(), + indexPattern: IndexPattern.optional(), + status: EngineStatus.optional(), + transforms: z.array(z.object({})).optional(), + indices: z.array(z.object({})).optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.schema.yaml new file mode 100644 index 0000000000000..8d8327d5e6468 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stats.schema.yaml @@ -0,0 +1,41 @@ +openapi: 3.0.0 + +info: + title: Get the entity store engine stats + version: '2023-10-31' +paths: + /api/entity_store/engines/{entityType}/stats: + post: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: GetEntityStoreStats + summary: Get the Entity Store engine stats + parameters: + - name: entityType + in: path + required: true + schema: + $ref: '../common.schema.yaml#/components/schemas/EntityType' + description: The entity type of the store (either 'user' or 'host'). + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + type: + $ref : '../common.schema.yaml#/components/schemas/EntityType' + indexPattern: + $ref : '../common.schema.yaml#/components/schemas/IndexPattern' + status: + $ref : '../common.schema.yaml#/components/schemas/EngineStatus' + transforms: + type: array + items: + type: object + indices: + type: array + items: + type: object diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.gen.ts new file mode 100644 index 0000000000000..ff3ef7a2f3eac --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.gen.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Stop the entity store engine + * version: 2023-10-31 + */ + +import { z } from '@kbn/zod'; + +import { EntityType } from '../common.gen'; + +export type StopEntityStoreRequestParams = z.infer; +export const StopEntityStoreRequestParams = z.object({ + /** + * The entity type of the store (either 'user' or 'host'). + */ + entityType: EntityType, +}); +export type StopEntityStoreRequestParamsInput = z.input; + +export type StopEntityStoreResponse = z.infer; +export const StopEntityStoreResponse = z.object({ + stopped: z.boolean().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.schema.yaml new file mode 100644 index 0000000000000..214f803a76e34 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/entity_store/engine/stop.schema.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 + +info: + title: Stop the entity store engine + version: '2023-10-31' +paths: + /api/entity_store/engines/{entityType}/stop: + post: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: StopEntityStore + summary: Stop the Entity Store engine + parameters: + - name: entityType + in: path + required: true + schema: + $ref: '../common.schema.yaml#/components/schemas/EntityType' + description: The entity type of the store (either 'user' or 'host'). + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + stopped: + type: boolean + diff --git a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts index edd0bfe89fc8c..c08f807d4926b 100644 --- a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -242,6 +242,33 @@ import type { InternalUploadAssetCriticalityRecordsResponse, UploadAssetCriticalityRecordsResponse, } from './entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; +import type { + DeleteEntityStoreRequestQueryInput, + DeleteEntityStoreRequestParamsInput, + DeleteEntityStoreResponse, +} from './entity_analytics/entity_store/engine/delete.gen'; +import type { + GetEntityStoreEngineRequestParamsInput, + GetEntityStoreEngineResponse, +} from './entity_analytics/entity_store/engine/get.gen'; +import type { + InitEntityStoreRequestParamsInput, + InitEntityStoreRequestBodyInput, + InitEntityStoreResponse, +} from './entity_analytics/entity_store/engine/init.gen'; +import type { ListEntityStoreEnginesResponse } from './entity_analytics/entity_store/engine/list.gen'; +import type { + StartEntityStoreRequestParamsInput, + StartEntityStoreResponse, +} from './entity_analytics/entity_store/engine/start.gen'; +import type { + GetEntityStoreStatsRequestParamsInput, + GetEntityStoreStatsResponse, +} from './entity_analytics/entity_store/engine/stats.gen'; +import type { + StopEntityStoreRequestParamsInput, + StopEntityStoreResponse, +} from './entity_analytics/entity_store/engine/stop.gen'; import type { DisableRiskEngineResponse } from './entity_analytics/risk_engine/engine_disable_route.gen'; import type { EnableRiskEngineResponse } from './entity_analytics/risk_engine/engine_enable_route.gen'; import type { InitRiskEngineResponse } from './entity_analytics/risk_engine/engine_init_route.gen'; @@ -620,6 +647,20 @@ Migrations are initiated per index. While the process is neither destructive nor }) .catch(catchAxiosErrorFormatAndThrow); } + async deleteEntityStore(props: DeleteEntityStoreProps) { + this.log.info(`${new Date().toISOString()} Calling API DeleteEntityStore`); + return this.kbnClient + .request({ + path: replaceParams('/api/entity_store/engines/{entityType}', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'DELETE', + + query: props.query, + }) + .catch(catchAxiosErrorFormatAndThrow); + } async deleteNote(props: DeleteNoteProps) { this.log.info(`${new Date().toISOString()} Calling API DeleteNote`); return this.kbnClient @@ -1155,6 +1196,30 @@ finalize it. }) .catch(catchAxiosErrorFormatAndThrow); } + async getEntityStoreEngine(props: GetEntityStoreEngineProps) { + this.log.info(`${new Date().toISOString()} Calling API GetEntityStoreEngine`); + return this.kbnClient + .request({ + path: replaceParams('/api/entity_store/engines/{entityType}', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + }) + .catch(catchAxiosErrorFormatAndThrow); + } + async getEntityStoreStats(props: GetEntityStoreStatsProps) { + this.log.info(`${new Date().toISOString()} Calling API GetEntityStoreStats`); + return this.kbnClient + .request({ + path: replaceParams('/api/entity_store/engines/{entityType}/stats', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + }) + .catch(catchAxiosErrorFormatAndThrow); + } /** * Gets notes */ @@ -1311,6 +1376,19 @@ finalize it. }) .catch(catchAxiosErrorFormatAndThrow); } + async initEntityStore(props: InitEntityStoreProps) { + this.log.info(`${new Date().toISOString()} Calling API InitEntityStore`); + return this.kbnClient + .request({ + path: replaceParams('/api/entity_store/engines/{entityType}/init', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + body: props.body, + }) + .catch(catchAxiosErrorFormatAndThrow); + } /** * Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine */ @@ -1367,6 +1445,18 @@ finalize it. }) .catch(catchAxiosErrorFormatAndThrow); } + async listEntityStoreEngines() { + this.log.info(`${new Date().toISOString()} Calling API ListEntityStoreEngines`); + return this.kbnClient + .request({ + path: '/api/entity_store/engines', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'GET', + }) + .catch(catchAxiosErrorFormatAndThrow); + } /** * Update specific fields of an existing detection rule using the `rule_id` or `id` field. */ @@ -1699,6 +1789,30 @@ detection engine rules. }) .catch(catchAxiosErrorFormatAndThrow); } + async startEntityStore(props: StartEntityStoreProps) { + this.log.info(`${new Date().toISOString()} Calling API StartEntityStore`); + return this.kbnClient + .request({ + path: replaceParams('/api/entity_store/engines/{entityType}/start', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + }) + .catch(catchAxiosErrorFormatAndThrow); + } + async stopEntityStore(props: StopEntityStoreProps) { + this.log.info(`${new Date().toISOString()} Calling API StopEntityStore`); + return this.kbnClient + .request({ + path: replaceParams('/api/entity_store/engines/{entityType}/stop', props.params), + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', + }, + method: 'POST', + }) + .catch(catchAxiosErrorFormatAndThrow); + } /** * Suggests user profiles. */ @@ -1809,6 +1923,10 @@ export interface CreateUpdateProtectionUpdatesNoteProps { export interface DeleteAssetCriticalityRecordProps { query: DeleteAssetCriticalityRecordRequestQueryInput; } +export interface DeleteEntityStoreProps { + query: DeleteEntityStoreRequestQueryInput; + params: DeleteEntityStoreRequestParamsInput; +} export interface DeleteNoteProps { body: DeleteNoteRequestBodyInput; } @@ -1902,6 +2020,12 @@ export interface GetEndpointSuggestionsProps { params: GetEndpointSuggestionsRequestParamsInput; body: GetEndpointSuggestionsRequestBodyInput; } +export interface GetEntityStoreEngineProps { + params: GetEntityStoreEngineRequestParamsInput; +} +export interface GetEntityStoreStatsProps { + params: GetEntityStoreStatsRequestParamsInput; +} export interface GetNotesProps { query: GetNotesRequestQueryInput; } @@ -1932,6 +2056,10 @@ export interface ImportRulesProps { export interface ImportTimelinesProps { body: ImportTimelinesRequestBodyInput; } +export interface InitEntityStoreProps { + params: InitEntityStoreRequestParamsInput; + body: InitEntityStoreRequestBodyInput; +} export interface InstallPrepackedTimelinesProps { body: InstallPrepackedTimelinesRequestBodyInput; } @@ -1984,6 +2112,12 @@ export interface SetAlertsStatusProps { export interface SetAlertTagsProps { body: SetAlertTagsRequestBodyInput; } +export interface StartEntityStoreProps { + params: StartEntityStoreRequestParamsInput; +} +export interface StopEntityStoreProps { + params: StopEntityStoreRequestParamsInput; +} export interface SuggestUserProfilesProps { query: SuggestUserProfilesRequestQueryInput; } diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 7d3edafedd1a9..e11965653526f 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -223,6 +223,11 @@ export const allowedExperimentalValues = Object.freeze({ * Enables the new data ingestion hub */ dataIngestionHubEnabled: false, + + /** + * Enables the new Entity Store engine routes + */ + entityStoreEnabled: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index 7ee7a3748df4b..9e56395f2af75 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -256,6 +256,187 @@ paths: summary: List Asset Criticality Records tags: - Security Solution Entity Analytics API + /api/entity_store/engines: + get: + operationId: ListEntityStoreEngines + responses: + '200': + content: + application/json: + schema: + type: object + properties: + count: + type: integer + engines: + items: + $ref: '#/components/schemas/EngineDescriptor' + type: array + description: Successful response + summary: List the Entity Store engines + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}': + delete: + operationId: DeleteEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + - description: Control flag to also delete the entity data. + in: query + name: data + required: false + schema: + type: boolean + responses: + '200': + content: + application/json: + schema: + type: object + properties: + deleted: + type: boolean + description: Successful response + summary: Delete the Entity Store engine + tags: + - Security Solution Entity Analytics API + get: + operationId: GetEntityStoreEngine + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/EngineDescriptor' + description: Successful response + summary: Get the Entity Store engine + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/init': + post: + operationId: InitEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + requestBody: + content: + application/json: + schema: + type: object + properties: + filter: + type: string + indexPattern: + $ref: '#/components/schemas/IndexPattern' + description: Schema for the engine initialization + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/EngineDescriptor' + description: Successful response + summary: Initialize the Entity Store + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/start': + post: + operationId: StartEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + started: + type: boolean + description: Successful response + summary: Start the Entity Store engine + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/stats': + post: + operationId: GetEntityStoreStats + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + indexPattern: + $ref: '#/components/schemas/IndexPattern' + indices: + items: + type: object + type: array + status: + $ref: '#/components/schemas/EngineStatus' + transforms: + items: + type: object + type: array + type: + $ref: '#/components/schemas/EntityType' + description: Successful response + summary: Get the Entity Store engine stats + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/stop': + post: + operationId: StopEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + stopped: + type: boolean + description: Successful response + summary: Stop the Entity Store engine + tags: + - Security Solution Entity Analytics API /api/risk_score/engine/schedule_now: post: operationId: ScheduleRiskEngineNow @@ -351,11 +532,35 @@ components: $ref: '#/components/schemas/AssetCriticalityLevel' required: - criticality_level + EngineDescriptor: + type: object + properties: + filter: + type: string + indexPattern: + $ref: '#/components/schemas/IndexPattern' + status: + $ref: '#/components/schemas/EngineStatus' + type: + $ref: '#/components/schemas/EntityType' + EngineStatus: + enum: + - installing + - started + - stopped + type: string + EntityType: + enum: + - user + - host + type: string IdField: enum: - host.name - user.name type: string + IndexPattern: + type: string RiskEngineScheduleNowErrorResponse: type: object properties: diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index 845b4ced91545..754c8f94d1c63 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -256,6 +256,187 @@ paths: summary: List Asset Criticality Records tags: - Security Solution Entity Analytics API + /api/entity_store/engines: + get: + operationId: ListEntityStoreEngines + responses: + '200': + content: + application/json: + schema: + type: object + properties: + count: + type: integer + engines: + items: + $ref: '#/components/schemas/EngineDescriptor' + type: array + description: Successful response + summary: List the Entity Store engines + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}': + delete: + operationId: DeleteEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + - description: Control flag to also delete the entity data. + in: query + name: data + required: false + schema: + type: boolean + responses: + '200': + content: + application/json: + schema: + type: object + properties: + deleted: + type: boolean + description: Successful response + summary: Delete the Entity Store engine + tags: + - Security Solution Entity Analytics API + get: + operationId: GetEntityStoreEngine + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/EngineDescriptor' + description: Successful response + summary: Get the Entity Store engine + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/init': + post: + operationId: InitEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + requestBody: + content: + application/json: + schema: + type: object + properties: + filter: + type: string + indexPattern: + $ref: '#/components/schemas/IndexPattern' + description: Schema for the engine initialization + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/EngineDescriptor' + description: Successful response + summary: Initialize the Entity Store + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/start': + post: + operationId: StartEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + started: + type: boolean + description: Successful response + summary: Start the Entity Store engine + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/stats': + post: + operationId: GetEntityStoreStats + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + indexPattern: + $ref: '#/components/schemas/IndexPattern' + indices: + items: + type: object + type: array + status: + $ref: '#/components/schemas/EngineStatus' + transforms: + items: + type: object + type: array + type: + $ref: '#/components/schemas/EntityType' + description: Successful response + summary: Get the Entity Store engine stats + tags: + - Security Solution Entity Analytics API + '/api/entity_store/engines/{entityType}/stop': + post: + operationId: StopEntityStore + parameters: + - description: The entity type of the store (either 'user' or 'host'). + in: path + name: entityType + required: true + schema: + $ref: '#/components/schemas/EntityType' + responses: + '200': + content: + application/json: + schema: + type: object + properties: + stopped: + type: boolean + description: Successful response + summary: Stop the Entity Store engine + tags: + - Security Solution Entity Analytics API /api/risk_score/engine/schedule_now: post: operationId: ScheduleRiskEngineNow @@ -351,11 +532,35 @@ components: $ref: '#/components/schemas/AssetCriticalityLevel' required: - criticality_level + EngineDescriptor: + type: object + properties: + filter: + type: string + indexPattern: + $ref: '#/components/schemas/IndexPattern' + status: + $ref: '#/components/schemas/EngineStatus' + type: + $ref: '#/components/schemas/EntityType' + EngineStatus: + enum: + - installing + - started + - stopped + type: string + EntityType: + enum: + - user + - host + type: string IdField: enum: - host.name - user.name type: string + IndexPattern: + type: string RiskEngineScheduleNowErrorResponse: type: object properties: diff --git a/x-pack/plugins/security_solution/kibana.jsonc b/x-pack/plugins/security_solution/kibana.jsonc index f682ca478a17f..e5840a6662e79 100644 --- a/x-pack/plugins/security_solution/kibana.jsonc +++ b/x-pack/plugins/security_solution/kibana.jsonc @@ -53,7 +53,8 @@ "notifications", "savedSearch", "unifiedDocViewer", - "charts" + "charts", + "entityManager" ], "optionalPlugins": [ "cloudExperiments", @@ -87,4 +88,4 @@ "common" ] } -} +} \ No newline at end of file diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index b39cba0cf4952..a5e0c8c60b1fc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -36,6 +36,7 @@ import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint import type { EndpointAuthz } from '../../../../../common/endpoint/types/authz'; import { riskEngineDataClientMock } from '../../../entity_analytics/risk_engine/risk_engine_data_client.mock'; import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/risk_score_data_client.mock'; +import { entityStoreDataClientMock } from '../../../entity_analytics/entity_store/entity_store_data_client.mock'; import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { detectionRulesClientMock } from '../../rule_management/logic/detection_rules_client/__mocks__/detection_rules_client'; @@ -72,6 +73,7 @@ export const createMockClients = () => { riskEngineDataClient: riskEngineDataClientMock.create(), riskScoreDataClient: riskScoreDataClientMock.create(), assetCriticalityDataClient: assetCriticalityDataClientMock.create(), + entityStoreDataClient: entityStoreDataClientMock.create(), internalFleetServices: { packages: packageServiceMock.createClient(), @@ -159,6 +161,7 @@ const createSecuritySolutionRequestContextMock = ( getRiskScoreDataClient: jest.fn(() => clients.riskScoreDataClient), getAssetCriticalityDataClient: jest.fn(() => clients.assetCriticalityDataClient), getAuditLogger: jest.fn(() => mockAuditLogger), + getEntityStoreDataClient: jest.fn(() => clients.entityStoreDataClient), }; }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts new file mode 100644 index 0000000000000..ce5a61fa7e6c9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +import type { EngineStatus } from '../../../../common/api/entity_analytics/entity_store/common.gen'; +import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; + +/** + * Default index pattern for entity store + * This is the same as the default index pattern for the SIEM app but might diverge in the future + */ +export const ENTITY_STORE_DEFAULT_SOURCE_INDICES = DEFAULT_INDEX_PATTERN; + +export const ENGINE_STATUS: Record, EngineStatus> = { + INSTALLING: 'installing', + STARTED: 'started', + STOPPED: 'stopped', +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/definition.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/definition.ts new file mode 100644 index 0000000000000..32859b9841e7f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/definition.ts @@ -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. + */ + +import { entityDefinitionSchema, type EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_STORE_DEFAULT_SOURCE_INDICES } from './constants'; + +export const HOST_ENTITY_DEFINITION: EntityDefinition = entityDefinitionSchema.parse({ + id: 'ea_host_entity_store', + name: 'EA Host Store', + type: 'host', + indexPatterns: ENTITY_STORE_DEFAULT_SOURCE_INDICES, + identityFields: ['host.name'], + displayNameTemplate: '{{host.name}}', + metadata: [ + 'host.domain', + 'host.hostname', + 'host.id', + 'host.ip', + 'host.mac', + 'host.name', + 'host.type', + 'host.architecture', + ], + history: { + timestampField: '@timestamp', + interval: '1m', + }, + version: '1.0.0', +}); + +export const USER_ENTITY_DEFINITION: EntityDefinition = entityDefinitionSchema.parse({ + id: 'ea_user_entity_store', + name: 'EA User Store', + type: 'user', + indexPatterns: ENTITY_STORE_DEFAULT_SOURCE_INDICES, + identityFields: ['user.name'], + displayNameTemplate: '{{user.name}}', + metadata: [ + 'user.domain', + 'user.email', + 'user.full_name', + 'user.hash', + 'user.id', + 'user.name', + 'user.roles', + ], + history: { + timestampField: '@timestamp', + interval: '1m', + }, + version: '1.0.0', +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.mock.ts new file mode 100644 index 0000000000000..095565343e130 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.mock.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +import type { EntityStoreDataClient } from './entity_store_data_client'; + +const createEntityStoreDataClientMock = () => + ({ + init: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + get: jest.fn(), + list: jest.fn(), + delete: jest.fn(), + } as unknown as jest.Mocked); + +export const entityStoreDataClientMock = { create: createEntityStoreDataClientMock }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts new file mode 100644 index 0000000000000..cb4d59139a25f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts @@ -0,0 +1,120 @@ +/* + * 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. + */ + +import type { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import type { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client'; + +import type { + InitEntityStoreRequestBody, + InitEntityStoreResponse, +} from '../../../../common/api/entity_analytics/entity_store/engine/init.gen'; +import type { + EngineDescriptor, + EntityType, +} from '../../../../common/api/entity_analytics/entity_store/common.gen'; +import { entityEngineDescriptorTypeName } from './saved_object'; +import { EngineDescriptorClient } from './saved_object/engine_descriptor'; +import { getEntityDefinition } from './utils/utils'; +import { ENGINE_STATUS } from './constants'; + +interface EntityStoreClientOpts { + logger: Logger; + esClient: ElasticsearchClient; + entityClient: EntityClient; + namespace: string; + soClient: SavedObjectsClientContract; +} + +export class EntityStoreDataClient { + private engineClient: EngineDescriptorClient; + constructor(private readonly options: EntityStoreClientOpts) { + this.engineClient = new EngineDescriptorClient(options.soClient); + } + + public async init( + entityType: EntityType, + { indexPattern = '', filter = '' }: InitEntityStoreRequestBody + ): Promise { + const definition = getEntityDefinition(entityType); + + this.options.logger.info(`Initializing entity store for ${entityType}`); + + const descriptor = await this.engineClient.init(entityType, definition, filter); + await this.options.entityClient.createEntityDefinition({ + definition: { + ...definition, + filter, + indexPatterns: indexPattern + ? [...definition.indexPatterns, ...indexPattern.split(',')] + : definition.indexPatterns, + }, + }); + const updated = await this.engineClient.update(definition.id, ENGINE_STATUS.STARTED); + + return { ...descriptor, ...updated }; + } + + public async start(entityType: EntityType) { + const definition = getEntityDefinition(entityType); + + const descriptor = await this.engineClient.get(entityType); + + if (descriptor.status !== ENGINE_STATUS.STOPPED) { + throw new Error( + `Cannot start Entity engine for ${entityType} when current status is: ${descriptor.status}` + ); + } + + this.options.logger.info(`Starting entity store for ${entityType}`); + await this.options.entityClient.startEntityDefinition(definition); + + return this.engineClient.update(definition.id, ENGINE_STATUS.STARTED); + } + + public async stop(entityType: EntityType) { + const definition = getEntityDefinition(entityType); + + const descriptor = await this.engineClient.get(entityType); + + if (descriptor.status !== ENGINE_STATUS.STARTED) { + throw new Error( + `Cannot stop Entity engine for ${entityType} when current status is: ${descriptor.status}` + ); + } + + this.options.logger.info(`Stopping entity store for ${entityType}`); + await this.options.entityClient.stopEntityDefinition(definition); + + return this.engineClient.update(definition.id, ENGINE_STATUS.STOPPED); + } + + public async get(entityType: EntityType) { + return this.engineClient.get(entityType); + } + + public async list() { + return this.options.soClient + .find({ + type: entityEngineDescriptorTypeName, + }) + .then(({ saved_objects: engines }) => ({ + engines: engines.map((engine) => engine.attributes), + count: engines.length, + })); + } + + public async delete(entityType: EntityType, deleteData: boolean) { + const { id } = getEntityDefinition(entityType); + + this.options.logger.info(`Deleting entity store for ${entityType}`); + + await this.options.entityClient.deleteEntityDefinition({ id, deleteData }); + await this.engineClient.delete(id); + + return { deleted: true }; + } +} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/delete.ts new file mode 100644 index 0000000000000..44352cfa47c57 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/delete.ts @@ -0,0 +1,64 @@ +/* + * 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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; + +import type { DeleteEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/delete.gen'; +import { + DeleteEntityStoreRequestQuery, + DeleteEntityStoreRequestParams, +} from '../../../../../common/api/entity_analytics/entity_store/engine/delete.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; + +export const deleteEntityEngineRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .delete({ + access: 'public', + path: '/api/entity_store/engines/{entityType}', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + query: buildRouteValidationWithZod(DeleteEntityStoreRequestQuery), + params: buildRouteValidationWithZod(DeleteEntityStoreRequestParams), + }, + }, + }, + + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + const body = await secSol + .getEntityStoreDataClient() + .delete(request.params.entityType, !!request.query.data); + + return response.ok({ body }); + } catch (e) { + logger.error('Error in DeleteEntityStore:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/get.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/get.ts new file mode 100644 index 0000000000000..79a74303c49c2 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/get.ts @@ -0,0 +1,62 @@ +/* + * 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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; + +import type { GetEntityStoreEngineResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/get.gen'; +import { GetEntityStoreEngineRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/get.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; + +export const getEntityEngineRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .get({ + access: 'public', + path: '/api/entity_store/engines/{entityType}', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: buildRouteValidationWithZod(GetEntityStoreEngineRequestParams), + }, + }, + }, + + async ( + context, + request, + response + ): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + const body = await secSol.getEntityStoreDataClient().get(request.params.entityType); + + return response.ok({ body }); + } catch (e) { + logger.error('Error in GetEntityStoreEngine:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/index.ts new file mode 100644 index 0000000000000..52aa6b22c2df8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { registerEntityStoreRoutes } from './register_entity_store_routes'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/init.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/init.ts new file mode 100644 index 0000000000000..6159cd584b06d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/init.ts @@ -0,0 +1,65 @@ +/* + * 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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; + +import type { InitEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/init.gen'; +import { + InitEntityStoreRequestBody, + InitEntityStoreRequestParams, +} from '../../../../../common/api/entity_analytics/entity_store/engine/init.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; + +export const initEntityEngineRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .post({ + access: 'public', + path: '/api/entity_store/engines/{entityType}/init', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: buildRouteValidationWithZod(InitEntityStoreRequestParams), + body: buildRouteValidationWithZod(InitEntityStoreRequestBody), + }, + }, + }, + + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + + const body: InitEntityStoreResponse = await secSol + .getEntityStoreDataClient() + .init(request.params.entityType, request.body); + + return response.ok({ body }); + } catch (e) { + logger.error('Error in InitEntityStore:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/list.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/list.ts new file mode 100644 index 0000000000000..53d9a8521ce00 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/list.ts @@ -0,0 +1,57 @@ +/* + * 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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; + +import type { ListEntityStoreEnginesResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/list.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; + +import type { EntityAnalyticsRoutesDeps } from '../../types'; + +export const listEntityEnginesRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .get({ + access: 'public', + path: '/api/entity_store/engines', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: {}, + }, + + async ( + context, + request, + response + ): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + const body = await secSol.getEntityStoreDataClient().list(); + + return response.ok({ body }); + } catch (e) { + logger.error('Error in ListEntityStoreEngines:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/register_entity_store_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/register_entity_store_routes.ts new file mode 100644 index 0000000000000..b78316b02c91e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/register_entity_store_routes.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +import type { EntityAnalyticsRoutesDeps } from '../../types'; +import { deleteEntityEngineRoute } from './delete'; +import { getEntityEngineRoute } from './get'; +import { initEntityEngineRoute } from './init'; +import { listEntityEnginesRoute } from './list'; +import { startEntityEngineRoute } from './start'; +import { stopEntityEngineRoute } from './stop'; + +export const registerEntityStoreRoutes = ({ router, logger }: EntityAnalyticsRoutesDeps) => { + initEntityEngineRoute(router, logger); + startEntityEngineRoute(router, logger); + stopEntityEngineRoute(router, logger); + deleteEntityEngineRoute(router, logger); + getEntityEngineRoute(router, logger); + listEntityEnginesRoute(router, logger); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/start.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/start.ts new file mode 100644 index 0000000000000..6ec6674a5473d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/start.ts @@ -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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; + +import type { StartEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/start.gen'; +import { StartEntityStoreRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/start.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; +import { ENGINE_STATUS } from '../constants'; + +export const startEntityEngineRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .post({ + access: 'public', + path: '/api/entity_store/engines/{entityType}/start', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: buildRouteValidationWithZod(StartEntityStoreRequestParams), + }, + }, + }, + + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + const engine = await secSol.getEntityStoreDataClient().start(request.params.entityType); + + return response.ok({ body: { started: engine.status === ENGINE_STATUS.STARTED } }); + } catch (e) { + logger.error('Error in StartEntityStore:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stats.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stats.ts new file mode 100644 index 0000000000000..1d7534c17f747 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stats.ts @@ -0,0 +1,58 @@ +/* + * 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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; + +import type { GetEntityStoreStatsResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/stats.gen'; +import { GetEntityStoreStatsRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/stats.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; + +export const getEntityEngineStatsRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .post({ + access: 'public', + path: '/api/entity_store/engines/{entityType}/stats', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: buildRouteValidationWithZod(GetEntityStoreStatsRequestParams), + }, + }, + }, + + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + // TODO + throw new Error('Not implemented'); + + // return response.ok({ body }); + } catch (e) { + logger.error('Error in GetEntityStoreStats:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts new file mode 100644 index 0000000000000..e1ddb464d1204 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/stop.ts @@ -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. + */ + +import type { IKibanaResponse, Logger } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; + +import type { StopEntityStoreResponse } from '../../../../../common/api/entity_analytics/entity_store/engine/stop.gen'; +import { StopEntityStoreRequestParams } from '../../../../../common/api/entity_analytics/entity_store/engine/stop.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; +import { ENGINE_STATUS } from '../constants'; + +export const stopEntityEngineRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger +) => { + router.versioned + .post({ + access: 'public', + path: '/api/entity_store/engines/{entityType}/stop', + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: buildRouteValidationWithZod(StopEntityStoreRequestParams), + }, + }, + }, + + async (context, request, response): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + const engine = await secSol.getEntityStoreDataClient().stop(request.params.entityType); + + return response.ok({ body: { stopped: engine.status === ENGINE_STATUS.STOPPED } }); + } catch (e) { + logger.error('Error in StopEntityStore:', e); + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts new file mode 100644 index 0000000000000..9d6a7821a2a9b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor.ts @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import type { + SavedObjectsClientContract, + SavedObjectsFindResponse, +} from '@kbn/core-saved-objects-api-server'; +import type { EntityDefinition } from '@kbn/entities-schema'; +import type { + EngineDescriptor, + EngineStatus, + EntityType, +} from '../../../../../common/api/entity_analytics/entity_store/common.gen'; + +import { entityEngineDescriptorTypeName } from './engine_descriptor_type'; +import { getByEntityTypeQuery, getEntityDefinition } from '../utils/utils'; +import { ENGINE_STATUS } from '../constants'; + +export class EngineDescriptorClient { + constructor(private readonly soClient: SavedObjectsClientContract) {} + + async init(entityType: EntityType, definition: EntityDefinition, filter: string) { + const engineDescriptor = await this.find(entityType); + + if (engineDescriptor.total > 0) + throw new Error(`Entity engine for ${entityType} already exists`); + + const { attributes } = await this.soClient.create( + entityEngineDescriptorTypeName, + { + status: ENGINE_STATUS.INSTALLING, + type: entityType, + indexPattern: definition.indexPatterns.join(','), + filter, + }, + { id: definition.id } + ); + return attributes; + } + + async update(id: string, status: EngineStatus) { + const { attributes } = await this.soClient.update( + entityEngineDescriptorTypeName, + id, + { status }, + { refresh: 'wait_for' } + ); + return attributes; + } + + async find(entityType: EntityType): Promise> { + return this.soClient.find({ + type: entityEngineDescriptorTypeName, + filter: getByEntityTypeQuery(entityType), + }); + } + + async get(entityType: EntityType): Promise { + const { id } = getEntityDefinition(entityType); + + const { attributes } = await this.soClient.get( + entityEngineDescriptorTypeName, + id + ); + + return attributes; + } + + async delete(id: string) { + return this.soClient.delete(entityEngineDescriptorTypeName, id); + } +} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor_type.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor_type.ts new file mode 100644 index 0000000000000..8513dfc018623 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/engine_descriptor_type.ts @@ -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. + */ + +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import type { SavedObjectsType } from '@kbn/core/server'; + +export const entityEngineDescriptorTypeName = 'entity-engine-status'; + +export const entityEngineDescriptorTypeMappings: SavedObjectsType['mappings'] = { + dynamic: false, + properties: { + indexPattern: { + type: 'keyword', + }, + filter: { + type: 'keyword', + }, + type: { + type: 'keyword', // EntityType: user | host + }, + status: { + type: 'keyword', // EngineStatus: installing | started | stopped + }, + }, +}; +export const entityEngineDescriptorType: SavedObjectsType = { + name: entityEngineDescriptorTypeName, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + hidden: false, + namespaceType: 'multiple-isolated', + mappings: entityEngineDescriptorTypeMappings, +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/index.ts new file mode 100644 index 0000000000000..d86800da1b5be --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/saved_object/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export * from './engine_descriptor_type'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/utils/utils.ts new file mode 100644 index 0000000000000..864fdb2367eb5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/utils/utils.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +import type { SavedObjectsFindResponse } from '@kbn/core-saved-objects-api-server'; +import type { + EngineDescriptor, + EntityType, +} from '../../../../../common/api/entity_analytics/entity_store/common.gen'; +import { HOST_ENTITY_DEFINITION, USER_ENTITY_DEFINITION } from '../definition'; +import { entityEngineDescriptorTypeName } from '../saved_object'; + +export const getEntityDefinition = (entityType: EntityType) => { + if (entityType === 'host') return HOST_ENTITY_DEFINITION; + if (entityType === 'user') return USER_ENTITY_DEFINITION; + + throw new Error(`Unsupported entity type: ${entityType}`); +}; + +export const ensureEngineExists = + (entityType: EntityType) => (results: SavedObjectsFindResponse) => { + if (results.total === 0) { + throw new Error(`Entity engine for ${entityType} does not exist`); + } + return results.saved_objects[0].attributes; + }; + +export const getByEntityTypeQuery = (entityType: EntityType) => { + return `${entityEngineDescriptorTypeName}.attributes.type: ${entityType}`; +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.ts index 31a7ccbb6f30c..b4eb0d36e21fb 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.ts @@ -9,9 +9,13 @@ import { registerAssetCriticalityRoutes } from './asset_criticality/routes'; import { registerRiskScoreRoutes } from './risk_score/routes'; import { registerRiskEngineRoutes } from './risk_engine/routes'; import type { EntityAnalyticsRoutesDeps } from './types'; +import { registerEntityStoreRoutes } from './entity_store/routes'; export const registerEntityAnalyticsRoutes = (routeDeps: EntityAnalyticsRoutesDeps) => { registerAssetCriticalityRoutes(routeDeps); registerRiskScoreRoutes(routeDeps); registerRiskEngineRoutes(routeDeps); + if (routeDeps.config.experimentalFeatures.entityStoreEnabled) { + registerEntityStoreRoutes(routeDeps); + } }; diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index 4bda7e0338aa8..6316ed3622841 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -10,6 +10,7 @@ import { memoize } from 'lodash'; import type { Logger, KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; import type { BuildFlavor } from '@kbn/config'; +import { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client'; import { DEFAULT_SPACE_ID } from '../common/constants'; import { AppClientFactory } from './client'; import type { ConfigType } from './config'; @@ -31,6 +32,7 @@ import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_scor import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; import { createDetectionRulesClient } from './lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client'; import { buildMlAuthz } from './lib/machine_learning/authz'; +import { EntityStoreDataClient } from './lib/entity_analytics/entity_store/entity_store_data_client'; export interface IRequestContextFactory { create( @@ -190,6 +192,22 @@ export class RequestContextFactory implements IRequestContextFactory { auditLogger: getAuditLogger(), }) ), + getEntityStoreDataClient: memoize(() => { + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const logger = options.logger; + const soClient = coreContext.savedObjects.client; + return new EntityStoreDataClient({ + namespace: getSpaceId(), + esClient, + logger, + soClient, + entityClient: new EntityClient({ + esClient, + soClient, + logger, + }), + }); + }), }; } } diff --git a/x-pack/plugins/security_solution/server/saved_objects.ts b/x-pack/plugins/security_solution/server/saved_objects.ts index 3659b15a04714..9412e62e6315c 100644 --- a/x-pack/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/saved_objects.ts @@ -15,6 +15,7 @@ import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules'; import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects'; import { manifestType, unifiedManifestType } from './endpoint/lib/artifacts/saved_object_mappings'; import { riskEngineConfigurationType } from './lib/entity_analytics/risk_engine/saved_object'; +import { entityEngineDescriptorType } from './lib/entity_analytics/entity_store/saved_object'; const types = [ noteType, @@ -26,6 +27,7 @@ const types = [ unifiedManifestType, signalsMigrationType, riskEngineConfigurationType, + entityEngineDescriptorType, protectionUpdatesNoteType, ]; diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 121eb7b1758f4..31e10b70adbcf 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -34,6 +34,7 @@ import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/ri import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; import type { IDetectionRulesClient } from './lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client_interface'; +import type { EntityStoreDataClient } from './lib/entity_analytics/entity_store/entity_store_data_client'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext { @@ -55,6 +56,7 @@ export interface SecuritySolutionApiRequestHandlerContext { getRiskEngineDataClient: () => RiskEngineDataClient; getRiskScoreDataClient: () => RiskScoreDataClient; getAssetCriticalityDataClient: () => AssetCriticalityDataClient; + getEntityStoreDataClient: () => EntityStoreDataClient; } export type SecuritySolutionRequestHandlerContext = CustomRequestHandlerContext<{ diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 6ccd61fd34394..8264a50988956 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -223,5 +223,7 @@ "@kbn/cloud-security-posture", "@kbn/security-solution-distribution-bar", "@kbn/cloud-security-posture-common", + "@kbn/entityManager-plugin", + "@kbn/entities-schema", ] } diff --git a/x-pack/test/api_integration/services/security_solution_api.gen.ts b/x-pack/test/api_integration/services/security_solution_api.gen.ts index cf9722e89b408..6a3d0cf8f3dce 100644 --- a/x-pack/test/api_integration/services/security_solution_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_api.gen.ts @@ -37,6 +37,10 @@ import { CreateUpdateProtectionUpdatesNoteRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/endpoint/protection_updates_note/protection_updates_note.gen'; import { DeleteAssetCriticalityRecordRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/delete_asset_criticality.gen'; +import { + DeleteEntityStoreRequestQueryInput, + DeleteEntityStoreRequestParamsInput, +} from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/delete.gen'; import { DeleteNoteRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/delete_note/delete_note_route.gen'; import { DeleteRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.gen'; import { DeleteTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/delete_timelines/delete_timelines_route.gen'; @@ -76,6 +80,8 @@ import { GetEndpointSuggestionsRequestParamsInput, GetEndpointSuggestionsRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/endpoint/suggestions/get_suggestions.gen'; +import { GetEntityStoreEngineRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/get.gen'; +import { GetEntityStoreStatsRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/stats.gen'; import { GetNotesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/timeline/get_notes/get_notes_route.gen'; import { GetPolicyResponseRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy_response.gen'; import { GetProtectionUpdatesNoteRequestParamsInput } from '@kbn/security-solution-plugin/common/api/endpoint/protection_updates_note/protection_updates_note.gen'; @@ -91,6 +97,10 @@ import { GetTimelineRequestQueryInput } from '@kbn/security-solution-plugin/comm import { GetTimelinesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/timeline/get_timelines/get_timelines_route.gen'; import { ImportRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/import_rules/import_rules_route.gen'; import { ImportTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/import_timelines/import_timelines_route.gen'; +import { + InitEntityStoreRequestParamsInput, + InitEntityStoreRequestBodyInput, +} from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/init.gen'; import { InstallPrepackedTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/install_prepackaged_timelines/install_prepackaged_timelines_route.gen'; import { PatchRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.gen'; import { PatchTimelineRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/patch_timelines/patch_timeline_route.gen'; @@ -110,6 +120,8 @@ import { SearchAlertsRequestBodyInput } from '@kbn/security-solution-plugin/comm import { SetAlertAssigneesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen'; import { SetAlertsStatusRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen'; import { SetAlertTagsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen'; +import { StartEntityStoreRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/start.gen'; +import { StopEntityStoreRequestParamsInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/engine/stop.gen'; import { SuggestUserProfilesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/users/suggest_user_profiles_route.gen'; import { TriggerRiskScoreCalculationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; import { UpdateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.gen'; @@ -313,6 +325,14 @@ Migrations are initiated per index. While the process is neither destructive nor .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + deleteEntityStore(props: DeleteEntityStoreProps) { + return supertest + .delete(replaceParams('/api/entity_store/engines/{entityType}', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, deleteNote(props: DeleteNoteProps) { return supertest .delete('/api/note') @@ -668,6 +688,20 @@ finalize it. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + getEntityStoreEngine(props: GetEntityStoreEngineProps) { + return supertest + .get(replaceParams('/api/entity_store/engines/{entityType}', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + getEntityStoreStats(props: GetEntityStoreStatsProps) { + return supertest + .post(replaceParams('/api/entity_store/engines/{entityType}/stats', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Gets notes */ @@ -764,6 +798,14 @@ finalize it. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + initEntityStore(props: InitEntityStoreProps) { + return supertest + .post(replaceParams('/api/entity_store/engines/{entityType}/init', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine */ @@ -799,6 +841,13 @@ finalize it. .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + listEntityStoreEngines() { + return supertest + .get('/api/entity_store/engines') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Update specific fields of an existing detection rule using the `rule_id` or `id` field. */ @@ -1018,6 +1067,20 @@ detection engine rules. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + startEntityStore(props: StartEntityStoreProps) { + return supertest + .post(replaceParams('/api/entity_store/engines/{entityType}/start', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + stopEntityStore(props: StopEntityStoreProps) { + return supertest + .post(replaceParams('/api/entity_store/engines/{entityType}/stop', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Suggests user profiles. */ @@ -1107,6 +1170,10 @@ export interface CreateUpdateProtectionUpdatesNoteProps { export interface DeleteAssetCriticalityRecordProps { query: DeleteAssetCriticalityRecordRequestQueryInput; } +export interface DeleteEntityStoreProps { + query: DeleteEntityStoreRequestQueryInput; + params: DeleteEntityStoreRequestParamsInput; +} export interface DeleteNoteProps { body: DeleteNoteRequestBodyInput; } @@ -1200,6 +1267,12 @@ export interface GetEndpointSuggestionsProps { params: GetEndpointSuggestionsRequestParamsInput; body: GetEndpointSuggestionsRequestBodyInput; } +export interface GetEntityStoreEngineProps { + params: GetEntityStoreEngineRequestParamsInput; +} +export interface GetEntityStoreStatsProps { + params: GetEntityStoreStatsRequestParamsInput; +} export interface GetNotesProps { query: GetNotesRequestQueryInput; } @@ -1229,6 +1302,10 @@ export interface ImportRulesProps { export interface ImportTimelinesProps { body: ImportTimelinesRequestBodyInput; } +export interface InitEntityStoreProps { + params: InitEntityStoreRequestParamsInput; + body: InitEntityStoreRequestBodyInput; +} export interface InstallPrepackedTimelinesProps { body: InstallPrepackedTimelinesRequestBodyInput; } @@ -1278,6 +1355,12 @@ export interface SetAlertsStatusProps { export interface SetAlertTagsProps { body: SetAlertTagsRequestBodyInput; } +export interface StartEntityStoreProps { + params: StartEntityStoreRequestParamsInput; +} +export interface StopEntityStoreProps { + params: StopEntityStoreRequestParamsInput; +} export interface SuggestUserProfilesProps { query: SuggestUserProfilesRequestQueryInput; } diff --git a/x-pack/test_serverless/api_integration/test_suites/security/platform_security/authorization.ts b/x-pack/test_serverless/api_integration/test_suites/security/platform_security/authorization.ts index 07dbcf7ded031..5cf491188ba96 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/platform_security/authorization.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/platform_security/authorization.ts @@ -349,6 +349,18 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:risk-engine-configuration/delete", "saved_object:risk-engine-configuration/bulk_delete", "saved_object:risk-engine-configuration/share_to_space", + "saved_object:entity-engine-status/bulk_get", + "saved_object:entity-engine-status/get", + "saved_object:entity-engine-status/find", + "saved_object:entity-engine-status/open_point_in_time", + "saved_object:entity-engine-status/close_point_in_time", + "saved_object:entity-engine-status/create", + "saved_object:entity-engine-status/bulk_create", + "saved_object:entity-engine-status/update", + "saved_object:entity-engine-status/bulk_update", + "saved_object:entity-engine-status/delete", + "saved_object:entity-engine-status/bulk_delete", + "saved_object:entity-engine-status/share_to_space", "saved_object:policy-settings-protection-updates-note/bulk_get", "saved_object:policy-settings-protection-updates-note/get", "saved_object:policy-settings-protection-updates-note/find", @@ -1182,6 +1194,18 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:risk-engine-configuration/delete", "saved_object:risk-engine-configuration/bulk_delete", "saved_object:risk-engine-configuration/share_to_space", + "saved_object:entity-engine-status/bulk_get", + "saved_object:entity-engine-status/get", + "saved_object:entity-engine-status/find", + "saved_object:entity-engine-status/open_point_in_time", + "saved_object:entity-engine-status/close_point_in_time", + "saved_object:entity-engine-status/create", + "saved_object:entity-engine-status/bulk_create", + "saved_object:entity-engine-status/update", + "saved_object:entity-engine-status/bulk_update", + "saved_object:entity-engine-status/delete", + "saved_object:entity-engine-status/bulk_delete", + "saved_object:entity-engine-status/share_to_space", "saved_object:policy-settings-protection-updates-note/bulk_get", "saved_object:policy-settings-protection-updates-note/get", "saved_object:policy-settings-protection-updates-note/find", @@ -1779,6 +1803,11 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:risk-engine-configuration/find", "saved_object:risk-engine-configuration/open_point_in_time", "saved_object:risk-engine-configuration/close_point_in_time", + "saved_object:entity-engine-status/bulk_get", + "saved_object:entity-engine-status/get", + "saved_object:entity-engine-status/find", + "saved_object:entity-engine-status/open_point_in_time", + "saved_object:entity-engine-status/close_point_in_time", "saved_object:policy-settings-protection-updates-note/bulk_get", "saved_object:policy-settings-protection-updates-note/get", "saved_object:policy-settings-protection-updates-note/find", @@ -2135,6 +2164,11 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:risk-engine-configuration/find", "saved_object:risk-engine-configuration/open_point_in_time", "saved_object:risk-engine-configuration/close_point_in_time", + "saved_object:entity-engine-status/bulk_get", + "saved_object:entity-engine-status/get", + "saved_object:entity-engine-status/find", + "saved_object:entity-engine-status/open_point_in_time", + "saved_object:entity-engine-status/close_point_in_time", "saved_object:policy-settings-protection-updates-note/bulk_get", "saved_object:policy-settings-protection-updates-note/get", "saved_object:policy-settings-protection-updates-note/find",