mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
Saved Object Mapping for Entity Source Sync Configuration (#219680)
## Summary This PR introduces a new Saved Object mapping for Monitoring Entity Source Configurations to support Privileged User Monitoring. ### ✅ Implemented * Saved object mapping schema aligned with the [current spec](https://docs.google.com/document/d/1-c6A82p2CCjYz8Mb6tL6R7BXXemQ4mpWZliKqki0SWo/edit?tab=t.0#heading=h.4axxr154e1lc) * DescriptorClient to handle saved object methods * OpenAPI schema and generated types * Implemented Data Client - SO operations exposed via GET and POST API * Created route and registered with privmonRoutes * Data Client Unit Testing * Rename typename of "monitoring_entity_source-sync" -> "entity-analytics-monitoring-entity-source" [Figma ](https://www.figma.com/board/yBr1pBDGu4JqNxb5ZrULtk/MonEntitySourceSyncTask?node-id=0-1&p=f&t=4hQaGsdvOpghi4QS-0)- currently working in the pink box ## Testing Steps: 1. privilegeMonitoringEnabled: true - set this flag 2. From Dev tools - create a saved object via POST (below) and view this saved object via GET (also below): ``` POST kbn:/api/entity_analytics/monitoring/entity_source { "type": "some-type-here", "name": "name-here", "managed": false, "indexPattern": "logs-*", "enabled": true, "integrationName": "Okta", "matchers": [ { "fields": ["user.role"], "values": ["admin"] } ], "filter": {} } ``` **Expected output:** ``` { "type": "some-type-here", "name": "name-here", "managed": false, "indexPattern": "logs-*", "enabled": true, "integrationName": "Okta", "matchers": [ { "fields": [ "user.role" ], "values": [ "admin" ] } ], "filter": {} } ``` GET Example ``` GET kbn:/api/entity_analytics/monitoring/entity_source ``` Output for get, expected to be the same as that for POST. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
cd02c54c74
commit
a3de6d82ba
18 changed files with 761 additions and 4 deletions
|
@ -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"
|
||||
],
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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<typeof MonitoringEntitySourceDescriptor>;
|
||||
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<typeof MonitoringEntitySourceResponse>;
|
||||
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(),
|
||||
});
|
|
@ -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
|
|
@ -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),
|
||||
|
|
|
@ -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<unknown, unknown>);
|
||||
|
||||
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<unknown>);
|
||||
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<unknown, unknown>;
|
||||
|
||||
defaultOpts.soClient.find.mockResolvedValue(
|
||||
existingDescriptor as unknown as SavedObjectsFindResponse<unknown, unknown>
|
||||
);
|
||||
|
||||
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}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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<MonitoringEntitySourceResponse> {
|
||||
const descriptor = await this.monitoringEntitySourceClient.create(input);
|
||||
this.log('debug', 'Initializing MonitoringEntitySourceDataClient Saved Object');
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public async get(): Promise<MonitoringEntitySourceResponse> {
|
||||
this.log('debug', 'Getting Monitoring Entity Source Sync saved object');
|
||||
return this.monitoringEntitySourceClient.get();
|
||||
}
|
||||
|
||||
public async update(update: Partial<MonitoringEntitySourceResponse>) {
|
||||
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<keyof Logger, 'get' | 'log' | 'isLevelEnabled'>, msg: string) {
|
||||
this.opts.logger[level](
|
||||
`[Monitoring Entity Source Sync][namespace: ${this.opts.namespace}] ${msg}`
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<IKibanaResponse<MonitoringEntitySourceResponse>> => {
|
||||
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<IKibanaResponse<MonitoringEntitySourceResponse>> => {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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<string, unknown>;
|
||||
}
|
||||
|
||||
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<MonitoringEntitySourceDescriptor>(
|
||||
monitoringEntitySourceTypeName,
|
||||
this.getSavedObjectId(),
|
||||
attributes,
|
||||
{ refresh: 'wait_for' }
|
||||
);
|
||||
return updated;
|
||||
}
|
||||
|
||||
const { attributes: created } =
|
||||
await this.dependencies.soClient.create<MonitoringEntitySourceDescriptor>(
|
||||
monitoringEntitySourceTypeName,
|
||||
attributes,
|
||||
{ id: this.getSavedObjectId() }
|
||||
);
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
async update(monitoringEntitySource: Partial<MonitoringEntitySourceDescriptor>) {
|
||||
const id = this.getSavedObjectId();
|
||||
const { attributes } =
|
||||
await this.dependencies.soClient.update<MonitoringEntitySourceDescriptor>(
|
||||
monitoringEntitySourceTypeName,
|
||||
id,
|
||||
monitoringEntitySource,
|
||||
{ refresh: 'wait_for' }
|
||||
);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
async find() {
|
||||
return this.dependencies.soClient.find<MonitoringEntitySourceDescriptor>({
|
||||
type: monitoringEntitySourceTypeName,
|
||||
namespaces: [this.dependencies.namespace],
|
||||
});
|
||||
}
|
||||
|
||||
async get() {
|
||||
const id = this.getSavedObjectId();
|
||||
const { attributes } = await this.dependencies.soClient.get<MonitoringEntitySourceDescriptor>(
|
||||
monitoringEntitySourceTypeName,
|
||||
id
|
||||
);
|
||||
return attributes;
|
||||
}
|
||||
|
||||
async delete() {
|
||||
const id = this.getSavedObjectId();
|
||||
await this.dependencies.soClient.delete(monitoringEntitySourceTypeName, id);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
};
|
|
@ -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(() => {
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue