Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ export const UpdatedMonitoringEntitySource = z.object({
.optional(),
});

export type MonitoringEntitySource = z.infer<typeof MonitoringEntitySource>;
export const MonitoringEntitySource = z.object({
id: z.string(),
name: z.string(),
type: z.string(),
export type MonitoringEntitySourceProperties = z.infer<typeof MonitoringEntitySourceProperties>;
export const MonitoringEntitySourceProperties = z.object({
name: z.string().optional(),
type: z.string().optional(),
managed: z.boolean().optional(),
indexPattern: z.string().optional(),
integrationName: z.string().optional(),
enabled: z.boolean().optional(),
Expand All @@ -88,6 +88,16 @@ export const MonitoringEntitySource = z.object({
.optional(),
});

export type MonitoringEntitySourceNoId = z.infer<typeof MonitoringEntitySourceNoId>;
export const MonitoringEntitySourceNoId = MonitoringEntitySourceProperties.merge(z.object({}));

export type MonitoringEntitySource = z.infer<typeof MonitoringEntitySource>;
export const MonitoringEntitySource = MonitoringEntitySourceProperties.merge(
z.object({
id: z.string(),
})
);

export type CreateEntitySourceRequestBody = z.infer<typeof CreateEntitySourceRequestBody>;
export const CreateEntitySourceRequestBody = CreateMonitoringEntitySource;
export type CreateEntitySourceRequestBodyInput = z.input<typeof CreateEntitySourceRequestBody>;
Expand Down Expand Up @@ -127,7 +137,7 @@ export const UpdateEntitySourceRequestParams = z.object({
export type UpdateEntitySourceRequestParamsInput = z.input<typeof UpdateEntitySourceRequestParams>;

export type UpdateEntitySourceRequestBody = z.infer<typeof UpdateEntitySourceRequestBody>;
export const UpdateEntitySourceRequestBody = MonitoringEntitySource;
export const UpdateEntitySourceRequestBody = MonitoringEntitySourceNoId;
export type UpdateEntitySourceRequestBodyInput = z.input<typeof UpdateEntitySourceRequestBody>;

export type UpdateEntitySourceResponse = z.infer<typeof UpdateEntitySourceResponse>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/MonitoringEntitySource"
$ref: "#/components/schemas/MonitoringEntitySourceNoId"
responses:
"200":
description: Entity source updated successfully
Expand Down Expand Up @@ -194,16 +194,15 @@ components:
- type: string
- type: object

MonitoringEntitySource:
MonitoringEntitySourceProperties:
type: object
required: [type, name, id, managed]
properties:
id:
type: string
name:
type: string
type:
type: string
managed:
type: boolean
indexPattern:
type: string
integrationName:
Expand Down Expand Up @@ -231,5 +230,20 @@ components:
properties:
kuery:
oneOf:
- type: string
- type: object
- type: string
- type: object

MonitoringEntitySourceNoId:
allOf:
- $ref: '#/components/schemas/MonitoringEntitySourceProperties'
- type: object
required: [type, name, managed]

MonitoringEntitySource:
allOf:
- $ref: '#/components/schemas/MonitoringEntitySourceProperties'
- type: object
required: [type, name, id, managed]
properties:
id:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const PRIVMON_PUBLIC_URL = `/api/entity_analytics/monitoring` as const;
export const PRIVMON_ENGINE_PUBLIC_URL = `${PRIVMON_PUBLIC_URL}/engine` as const;
export const PRIVMON_USER_PUBLIC_CSV_UPLOAD_URL = `${PRIVMON_PUBLIC_URL}/users/_csv` as const;
export const PRIVMON_PUBLIC_INIT = `${PRIVMON_PUBLIC_URL}/engine/init` as const;
export const getPrivmonMonitoringSourceByIdUrl = (id: string) =>
`${PRIVMON_PUBLIC_URL}/entity_source/${id}` as const;

export const PRIVMON_USERS_CSV_MAX_SIZE_BYTES = 1024 * 1024; // 1MB
export const PRIVMON_USERS_CSV_SIZE_TOLERANCE_BYTES = 1024 * 50; // ~= 50kb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { CreatePrivilegesImportIndexResponse } from '../../../common/api/en
import type { PrivMonHealthResponse } from '../../../common/api/entity_analytics/privilege_monitoring/health.gen';
import type { InitMonitoringEngineResponse } from '../../../common/api/entity_analytics/privilege_monitoring/engine/init.gen';
import {
getPrivmonMonitoringSourceByIdUrl,
PRIVMON_PUBLIC_INIT,
PRIVMON_USER_PUBLIC_CSV_UPLOAD_URL,
} from '../../../common/entity_analytics/privileged_user_monitoring/constants';
Expand Down Expand Up @@ -266,11 +267,10 @@ export const useEntityAnalyticsRoutes = () => {
* Update a data source for privilege monitoring engine
*/
const updatePrivMonMonitoredIndices = async (id: string, indexPattern: string | undefined) =>
http.fetch<UpdateEntitySourceResponse>('/api/entity_analytics/monitoring/entity_source', {
http.fetch<UpdateEntitySourceResponse>(getPrivmonMonitoringSourceByIdUrl(id), {
version: API_VERSIONS.public.v1,
method: 'PUT',
body: JSON.stringify({
id,
type: 'index',
name: ENTITY_SOURCE_NAME,
indexPattern,
Expand All @@ -279,6 +279,13 @@ export const useEntityAnalyticsRoutes = () => {

/**
* Create asset criticality
/**
*
*
* @param {(Pick<AssetCriticality, 'idField' | 'idValue' | 'criticalityLevel'> & {
* refresh?: 'wait_for';
* })} params
* @return {*} {Promise<AssetCriticalityRecord>}
*/
const createAssetCriticality = async (
params: Pick<AssetCriticality, 'idField' | 'idValue' | 'criticalityLevel'> & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,12 @@ import {
loggingSystemMock,
} from '@kbn/core/server/mocks';
import { monitoringEntitySourceTypeName } from './saved_objects';
import type {
SavedObject,
SavedObjectsClientContract,
SavedObjectsFindResponse,
} from '@kbn/core/server';
import type { SavedObject, SavedObjectsClientContract } 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 = {
Expand Down Expand Up @@ -69,73 +64,20 @@ describe('MonitoringEntitySourceDataClient', () => {
} as unknown as SavedObjectsClientContract);

defaultOpts.soClient.create.mockResolvedValue({
id: 'temp-id', // TODO: update to use dynamic ID
id: 'abcdefg',
type: monitoringEntitySourceTypeName,
attributes: testDescriptor,
references: [],
});

const result = await dataClient.init(testDescriptor);
const id = `entity-analytics-monitoring-entity-source-${namespace}-${testDescriptor.type}-${testDescriptor.indexPattern}`;

expect(defaultOpts.soClient.create).toHaveBeenCalledWith(
monitoringEntitySourceTypeName,
testDescriptor,
{
id,
}
testDescriptor
);

expect(result).toEqual({ ...testDescriptor, managed: false, id });
});

it('should update Monitoring Entity Source Sync Config Successfully when calling init when the SO already exists', async () => {
const existingDescriptor = {
total: 1,
saved_objects: [
{
attributes: testDescriptor,
id: 'entity-analytics-monitoring-entity-source-test-namespace-test-type-test-index-pattern',
},
],
} as unknown as SavedObjectsFindResponse<unknown, unknown>;

const testSourceObject = {
filter: {},
indexPattern: 'test-index-pattern',
matchers: [
{
fields: ['user.role'],
values: ['admin'],
},
],
name: 'Test Source',
type: 'test-type',
managed: false,
};

defaultOpts.soClient.asScopedToNamespace.mockReturnValue({
find: jest.fn().mockResolvedValue(existingDescriptor),
} as unknown as SavedObjectsClientContract);

defaultOpts.soClient.update.mockResolvedValue({
id: 'entity-analytics-monitoring-entity-source-test-namespace-test-type-test-index-pattern',
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.type}-${testDescriptor.indexPattern}`,
testSourceObject,
{ refresh: 'wait_for' }
);

expect(result).toEqual(updatedDescriptor);
expect(result).toEqual({ ...testDescriptor, managed: false, id: 'abcdefg' });
});

it('should not create Monitoring Entity Source Sync Config when a SO already exist with the same name', async () => {
Expand Down Expand Up @@ -165,18 +107,18 @@ describe('MonitoringEntitySourceDataClient', () => {
references: [],
};
defaultOpts.soClient.get.mockResolvedValue(getResponse as unknown as SavedObject<unknown>);
const result = await dataClient.get();
const result = await dataClient.get('abcdefg');
expect(defaultOpts.soClient.get).toHaveBeenCalledWith(
monitoringEntitySourceTypeName,
`temp-id` // TODO: https://github.com/elastic/security-team/issues/12851
`abcdefg`
);
expect(result).toEqual(getResponse.attributes);
});
});

describe('update', () => {
it('should update Monitoring Entity Source Sync Config Successfully', async () => {
const id = 'temp-id'; // TODO: https://github.com/elastic/security-team/issues/12851
const id = 'abcdefg';
const updateDescriptor = {
...testDescriptor,
managed: false,
Expand Down Expand Up @@ -212,10 +154,10 @@ describe('MonitoringEntitySourceDataClient', () => {

describe('delete', () => {
it('should delete Monitoring Entity Source Sync Config Successfully', async () => {
await dataClient.delete();
await dataClient.delete('abcdefg');
expect(mockSavedObjectClient.delete).toHaveBeenCalledWith(
monitoringEntitySourceTypeName,
`temp-id` // TODO: https://github.com/elastic/security-team/issues/12851
'abcdefg'
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ export class MonitoringEntitySourceDataClient {
return descriptor;
}

public async get(): Promise<MonitoringEntitySource> {
this.log('debug', 'Getting Monitoring Entity Source Sync saved object');
return this.monitoringEntitySourceClient.get();
public async get(id: string): Promise<MonitoringEntitySource> {
this.log('debug', `Getting Monitoring Entity Source Sync saved object with id: ${id}`);
return this.monitoringEntitySourceClient.get(id);
}

public async update(update: Partial<MonitoringEntitySource> & { id: string }) {
this.log('debug', 'Updating Monitoring Entity Source Sync saved object');
this.log('debug', `Updating Monitoring Entity Source Sync saved object with id: ${update.id}`);

const sanitizedUpdate = {
...update,
Expand All @@ -56,9 +56,9 @@ export class MonitoringEntitySourceDataClient {
return this.monitoringEntitySourceClient.update(sanitizedUpdate);
}

public async delete() {
this.log('debug', 'Deleting Monitoring Entity Source Sync saved object');
return this.monitoringEntitySourceClient.delete();
public async delete(id: string) {
this.log('debug', `Deleting Monitoring Entity Source Sync saved object with id: ${id}`);
return this.monitoringEntitySourceClient.delete(id);
}

public async list(query: ListEntitySourcesRequestQuery): Promise<MonitoringEntitySource[]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
CreateEntitySourceRequestBody,
UpdateEntitySourceRequestBody,
type CreateEntitySourceResponse,
GetEntitySourceRequestParams,
UpdateEntitySourceRequestParams,
} from '../../../../../../common/api/entity_analytics/privilege_monitoring/monitoring_entity_source/monitoring_entity_source.gen';

export const monitoringEntitySourceRoute = (
Expand Down Expand Up @@ -69,7 +71,7 @@ export const monitoringEntitySourceRoute = (
router.versioned
.get({
access: 'public',
path: '/api/entity_analytics/monitoring/entity_source',
path: '/api/entity_analytics/monitoring/entity_source/{id}',
security: {
authz: {
requiredPrivileges: ['securitySolution', `${APP_ID}-entity-analytics`],
Expand All @@ -79,15 +81,19 @@ export const monitoringEntitySourceRoute = (
.addVersion(
{
version: API_VERSIONS.public.v1,
validate: {},
validate: {
request: {
params: GetEntitySourceRequestParams,
},
},
},
async (context, request, response): Promise<IKibanaResponse<GetEntitySourceResponse>> => {
const siemResponse = buildSiemResponse(response);

try {
const secSol = await context.securitySolution;
const client = secSol.getMonitoringEntitySourceDataClient();
const body = await client.get();
const body = await client.get(request.params.id);
return response.ok({ body });
} catch (e) {
const error = transformError(e);
Expand All @@ -103,7 +109,7 @@ export const monitoringEntitySourceRoute = (
router.versioned
.put({
access: 'public',
path: '/api/entity_analytics/monitoring/entity_source',
path: '/api/entity_analytics/monitoring/entity_source/{id}',
security: {
authz: {
requiredPrivileges: ['securitySolution', `${APP_ID}-entity-analytics`],
Expand All @@ -116,6 +122,7 @@ export const monitoringEntitySourceRoute = (
validate: {
request: {
body: UpdateEntitySourceRequestBody,
params: UpdateEntitySourceRequestParams,
},
},
},
Expand All @@ -125,7 +132,7 @@ export const monitoringEntitySourceRoute = (
try {
const secSol = await context.securitySolution;
const client = secSol.getMonitoringEntitySourceDataClient();
const body = await client.update(request.body);
const body = await client.update({ ...request.body, id: request.params.id });

return response.ok({ body });
} catch (e) {
Expand Down
Loading