diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 26a514f6fc10d..5d7aa4d9d82d0 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -309,6 +309,19 @@ "schemaVersion" ], "enterprise_search_telemetry": [], + "entity-analytics-monitoring-entity-source": [ + "enabled", + "error", + "filter", + "indexPattern", + "integrationName", + "managed", + "matchers", + "matchers.fields", + "matchers.values", + "name", + "type" + ], "entity-definition": [ "description", "filter", @@ -828,6 +841,19 @@ "job.job_id", "model_id" ], + "monitoring-entity-source": [ + "enabled", + "error", + "filter", + "indexPattern", + "integrationName", + "managed", + "matchers", + "matchers.fields", + "matchers.values", + "name", + "type" + ], "monitoring-telemetry": [ "reportedClusterUuids" ], diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index d2e8c3a1b74f8..a49b1f4ff9f1b 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1059,6 +1059,37 @@ "dynamic": false, "properties": {} }, + "entity-analytics-monitoring-entity-source": { + "dynamic": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "error": { + "type": "keyword" + }, + "filter": { + "dynamic": false, + "type": "object" + }, + "integrationName": { + "type": "keyword" + }, + "managed": { + "type": "boolean" + }, + "matchers": { + "dynamic": false, + "type": "object" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, "entity-definition": { "dynamic": false, "properties": { diff --git a/src/core/packages/saved-objects/server-internal/src/object_types/index.ts b/src/core/packages/saved-objects/server-internal/src/object_types/index.ts index 91f331a85375c..a82bbddb1cb60 100644 --- a/src/core/packages/saved-objects/server-internal/src/object_types/index.ts +++ b/src/core/packages/saved-objects/server-internal/src/object_types/index.ts @@ -10,5 +10,5 @@ export { registerCoreObjectTypes } from './registration'; // set minimum number of registered saved objects to ensure no object types are removed after 8.8 -// declared in internal implementation exclicilty to prevent unintended changes. -export const SAVED_OBJECT_TYPES_COUNT = 133 as const; +// declared in internal implementation explicitly to prevent unintended changes. +export const SAVED_OBJECT_TYPES_COUNT = 134 as const; 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 b3149d04e8777..8848ae7a78aaf 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 @@ -96,6 +96,7 @@ describe('checking migration metadata changes on all registered SO types', () => "endpoint:unified-user-artifact-manifest": "71c7fcb52c658b21ea2800a6b6a76972ae1c776e", "endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b", "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", + "entity-analytics-monitoring-entity-source": "207ca6f7ed3a04ebe33d81675a09e253446fe897", "entity-definition": "1c6bff35c423d5dc5650bc806cf2899e4706a0bc", "entity-discovery-api-key": "c267a65c69171d1804362155c1378365f5acef88", "entity-engine-status": "09f6a617020708e4f638137e5ef35bd9534133be", diff --git a/src/core/server/integration_tests/saved_objects/registration/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/registration/type_registrations.test.ts index 34c3975a6db54..03b33a3e404d0 100644 --- a/src/core/server/integration_tests/saved_objects/registration/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/registration/type_registrations.test.ts @@ -56,6 +56,7 @@ const previouslyRegisteredTypes = [ 'endpoint:user-artifact-manifest', 'endpoint:unified-user-artifact-manifest', 'enterprise_search_telemetry', + 'entity-analytics-monitoring-entity-source', 'entity-definition', 'entity-discovery-api-key', 'epm-packages', diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen.ts new file mode 100644 index 0000000000000..14f90de1cc715 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Monitoring Entity Source Schema + * version: 1 + */ + +import { z } from '@kbn/zod'; + +export type MonitoringEntitySourceDescriptor = z.infer; +export const MonitoringEntitySourceDescriptor = z.object({ + type: z.string(), + name: z.string(), + managed: z.boolean().optional(), + indexPattern: z.string().optional(), + enabled: z.boolean().optional(), + error: z.string().optional(), + integrationName: z.string().optional(), + matchers: z + .array( + z.object({ + fields: z.array(z.string()), + values: z.array(z.string()), + }) + ) + .optional(), + filter: z.object({}).optional(), +}); + +export type MonitoringEntitySourceResponse = z.infer; +export const MonitoringEntitySourceResponse = z.object({ + id: z.string().optional(), + name: z.string().optional(), + type: z.string().optional(), + indexPattern: z.string().optional(), + integrationName: z.string().optional(), + enabled: z.boolean().optional(), + matchers: z + .array( + z.object({ + fields: z.array(z.string()), + values: z.array(z.string()), + }) + ) + .optional(), +}); diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.schema.yaml new file mode 100644 index 0000000000000..4e1c209a6da35 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.schema.yaml @@ -0,0 +1,145 @@ +openapi: 3.0.0 +info: + title: Monitoring Entity Source Schema + description: Schema for managing entity source configurations in the monitoring system. + version: "1" + +paths: + /api/entity_analytics/monitoring/entity_source: + post: + operationId: createEntitySource + summary: Create a new entity source configuration + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MonitoringEntitySourceDescriptor" + responses: + "200": + description: Entity source created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/MonitoringEntitySourceResponse" + + /api/entity_analytics/monitoring/entity_source/{id}: + get: + operationId: getEntitySource + summary: Get an entity source configuration by ID + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: Entity source details retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/MonitoringEntitySourceResponse" + + put: + operationId: updateEntitySource + summary: Update an entity source configuration + parameters: + - name: id + in: path + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MonitoringEntitySourceDescriptor" + responses: + "200": + description: Entity source updated successfully + + delete: + operationId: deleteEntitySource + summary: Delete an entity source configuration + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + "200": + description: Entity source deleted successfully + +components: + schemas: + MonitoringEntitySourceDescriptor: + type: object + required: [type, name] + properties: + type: + type: string + name: + type: string + managed: + type: boolean + indexPattern: + type: string + enabled: + type: boolean + error: + type: string + integrationName: + type: string + matchers: + type: array + items: + type: object + required: + - fields + - values + properties: + fields: + type: array + items: + type: string + values: + type: array + items: + type: string + filter: + type: object + + MonitoringEntitySourceResponse: + type: object + properties: + id: + type: string + name: + type: string + type: + type: string + indexPattern: + type: string + integrationName: + type: string + enabled: + type: boolean + matchers: + type: array + items: + type: object + required: + - fields + - values + properties: + fields: + type: array + items: + type: string + values: + type: array + items: + type: string \ No newline at end of file diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 9de652ee44f4c..18d7d7ea35bb9 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -191,6 +191,7 @@ const createSecuritySolutionRequestContextMock = ( getEntityStoreApiKeyManager: jest.fn(), getEntityStoreDataClient: jest.fn(() => clients.entityStoreDataClient), getPrivilegeMonitoringDataClient: jest.fn(() => clients.privilegeMonitorDataClient), + getMonitoringEntitySourceDataClient: jest.fn(), getSiemRuleMigrationsClient: jest.fn(() => clients.siemRuleMigrationsClient), getInferenceClient: jest.fn(() => clients.getInferenceClient()), getAssetInventoryClient: jest.fn(() => clients.assetInventoryDataClient), diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client.test.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client.test.ts new file mode 100644 index 0000000000000..f95aa247786ee --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client.test.ts @@ -0,0 +1,135 @@ +/* + * 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 { MonitoringEntitySourceDataClient } from './monitoring_entity_source_data_client'; +import { + savedObjectsClientMock, + elasticsearchServiceMock, + loggingSystemMock, +} from '@kbn/core/server/mocks'; +import { monitoringEntitySourceTypeName } from './saved_object/monitoring_entity_source_type'; +import type { SavedObject, SavedObjectsFindResponse } from '@kbn/core/server'; + +describe('MonitoringEntitySourceDataClient', () => { + const mockSavedObjectClient = savedObjectsClientMock.create(); + const clusterClientMock = elasticsearchServiceMock.createScopedClusterClient(); + const loggerMock = loggingSystemMock.createLogger(); + const namespace = 'test-namespace'; + loggerMock.debug = jest.fn(); + + const defaultOpts = { + logger: loggerMock, + clusterClient: clusterClientMock, + namespace: 'test-namespace', + soClient: mockSavedObjectClient, + kibanaVersion: '8.0.0', + }; + + const testDescriptor = { + type: 'test-type', + name: 'Test Source', + matchers: [ + { + fields: ['user.role'], + values: ['admin'], + }, + ], + filter: {}, + }; + + let dataClient: MonitoringEntitySourceDataClient; + beforeEach(() => { + jest.clearAllMocks(); + dataClient = new MonitoringEntitySourceDataClient(defaultOpts); + }); + + describe('init', () => { + it('should initialize Monitoring Entity Source Sync Config Successfully', async () => { + defaultOpts.soClient.find.mockResolvedValue({ + total: 0, + saved_objects: [], + } as unknown as SavedObjectsFindResponse); + + defaultOpts.soClient.create.mockResolvedValue({ + id: `entity-analytics-monitoring-entity-source-${namespace}`, + type: monitoringEntitySourceTypeName, + attributes: testDescriptor, + references: [], + }); + + const result = await dataClient.init(testDescriptor); + + expect(defaultOpts.soClient.create).toHaveBeenCalledWith( + monitoringEntitySourceTypeName, + testDescriptor, + { id: `entity-analytics-monitoring-entity-source-${namespace}` } + ); + + expect(result).toEqual(testDescriptor); + }); + }); + + describe('get', () => { + it('should get Monitoring Entity Source Sync Config Successfully', async () => { + const getResponse = { + id: `entity-analytics-monitoring-entity-source-${namespace}`, + type: monitoringEntitySourceTypeName, + attributes: testDescriptor, + references: [], + }; + defaultOpts.soClient.get.mockResolvedValue(getResponse as unknown as SavedObject); + const result = await dataClient.get(); + expect(defaultOpts.soClient.get).toHaveBeenCalledWith( + monitoringEntitySourceTypeName, + `entity-analytics-monitoring-entity-source-${namespace}` + ); + expect(result).toEqual(getResponse.attributes); + }); + }); + + describe('update', () => { + it('should update Monitoring Entity Source Sync Config Successfully', async () => { + const existingDescriptor = { + total: 1, + saved_objects: [{ attributes: testDescriptor }], + } as unknown as SavedObjectsFindResponse; + + defaultOpts.soClient.find.mockResolvedValue( + existingDescriptor as unknown as SavedObjectsFindResponse + ); + + defaultOpts.soClient.update.mockResolvedValue({ + id: `entity-analytics-monitoring-entity-source-${namespace}`, + type: monitoringEntitySourceTypeName, + attributes: { ...testDescriptor, name: 'Updated Source' }, + references: [], + }); + + const updatedDescriptor = { ...testDescriptor, name: 'Updated Source' }; + const result = await dataClient.init(testDescriptor); + + expect(defaultOpts.soClient.update).toHaveBeenCalledWith( + monitoringEntitySourceTypeName, + `entity-analytics-monitoring-entity-source-${namespace}`, + testDescriptor, + { refresh: 'wait_for' } + ); + + expect(result).toEqual(updatedDescriptor); + }); + }); + + describe('delete', () => { + it('should delete Monitoring Entity Source Sync Config Successfully', async () => { + await dataClient.delete(); + expect(mockSavedObjectClient.delete).toHaveBeenCalledWith( + monitoringEntitySourceTypeName, + `entity-analytics-monitoring-entity-source-${namespace}` + ); + }); + }); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client.ts new file mode 100644 index 0000000000000..1b106825c1ca2 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client.ts @@ -0,0 +1,68 @@ +/* + * 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 { IScopedClusterClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import type { + MonitoringEntitySourceDescriptor, + MonitoringEntitySourceResponse, +} from '../../../../common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen'; +import { MonitoringEntitySourceDescriptorClient } from './saved_object/monitoring_entity_source'; + +interface MonitoringEntitySourceDataClientOpts { + logger: Logger; + clusterClient: IScopedClusterClient; + soClient: SavedObjectsClientContract; + namespace: string; +} + +export class MonitoringEntitySourceDataClient { + private monitoringEntitySourceClient: MonitoringEntitySourceDescriptorClient; + constructor(private readonly opts: MonitoringEntitySourceDataClientOpts) { + this.monitoringEntitySourceClient = new MonitoringEntitySourceDescriptorClient({ + soClient: this.opts.soClient, + namespace: this.opts.namespace, + }); + } + + public async init( + input: MonitoringEntitySourceDescriptor + ): Promise { + const descriptor = await this.monitoringEntitySourceClient.create(input); + this.log('debug', 'Initializing MonitoringEntitySourceDataClient Saved Object'); + return descriptor; + } + + public async get(): Promise { + this.log('debug', 'Getting Monitoring Entity Source Sync saved object'); + return this.monitoringEntitySourceClient.get(); + } + + public async update(update: Partial) { + this.log('debug', 'Updating Monitoring Entity Source Sync saved object'); + + const sanitizedUpdate = { + ...update, + matchers: update.matchers?.map((matcher) => ({ + fields: matcher.fields ?? [], + values: matcher.values ?? [], + })), + }; + + return this.monitoringEntitySourceClient.update(sanitizedUpdate); + } + + public async delete() { + this.log('debug', 'Deleting Monitoring Entity Source Sync saved object'); + return this.monitoringEntitySourceClient.delete(); + } + + private log(level: Exclude, msg: string) { + this.opts.logger[level]( + `[Monitoring Entity Source Sync][namespace: ${this.opts.namespace}] ${msg}` + ); + } +} diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/monitoring_entity_source.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/monitoring_entity_source.ts new file mode 100644 index 0000000000000..239f7a09a9ea2 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/monitoring_entity_source.ts @@ -0,0 +1,103 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/* + * 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 type { IKibanaResponse, Logger } from '@kbn/core/server'; + +import type { MonitoringEntitySourceResponse } from '../../../../../common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen'; +import { API_VERSIONS, APP_ID } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; +import { MonitoringEntitySourceDescriptor } from '../../../../../common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen'; + +export const monitoringEntitySourceRoute = ( + router: EntityAnalyticsRoutesDeps['router'], + logger: Logger, + config: EntityAnalyticsRoutesDeps['config'] +) => { + router.versioned + .post({ + access: 'public', + path: '/api/entity_analytics/monitoring/entity_source', + security: { + authz: { + requiredPrivileges: ['securitySolution', `${APP_ID}-entity-analytics`], + }, + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + body: MonitoringEntitySourceDescriptor, + }, + }, + }, + async ( + context, + request, + response + ): Promise> => { + const siemResponse = buildSiemResponse(response); + + try { + const secSol = await context.securitySolution; + const client = secSol.getMonitoringEntitySourceDataClient(); + const body = await client.init(request.body); + + return response.ok({ body }); + } catch (e) { + const error = transformError(e); + logger.error(`Error creating monitoring entity source sync config: ${error.message}`); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); + router.versioned + .get({ + access: 'public', + path: '/api/entity_analytics/monitoring/entity_source', + security: { + authz: { + requiredPrivileges: ['securitySolution', `${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 client = secSol.getMonitoringEntitySourceDataClient(); + const body = await client.get(); + return response.ok({ body }); + } catch (e) { + const error = transformError(e); + logger.error(`Error getting monitoring entity source sync config: ${error.message}`); + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/register_privilege_monitoring_routes.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/register_privilege_monitoring_routes.ts index 1d0a7dd43f310..9f8d6b52d94de 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/register_privilege_monitoring_routes.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/routes/register_privilege_monitoring_routes.ts @@ -8,6 +8,7 @@ import type { EntityAnalyticsRoutesDeps } from '../../types'; import { healthCheckPrivilegeMonitoringRoute } from './health'; import { initPrivilegeMonitoringEngineRoute } from './init'; +import { monitoringEntitySourceRoute } from './monitoring_entity_source'; import { searchPrivilegeMonitoringIndicesRoute } from './search_indices'; import { @@ -28,6 +29,7 @@ export const registerPrivilegeMonitoringRoutes = ({ initPrivilegeMonitoringEngineRoute(router, logger, config); healthCheckPrivilegeMonitoringRoute(router, logger, config); searchPrivilegeMonitoringIndicesRoute(router, logger, config); + monitoringEntitySourceRoute(router, logger, config); getUserRoute(router, logger); createUserRoute(router, logger); deleteUserRoute(router, logger); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source.ts new file mode 100644 index 0000000000000..70555be470419 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source.ts @@ -0,0 +1,93 @@ +/* + * 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 } from '@kbn/core/server'; +import { monitoringEntitySourceTypeName } from './monitoring_entity_source_type'; + +export interface MonitoringEntitySourceDependencies { + soClient: SavedObjectsClientContract; + namespace: string; +} + +export interface MonitoringEntitySourceDescriptor { + type: string; + name: string; + managed?: boolean; + indexPattern?: string; + enabled?: boolean; + error?: string; + integrationName?: string; + matchers?: Array<{ + fields: string[]; + values: string[]; + }>; + filter?: Record; +} + +export class MonitoringEntitySourceDescriptorClient { + constructor(private readonly dependencies: MonitoringEntitySourceDependencies) {} + + getSavedObjectId() { + return `entity-analytics-monitoring-entity-source-${this.dependencies.namespace}`; + } + + async create(attributes: MonitoringEntitySourceDescriptor) { + const entitySourceDescriptor = await this.find(); + + if (entitySourceDescriptor.total === 1) { + const { attributes: updated } = + await this.dependencies.soClient.update( + monitoringEntitySourceTypeName, + this.getSavedObjectId(), + attributes, + { refresh: 'wait_for' } + ); + return updated; + } + + const { attributes: created } = + await this.dependencies.soClient.create( + monitoringEntitySourceTypeName, + attributes, + { id: this.getSavedObjectId() } + ); + + return created; + } + + async update(monitoringEntitySource: Partial) { + const id = this.getSavedObjectId(); + const { attributes } = + await this.dependencies.soClient.update( + monitoringEntitySourceTypeName, + id, + monitoringEntitySource, + { refresh: 'wait_for' } + ); + return attributes; + } + + async find() { + return this.dependencies.soClient.find({ + type: monitoringEntitySourceTypeName, + namespaces: [this.dependencies.namespace], + }); + } + + async get() { + const id = this.getSavedObjectId(); + const { attributes } = await this.dependencies.soClient.get( + monitoringEntitySourceTypeName, + id + ); + return attributes; + } + + async delete() { + const id = this.getSavedObjectId(); + await this.dependencies.soClient.delete(monitoringEntitySourceTypeName, id); + } +} diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source_type.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source_type.ts new file mode 100644 index 0000000000000..7718fbb82f438 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source_type.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 type { SavedObjectsType } from '@kbn/core/server'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; + +export const monitoringEntitySourceTypeName = 'entity-analytics-monitoring-entity-source'; + +export const monitoringEntitySourceTypeNameMappings: SavedObjectsType['mappings'] = { + dynamic: false, + properties: { + type: { + type: 'keyword', + }, + name: { + type: 'keyword', + }, + managed: { + type: 'boolean', + }, + enabled: { + type: 'boolean', + }, + error: { + type: 'keyword', + }, + integrationName: { + type: 'keyword', + }, + matchers: { + type: 'object', + dynamic: false, + }, + filter: { + dynamic: false, + type: 'object', + }, + }, +}; + +export const monitoringEntitySourceType: SavedObjectsType = { + name: monitoringEntitySourceTypeName, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + hidden: false, + namespaceType: 'multiple-isolated', + mappings: monitoringEntitySourceTypeNameMappings, +}; diff --git a/x-pack/solutions/security/plugins/security_solution/server/request_context_factory.ts b/x-pack/solutions/security/plugins/security_solution/server/request_context_factory.ts index 70fb6a1d31485..507aea2e6371d 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/request_context_factory.ts @@ -11,6 +11,7 @@ import type { KibanaRequest, Logger, RequestHandlerContext } from '@kbn/core/ser import type { BuildFlavor } from '@kbn/config'; import { EntityDiscoveryApiKeyType } from '@kbn/entityManager-plugin/server/saved_objects'; +import { MonitoringEntitySourceDataClient } from './lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client'; import { DEFAULT_SPACE_ID } from '../common/constants'; import type { Immutable } from '../common/endpoint/types'; import type { EndpointAuthz } from '../common/endpoint/types/authz'; @@ -255,7 +256,6 @@ export class RequestContextFactory implements IRequestContextFactory { }) ), getPrivilegeMonitoringDataClient: memoize(() => { - // TODO:add soClient with ApiKeyType as with getEntityStoreDataClient return new PrivilegeMonitoringDataClient({ logger: options.logger, clusterClient: coreContext.elasticsearch.client, @@ -265,7 +265,14 @@ export class RequestContextFactory implements IRequestContextFactory { auditLogger: getAuditLogger(), kibanaVersion: options.kibanaVersion, telemetry: core.analytics, - // TODO: add apiKeyManager + }); + }), + getMonitoringEntitySourceDataClient: memoize(() => { + return new MonitoringEntitySourceDataClient({ + logger: options.logger, + clusterClient: coreContext.elasticsearch.client, + namespace: getSpaceId(), + soClient: coreContext.savedObjects.client, }); }), getEntityStoreDataClient: memoize(() => { diff --git a/x-pack/solutions/security/plugins/security_solution/server/saved_objects.ts b/x-pack/solutions/security/plugins/security_solution/server/saved_objects.ts index 2a45e630b1b63..ce5f7f02a42b3 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/saved_objects.ts @@ -18,6 +18,7 @@ import { manifestType, unifiedManifestType } from './endpoint/lib/artifacts/save import { riskEngineConfigurationType } from './lib/entity_analytics/risk_engine/saved_object'; import { entityEngineDescriptorType } from './lib/entity_analytics/entity_store/saved_object'; import { privilegeMonitoringType } from './lib/entity_analytics/privilege_monitoring/saved_object/privilege_monitoring_type'; +import { monitoringEntitySourceType } from './lib/entity_analytics/privilege_monitoring/saved_object/monitoring_entity_source_type'; const types = [ noteType, @@ -31,6 +32,7 @@ const types = [ riskEngineConfigurationType, entityEngineDescriptorType, privilegeMonitoringType, + monitoringEntitySourceType, protectionUpdatesNoteType, promptType, ]; diff --git a/x-pack/solutions/security/plugins/security_solution/server/types.ts b/x-pack/solutions/security/plugins/security_solution/server/types.ts index 7dcad7372e556..3c82d736bd98f 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/types.ts @@ -43,6 +43,7 @@ import type { AssetInventoryDataClient } from './lib/asset_inventory/asset_inven import type { PrivilegeMonitoringDataClient } from './lib/entity_analytics/privilege_monitoring/privilege_monitoring_data_client'; import type { ApiKeyManager } from './lib/entity_analytics/entity_store/auth/api_key'; import type { ProductFeaturesService } from './lib/product_features_service'; +import type { MonitoringEntitySourceDataClient } from './lib/entity_analytics/privilege_monitoring/monitoring_entity_source_data_client'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext { @@ -69,6 +70,7 @@ export interface SecuritySolutionApiRequestHandlerContext { getAssetCriticalityDataClient: () => AssetCriticalityDataClient; getEntityStoreDataClient: () => EntityStoreDataClient; getPrivilegeMonitoringDataClient: () => PrivilegeMonitoringDataClient; + getMonitoringEntitySourceDataClient: () => MonitoringEntitySourceDataClient; getSiemRuleMigrationsClient: () => SiemRuleMigrationsClient; getInferenceClient: () => InferenceClient; getAssetInventoryClient: () => AssetInventoryDataClient; 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 05f6dfd91841e..69fd0d9d54fb5 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 @@ -386,6 +386,18 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:privilege-monitoring-status/delete", "saved_object:privilege-monitoring-status/bulk_delete", "saved_object:privilege-monitoring-status/share_to_space", + "saved_object:entity-analytics-monitoring-entity-source/bulk_get", + "saved_object:entity-analytics-monitoring-entity-source/get", + "saved_object:entity-analytics-monitoring-entity-source/find", + "saved_object:entity-analytics-monitoring-entity-source/open_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/close_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/create", + "saved_object:entity-analytics-monitoring-entity-source/bulk_create", + "saved_object:entity-analytics-monitoring-entity-source/update", + "saved_object:entity-analytics-monitoring-entity-source/bulk_update", + "saved_object:entity-analytics-monitoring-entity-source/delete", + "saved_object:entity-analytics-monitoring-entity-source/bulk_delete", + "saved_object:entity-analytics-monitoring-entity-source/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", @@ -1242,6 +1254,18 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:privilege-monitoring-status/delete", "saved_object:privilege-monitoring-status/bulk_delete", "saved_object:privilege-monitoring-status/share_to_space", + "saved_object:entity-analytics-monitoring-entity-source/bulk_get", + "saved_object:entity-analytics-monitoring-entity-source/get", + "saved_object:entity-analytics-monitoring-entity-source/find", + "saved_object:entity-analytics-monitoring-entity-source/open_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/close_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/create", + "saved_object:entity-analytics-monitoring-entity-source/bulk_create", + "saved_object:entity-analytics-monitoring-entity-source/update", + "saved_object:entity-analytics-monitoring-entity-source/bulk_update", + "saved_object:entity-analytics-monitoring-entity-source/delete", + "saved_object:entity-analytics-monitoring-entity-source/bulk_delete", + "saved_object:entity-analytics-monitoring-entity-source/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", @@ -1869,6 +1893,11 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:privilege-monitoring-status/find", "saved_object:privilege-monitoring-status/open_point_in_time", "saved_object:privilege-monitoring-status/close_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/bulk_get", + "saved_object:entity-analytics-monitoring-entity-source/get", + "saved_object:entity-analytics-monitoring-entity-source/find", + "saved_object:entity-analytics-monitoring-entity-source/open_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/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", @@ -2243,6 +2272,11 @@ export default function ({ getService }: FtrProviderContext) { "saved_object:privilege-monitoring-status/find", "saved_object:privilege-monitoring-status/open_point_in_time", "saved_object:privilege-monitoring-status/close_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/bulk_get", + "saved_object:entity-analytics-monitoring-entity-source/get", + "saved_object:entity-analytics-monitoring-entity-source/find", + "saved_object:entity-analytics-monitoring-entity-source/open_point_in_time", + "saved_object:entity-analytics-monitoring-entity-source/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",