From c66a373d18a0f18e24b108a8653ba1ad5e2d2d85 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 23 Jan 2024 14:50:46 +0100 Subject: [PATCH] [Security Solution] Create Entity Store index (#175025) **This PR is going to be merged to the [entity-store-poc](https://github.com/elastic/kibana/tree/security/feature/entity-store-poc) feature branch; it won't impact the main branch.** ## Summary * Create `entity_store/init` route that creates the Entity Store index. * Create FTR tests. ### Out of scope * User fields are out of scope. * API privileges are out of scope. ### How to test it? * Call API ``` KIBANA_URL="http://localhost:5601" USER_PASS="{USER}:{PASSWORD}" curl "$KIBANA_URL/internal/entity_store/init" \ -H 'kbn-xsrf:bleh' \ --user "$USER_PASS"\ -X 'POST' \ -H 'elastic-api-version: 1' ``` * Open the console and check if the index `.entities.entities-default` exists #### Run tests **serverless** `yarn run initialize-server:ea:default entity_store serverless` `yarn run run-tests:ea:default entity_store serverless serverlessEnv` **ess** `yarn run initialize-server:ea:default entity_store ess` `yarn run run-tests:ea:default entity_store ess essEnv` ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/common/constants.ts | 2 + .../common/experimental_features.ts | 4 + .../routes/__mocks__/request_context.ts | 3 + .../entity_store/constants.ts | 97 ++++++++++++++ .../entity_store_data_client.mock.ts | 18 +++ .../entity_store_data_client.test.ts | 124 ++++++++++++++++++ .../entity_store/entity_store_data_client.ts | 34 +++++ .../entity_store/routes/index.ts | 4 +- .../entity_store/routes/init.ts | 51 +++++++ .../server/request_context_factory.ts | 9 ++ .../security_solution/server/routes/index.ts | 3 + .../plugins/security_solution/server/types.ts | 2 + .../entity_store/configs/ess.config.ts | 21 +++ .../entity_store/configs/serverless.config.ts | 16 +++ .../default_license/entity_store/index.ts | 13 ++ .../default_license/entity_store/init.ts | 46 +++++++ .../entity_analytics/utils/entity_store.ts | 55 ++++++++ .../entity_analytics/utils/index.ts | 1 + 18 files changed, 500 insertions(+), 3 deletions(-) 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/entity_store_data_client.mock.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.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/init.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/ess.config.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/serverless.config.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/index.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/init.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index a897c108d6f62..d376bb53c41a1 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -248,6 +248,8 @@ export const DETECTION_ENGINE_RULES_BULK_UPDATE = `${DETECTION_ENGINE_RULES_URL}/_bulk_update` as const; export * from './entity_analytics/constants'; +export const INTERNAL_ENTITY_STORE_URL = '/internal/entity_store' as const; +export const ENTITY_STORE_INIT_URL = `${INTERNAL_ENTITY_STORE_URL}/init`; export const INTERNAL_DASHBOARDS_URL = `/internal/dashboards` as const; export const INTERNAL_TAGS_URL = `/internal/tags`; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 918d734561fa5..e1b2c4785bd3e 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -184,6 +184,10 @@ export const allowedExperimentalValues = Object.freeze({ */ alertSuppressionForNonSequenceEqlRuleEnabled: false, + /** + * Enables Entity Store POC + */ + entityStoreEnabled: true, /** * Enables experimental Experimental S1 integration data to be available in Analyzer */ 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 cb0b48b8e3f0d..44dfa0bedcdc6 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 @@ -37,6 +37,7 @@ 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 { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock'; +import { entityStoreDataClientMock } from '../../../entity_analytics/entity_store/entity_store_data_client.mock'; export const createMockClients = () => { const core = coreMock.createRequestHandlerContext(); @@ -67,6 +68,7 @@ export const createMockClients = () => { riskEngineDataClient: riskEngineDataClientMock.create(), riskScoreDataClient: riskScoreDataClientMock.create(), assetCriticalityDataClient: assetCriticalityDataClientMock.create(), + entityStoreyDataClient: entityStoreDataClientMock.create(), }; }; @@ -148,6 +150,7 @@ const createSecuritySolutionRequestContextMock = ( getRiskEngineDataClient: jest.fn(() => clients.riskEngineDataClient), getRiskScoreDataClient: jest.fn(() => clients.riskScoreDataClient), getAssetCriticalityDataClient: jest.fn(() => clients.assetCriticalityDataClient), + getEntityStoreDataClient: jest.fn(() => clients.entityStoreyDataClient), }; }; 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..634d7bd3d750f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/constants.ts @@ -0,0 +1,97 @@ +/* + * 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 { FieldMap } from '@kbn/alerts-as-data-utils'; + +export const entityStoreFieldMap: FieldMap = { + '@timestamp': { + type: 'date', + array: false, + required: false, + }, + // user or host + entity_type: { + type: 'keyword', + array: false, + required: true, + }, + // HOST + 'host.architecture': { + type: 'keyword', + required: false, + array: true, + }, + 'host.id': { + type: 'keyword', + required: false, + array: true, + }, + 'host.ip': { + type: 'ip', + required: false, + array: true, + }, + 'host.name': { + type: 'keyword', + required: true, + array: false, + }, + 'host.os.platform': { + type: 'keyword', + required: false, + array: true, + }, + 'host.os.version': { + type: 'keyword', + required: false, + array: true, + }, + // AGENT + 'agent.type': { + type: 'keyword', + required: false, + array: true, + }, + 'agent.id': { + type: 'keyword', + required: false, + array: true, + }, + // CLOUD + 'cloud.provider': { + type: 'keyword', + required: false, + array: true, + }, + 'cloud.region': { + type: 'keyword', + required: false, + array: true, + }, + // RISK SCORE + 'host.risk.calculated_level': { + type: 'keyword', + array: false, + required: false, + }, + 'host.risk.calculated_score': { + type: 'float', + array: false, + required: false, + }, + 'host.risk.calculated_score_norm': { + type: 'float', + array: false, + required: false, + }, + // ASSET CRITICALITY + 'host.asset.criticality': { + type: 'keyword', + array: false, + required: false, + }, +} as const; 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..c4d20022d39bf --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.mock.ts @@ -0,0 +1,18 @@ +/* + * 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 = () => + ({ + doesIndexExist: jest.fn(), + getStatus: jest.fn(), + init: jest.fn(), + search: 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.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts new file mode 100644 index 0000000000000..a6023d05abdea --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.test.ts @@ -0,0 +1,124 @@ +/* + * 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 { loggingSystemMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { createOrUpdateIndex } from '../utils/create_or_update_index'; +import { EntityStoreDataClient } from './entity_store_data_client'; + +jest.mock('../utils/create_or_update_index', () => ({ + createOrUpdateIndex: jest.fn(), +})); + +describe('EntityStoreDataClient', () => { + let entityStoreDataClient: EntityStoreDataClient; + let logger: ReturnType; + const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + const options = { + logger, + esClient, + namespace: 'default', + }; + entityStoreDataClient = new EntityStoreDataClient(options); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should initialize entity store resources successfully', async () => { + await entityStoreDataClient.init(); + + expect(createOrUpdateIndex).toHaveBeenCalledWith({ + logger, + esClient, + options: { + index: '.entities.entities-default', + mappings: { + dynamic: 'strict', + properties: { + '@timestamp': { + ignore_malformed: false, + type: 'date', + }, + agent: { + properties: { + id: { + type: 'keyword', + }, + type: { + type: 'keyword', + }, + }, + }, + cloud: { + properties: { + provider: { + type: 'keyword', + }, + region: { + type: 'keyword', + }, + }, + }, + entity_type: { + type: 'keyword', + }, + host: { + properties: { + architecture: { + type: 'keyword', + }, + asset: { + properties: { + criticality: { + type: 'keyword', + }, + }, + }, + id: { + type: 'keyword', + }, + ip: { + type: 'ip', + }, + name: { + type: 'keyword', + }, + os: { + properties: { + platform: { + type: 'keyword', + }, + version: { + type: 'keyword', + }, + }, + }, + risk: { + properties: { + calculated_level: { + type: 'keyword', + }, + calculated_score: { + type: 'float', + }, + calculated_score_norm: { + type: 'float', + }, + }, + }, + }, + }, + }, + }, + }, + }); + }); +}); 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..15efd5761be62 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/entity_store_data_client.ts @@ -0,0 +1,34 @@ +/* + * 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 } from '@kbn/core/server'; +import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; +import { getEntityStoreIndex } from '../../../../common/entity_analytics/entity_store'; +import { createOrUpdateIndex } from '../utils/create_or_update_index'; +import { entityStoreFieldMap } from './constants'; + +interface EntityStoreClientOpts { + logger: Logger; + esClient: ElasticsearchClient; + namespace: string; +} + +export class EntityStoreDataClient { + constructor(private readonly options: EntityStoreClientOpts) {} + /** + * It creates the entity store index or update mappings if index exists + */ + public async init() { + await createOrUpdateIndex({ + esClient: this.options.esClient, + logger: this.options.logger, + options: { + index: getEntityStoreIndex(this.options.namespace), + mappings: mappingFromFieldMap(entityStoreFieldMap, 'strict'), + }, + }); + } +} 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 index 7b8095b9f551a..33ce2f80f4292 100644 --- 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 @@ -5,6 +5,4 @@ * 2.0. */ -// 🚧 TODO: make the entity store - -export {}; +export { entityStoreInitRoute } from './init'; 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..65b3f4e44a2d9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/routes/init.ts @@ -0,0 +1,51 @@ +/* + * 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 { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { ENTITY_STORE_INIT_URL } from '../../../../../common/constants'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +export const entityStoreInitRoute = (router: SecuritySolutionPluginRouter) => { + router.versioned + .post({ + access: 'internal', + path: ENTITY_STORE_INIT_URL, + options: { + tags: ['access:securitySolution'], // TODO entity store access `access:${APP_ID}-entity-analytics` + }, + }) + .addVersion( + { version: '1', validate: {} }, + // TODO Implement entity store privileges like `withRiskEnginePrivilegeCheck` in risk_engine_privileges.ts + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const securitySolution = await context.securitySolution; + const entityStoreDataClient = securitySolution.getEntityStoreDataClient(); + + try { + await entityStoreDataClient.init(); + + return response.ok({ + body: { + result: { + entity_store_created: true, + errors: [], + }, + }, + }); + } catch (e) { + const error = transformError(e); + + return siemResponse.error({ + statusCode: error.statusCode, + body: { message: error.message, full_error: JSON.stringify(e) }, + bypassErrorFormat: true, + }); + } + } + ); +}; 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 3f1d9f818e431..5cdfba3dc8947 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -28,6 +28,7 @@ import type { EndpointAppContextService } from './endpoint/endpoint_app_context_ import { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; +import { EntityStoreDataClient } from './lib/entity_analytics/entity_store/entity_store_data_client'; export interface IRequestContextFactory { create( @@ -161,6 +162,14 @@ export class RequestContextFactory implements IRequestContextFactory { namespace: getSpaceId(), }) ), + getEntityStoreDataClient: memoize( + () => + new EntityStoreDataClient({ + logger: options.logger, + esClient: coreContext.elasticsearch.client.asCurrentUser, + namespace: getSpaceId(), + }) + ), }; } } diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 4365eb4bcc3fa..d9f8383d34322 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -147,4 +147,7 @@ export const initRoutes = ( registerEntityAnalyticsRoutes({ router, config, getStartServices, logger }); // Security Integrations getFleetManagedIndexTemplatesRoute(router); + if (config.experimentalFeatures.entityStoreEnabled) { + entityStoreInitRoute(router); + } }; diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 7833c1dff6b8a..e1e68c598963a 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -32,6 +32,7 @@ import type { EndpointInternalFleetServicesInterface } from './endpoint/services import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; +import type { EntityStoreDataClient } from './lib/entity_analytics/entity_store/entity_store_data_client'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext { @@ -51,6 +52,7 @@ export interface SecuritySolutionApiRequestHandlerContext { getRiskEngineDataClient: () => RiskEngineDataClient; getRiskScoreDataClient: () => RiskScoreDataClient; getAssetCriticalityDataClient: () => AssetCriticalityDataClient; + getEntityStoreDataClient: () => EntityStoreDataClient; } export type SecuritySolutionRequestHandlerContext = CustomRequestHandlerContext<{ diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/ess.config.ts new file mode 100644 index 0000000000000..f42a791cedbd3 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/ess.config.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 { FtrConfigProviderContext } from '@kbn/test'; +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.trial') + ); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: 'Entity Analytics - Entity Store Integration Tests - ESS Env - Trial License', + }, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/serverless.config.ts new file mode 100644 index 0000000000000..cd5e87820ed41 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/configs/serverless.config.ts @@ -0,0 +1,16 @@ +/* + * 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 { createTestConfig } from '../../../../../config/serverless/config.base'; + +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: + 'Entity Analytics - Entity Store Integration Tests - Serverless Env - Complete License', + }, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/index.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/index.ts new file mode 100644 index 0000000000000..3e835423cd3be --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/index.ts @@ -0,0 +1,13 @@ +/* + * 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 { FtrProviderContext } from '../../../../ftr_provider_context'; +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Entity Store API', function () { + loadTestFile(require.resolve('./init')); + }); +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/init.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/init.ts new file mode 100644 index 0000000000000..3ab03b614ce32 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/default_license/entity_store/init.ts @@ -0,0 +1,46 @@ +/* + * 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 expect from '@kbn/expect'; +import { cleanEntityStore, entityStoreRouteHelpersFactory } from '../../utils'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const es = getService('es'); + const supertest = getService('supertest'); + const entityStoreRoutes = entityStoreRouteHelpersFactory(supertest); + const log = getService('log'); + + describe('@ess @serverless Entity Store API', () => { + afterEach(async () => { + await cleanEntityStore({ es, log }); + }); + + describe('init api', () => { + it('should return response with success status', async () => { + const response = await entityStoreRoutes.init(); + expect(response.body).to.eql({ + result: { + errors: [], + entity_store_created: true, + }, + }); + }); + + it('should install resources on init call', async () => { + const latestIndexName = '.entities.entities-default'; + + await entityStoreRoutes.init(); + + const indexExist = await es.indices.exists({ + index: latestIndexName, + }); + expect(indexExist).to.eql(true); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts new file mode 100644 index 0000000000000..3e28d3087579a --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts @@ -0,0 +1,55 @@ +/* + * 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 SuperTest from 'supertest'; +import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common'; +import { ENTITY_STORE_INIT_URL } from '@kbn/security-solution-plugin/common/constants'; +import { ToolingLog } from '@kbn/tooling-log'; +import type { Client } from '@elastic/elasticsearch'; +import { routeWithNamespace } from '../../detections_response/utils'; + +export const entityStoreRouteHelpersFactory = ( + supertest: SuperTest.SuperTest, + namespace?: string +) => ({ + init: async () => + await supertest + .post(routeWithNamespace(ENTITY_STORE_INIT_URL, namespace)) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send() + .expect(200), +}); + +export const cleanEntityStore = async ({ + es, + log, +}: { + es: Client; + log: ToolingLog; +}): Promise => { + await deleteEntityStoreIndices({ log, es }); +}; + +const deleteEntityStoreIndices = async ({ + log, + es, + namespace = 'default', +}: { + log: ToolingLog; + es: Client; + namespace?: string; +}) => { + try { + await es.indices.delete({ + index: [`.entities.entities-${namespace}`], + }); + } catch (e) { + log.warning(`Error deleting entity store indices: ${e.message}`); + } +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts index 7ff049a997da1..8f6a368807241 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/index.ts @@ -8,3 +8,4 @@ export * from './risk_engine'; export * from './get_risk_engine_stats'; export * from './asset_criticality'; +export * from './entity_store';