From 5e81d857e14312a0ff7fdb65baef76a0b779d202 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 4 Oct 2024 17:46:59 +0100 Subject: [PATCH 01/14] [ML] Adding ML tasks to kibana audit log --- x-pack/plugins/ml/server/audit_logger.ts | 94 +++++++++++++++ .../ml/server/lib/ml_client/ml_client.ts | 111 +++++++++++------- x-pack/plugins/ml/server/lib/route_guard.ts | 7 +- x-pack/plugins/ml/server/plugin.ts | 4 + .../server/shared_services/shared_services.ts | 20 +++- 5 files changed, 191 insertions(+), 45 deletions(-) create mode 100644 x-pack/plugins/ml/server/audit_logger.ts diff --git a/x-pack/plugins/ml/server/audit_logger.ts b/x-pack/plugins/ml/server/audit_logger.ts new file mode 100644 index 0000000000000..fe06b96c85710 --- /dev/null +++ b/x-pack/plugins/ml/server/audit_logger.ts @@ -0,0 +1,94 @@ +/* + * 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 Ml from '@elastic/elasticsearch/lib/api/api/ml'; +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { AuditLogger, CoreAuditService } from '@kbn/core/server'; + +type TaskType = + | 'put_ad_job' + | 'delete_ad_job' + | 'open_ad_job' + | 'close_ad_job' + | 'update_ad_job' + | 'reset_ad_job' + | 'revert_ad_job' + | 'put_ad_datafeed' + | 'delete_ad_datafeed' + | 'start_ad_datafeed' + | 'stop_ad_datafeed' + | 'update_ad_datafeed' + | 'put_dfa_job' + | 'delete_dfa_job' + | 'start_dfa_job' + | 'stop_dfa_job' + | 'get_ad_jobs' + | 'get_dfa_jobs'; + +const TASKS = new Map([ + ['put_ad_job', 'Creating anomaly detection job'], + ['delete_ad_job', 'Deleting anomaly detection job'], + ['open_ad_job', 'Opening anomaly detection job'], + ['close_ad_job', 'Closing anomaly detection job'], + ['update_ad_job', 'Updating anomaly detection job'], + ['reset_ad_job', 'Resetting anomaly detection job'], + ['revert_ad_job', 'Reverting anomaly detection job'], + ['put_ad_datafeed', 'Creating anomaly detection datafeed'], + ['delete_ad_datafeed', 'Deleting anomaly detection datafeed'], + ['start_ad_datafeed', 'Starting anomaly detection datafeed'], + ['stop_ad_datafeed', 'Stopping anomaly detection datafeed'], + ['update_ad_datafeed', 'Updating anomaly detection datafeed'], + ['put_dfa_job', 'Creating data frame analytics job'], + ['delete_dfa_job', 'Deleting data frame analytics job'], + ['start_dfa_job', 'Starting data frame analytics job'], + ['stop_dfa_job', 'Stopping data frame analytics job'], + ['get_ad_jobs', 'Getting anomaly detection jobs'], + ['get_dfa_jobs', 'Getting data frame analytics jobs'], +]); + +type ClientTasks = Ml['putJob']; +type ClientTasksParam = Parameters; + +export class MlAuditLogger { + private auditLogger: AuditLogger; + constructor(audit: CoreAuditService, request?: KibanaRequest) { + this.auditLogger = request ? audit.asScoped(request) : audit.withoutRequest; + } + + public log(message: string) { + this.auditLogger.log({ message, labels: { application: 'elastic/ml' } }); + } + + public async wrapTask(task: () => T, taskType: TaskType, id: string) { + const taskName = TASKS.get(taskType) ?? 'Unknown ML task'; + try { + const resp = await task(); + this.log(`${taskName} ${id}`); + return resp; + } catch (error) { + this.log(`Failed ${taskName} ${id}`); + throw error; + } + } + + // public async wrapTaskTest( + // task: ClientTasks, + // args: ClientTasksParam, + // taskType: TaskType, + // id: string + // ) { + // const taskName = TASKS.get(taskType) ?? 'Unknown ML task'; + // try { + // const resp = await task(args); + // this.log(`${taskName} ${id}`); + // return resp; + // } catch (error) { + // this.log(`Failed ${taskName} ${id}`); + // throw error; + // } + // } +} diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 33f0e1462b2b2..882684b102508 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -25,10 +25,12 @@ import type { MlGetDatafeedParams, MlGetTrainedModelParams, } from './types'; +import type { MlAuditLogger } from '../../audit_logger'; export function getMlClient( client: IScopedClusterClient, - mlSavedObjectService: MLSavedObjectService + mlSavedObjectService: MLSavedObjectService, + auditLogger: MlAuditLogger ): MlClient { const mlClient = client.asInternalUser.ml; @@ -159,8 +161,9 @@ export function getMlClient( // @ts-expect-error promise and TransportRequestPromise are incompatible. missing abort return { async closeJob(...p: Parameters) { + const [jobId] = getADJobIdsFromRequest(p); await jobIdsCheck('anomaly-detector', p); - return mlClient.closeJob(...p); + return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'close_ad_job', jobId); }, async deleteCalendar(...p: Parameters) { return mlClient.deleteCalendar(...p); @@ -173,15 +176,24 @@ export function getMlClient( }, async deleteDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - const resp = await mlClient.deleteDataFrameAnalytics(...p); + const [analyticsId] = getDFAJobIdsFromRequest(p); + const resp = auditLogger.wrapTask( + () => mlClient.deleteDataFrameAnalytics(...p), + 'delete_dfa_job', + analyticsId + ); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. return resp; }, async deleteDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - const resp = await mlClient.deleteDatafeed(...p); const [datafeedId] = getDatafeedIdsFromRequest(p); + const resp = auditLogger.wrapTask( + () => mlClient.deleteDatafeed(...p), + 'delete_ad_datafeed', + datafeedId + ); if (datafeedId !== undefined) { await mlSavedObjectService.deleteDatafeed(datafeedId); } @@ -200,12 +212,18 @@ export function getMlClient( }, async deleteJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const resp = await mlClient.deleteJob(...p); + const [jobId] = getADJobIdsFromRequest(p); + const resp = await auditLogger.wrapTask( + () => mlClient.deleteJob(...p), + 'delete_ad_job', + jobId + ); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. return resp; }, async deleteModelSnapshot(...p: Parameters) { + // !!!!!!!!!!!!!!!!!!!! how do we pass the snapshot id to audit log?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! await jobIdsCheck('anomaly-detector', p); return mlClient.deleteModelSnapshot(...p); }, @@ -562,7 +580,8 @@ export function getMlClient( }, async openJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.openJob(...p); + const [jobId] = getADJobIdsFromRequest(p); + return auditLogger.wrapTask(() => mlClient.openJob(...p), 'open_ad_job', jobId); }, async postCalendarEvents(...p: Parameters) { return mlClient.postCalendarEvents(...p); @@ -582,16 +601,24 @@ export function getMlClient( return mlClient.putCalendarJob(...p); }, async putDataFrameAnalytics(...p: Parameters) { - const resp = await mlClient.putDataFrameAnalytics(...p); const [analyticsId] = getDFAJobIdsFromRequest(p); + const resp = await auditLogger.wrapTask( + () => mlClient.putDataFrameAnalytics(...p), + 'put_dfa_job', + analyticsId + ); if (analyticsId !== undefined) { await mlSavedObjectService.createDataFrameAnalyticsJob(analyticsId); } return resp; }, async putDatafeed(...p: Parameters) { - const resp = await mlClient.putDatafeed(...p); const [datafeedId] = getDatafeedIdsFromRequest(p); + const resp = auditLogger.wrapTask( + () => mlClient.putDatafeed(...p), + 'put_ad_datafeed', + datafeedId + ); const jobId = getJobIdFromBody(p); if (datafeedId !== undefined && jobId !== undefined) { await mlSavedObjectService.addDatafeed(datafeedId, jobId); @@ -603,8 +630,8 @@ export function getMlClient( return mlClient.putFilter(...p); }, async putJob(...p: Parameters) { - const resp = await mlClient.putJob(...p); const [jobId] = getADJobIdsFromRequest(p); + const resp = auditLogger.wrapTask(() => mlClient.putJob(...p), 'put_ad_job', jobId); if (jobId !== undefined) { await mlSavedObjectService.createAnomalyDetectionJob(jobId); } @@ -622,26 +649,47 @@ export function getMlClient( }, async revertModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.revertModelSnapshot(...p); + const [jobId] = getADJobIdsFromRequest(p); + return auditLogger.wrapTask(() => mlClient.revertModelSnapshot(...p), 'revert_ad_job', jobId); }, async setUpgradeMode(...p: Parameters) { return mlClient.setUpgradeMode(...p); }, async startDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return mlClient.startDataFrameAnalytics(...p); + const [analyticsId] = getDFAJobIdsFromRequest(p); + return auditLogger.wrapTask( + () => mlClient.startDataFrameAnalytics(...p), + 'start_dfa_job', + analyticsId + ); }, async startDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return mlClient.startDatafeed(...p); + const [datafeedId] = getDatafeedIdsFromRequest(p); + return auditLogger.wrapTask( + () => mlClient.startDatafeed(...p), + 'start_ad_datafeed', + datafeedId + ); }, async stopDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return mlClient.stopDataFrameAnalytics(...p); + const [datafeedId] = getDatafeedIdsFromRequest(p); + return auditLogger.wrapTask( + () => mlClient.stopDataFrameAnalytics(...p), + 'stop_dfa_job', + datafeedId + ); }, async stopDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return mlClient.stopDatafeed(...p); + const [datafeedId] = getDatafeedIdsFromRequest(p); + return auditLogger.wrapTask( + () => mlClient.stopDatafeed(...p), + 'stop_ad_datafeed', + datafeedId + ); }, async updateDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); @@ -649,43 +697,26 @@ export function getMlClient( }, async updateDatafeed(...p: Parameters) { await datafeedIdsCheck(p); + const [datafeedId] = getDatafeedIdsFromRequest(p); - // Temporary workaround for the incorrect updateDatafeed function in the esclient - if ( - // @ts-expect-error TS complains it's always false - p.length === 0 || - p[0] === undefined - ) { - // Temporary generic error message. This should never be triggered - // but is added for type correctness below - throw new Error('Incorrect arguments supplied'); - } - // @ts-expect-error body doesn't exist in the type - const { datafeed_id: id, body } = p[0]; - - return client.asInternalUser.transport.request( - { - method: 'POST', - path: `/_ml/datafeeds/${id}/_update`, - body, - }, - p[1] + return auditLogger.wrapTask( + () => mlClient.updateDatafeed(...p), + 'update_ad_datafeed', + datafeedId ); - - // this should be reinstated once https://github.com/elastic/elasticsearch-js/issues/1601 - // is fixed - // return mlClient.updateDatafeed(...p); }, async updateFilter(...p: Parameters) { return mlClient.updateFilter(...p); }, async updateJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.updateJob(...p); + const [jobId] = getADJobIdsFromRequest(p); + return auditLogger.wrapTask(() => mlClient.updateJob(...p), 'update_ad_job', jobId); }, async resetJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.resetJob(...p); + const [jobId] = getADJobIdsFromRequest(p); + return auditLogger.wrapTask(() => mlClient.resetJob(...p), 'reset_ad_job', jobId); }, async updateModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); diff --git a/x-pack/plugins/ml/server/lib/route_guard.ts b/x-pack/plugins/ml/server/lib/route_guard.ts index 733dcb66f06f2..bfac399768b09 100644 --- a/x-pack/plugins/ml/server/lib/route_guard.ts +++ b/x-pack/plugins/ml/server/lib/route_guard.ts @@ -29,6 +29,7 @@ import type { MlLicense } from '../../common/license'; import type { MlClient } from './ml_client'; import { getMlClient } from './ml_client'; import { getDataViewsServiceFactory } from './data_views_utils'; +import { MlAuditLogger } from '../audit_logger'; type MLRequestHandlerContext = CustomRequestHandlerContext<{ alerting?: AlertingApiRequestHandlerContext; @@ -42,6 +43,7 @@ type Handler

= (handlerParams: { mlSavedObjectService: MLSavedObjectService; mlClient: MlClient; getDataViewsService(): Promise; + auditLogger: MlAuditLogger; }) => ReturnType>; type GetMlSavedObjectClient = (request: KibanaRequest) => SavedObjectsClientContract | null; @@ -121,6 +123,8 @@ export class RouteGuard { const [coreStart] = await this._getStartServices(); const executionContext = createExecutionContext(coreStart, PLUGIN_ID, request.route.path); + const auditLogger = new MlAuditLogger(coreStart.security.audit, request); + return await coreStart.executionContext.withContext(executionContext, () => handler({ client, @@ -128,13 +132,14 @@ export class RouteGuard { response, context, mlSavedObjectService, - mlClient: getMlClient(client, mlSavedObjectService), + mlClient: getMlClient(client, mlSavedObjectService, auditLogger), getDataViewsService: getDataViewsServiceFactory( this._getDataViews, savedObjectClient, client, request ), + auditLogger, }) ); }; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 2a36e94f2f2e7..01f3ec7e5d131 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -17,6 +17,7 @@ import type { IClusterClient, SavedObjectsServiceStart, UiSettingsServiceStart, + CoreAuditService, } from '@kbn/core/server'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; @@ -94,6 +95,7 @@ export class MlServerPlugin private home: HomeServerPluginSetup | null = null; private cases: CasesServerSetup | null | undefined = null; private dataViews: DataViewsPluginStart | null = null; + private auditService: CoreAuditService | null = null; private isMlReady: Promise; private setMlReady: () => void = () => {}; private savedObjectsSyncService: SavedObjectsSyncService; @@ -218,6 +220,7 @@ export class MlServerPlugin () => this.uiSettings, () => this.fieldsFormat, getDataViews, + () => this.auditService, () => this.isMlReady, this.compatibleModuleType ); @@ -313,6 +316,7 @@ export class MlServerPlugin this.capabilities = coreStart.capabilities; this.clusterClient = coreStart.elasticsearch.client; this.savedObjectsStart = coreStart.savedObjects; + this.auditService = coreStart.security.audit; this.dataViews = plugins.dataViews; this.mlLicense.setup(plugins.licensing.license$, async (mlLicense: MlLicense) => { diff --git a/x-pack/plugins/ml/server/shared_services/shared_services.ts b/x-pack/plugins/ml/server/shared_services/shared_services.ts index dc65a5bc01817..aa9a8f2c2a999 100644 --- a/x-pack/plugins/ml/server/shared_services/shared_services.ts +++ b/x-pack/plugins/ml/server/shared_services/shared_services.ts @@ -11,6 +11,7 @@ import type { SavedObjectsClientContract, UiSettingsServiceStart, KibanaRequest, + CoreAuditService, } from '@kbn/core/server'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { CoreKibanaRequest } from '@kbn/core/server'; @@ -59,6 +60,7 @@ import { getJobsHealthServiceProvider } from '../lib/alerts/jobs_health_service' import type { FieldFormatsRegistryProvider } from '../../common/types/kibana'; import type { GetDataViewsService } from '../lib/data_views_utils'; import { getDataViewsServiceFactory } from '../lib/data_views_utils'; +import { MlAuditLogger } from '../audit_logger'; export type SharedServices = JobServiceProvider & AnomalyDetectorsProvider & @@ -110,6 +112,7 @@ export function createSharedServices( getUiSettings: () => UiSettingsServiceStart | null, getFieldsFormat: () => FieldFormatsStart | null, getDataViews: () => DataViewsPluginStart, + getAuditService: () => CoreAuditService | null, isMlReady: () => Promise, compatibleModuleType: CompatibleModule | null ): { @@ -137,7 +140,8 @@ export function createSharedServices( isMlReady, getUiSettings, getFieldsFormat, - getDataViews + getDataViews, + getAuditService ); const { @@ -209,7 +213,8 @@ function getRequestItemsProvider( isMlReady: () => Promise, getUiSettings: () => UiSettingsServiceStart | null, getFieldsFormat: () => FieldFormatsStart | null, - getDataViews: () => DataViewsPluginStart + getDataViews: () => DataViewsPluginStart, + getAuditService: () => CoreAuditService | null ) { return (request: KibanaRequest) => { let hasMlCapabilities: HasMlCapabilities = hasMlCapabilitiesProvider( @@ -237,6 +242,11 @@ function getRequestItemsProvider( throw new MLClusterClientUninitialized(`ML's cluster client has not been initialized`); } + const auditService = getAuditService(); + if (!auditService) { + throw new Error('Audit service not initialized'); + } + const uiSettingsClient = getUiSettings()?.asScopedToClient(savedObjectsClient); if (!uiSettingsClient) { throw new MLUISettingsClientUninitialized(`ML's UI settings client has not been initialized`); @@ -263,7 +273,8 @@ function getRequestItemsProvider( if (request instanceof CoreKibanaRequest) { scopedClient = clusterClient.asScoped(request); mlSavedObjectService = getSobSavedObjectService(scopedClient); - mlClient = getMlClient(scopedClient, mlSavedObjectService); + const auditLogger = new MlAuditLogger(auditService, request); + mlClient = getMlClient(scopedClient, mlSavedObjectService, auditLogger); } else { hasMlCapabilities = () => Promise.resolve(); const { asInternalUser } = clusterClient; @@ -273,7 +284,8 @@ function getRequestItemsProvider( asSecondaryAuthUser: asInternalUser, }; mlSavedObjectService = getSobSavedObjectService(scopedClient); - mlClient = getMlClient(scopedClient, mlSavedObjectService); + const auditLogger = new MlAuditLogger(auditService); + mlClient = getMlClient(scopedClient, mlSavedObjectService, auditLogger); } const getDataViewsService = getDataViewsServiceFactory( From 3fbb3043824d5694b32d599115dd9956fcc2e443 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 8 Oct 2024 16:56:25 +0100 Subject: [PATCH 02/14] improving task wrapper --- x-pack/plugins/ml/server/audit_logger.ts | 160 +++++++++++++----- .../ml/server/lib/ml_client/ml_client.ts | 95 ++++------- 2 files changed, 144 insertions(+), 111 deletions(-) diff --git a/x-pack/plugins/ml/server/audit_logger.ts b/x-pack/plugins/ml/server/audit_logger.ts index fe06b96c85710..79ca4ee43b2bf 100644 --- a/x-pack/plugins/ml/server/audit_logger.ts +++ b/x-pack/plugins/ml/server/audit_logger.ts @@ -5,18 +5,24 @@ * 2.0. */ -import type Ml from '@elastic/elasticsearch/lib/api/api/ml'; import type { KibanaRequest } from '@kbn/core-http-server'; import type { AuditLogger, CoreAuditService } from '@kbn/core/server'; +import type { MlClient, MlClientParams } from './lib/ml_client/types'; +import { + getADJobIdsFromRequest, + getDFAJobIdsFromRequest, + getDatafeedIdsFromRequest, +} from './lib/ml_client/ml_client'; type TaskType = | 'put_ad_job' | 'delete_ad_job' + | 'delete_model_snapshot' | 'open_ad_job' | 'close_ad_job' | 'update_ad_job' | 'reset_ad_job' - | 'revert_ad_job' + | 'revert_ad_snapshot' | 'put_ad_datafeed' | 'delete_ad_datafeed' | 'start_ad_datafeed' @@ -29,30 +35,6 @@ type TaskType = | 'get_ad_jobs' | 'get_dfa_jobs'; -const TASKS = new Map([ - ['put_ad_job', 'Creating anomaly detection job'], - ['delete_ad_job', 'Deleting anomaly detection job'], - ['open_ad_job', 'Opening anomaly detection job'], - ['close_ad_job', 'Closing anomaly detection job'], - ['update_ad_job', 'Updating anomaly detection job'], - ['reset_ad_job', 'Resetting anomaly detection job'], - ['revert_ad_job', 'Reverting anomaly detection job'], - ['put_ad_datafeed', 'Creating anomaly detection datafeed'], - ['delete_ad_datafeed', 'Deleting anomaly detection datafeed'], - ['start_ad_datafeed', 'Starting anomaly detection datafeed'], - ['stop_ad_datafeed', 'Stopping anomaly detection datafeed'], - ['update_ad_datafeed', 'Updating anomaly detection datafeed'], - ['put_dfa_job', 'Creating data frame analytics job'], - ['delete_dfa_job', 'Deleting data frame analytics job'], - ['start_dfa_job', 'Starting data frame analytics job'], - ['stop_dfa_job', 'Stopping data frame analytics job'], - ['get_ad_jobs', 'Getting anomaly detection jobs'], - ['get_dfa_jobs', 'Getting data frame analytics jobs'], -]); - -type ClientTasks = Ml['putJob']; -type ClientTasksParam = Parameters; - export class MlAuditLogger { private auditLogger: AuditLogger; constructor(audit: CoreAuditService, request?: KibanaRequest) { @@ -63,32 +45,118 @@ export class MlAuditLogger { this.auditLogger.log({ message, labels: { application: 'elastic/ml' } }); } - public async wrapTask(task: () => T, taskType: TaskType, id: string) { - const taskName = TASKS.get(taskType) ?? 'Unknown ML task'; + private logSuccess(logEntry: any) { + this.auditLogger.log({ ...logEntry, labels: { application: 'elastic/ml' } }); + } + + private logFailure(logEntry: any) { + this.auditLogger.log({ ...logEntry, labels: { application: 'elastic/ml' } }); + } + + public async wrapTask(task: () => T, taskType: TaskType, p: P) { + const logEntry = this.createLogEntry(taskType, p); try { const resp = await task(); - this.log(`${taskName} ${id}`); + this.logSuccess(logEntry); return resp; } catch (error) { - this.log(`Failed ${taskName} ${id}`); + this.logFailure(logEntry); throw error; } } - // public async wrapTaskTest( - // task: ClientTasks, - // args: ClientTasksParam, - // taskType: TaskType, - // id: string - // ) { - // const taskName = TASKS.get(taskType) ?? 'Unknown ML task'; - // try { - // const resp = await task(args); - // this.log(`${taskName} ${id}`); - // return resp; - // } catch (error) { - // this.log(`Failed ${taskName} ${id}`); - // throw error; - // } - // } + private createLogEntry(taskName: string, p: MlClientParams) { + switch (taskName) { + case 'put_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Creating anomaly detection job ${jobId}` }; + } + case 'delete_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Deleting anomaly detection job ${jobId}` }; + } + case 'delete_model_snapshot': { + const [jobId] = getADJobIdsFromRequest(p); + const snapshotId = getSnapshotIdFromBody(p as DeleteModelSnapshotParams); + return { message: `Deleting model snapshot ${snapshotId} from job ${jobId}` }; + } + case 'open_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Opening anomaly detection job ${jobId}` }; + } + case 'close_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Closing anomaly detection job ${jobId}` }; + } + case 'update_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Updating anomaly detection job ${jobId}` }; + } + case 'reset_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Resetting anomaly detection job ${jobId}` }; + } + case 'revert_ad_snapshot': { + const [jobId] = getADJobIdsFromRequest(p); + const snapshotId = getSnapshotIdFromBody(p as RevertModelSnapshotParams); + return { message: `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}` }; + } + case 'put_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Creating anomaly detection datafeed ${datafeedId} for job ${jobId}` }; + } + case 'delete_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { message: `Deleting anomaly detection datafeed ${datafeedId}` }; + } + case 'start_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { message: `Starting anomaly detection datafeed ${datafeedId}` }; + } + case 'stop_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { message: `Stopping anomaly detection datafeed ${datafeedId}` }; + } + case 'update_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { message: `Updating anomaly detection datafeed ${datafeedId}` }; + } + case 'put_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { message: `Creating data frame analytics job ${analyticsId}` }; + } + case 'delete_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { message: `Deleting data frame analytics job ${analyticsId}` }; + } + case 'start_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { message: `Starting data frame analytics job ${analyticsId}` }; + } + case 'stop_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { message: `Stopping data frame analytics job ${analyticsId}` }; + } + case 'get_ad_jobs': { + return { message: 'Getting anomaly detection jobs' }; + } + case 'get_dfa_jobs': { + return { message: 'Getting data frame analytics jobs' }; + } + default: { + return { message: `Unknown task ${taskName}` }; + } + } + } +} + +type DeleteModelSnapshotParams = Parameters; +type RevertModelSnapshotParams = Parameters; + +function getSnapshotIdFromBody( + p: DeleteModelSnapshotParams | RevertModelSnapshotParams +): string | undefined { + const [params] = p; + return params?.snapshot_id; } diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 882684b102508..3a5e0a8000fc7 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -161,9 +161,8 @@ export function getMlClient( // @ts-expect-error promise and TransportRequestPromise are incompatible. missing abort return { async closeJob(...p: Parameters) { - const [jobId] = getADJobIdsFromRequest(p); await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'close_ad_job', jobId); + return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'close_ad_job', p); }, async deleteCalendar(...p: Parameters) { return mlClient.deleteCalendar(...p); @@ -176,11 +175,10 @@ export function getMlClient( }, async deleteDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - const [analyticsId] = getDFAJobIdsFromRequest(p); const resp = auditLogger.wrapTask( () => mlClient.deleteDataFrameAnalytics(...p), 'delete_dfa_job', - analyticsId + p ); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. @@ -192,7 +190,7 @@ export function getMlClient( const resp = auditLogger.wrapTask( () => mlClient.deleteDatafeed(...p), 'delete_ad_datafeed', - datafeedId + p ); if (datafeedId !== undefined) { await mlSavedObjectService.deleteDatafeed(datafeedId); @@ -212,20 +210,17 @@ export function getMlClient( }, async deleteJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const [jobId] = getADJobIdsFromRequest(p); - const resp = await auditLogger.wrapTask( - () => mlClient.deleteJob(...p), - 'delete_ad_job', - jobId - ); + return auditLogger.wrapTask(() => mlClient.deleteJob(...p), 'delete_ad_job', p); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. - return resp; }, async deleteModelSnapshot(...p: Parameters) { - // !!!!!!!!!!!!!!!!!!!! how do we pass the snapshot id to audit log?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! await jobIdsCheck('anomaly-detector', p); - return mlClient.deleteModelSnapshot(...p); + return auditLogger.wrapTask( + () => mlClient.deleteModelSnapshot(...p), + 'delete_model_snapshot', + p + ); }, async deleteTrainedModel(...p: Parameters) { await modelIdsCheck(p); @@ -580,8 +575,7 @@ export function getMlClient( }, async openJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const [jobId] = getADJobIdsFromRequest(p); - return auditLogger.wrapTask(() => mlClient.openJob(...p), 'open_ad_job', jobId); + return auditLogger.wrapTask(() => mlClient.openJob(...p), 'open_ad_job', p); }, async postCalendarEvents(...p: Parameters) { return mlClient.postCalendarEvents(...p); @@ -605,7 +599,7 @@ export function getMlClient( const resp = await auditLogger.wrapTask( () => mlClient.putDataFrameAnalytics(...p), 'put_dfa_job', - analyticsId + p ); if (analyticsId !== undefined) { await mlSavedObjectService.createDataFrameAnalyticsJob(analyticsId); @@ -614,11 +608,7 @@ export function getMlClient( }, async putDatafeed(...p: Parameters) { const [datafeedId] = getDatafeedIdsFromRequest(p); - const resp = auditLogger.wrapTask( - () => mlClient.putDatafeed(...p), - 'put_ad_datafeed', - datafeedId - ); + const resp = auditLogger.wrapTask(() => mlClient.putDatafeed(...p), 'put_ad_datafeed', p); const jobId = getJobIdFromBody(p); if (datafeedId !== undefined && jobId !== undefined) { await mlSavedObjectService.addDatafeed(datafeedId, jobId); @@ -631,7 +621,7 @@ export function getMlClient( }, async putJob(...p: Parameters) { const [jobId] = getADJobIdsFromRequest(p); - const resp = auditLogger.wrapTask(() => mlClient.putJob(...p), 'put_ad_job', jobId); + const resp = auditLogger.wrapTask(() => mlClient.putJob(...p), 'put_ad_job', p); if (jobId !== undefined) { await mlSavedObjectService.createAnomalyDetectionJob(jobId); } @@ -649,47 +639,30 @@ export function getMlClient( }, async revertModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const [jobId] = getADJobIdsFromRequest(p); - return auditLogger.wrapTask(() => mlClient.revertModelSnapshot(...p), 'revert_ad_job', jobId); + return auditLogger.wrapTask( + () => mlClient.revertModelSnapshot(...p), + 'revert_ad_snapshot', + p + ); }, async setUpgradeMode(...p: Parameters) { return mlClient.setUpgradeMode(...p); }, async startDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - const [analyticsId] = getDFAJobIdsFromRequest(p); - return auditLogger.wrapTask( - () => mlClient.startDataFrameAnalytics(...p), - 'start_dfa_job', - analyticsId - ); + return auditLogger.wrapTask(() => mlClient.startDataFrameAnalytics(...p), 'start_dfa_job', p); }, async startDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - const [datafeedId] = getDatafeedIdsFromRequest(p); - return auditLogger.wrapTask( - () => mlClient.startDatafeed(...p), - 'start_ad_datafeed', - datafeedId - ); + return auditLogger.wrapTask(() => mlClient.startDatafeed(...p), 'start_ad_datafeed', p); }, async stopDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - const [datafeedId] = getDatafeedIdsFromRequest(p); - return auditLogger.wrapTask( - () => mlClient.stopDataFrameAnalytics(...p), - 'stop_dfa_job', - datafeedId - ); + return auditLogger.wrapTask(() => mlClient.stopDataFrameAnalytics(...p), 'stop_dfa_job', p); }, async stopDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - const [datafeedId] = getDatafeedIdsFromRequest(p); - return auditLogger.wrapTask( - () => mlClient.stopDatafeed(...p), - 'stop_ad_datafeed', - datafeedId - ); + return auditLogger.wrapTask(() => mlClient.stopDatafeed(...p), 'stop_ad_datafeed', p); }, async updateDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); @@ -697,26 +670,18 @@ export function getMlClient( }, async updateDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - const [datafeedId] = getDatafeedIdsFromRequest(p); - - return auditLogger.wrapTask( - () => mlClient.updateDatafeed(...p), - 'update_ad_datafeed', - datafeedId - ); + return auditLogger.wrapTask(() => mlClient.updateDatafeed(...p), 'update_ad_datafeed', p); }, async updateFilter(...p: Parameters) { return mlClient.updateFilter(...p); }, async updateJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const [jobId] = getADJobIdsFromRequest(p); - return auditLogger.wrapTask(() => mlClient.updateJob(...p), 'update_ad_job', jobId); + return auditLogger.wrapTask(() => mlClient.updateJob(...p), 'update_ad_job', p); }, async resetJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const [jobId] = getADJobIdsFromRequest(p); - return auditLogger.wrapTask(() => mlClient.resetJob(...p), 'reset_ad_job', jobId); + return auditLogger.wrapTask(() => mlClient.resetJob(...p), 'reset_ad_job', p); }, async updateModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -736,29 +701,29 @@ export function getMlClient( } as MlClient; } -function getDFAJobIdsFromRequest([params]: MlGetDFAParams): string[] { +export function getDFAJobIdsFromRequest([params]: MlGetDFAParams): string[] { const ids = params?.id?.split(','); return ids || []; } -function getModelIdsFromRequest([params]: MlGetTrainedModelParams): string[] { +export function getModelIdsFromRequest([params]: MlGetTrainedModelParams): string[] { const id = params?.model_id; const ids = Array.isArray(id) ? id : id?.split(','); return ids || []; } -function getADJobIdsFromRequest([params]: MlGetADParams): string[] { +export function getADJobIdsFromRequest([params]: MlGetADParams): string[] { const ids = typeof params?.job_id === 'string' ? params?.job_id.split(',') : params?.job_id; return ids || []; } -function getDatafeedIdsFromRequest([params]: MlGetDatafeedParams): string[] { +export function getDatafeedIdsFromRequest([params]: MlGetDatafeedParams): string[] { const ids = typeof params?.datafeed_id === 'string' ? params?.datafeed_id.split(',') : params?.datafeed_id; return ids || []; } -function getJobIdFromBody(p: any): string | undefined { +export function getJobIdFromBody(p: any): string | undefined { const [params] = p; return params?.body?.job_id; } From 381a6b5b42590f5d4c2e3b31e0c44b9e99b9e063 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:12:43 +0000 Subject: [PATCH 03/14] [CI] Auto-commit changed files from 'node scripts/yarn_deduplicate' --- x-pack/plugins/ml/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 24ed35277b93a..d5d7a3d07727f 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -131,6 +131,7 @@ "@kbn/ml-field-stats-flyout", "@kbn/ml-parse-interval", "@kbn/ml-validators", - "@kbn/aiops-common" + "@kbn/aiops-common", + "@kbn/core-http-server" ] } From 22b9675811a9f1b8e23eef0df91f1ed2baa66314 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Oct 2024 08:49:18 +0100 Subject: [PATCH 04/14] sorting tsconfig --- x-pack/plugins/ml/tsconfig.json | 92 ++++++++++++++++----------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index d5d7a3d07727f..73ab75d5f2515 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -25,28 +25,46 @@ }, // add references to other TypeScript projects the plugin depends on "@kbn/actions-plugin", + "@kbn/aiops-change-point-detection", + "@kbn/aiops-common", "@kbn/aiops-plugin", "@kbn/alerting-plugin", + "@kbn/alerts-as-data-utils", "@kbn/analytics", "@kbn/cases-plugin", "@kbn/charts-plugin", "@kbn/cloud-plugin", + "@kbn/code-editor", "@kbn/config-schema", + "@kbn/content-management-plugin", + "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/core-elasticsearch-server", + "@kbn/core-http-browser", + "@kbn/core-http-server", + "@kbn/core-lifecycle-browser", + "@kbn/core-notifications-browser-mocks", + "@kbn/core-ui-settings-browser", "@kbn/dashboard-plugin", "@kbn/data-plugin", + "@kbn/data-view-editor-plugin", "@kbn/data-views-plugin", "@kbn/data-visualizer-plugin", + "@kbn/deeplinks-management", + "@kbn/deeplinks-ml", "@kbn/embeddable-plugin", "@kbn/es-query", "@kbn/es-types", "@kbn/es-ui-shared-plugin", + "@kbn/esql-utils", "@kbn/features-plugin", "@kbn/field-formats-plugin", "@kbn/field-types", "@kbn/home-plugin", "@kbn/i18n-react", "@kbn/i18n", + "@kbn/inference_integration_flyout", "@kbn/inspector-plugin", + "@kbn/json-schemas", "@kbn/kibana-react-plugin", "@kbn/kibana-utils-plugin", "@kbn/lens-plugin", @@ -55,28 +73,55 @@ "@kbn/management-plugin", "@kbn/maps-plugin", "@kbn/ml-agg-utils", + "@kbn/ml-anomaly-utils", + "@kbn/ml-category-validator", + "@kbn/ml-creation-wizard-utils", + "@kbn/ml-data-frame-analytics-utils", + "@kbn/ml-data-grid", + "@kbn/ml-data-view-utils", "@kbn/ml-date-picker", + "@kbn/ml-date-utils", + "@kbn/ml-error-utils", + "@kbn/ml-field-stats-flyout", + "@kbn/ml-in-memory-table", "@kbn/ml-is-defined", "@kbn/ml-is-populated-object", + "@kbn/ml-kibana-theme", "@kbn/ml-local-storage", "@kbn/ml-nested-property", "@kbn/ml-number-utils", + "@kbn/ml-parse-interval", "@kbn/ml-query-utils", "@kbn/ml-route-utils", + "@kbn/ml-runtime-field-utils", "@kbn/ml-string-hash", + "@kbn/ml-time-buckets", "@kbn/ml-trained-models-utils", "@kbn/ml-trained-models-utils", + "@kbn/ml-ui-actions", "@kbn/ml-url-state", + "@kbn/ml-validators", "@kbn/monaco", + "@kbn/observability-ai-assistant-plugin", + "@kbn/presentation-containers", + "@kbn/presentation-panel-plugin", + "@kbn/presentation-publishing", + "@kbn/presentation-util-plugin", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-mount", "@kbn/rison", + "@kbn/rule-data-utils", + "@kbn/rule-registry-plugin", "@kbn/saved-objects-finder-plugin", "@kbn/saved-objects-management-plugin", "@kbn/saved-search-plugin", "@kbn/security-plugin", + "@kbn/securitysolution-ecs", "@kbn/share-plugin", "@kbn/shared-ux-link-redirect-app", "@kbn/shared-ux-page-kibana-template", "@kbn/shared-ux-router", + "@kbn/shared-ux-utility", "@kbn/spaces-plugin", "@kbn/std", "@kbn/task-manager-plugin", @@ -84,54 +129,9 @@ "@kbn/triggers-actions-ui-plugin", "@kbn/ui-actions-plugin", "@kbn/ui-theme", + "@kbn/unified-field-list", "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", - "@kbn/ml-error-utils", - "@kbn/ml-anomaly-utils", - "@kbn/ml-data-frame-analytics-utils", - "@kbn/ml-data-grid", - "@kbn/ml-kibana-theme", - "@kbn/ml-runtime-field-utils", - "@kbn/ml-date-utils", - "@kbn/ml-category-validator", - "@kbn/ml-ui-actions", - "@kbn/deeplinks-ml", - "@kbn/core-notifications-browser-mocks", - "@kbn/unified-field-list", - "@kbn/core-ui-settings-browser", - "@kbn/content-management-plugin", - "@kbn/ml-in-memory-table", - "@kbn/presentation-util-plugin", - "@kbn/react-kibana-mount", - "@kbn/core-http-browser", - "@kbn/data-view-editor-plugin", - "@kbn/rule-data-utils", - "@kbn/alerts-as-data-utils", - "@kbn/rule-registry-plugin", - "@kbn/securitysolution-ecs", - "@kbn/ml-data-view-utils", - "@kbn/ml-creation-wizard-utils", - "@kbn/deeplinks-management", - "@kbn/code-editor", - "@kbn/presentation-publishing", - "@kbn/core-elasticsearch-server", - "@kbn/core-elasticsearch-client-server-mocks", - "@kbn/ml-time-buckets", - "@kbn/aiops-change-point-detection", - "@kbn/inference_integration_flyout", - "@kbn/presentation-containers", - "@kbn/presentation-panel-plugin", - "@kbn/shared-ux-utility", - "@kbn/react-kibana-context-render", - "@kbn/esql-utils", - "@kbn/core-lifecycle-browser", - "@kbn/observability-ai-assistant-plugin", - "@kbn/json-schemas", - "@kbn/ml-field-stats-flyout", - "@kbn/ml-parse-interval", - "@kbn/ml-validators", - "@kbn/aiops-common", - "@kbn/core-http-server" ] } From e3baa7f6f5de6067226c14b723dff62fa340bfbb Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Oct 2024 13:13:14 +0100 Subject: [PATCH 05/14] adding more task types --- x-pack/plugins/ml/server/audit_logger.ts | 117 +++++++++++++++--- .../ml/server/lib/ml_client/ml_client.ts | 111 +++++++++++------ 2 files changed, 175 insertions(+), 53 deletions(-) diff --git a/x-pack/plugins/ml/server/audit_logger.ts b/x-pack/plugins/ml/server/audit_logger.ts index 79ca4ee43b2bf..c7152f096df16 100644 --- a/x-pack/plugins/ml/server/audit_logger.ts +++ b/x-pack/plugins/ml/server/audit_logger.ts @@ -12,9 +12,10 @@ import { getADJobIdsFromRequest, getDFAJobIdsFromRequest, getDatafeedIdsFromRequest, + getModelIdsFromRequest, } from './lib/ml_client/ml_client'; -type TaskType = +type TaskTypeAD = | 'put_ad_job' | 'delete_ad_job' | 'delete_model_snapshot' @@ -28,12 +29,28 @@ type TaskType = | 'start_ad_datafeed' | 'stop_ad_datafeed' | 'update_ad_datafeed' + | 'put_calendar' + | 'put_calendar_job' + | 'post_calendar_events' + | 'put_filter' + | 'update_filter'; + +type TaskTypeDFA = | 'put_dfa_job' | 'delete_dfa_job' | 'start_dfa_job' | 'stop_dfa_job' - | 'get_ad_jobs' - | 'get_dfa_jobs'; + | 'update_dfa_job'; + +type TaskTypeNLP = + | 'put_trained_model' + | 'delete_trained_model' + | 'start_trained_model_deployment' + | 'stop_trained_model_deployment' + | 'update_trained_model_deployment' + | 'infer_trained_model'; + +type TaskType = TaskTypeAD | TaskTypeDFA | TaskTypeNLP; export class MlAuditLogger { private auditLogger: AuditLogger; @@ -67,6 +84,7 @@ export class MlAuditLogger { private createLogEntry(taskName: string, p: MlClientParams) { switch (taskName) { + /* Anomaly Detection */ case 'put_ad_job': { const [jobId] = getADJobIdsFromRequest(p); return { message: `Creating anomaly detection job ${jobId}` }; @@ -77,7 +95,9 @@ export class MlAuditLogger { } case 'delete_model_snapshot': { const [jobId] = getADJobIdsFromRequest(p); - const snapshotId = getSnapshotIdFromBody(p as DeleteModelSnapshotParams); + const snapshotId = getSnapshotIdFromRequest( + p as Parameters + ); return { message: `Deleting model snapshot ${snapshotId} from job ${jobId}` }; } case 'open_ad_job': { @@ -98,7 +118,9 @@ export class MlAuditLogger { } case 'revert_ad_snapshot': { const [jobId] = getADJobIdsFromRequest(p); - const snapshotId = getSnapshotIdFromBody(p as RevertModelSnapshotParams); + const snapshotId = getSnapshotIdFromRequest( + p as Parameters + ); return { message: `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}` }; } case 'put_ad_datafeed': { @@ -122,6 +144,46 @@ export class MlAuditLogger { const [datafeedId] = getDatafeedIdsFromRequest(p); return { message: `Updating anomaly detection datafeed ${datafeedId}` }; } + case 'put_calendar': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + // @ts-expect-error body is optional + const jobIds = (params.body ?? params).job_ids; + return { + message: `Creating calendar ${calendarId} ${ + jobIds ? `with job(s) ${jobIds.join()}` : '' + }`, + }; + } + case 'put_calendar_job': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + const jobIds = params.job_id; + return { + message: `Adding job(s) ${jobIds} to calendar ${calendarId}`, + }; + } + case 'post_calendar_events': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + // @ts-expect-error body is optional + const eventsCount = (params.body ?? params).events; + return { + message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, + }; + } + case 'put_filter': { + const [params] = p as Parameters; + const filterId = params.filter_id; + return { message: `Creating filter ${filterId}` }; + } + case 'update_filter': { + const [params] = p as Parameters; + const filterId = params.filter_id; + return { message: `Updating filter ${filterId}` }; + } + + /* Data Frame Analytics */ case 'put_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); return { message: `Creating data frame analytics job ${analyticsId}` }; @@ -138,25 +200,46 @@ export class MlAuditLogger { const [analyticsId] = getDFAJobIdsFromRequest(p); return { message: `Stopping data frame analytics job ${analyticsId}` }; } - case 'get_ad_jobs': { - return { message: 'Getting anomaly detection jobs' }; + case 'update_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { message: `Updating data frame analytics job ${analyticsId}` }; + } + + /* Trained Models */ + case 'put_trained_model': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Creating trained model ${modelId}` }; + } + case 'delete_trained_model': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Deleting trained model ${modelId}` }; + } + case 'start_trained_model_deployment': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Starting trained model deployment for model ${modelId}` }; + } + case 'stop_trained_model_deployment': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Stopping trained model deployment for model ${modelId}` }; } - case 'get_dfa_jobs': { - return { message: 'Getting data frame analytics jobs' }; + case 'update_trained_model_deployment': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Updating trained model deployment for model ${modelId}` }; } + case 'infer_trained_model': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Inferring trained model ${modelId}` }; + } + default: { - return { message: `Unknown task ${taskName}` }; + return { message: `Unknown ML task ${taskName}` }; } } } } -type DeleteModelSnapshotParams = Parameters; -type RevertModelSnapshotParams = Parameters; - -function getSnapshotIdFromBody( - p: DeleteModelSnapshotParams | RevertModelSnapshotParams -): string | undefined { - const [params] = p; +function getSnapshotIdFromRequest([params]: + | Parameters + | Parameters): string | undefined { return params?.snapshot_id; } diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 3a5e0a8000fc7..2dbfc44079f7b 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -175,7 +175,7 @@ export function getMlClient( }, async deleteDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - const resp = auditLogger.wrapTask( + const resp = await auditLogger.wrapTask( () => mlClient.deleteDataFrameAnalytics(...p), 'delete_dfa_job', p @@ -187,7 +187,7 @@ export function getMlClient( async deleteDatafeed(...p: Parameters) { await datafeedIdsCheck(p); const [datafeedId] = getDatafeedIdsFromRequest(p); - const resp = auditLogger.wrapTask( + const resp = await auditLogger.wrapTask( () => mlClient.deleteDatafeed(...p), 'delete_ad_datafeed', p @@ -224,7 +224,11 @@ export function getMlClient( }, async deleteTrainedModel(...p: Parameters) { await modelIdsCheck(p); - return mlClient.deleteTrainedModel(...p); + return auditLogger.wrapTask( + () => mlClient.deleteTrainedModel(...p), + 'delete_trained_model', + p + ); }, async estimateModelMemory(...p: Parameters) { return mlClient.estimateModelMemory(...p); @@ -515,16 +519,21 @@ export function getMlClient( await modelIdsCheck(p); // TODO use mlClient.startTrainedModelDeployment when esClient is updated const { model_id: modelId, adaptive_allocations: adaptiveAllocations, ...queryParams } = p[0]; - return client.asInternalUser.transport.request( - { - method: 'POST', - path: `_ml/trained_models/${modelId}/deployment/_start`, - ...(isPopulatedObject(queryParams) ? { querystring: queryParams } : {}), - ...(isPopulatedObject(adaptiveAllocations) - ? { body: { adaptive_allocations: adaptiveAllocations } } - : {}), - }, - p[1] + return auditLogger.wrapTask( + () => + client.asInternalUser.transport.request( + { + method: 'POST', + path: `_ml/trained_models/${modelId}/deployment/_start`, + ...(isPopulatedObject(queryParams) ? { querystring: queryParams } : {}), + ...(isPopulatedObject(adaptiveAllocations) + ? { body: { adaptive_allocations: adaptiveAllocations } } + : {}), + }, + p[1] + ), + 'start_trained_model_deployment', + p ); }, async updateTrainedModelDeployment(...p: Parameters) { @@ -532,17 +541,26 @@ export function getMlClient( const { deployment_id: deploymentId, model_id: modelId, ...bodyParams } = p[0]; // TODO use mlClient.updateTrainedModelDeployment when esClient is updated - return client.asInternalUser.transport.request({ - method: 'POST', - path: `/_ml/trained_models/${deploymentId}/deployment/_update`, - body: bodyParams, - }); + return auditLogger.wrapTask( + () => + client.asInternalUser.transport.request({ + method: 'POST', + path: `/_ml/trained_models/${deploymentId}/deployment/_update`, + body: bodyParams, + }), + 'update_trained_model_deployment', + p + ); }, async stopTrainedModelDeployment(...p: Parameters) { await modelIdsCheck(p); switchDeploymentId(p); - return mlClient.stopTrainedModelDeployment(...p); + return auditLogger.wrapTask( + () => mlClient.stopTrainedModelDeployment(...p), + 'stop_trained_model_deployment', + p + ); }, async inferTrainedModel(...p: Parameters) { await modelIdsCheck(p); @@ -560,14 +578,19 @@ export function getMlClient( // @ts-expect-error body doesn't exist in the type const { model_id: id, body, query: querystring } = p[0]; - return client.asInternalUser.transport.request( - { - method: 'POST', - path: `/_ml/trained_models/${id}/_infer`, - body, - querystring, - }, - p[1] + return auditLogger.wrapTask( + () => + client.asInternalUser.transport.request( + { + method: 'POST', + path: `/_ml/trained_models/${id}/_infer`, + body, + querystring, + }, + p[1] + ), + 'infer_trained_model', + p ); }, async info(...p: Parameters) { @@ -578,7 +601,11 @@ export function getMlClient( return auditLogger.wrapTask(() => mlClient.openJob(...p), 'open_ad_job', p); }, async postCalendarEvents(...p: Parameters) { - return mlClient.postCalendarEvents(...p); + return auditLogger.wrapTask( + () => mlClient.postCalendarEvents(...p), + 'post_calendar_events', + p + ); }, async postData(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -589,10 +616,10 @@ export function getMlClient( return mlClient.previewDatafeed(...p); }, async putCalendar(...p: Parameters) { - return mlClient.putCalendar(...p); + return auditLogger.wrapTask(() => mlClient.putCalendar(...p), 'put_calendar', p); }, async putCalendarJob(...p: Parameters) { - return mlClient.putCalendarJob(...p); + return auditLogger.wrapTask(() => mlClient.putCalendarJob(...p), 'put_calendar_job', p); }, async putDataFrameAnalytics(...p: Parameters) { const [analyticsId] = getDFAJobIdsFromRequest(p); @@ -608,7 +635,11 @@ export function getMlClient( }, async putDatafeed(...p: Parameters) { const [datafeedId] = getDatafeedIdsFromRequest(p); - const resp = auditLogger.wrapTask(() => mlClient.putDatafeed(...p), 'put_ad_datafeed', p); + const resp = await auditLogger.wrapTask( + () => mlClient.putDatafeed(...p), + 'put_ad_datafeed', + p + ); const jobId = getJobIdFromBody(p); if (datafeedId !== undefined && jobId !== undefined) { await mlSavedObjectService.addDatafeed(datafeedId, jobId); @@ -617,18 +648,22 @@ export function getMlClient( return resp; }, async putFilter(...p: Parameters) { - return mlClient.putFilter(...p); + return auditLogger.wrapTask(() => mlClient.putFilter(...p), 'put_filter', p); }, async putJob(...p: Parameters) { const [jobId] = getADJobIdsFromRequest(p); - const resp = auditLogger.wrapTask(() => mlClient.putJob(...p), 'put_ad_job', p); + const resp = await auditLogger.wrapTask(() => mlClient.putJob(...p), 'put_ad_job', p); if (jobId !== undefined) { await mlSavedObjectService.createAnomalyDetectionJob(jobId); } return resp; }, async putTrainedModel(...p: Parameters) { - const resp = await mlClient.putTrainedModel(...p); + const resp = await auditLogger.wrapTask( + () => mlClient.putTrainedModel(...p), + 'put_trained_model', + p + ); const [modelId] = getModelIdsFromRequest(p); if (modelId !== undefined) { const model = (p[0] as estypes.MlPutTrainedModelRequest).body; @@ -666,14 +701,18 @@ export function getMlClient( }, async updateDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return mlClient.updateDataFrameAnalytics(...p); + return auditLogger.wrapTask( + () => mlClient.updateDataFrameAnalytics(...p), + 'update_dfa_job', + p + ); }, async updateDatafeed(...p: Parameters) { await datafeedIdsCheck(p); return auditLogger.wrapTask(() => mlClient.updateDatafeed(...p), 'update_ad_datafeed', p); }, async updateFilter(...p: Parameters) { - return mlClient.updateFilter(...p); + return auditLogger.wrapTask(() => mlClient.updateFilter(...p), 'update_filter', p); }, async updateJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); From 44cc25affe43df3c4cc5a542d2d26f2a6c5c07d3 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Oct 2024 15:03:39 +0100 Subject: [PATCH 06/14] adding missing calendar and filter tasks --- x-pack/plugins/ml/server/audit_logger.ts | 48 +++++++++++++++++-- .../ml/server/lib/ml_client/ml_client.ts | 16 ++++--- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/ml/server/audit_logger.ts b/x-pack/plugins/ml/server/audit_logger.ts index c7152f096df16..216fcb05c80b2 100644 --- a/x-pack/plugins/ml/server/audit_logger.ts +++ b/x-pack/plugins/ml/server/audit_logger.ts @@ -30,10 +30,16 @@ type TaskTypeAD = | 'stop_ad_datafeed' | 'update_ad_datafeed' | 'put_calendar' + | 'delete_calendar' | 'put_calendar_job' + | 'delete_calendar_job' | 'post_calendar_events' + | 'delete_calendar_event' | 'put_filter' - | 'update_filter'; + | 'update_filter' + | 'delete_filter' + | 'forecast' + | 'delete_forecast'; type TaskTypeDFA = | 'put_dfa_job' @@ -150,11 +156,14 @@ export class MlAuditLogger { // @ts-expect-error body is optional const jobIds = (params.body ?? params).job_ids; return { - message: `Creating calendar ${calendarId} ${ - jobIds ? `with job(s) ${jobIds.join()}` : '' - }`, + message: `Creating calendar ${calendarId} ${jobIds ? `with job(s) ${jobIds}` : ''}`, }; } + case 'delete_calendar': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + return { message: `Deleting calendar ${calendarId}` }; + } case 'put_calendar_job': { const [params] = p as Parameters; const calendarId = params.calendar_id; @@ -163,6 +172,14 @@ export class MlAuditLogger { message: `Adding job(s) ${jobIds} to calendar ${calendarId}`, }; } + case 'delete_calendar_job': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + const jobIds = params.job_id; + return { + message: `Removing job(s) ${jobIds} from calendar ${calendarId}`, + }; + } case 'post_calendar_events': { const [params] = p as Parameters; const calendarId = params.calendar_id; @@ -172,6 +189,14 @@ export class MlAuditLogger { message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, }; } + case 'delete_calendar_event': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + const eventId = params.event_id; + return { + message: `Removing event(s) ${eventId} from calendar ${calendarId}`, + }; + } case 'put_filter': { const [params] = p as Parameters; const filterId = params.filter_id; @@ -182,6 +207,21 @@ export class MlAuditLogger { const filterId = params.filter_id; return { message: `Updating filter ${filterId}` }; } + case 'delete_filter': { + const [params] = p as Parameters; + const filterId = params.filter_id; + return { message: `Deleting filter ${filterId}` }; + } + case 'forecast': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Forecasting for job ${jobId}` }; + } + case 'delete_forecast': { + const [params] = p as Parameters; + const forecastId = params.forecast_id; + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Deleting forecast ${forecastId} for job ${jobId}` }; + } /* Data Frame Analytics */ case 'put_dfa_job': { diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 2dbfc44079f7b..fb5fec9506c2b 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -165,13 +165,17 @@ export function getMlClient( return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'close_ad_job', p); }, async deleteCalendar(...p: Parameters) { - return mlClient.deleteCalendar(...p); + return auditLogger.wrapTask(() => mlClient.deleteCalendar(...p), 'delete_calendar', p); }, async deleteCalendarEvent(...p: Parameters) { - return mlClient.deleteCalendarEvent(...p); + return auditLogger.wrapTask( + () => mlClient.deleteCalendarEvent(...p), + 'delete_calendar_event', + p + ); }, async deleteCalendarJob(...p: Parameters) { - return mlClient.deleteCalendarJob(...p); + return auditLogger.wrapTask(() => mlClient.deleteCalendarJob(...p), 'delete_calendar_job', p); }, async deleteDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); @@ -202,11 +206,11 @@ export function getMlClient( return mlClient.deleteExpiredData(...p); }, async deleteFilter(...p: Parameters) { - return mlClient.deleteFilter(...p); + return auditLogger.wrapTask(() => mlClient.deleteFilter(...p), 'delete_filter', p); }, async deleteForecast(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.deleteForecast(...p); + return auditLogger.wrapTask(() => mlClient.deleteForecast(...p), 'delete_forecast', p); }, async deleteJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -246,7 +250,7 @@ export function getMlClient( }, async forecast(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.forecast(...p); + return auditLogger.wrapTask(() => mlClient.forecast(...p), 'forecast', p); }, async getBuckets(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); From 44060eae4488df91ede75a6a9e13f3f6e7f4160f Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Oct 2024 16:56:25 +0100 Subject: [PATCH 07/14] code clean up --- x-pack/plugins/ml/server/audit_logger.ts | 152 +++++++++++------------ 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/x-pack/plugins/ml/server/audit_logger.ts b/x-pack/plugins/ml/server/audit_logger.ts index 216fcb05c80b2..731bd2e74f4c8 100644 --- a/x-pack/plugins/ml/server/audit_logger.ts +++ b/x-pack/plugins/ml/server/audit_logger.ts @@ -58,228 +58,226 @@ type TaskTypeNLP = type TaskType = TaskTypeAD | TaskTypeDFA | TaskTypeNLP; +const APPLICATION = 'elastic/ml'; + export class MlAuditLogger { private auditLogger: AuditLogger; constructor(audit: CoreAuditService, request?: KibanaRequest) { this.auditLogger = request ? audit.asScoped(request) : audit.withoutRequest; } - public log(message: string) { - this.auditLogger.log({ message, labels: { application: 'elastic/ml' } }); - } - - private logSuccess(logEntry: any) { - this.auditLogger.log({ ...logEntry, labels: { application: 'elastic/ml' } }); - } - - private logFailure(logEntry: any) { - this.auditLogger.log({ ...logEntry, labels: { application: 'elastic/ml' } }); - } - public async wrapTask(task: () => T, taskType: TaskType, p: P) { - const logEntry = this.createLogEntry(taskType, p); try { const resp = await task(); - this.logSuccess(logEntry); + this.logSuccess(taskType, p); return resp; } catch (error) { - this.logFailure(logEntry); + this.logFailure(taskType, p); throw error; } } - private createLogEntry(taskName: string, p: MlClientParams) { - switch (taskName) { + public logMessage(message: string) { + this.auditLogger.log({ + message, + labels: { + application: APPLICATION, + }, + }); + } + + private logSuccess(taskType: TaskType, p: MlClientParams) { + const entry = this.createLogEntry(taskType, p, true); + this.auditLogger.log(entry); + } + private logFailure(taskType: TaskType, p: MlClientParams) { + const entry = this.createLogEntry(taskType, p, false); + this.auditLogger.log(entry); + } + + private createLogEntry(taskType: TaskType, p: MlClientParams, success: boolean) { + return { + event: { action: taskType, outcome: success ? 'success' : 'failure' }, + message: this.createMessage(taskType, p), + labels: { + application: APPLICATION, + }, + }; + } + + private createMessage(taskType: TaskType, p: MlClientParams): string { + switch (taskType) { /* Anomaly Detection */ case 'put_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Creating anomaly detection job ${jobId}` }; + return `Creating anomaly detection job ${jobId}`; } case 'delete_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Deleting anomaly detection job ${jobId}` }; + return `Deleting anomaly detection job ${jobId}`; } case 'delete_model_snapshot': { const [jobId] = getADJobIdsFromRequest(p); - const snapshotId = getSnapshotIdFromRequest( - p as Parameters - ); - return { message: `Deleting model snapshot ${snapshotId} from job ${jobId}` }; + const [params] = p as Parameters; + const snapshotId = params.snapshot_id; + return `Deleting model snapshot ${snapshotId} from job ${jobId}`; } case 'open_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Opening anomaly detection job ${jobId}` }; + return `Opening anomaly detection job ${jobId}`; } case 'close_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Closing anomaly detection job ${jobId}` }; + return `Closing anomaly detection job ${jobId}`; } case 'update_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Updating anomaly detection job ${jobId}` }; + return `Updating anomaly detection job ${jobId}`; } case 'reset_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Resetting anomaly detection job ${jobId}` }; + return `Resetting anomaly detection job ${jobId}`; } case 'revert_ad_snapshot': { const [jobId] = getADJobIdsFromRequest(p); - const snapshotId = getSnapshotIdFromRequest( - p as Parameters - ); - return { message: `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}` }; + const [params] = p as Parameters; + const snapshotId = params.snapshot_id; + return `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}`; } case 'put_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); const [jobId] = getADJobIdsFromRequest(p); - return { message: `Creating anomaly detection datafeed ${datafeedId} for job ${jobId}` }; + return `Creating anomaly detection datafeed ${datafeedId} for job ${jobId}`; } case 'delete_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return { message: `Deleting anomaly detection datafeed ${datafeedId}` }; + return `Deleting anomaly detection datafeed ${datafeedId}`; } case 'start_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return { message: `Starting anomaly detection datafeed ${datafeedId}` }; + return `Starting anomaly detection datafeed ${datafeedId}`; } case 'stop_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return { message: `Stopping anomaly detection datafeed ${datafeedId}` }; + return `Stopping anomaly detection datafeed ${datafeedId}`; } case 'update_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return { message: `Updating anomaly detection datafeed ${datafeedId}` }; + return `Updating anomaly detection datafeed ${datafeedId}`; } case 'put_calendar': { const [params] = p as Parameters; const calendarId = params.calendar_id; // @ts-expect-error body is optional const jobIds = (params.body ?? params).job_ids; - return { - message: `Creating calendar ${calendarId} ${jobIds ? `with job(s) ${jobIds}` : ''}`, - }; + return `Creating calendar ${calendarId} ${jobIds ? `with job(s) ${jobIds}` : ''}`; } case 'delete_calendar': { const [params] = p as Parameters; const calendarId = params.calendar_id; - return { message: `Deleting calendar ${calendarId}` }; + return `Deleting calendar ${calendarId}`; } case 'put_calendar_job': { const [params] = p as Parameters; const calendarId = params.calendar_id; const jobIds = params.job_id; - return { - message: `Adding job(s) ${jobIds} to calendar ${calendarId}`, - }; + return `Adding job(s) ${jobIds} to calendar ${calendarId}`; } case 'delete_calendar_job': { const [params] = p as Parameters; const calendarId = params.calendar_id; const jobIds = params.job_id; - return { - message: `Removing job(s) ${jobIds} from calendar ${calendarId}`, - }; + return `Removing job(s) ${jobIds} from calendar ${calendarId}`; } case 'post_calendar_events': { const [params] = p as Parameters; const calendarId = params.calendar_id; // @ts-expect-error body is optional const eventsCount = (params.body ?? params).events; - return { - message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, - }; + return `Adding ${eventsCount} event(s) to calendar ${calendarId}`; } case 'delete_calendar_event': { const [params] = p as Parameters; const calendarId = params.calendar_id; const eventId = params.event_id; - return { - message: `Removing event(s) ${eventId} from calendar ${calendarId}`, - }; + return `Removing event(s) ${eventId} from calendar ${calendarId}`; } case 'put_filter': { const [params] = p as Parameters; const filterId = params.filter_id; - return { message: `Creating filter ${filterId}` }; + return `Creating filter ${filterId}`; } case 'update_filter': { const [params] = p as Parameters; const filterId = params.filter_id; - return { message: `Updating filter ${filterId}` }; + return `Updating filter ${filterId}`; } case 'delete_filter': { const [params] = p as Parameters; const filterId = params.filter_id; - return { message: `Deleting filter ${filterId}` }; + return `Deleting filter ${filterId}`; } case 'forecast': { const [jobId] = getADJobIdsFromRequest(p); - return { message: `Forecasting for job ${jobId}` }; + return `Forecasting for job ${jobId}`; } case 'delete_forecast': { const [params] = p as Parameters; const forecastId = params.forecast_id; const [jobId] = getADJobIdsFromRequest(p); - return { message: `Deleting forecast ${forecastId} for job ${jobId}` }; + return `Deleting forecast ${forecastId} for job ${jobId}`; } /* Data Frame Analytics */ case 'put_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return { message: `Creating data frame analytics job ${analyticsId}` }; + return `Creating data frame analytics job ${analyticsId}`; } case 'delete_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return { message: `Deleting data frame analytics job ${analyticsId}` }; + return `Deleting data frame analytics job ${analyticsId}`; } case 'start_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return { message: `Starting data frame analytics job ${analyticsId}` }; + return `Starting data frame analytics job ${analyticsId}`; } case 'stop_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return { message: `Stopping data frame analytics job ${analyticsId}` }; + return `Stopping data frame analytics job ${analyticsId}`; } case 'update_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return { message: `Updating data frame analytics job ${analyticsId}` }; + return `Updating data frame analytics job ${analyticsId}`; } /* Trained Models */ case 'put_trained_model': { const [modelId] = getModelIdsFromRequest(p); - return { message: `Creating trained model ${modelId}` }; + return `Creating trained model ${modelId}`; } case 'delete_trained_model': { const [modelId] = getModelIdsFromRequest(p); - return { message: `Deleting trained model ${modelId}` }; + return `Deleting trained model ${modelId}`; } case 'start_trained_model_deployment': { const [modelId] = getModelIdsFromRequest(p); - return { message: `Starting trained model deployment for model ${modelId}` }; + return `Starting trained model deployment for model ${modelId}`; } case 'stop_trained_model_deployment': { const [modelId] = getModelIdsFromRequest(p); - return { message: `Stopping trained model deployment for model ${modelId}` }; + return `Stopping trained model deployment for model ${modelId}`; } case 'update_trained_model_deployment': { const [modelId] = getModelIdsFromRequest(p); - return { message: `Updating trained model deployment for model ${modelId}` }; + return `Updating trained model deployment for model ${modelId}`; } case 'infer_trained_model': { const [modelId] = getModelIdsFromRequest(p); - return { message: `Inferring trained model ${modelId}` }; + return `Inferring trained model ${modelId}`; } - default: { - return { message: `Unknown ML task ${taskName}` }; - } + default: + return `Unknown ML task ${taskType}`; } } } - -function getSnapshotIdFromRequest([params]: - | Parameters - | Parameters): string | undefined { - return params?.snapshot_id; -} From 5da249d099587be3150065a27fd3ac35d621fa1c Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Oct 2024 16:59:32 +0100 Subject: [PATCH 08/14] refactor --- .../{audit_logger.ts => lib/ml_client/ml_audit_logger.ts} | 4 ++-- x-pack/plugins/ml/server/lib/ml_client/ml_client.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename x-pack/plugins/ml/server/{audit_logger.ts => lib/ml_client/ml_audit_logger.ts} (98%) diff --git a/x-pack/plugins/ml/server/audit_logger.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts similarity index 98% rename from x-pack/plugins/ml/server/audit_logger.ts rename to x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts index 731bd2e74f4c8..bec3ab4a5d025 100644 --- a/x-pack/plugins/ml/server/audit_logger.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts @@ -7,13 +7,13 @@ import type { KibanaRequest } from '@kbn/core-http-server'; import type { AuditLogger, CoreAuditService } from '@kbn/core/server'; -import type { MlClient, MlClientParams } from './lib/ml_client/types'; +import type { MlClient, MlClientParams } from './types'; import { getADJobIdsFromRequest, getDFAJobIdsFromRequest, getDatafeedIdsFromRequest, getModelIdsFromRequest, -} from './lib/ml_client/ml_client'; +} from './ml_client'; type TaskTypeAD = | 'put_ad_job' diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index fb5fec9506c2b..44788a532b063 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -25,7 +25,7 @@ import type { MlGetDatafeedParams, MlGetTrainedModelParams, } from './types'; -import type { MlAuditLogger } from '../../audit_logger'; +import type { MlAuditLogger } from './ml_audit_logger'; export function getMlClient( client: IScopedClusterClient, From 8680bb918a3c0d9b0e5dfa0e9c7b4bf3b5ba07fc Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 9 Oct 2024 17:10:18 +0100 Subject: [PATCH 09/14] fixing imports --- x-pack/plugins/ml/server/lib/route_guard.ts | 2 +- x-pack/plugins/ml/server/shared_services/shared_services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/server/lib/route_guard.ts b/x-pack/plugins/ml/server/lib/route_guard.ts index bfac399768b09..cd4cb2615fc53 100644 --- a/x-pack/plugins/ml/server/lib/route_guard.ts +++ b/x-pack/plugins/ml/server/lib/route_guard.ts @@ -29,7 +29,7 @@ import type { MlLicense } from '../../common/license'; import type { MlClient } from './ml_client'; import { getMlClient } from './ml_client'; import { getDataViewsServiceFactory } from './data_views_utils'; -import { MlAuditLogger } from '../audit_logger'; +import { MlAuditLogger } from './ml_client/ml_audit_logger'; type MLRequestHandlerContext = CustomRequestHandlerContext<{ alerting?: AlertingApiRequestHandlerContext; diff --git a/x-pack/plugins/ml/server/shared_services/shared_services.ts b/x-pack/plugins/ml/server/shared_services/shared_services.ts index aa9a8f2c2a999..3480e6ee0713a 100644 --- a/x-pack/plugins/ml/server/shared_services/shared_services.ts +++ b/x-pack/plugins/ml/server/shared_services/shared_services.ts @@ -60,7 +60,7 @@ import { getJobsHealthServiceProvider } from '../lib/alerts/jobs_health_service' import type { FieldFormatsRegistryProvider } from '../../common/types/kibana'; import type { GetDataViewsService } from '../lib/data_views_utils'; import { getDataViewsServiceFactory } from '../lib/data_views_utils'; -import { MlAuditLogger } from '../audit_logger'; +import { MlAuditLogger } from '../lib/ml_client/ml_audit_logger'; export type SharedServices = JobServiceProvider & AnomalyDetectorsProvider & From 7fb2ad938894057843fa9d85469d6c8bb3b65000 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 10 Oct 2024 09:02:01 +0100 Subject: [PATCH 10/14] import clean up --- x-pack/plugins/ml/server/lib/ml_client/index.ts | 1 + x-pack/plugins/ml/server/lib/route_guard.ts | 3 +-- x-pack/plugins/ml/server/shared_services/shared_services.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/server/lib/ml_client/index.ts b/x-pack/plugins/ml/server/lib/ml_client/index.ts index 03fc5e6244b31..92ef7b822e80a 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/index.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/index.ts @@ -8,3 +8,4 @@ export { getMlClient } from './ml_client'; export { MLJobNotFound, MLModelNotFound } from './errors'; export type { MlClient } from './types'; +export { MlAuditLogger } from './ml_audit_logger'; diff --git a/x-pack/plugins/ml/server/lib/route_guard.ts b/x-pack/plugins/ml/server/lib/route_guard.ts index cd4cb2615fc53..23a324e9fd2fc 100644 --- a/x-pack/plugins/ml/server/lib/route_guard.ts +++ b/x-pack/plugins/ml/server/lib/route_guard.ts @@ -27,9 +27,8 @@ import { mlSavedObjectServiceFactory } from '../saved_objects'; import type { MlLicense } from '../../common/license'; import type { MlClient } from './ml_client'; -import { getMlClient } from './ml_client'; +import { MlAuditLogger, getMlClient } from './ml_client'; import { getDataViewsServiceFactory } from './data_views_utils'; -import { MlAuditLogger } from './ml_client/ml_audit_logger'; type MLRequestHandlerContext = CustomRequestHandlerContext<{ alerting?: AlertingApiRequestHandlerContext; diff --git a/x-pack/plugins/ml/server/shared_services/shared_services.ts b/x-pack/plugins/ml/server/shared_services/shared_services.ts index 3480e6ee0713a..f7b0d42409221 100644 --- a/x-pack/plugins/ml/server/shared_services/shared_services.ts +++ b/x-pack/plugins/ml/server/shared_services/shared_services.ts @@ -50,7 +50,7 @@ import { MLUISettingsClientUninitialized, } from './errors'; import type { MlClient } from '../lib/ml_client'; -import { getMlClient } from '../lib/ml_client'; +import { getMlClient, MlAuditLogger } from '../lib/ml_client'; import type { MLSavedObjectService } from '../saved_objects'; import { mlSavedObjectServiceFactory } from '../saved_objects'; import type { MlAlertingServiceProvider } from './providers/alerting_service'; @@ -60,7 +60,6 @@ import { getJobsHealthServiceProvider } from '../lib/alerts/jobs_health_service' import type { FieldFormatsRegistryProvider } from '../../common/types/kibana'; import type { GetDataViewsService } from '../lib/data_views_utils'; import { getDataViewsServiceFactory } from '../lib/data_views_utils'; -import { MlAuditLogger } from '../lib/ml_client/ml_audit_logger'; export type SharedServices = JobServiceProvider & AnomalyDetectorsProvider & From 4fd355009ec6821b50579e356405b4b61b5e228d Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 10 Oct 2024 16:56:29 +0100 Subject: [PATCH 11/14] adding additional ecs fields --- .../server/lib/ml_client/ml_audit_logger.ts | 336 ++++++++++++------ .../ml/server/lib/ml_client/ml_client.ts | 82 +++-- 2 files changed, 265 insertions(+), 153 deletions(-) diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts index bec3ab4a5d025..e9c780f8b1a4b 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts @@ -6,7 +6,8 @@ */ import type { KibanaRequest } from '@kbn/core-http-server'; -import type { AuditLogger, CoreAuditService } from '@kbn/core/server'; +import type { AuditLogger, CoreAuditService, EcsEvent } from '@kbn/core/server'; +import type { ArrayElement } from '@kbn/utility-types'; import type { MlClient, MlClientParams } from './types'; import { getADJobIdsFromRequest, @@ -16,49 +17,64 @@ import { } from './ml_client'; type TaskTypeAD = - | 'put_ad_job' - | 'delete_ad_job' - | 'delete_model_snapshot' - | 'open_ad_job' - | 'close_ad_job' - | 'update_ad_job' - | 'reset_ad_job' - | 'revert_ad_snapshot' - | 'put_ad_datafeed' - | 'delete_ad_datafeed' - | 'start_ad_datafeed' - | 'stop_ad_datafeed' - | 'update_ad_datafeed' - | 'put_calendar' - | 'delete_calendar' - | 'put_calendar_job' - | 'delete_calendar_job' - | 'post_calendar_events' - | 'delete_calendar_event' - | 'put_filter' - | 'update_filter' - | 'delete_filter' - | 'forecast' - | 'delete_forecast'; + | 'ml_put_ad_job' + | 'ml_delete_ad_job' + | 'ml_delete_model_snapshot' + | 'ml_open_ad_job' + | 'ml_close_ad_job' + | 'ml_update_ad_job' + | 'ml_reset_ad_job' + | 'ml_revert_ad_snapshot' + | 'ml_put_ad_datafeed' + | 'ml_delete_ad_datafeed' + | 'ml_start_ad_datafeed' + | 'ml_stop_ad_datafeed' + | 'ml_update_ad_datafeed' + | 'ml_put_calendar' + | 'ml_delete_calendar' + | 'ml_put_calendar_job' + | 'ml_delete_calendar_job' + | 'ml_post_calendar_events' + | 'ml_delete_calendar_event' + | 'ml_put_filter' + | 'ml_update_filter' + | 'ml_delete_filter' + | 'ml_forecast' + | 'ml_delete_forecast'; type TaskTypeDFA = - | 'put_dfa_job' - | 'delete_dfa_job' - | 'start_dfa_job' - | 'stop_dfa_job' - | 'update_dfa_job'; + | 'ml_put_dfa_job' + | 'ml_delete_dfa_job' + | 'ml_start_dfa_job' + | 'ml_stop_dfa_job' + | 'ml_update_dfa_job'; type TaskTypeNLP = - | 'put_trained_model' - | 'delete_trained_model' - | 'start_trained_model_deployment' - | 'stop_trained_model_deployment' - | 'update_trained_model_deployment' - | 'infer_trained_model'; + | 'ml_put_trained_model' + | 'ml_delete_trained_model' + | 'ml_start_trained_model_deployment' + | 'ml_stop_trained_model_deployment' + | 'ml_update_trained_model_deployment' + | 'ml_infer_trained_model'; type TaskType = TaskTypeAD | TaskTypeDFA | TaskTypeNLP; const APPLICATION = 'elastic/ml'; +const CATEGORY = 'database'; + +const EVENT_TYPES: Record> = { + creation: 'creation', + deletion: 'deletion', + change: 'change', + access: 'access', +} as const; +type EventTypes = keyof typeof EVENT_TYPES; + +interface MlLogEntry { + event: EcsEvent; + message: string; + labels: { application: typeof APPLICATION }; +} export class MlAuditLogger { private auditLogger: AuditLogger; @@ -88,196 +104,280 @@ export class MlAuditLogger { private logSuccess(taskType: TaskType, p: MlClientParams) { const entry = this.createLogEntry(taskType, p, true); - this.auditLogger.log(entry); + if (entry) { + this.auditLogger.log(entry); + } } private logFailure(taskType: TaskType, p: MlClientParams) { const entry = this.createLogEntry(taskType, p, false); - this.auditLogger.log(entry); + if (entry) { + this.auditLogger.log(entry); + } } - private createLogEntry(taskType: TaskType, p: MlClientParams, success: boolean) { - return { - event: { action: taskType, outcome: success ? 'success' : 'failure' }, - message: this.createMessage(taskType, p), - labels: { - application: APPLICATION, - }, - }; + private createLogEntry( + taskType: TaskType, + p: MlClientParams, + success: boolean + ): MlLogEntry | undefined { + try { + const { message, type } = this.createPartialLogEntry(taskType, p); + return { + event: { + action: taskType, + type, + category: [CATEGORY], + outcome: success ? 'success' : 'failure', + }, + message, + labels: { + application: APPLICATION, + }, + }; + } catch (error) { + // if an unknown task type is passed, we won't log anything + } } - private createMessage(taskType: TaskType, p: MlClientParams): string { + private createPartialLogEntry( + taskType: TaskType, + p: MlClientParams + ): { message: string; type: EventTypes[] } { switch (taskType) { /* Anomaly Detection */ - case 'put_ad_job': { + case 'ml_put_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return `Creating anomaly detection job ${jobId}`; + return { message: `Creating anomaly detection job ${jobId}`, type: [EVENT_TYPES.creation] }; } - case 'delete_ad_job': { + case 'ml_delete_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return `Deleting anomaly detection job ${jobId}`; + return { message: `Deleting anomaly detection job ${jobId}`, type: [EVENT_TYPES.deletion] }; } - case 'delete_model_snapshot': { + case 'ml_delete_model_snapshot': { const [jobId] = getADJobIdsFromRequest(p); const [params] = p as Parameters; const snapshotId = params.snapshot_id; - return `Deleting model snapshot ${snapshotId} from job ${jobId}`; + return { + message: `Deleting model snapshot ${snapshotId} from job ${jobId}`, + type: [EVENT_TYPES.deletion], + }; } - case 'open_ad_job': { + case 'ml_open_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return `Opening anomaly detection job ${jobId}`; + return { message: `Opening anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; } - case 'close_ad_job': { + case 'ml_close_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return `Closing anomaly detection job ${jobId}`; + return { message: `Closing anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; } - case 'update_ad_job': { + case 'ml_update_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return `Updating anomaly detection job ${jobId}`; + return { message: `Updating anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; } - case 'reset_ad_job': { + case 'ml_reset_ad_job': { const [jobId] = getADJobIdsFromRequest(p); - return `Resetting anomaly detection job ${jobId}`; + return { message: `Resetting anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; } - case 'revert_ad_snapshot': { + case 'ml_revert_ad_snapshot': { const [jobId] = getADJobIdsFromRequest(p); const [params] = p as Parameters; const snapshotId = params.snapshot_id; - return `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}`; + return { + message: `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}`, + type: [EVENT_TYPES.change], + }; } - case 'put_ad_datafeed': { + case 'ml_put_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); const [jobId] = getADJobIdsFromRequest(p); - return `Creating anomaly detection datafeed ${datafeedId} for job ${jobId}`; + return { + message: `Creating anomaly detection datafeed ${datafeedId} for job ${jobId}`, + type: [EVENT_TYPES.creation], + }; } - case 'delete_ad_datafeed': { + case 'ml_delete_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return `Deleting anomaly detection datafeed ${datafeedId}`; + return { + message: `Deleting anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.deletion], + }; } - case 'start_ad_datafeed': { + case 'ml_start_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return `Starting anomaly detection datafeed ${datafeedId}`; + return { + message: `Starting anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.change], + }; } - case 'stop_ad_datafeed': { + case 'ml_stop_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return `Stopping anomaly detection datafeed ${datafeedId}`; + return { + message: `Stopping anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.change], + }; } - case 'update_ad_datafeed': { + case 'ml_update_ad_datafeed': { const [datafeedId] = getDatafeedIdsFromRequest(p); - return `Updating anomaly detection datafeed ${datafeedId}`; + return { + message: `Updating anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.change], + }; } - case 'put_calendar': { + case 'ml_put_calendar': { const [params] = p as Parameters; const calendarId = params.calendar_id; // @ts-expect-error body is optional const jobIds = (params.body ?? params).job_ids; - return `Creating calendar ${calendarId} ${jobIds ? `with job(s) ${jobIds}` : ''}`; + return { + message: `Creating calendar ${calendarId} ${jobIds ? `with job(s) ${jobIds}` : ''}`, + type: [EVENT_TYPES.creation], + }; } - case 'delete_calendar': { + case 'ml_delete_calendar': { const [params] = p as Parameters; const calendarId = params.calendar_id; - return `Deleting calendar ${calendarId}`; + return { message: `Deleting calendar ${calendarId}`, type: [EVENT_TYPES.deletion] }; } - case 'put_calendar_job': { + case 'ml_put_calendar_job': { const [params] = p as Parameters; const calendarId = params.calendar_id; const jobIds = params.job_id; - return `Adding job(s) ${jobIds} to calendar ${calendarId}`; + return { + message: `Adding job(s) ${jobIds} to calendar ${calendarId}`, + type: [EVENT_TYPES.creation], + }; } - case 'delete_calendar_job': { + case 'ml_delete_calendar_job': { const [params] = p as Parameters; const calendarId = params.calendar_id; const jobIds = params.job_id; - return `Removing job(s) ${jobIds} from calendar ${calendarId}`; + return { + message: `Removing job(s) ${jobIds} from calendar ${calendarId}`, + type: [EVENT_TYPES.deletion], + }; } - case 'post_calendar_events': { + case 'ml_post_calendar_events': { const [params] = p as Parameters; const calendarId = params.calendar_id; // @ts-expect-error body is optional const eventsCount = (params.body ?? params).events; - return `Adding ${eventsCount} event(s) to calendar ${calendarId}`; + return { + message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, + type: [EVENT_TYPES.creation], + }; } - case 'delete_calendar_event': { + case 'ml_delete_calendar_event': { const [params] = p as Parameters; const calendarId = params.calendar_id; const eventId = params.event_id; - return `Removing event(s) ${eventId} from calendar ${calendarId}`; + return { + message: `Removing event(s) ${eventId} from calendar ${calendarId}`, + type: [EVENT_TYPES.deletion], + }; } - case 'put_filter': { + case 'ml_put_filter': { const [params] = p as Parameters; const filterId = params.filter_id; - return `Creating filter ${filterId}`; + return { message: `Creating filter ${filterId}`, type: [EVENT_TYPES.creation] }; } - case 'update_filter': { + case 'ml_update_filter': { const [params] = p as Parameters; const filterId = params.filter_id; - return `Updating filter ${filterId}`; + return { message: `Updating filter ${filterId}`, type: [EVENT_TYPES.change] }; } - case 'delete_filter': { + case 'ml_delete_filter': { const [params] = p as Parameters; const filterId = params.filter_id; - return `Deleting filter ${filterId}`; + return { message: `Deleting filter ${filterId}`, type: [EVENT_TYPES.deletion] }; } - case 'forecast': { + case 'ml_forecast': { const [jobId] = getADJobIdsFromRequest(p); - return `Forecasting for job ${jobId}`; + return { message: `Forecasting for job ${jobId}`, type: [EVENT_TYPES.creation] }; } - case 'delete_forecast': { + case 'ml_delete_forecast': { const [params] = p as Parameters; const forecastId = params.forecast_id; const [jobId] = getADJobIdsFromRequest(p); - return `Deleting forecast ${forecastId} for job ${jobId}`; + return { + message: `Deleting forecast ${forecastId} for job ${jobId}`, + type: [EVENT_TYPES.deletion], + }; } /* Data Frame Analytics */ - case 'put_dfa_job': { + case 'ml_put_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return `Creating data frame analytics job ${analyticsId}`; + return { + message: `Creating data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.creation], + }; } - case 'delete_dfa_job': { + case 'ml_delete_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return `Deleting data frame analytics job ${analyticsId}`; + return { + message: `Deleting data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.deletion], + }; } - case 'start_dfa_job': { + case 'ml_start_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return `Starting data frame analytics job ${analyticsId}`; + return { + message: `Starting data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.change], + }; } - case 'stop_dfa_job': { + case 'ml_stop_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return `Stopping data frame analytics job ${analyticsId}`; + return { + message: `Stopping data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.change], + }; } - case 'update_dfa_job': { + case 'ml_update_dfa_job': { const [analyticsId] = getDFAJobIdsFromRequest(p); - return `Updating data frame analytics job ${analyticsId}`; + return { + message: `Updating data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.change], + }; } /* Trained Models */ - case 'put_trained_model': { + case 'ml_put_trained_model': { const [modelId] = getModelIdsFromRequest(p); - return `Creating trained model ${modelId}`; + return { message: `Creating trained model ${modelId}`, type: [EVENT_TYPES.creation] }; } - case 'delete_trained_model': { + case 'ml_delete_trained_model': { const [modelId] = getModelIdsFromRequest(p); - return `Deleting trained model ${modelId}`; + return { message: `Deleting trained model ${modelId}`, type: [EVENT_TYPES.deletion] }; } - case 'start_trained_model_deployment': { + case 'ml_start_trained_model_deployment': { const [modelId] = getModelIdsFromRequest(p); - return `Starting trained model deployment for model ${modelId}`; + return { + message: `Starting trained model deployment for model ${modelId}`, + type: [EVENT_TYPES.change], + }; } - case 'stop_trained_model_deployment': { + case 'ml_stop_trained_model_deployment': { const [modelId] = getModelIdsFromRequest(p); - return `Stopping trained model deployment for model ${modelId}`; + return { + message: `Stopping trained model deployment for model ${modelId}`, + type: [EVENT_TYPES.change], + }; } - case 'update_trained_model_deployment': { + case 'ml_update_trained_model_deployment': { const [modelId] = getModelIdsFromRequest(p); - return `Updating trained model deployment for model ${modelId}`; + return { + message: `Updating trained model deployment for model ${modelId}`, + type: [EVENT_TYPES.change], + }; } - case 'infer_trained_model': { + case 'ml_infer_trained_model': { const [modelId] = getModelIdsFromRequest(p); - return `Inferring trained model ${modelId}`; + return { message: `Inferring trained model ${modelId}`, type: [EVENT_TYPES.access] }; } default: - return `Unknown ML task ${taskType}`; + throw new Error(`Unsupported task type: ${taskType}`); } } } diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 44788a532b063..c7bf9ca8bc5d8 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -162,26 +162,30 @@ export function getMlClient( return { async closeJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'close_ad_job', p); + return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'ml_close_ad_job', p); }, async deleteCalendar(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.deleteCalendar(...p), 'delete_calendar', p); + return auditLogger.wrapTask(() => mlClient.deleteCalendar(...p), 'ml_delete_calendar', p); }, async deleteCalendarEvent(...p: Parameters) { return auditLogger.wrapTask( () => mlClient.deleteCalendarEvent(...p), - 'delete_calendar_event', + 'ml_delete_calendar_event', p ); }, async deleteCalendarJob(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.deleteCalendarJob(...p), 'delete_calendar_job', p); + return auditLogger.wrapTask( + () => mlClient.deleteCalendarJob(...p), + 'ml_delete_calendar_job', + p + ); }, async deleteDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); const resp = await auditLogger.wrapTask( () => mlClient.deleteDataFrameAnalytics(...p), - 'delete_dfa_job', + 'ml_delete_dfa_job', p ); // don't delete the job saved object as the real job will not be @@ -193,7 +197,7 @@ export function getMlClient( const [datafeedId] = getDatafeedIdsFromRequest(p); const resp = await auditLogger.wrapTask( () => mlClient.deleteDatafeed(...p), - 'delete_ad_datafeed', + 'ml_delete_ad_datafeed', p ); if (datafeedId !== undefined) { @@ -206,15 +210,15 @@ export function getMlClient( return mlClient.deleteExpiredData(...p); }, async deleteFilter(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.deleteFilter(...p), 'delete_filter', p); + return auditLogger.wrapTask(() => mlClient.deleteFilter(...p), 'ml_delete_filter', p); }, async deleteForecast(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.deleteForecast(...p), 'delete_forecast', p); + return auditLogger.wrapTask(() => mlClient.deleteForecast(...p), 'ml_delete_forecast', p); }, async deleteJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.deleteJob(...p), 'delete_ad_job', p); + return auditLogger.wrapTask(() => mlClient.deleteJob(...p), 'ml_delete_ad_job', p); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. }, @@ -222,7 +226,7 @@ export function getMlClient( await jobIdsCheck('anomaly-detector', p); return auditLogger.wrapTask( () => mlClient.deleteModelSnapshot(...p), - 'delete_model_snapshot', + 'ml_delete_model_snapshot', p ); }, @@ -230,7 +234,7 @@ export function getMlClient( await modelIdsCheck(p); return auditLogger.wrapTask( () => mlClient.deleteTrainedModel(...p), - 'delete_trained_model', + 'ml_delete_trained_model', p ); }, @@ -250,7 +254,7 @@ export function getMlClient( }, async forecast(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.forecast(...p), 'forecast', p); + return auditLogger.wrapTask(() => mlClient.forecast(...p), 'ml_forecast', p); }, async getBuckets(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -536,7 +540,7 @@ export function getMlClient( }, p[1] ), - 'start_trained_model_deployment', + 'ml_start_trained_model_deployment', p ); }, @@ -552,7 +556,7 @@ export function getMlClient( path: `/_ml/trained_models/${deploymentId}/deployment/_update`, body: bodyParams, }), - 'update_trained_model_deployment', + 'ml_update_trained_model_deployment', p ); }, @@ -562,7 +566,7 @@ export function getMlClient( return auditLogger.wrapTask( () => mlClient.stopTrainedModelDeployment(...p), - 'stop_trained_model_deployment', + 'ml_stop_trained_model_deployment', p ); }, @@ -593,7 +597,7 @@ export function getMlClient( }, p[1] ), - 'infer_trained_model', + 'ml_infer_trained_model', p ); }, @@ -602,12 +606,12 @@ export function getMlClient( }, async openJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.openJob(...p), 'open_ad_job', p); + return auditLogger.wrapTask(() => mlClient.openJob(...p), 'ml_open_ad_job', p); }, async postCalendarEvents(...p: Parameters) { return auditLogger.wrapTask( () => mlClient.postCalendarEvents(...p), - 'post_calendar_events', + 'ml_post_calendar_events', p ); }, @@ -620,16 +624,16 @@ export function getMlClient( return mlClient.previewDatafeed(...p); }, async putCalendar(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.putCalendar(...p), 'put_calendar', p); + return auditLogger.wrapTask(() => mlClient.putCalendar(...p), 'ml_put_calendar', p); }, async putCalendarJob(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.putCalendarJob(...p), 'put_calendar_job', p); + return auditLogger.wrapTask(() => mlClient.putCalendarJob(...p), 'ml_put_calendar_job', p); }, async putDataFrameAnalytics(...p: Parameters) { const [analyticsId] = getDFAJobIdsFromRequest(p); const resp = await auditLogger.wrapTask( () => mlClient.putDataFrameAnalytics(...p), - 'put_dfa_job', + 'ml_put_dfa_job', p ); if (analyticsId !== undefined) { @@ -641,7 +645,7 @@ export function getMlClient( const [datafeedId] = getDatafeedIdsFromRequest(p); const resp = await auditLogger.wrapTask( () => mlClient.putDatafeed(...p), - 'put_ad_datafeed', + 'ml_put_ad_datafeed', p ); const jobId = getJobIdFromBody(p); @@ -652,11 +656,11 @@ export function getMlClient( return resp; }, async putFilter(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.putFilter(...p), 'put_filter', p); + return auditLogger.wrapTask(() => mlClient.putFilter(...p), 'ml_put_filter', p); }, async putJob(...p: Parameters) { const [jobId] = getADJobIdsFromRequest(p); - const resp = await auditLogger.wrapTask(() => mlClient.putJob(...p), 'put_ad_job', p); + const resp = await auditLogger.wrapTask(() => mlClient.putJob(...p), 'ml_put_ad_job', p); if (jobId !== undefined) { await mlSavedObjectService.createAnomalyDetectionJob(jobId); } @@ -665,7 +669,7 @@ export function getMlClient( async putTrainedModel(...p: Parameters) { const resp = await auditLogger.wrapTask( () => mlClient.putTrainedModel(...p), - 'put_trained_model', + 'ml_put_trained_model', p ); const [modelId] = getModelIdsFromRequest(p); @@ -680,7 +684,7 @@ export function getMlClient( await jobIdsCheck('anomaly-detector', p); return auditLogger.wrapTask( () => mlClient.revertModelSnapshot(...p), - 'revert_ad_snapshot', + 'ml_revert_ad_snapshot', p ); }, @@ -689,42 +693,50 @@ export function getMlClient( }, async startDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return auditLogger.wrapTask(() => mlClient.startDataFrameAnalytics(...p), 'start_dfa_job', p); + return auditLogger.wrapTask( + () => mlClient.startDataFrameAnalytics(...p), + 'ml_start_dfa_job', + p + ); }, async startDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return auditLogger.wrapTask(() => mlClient.startDatafeed(...p), 'start_ad_datafeed', p); + return auditLogger.wrapTask(() => mlClient.startDatafeed(...p), 'ml_start_ad_datafeed', p); }, async stopDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return auditLogger.wrapTask(() => mlClient.stopDataFrameAnalytics(...p), 'stop_dfa_job', p); + return auditLogger.wrapTask( + () => mlClient.stopDataFrameAnalytics(...p), + 'ml_stop_dfa_job', + p + ); }, async stopDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return auditLogger.wrapTask(() => mlClient.stopDatafeed(...p), 'stop_ad_datafeed', p); + return auditLogger.wrapTask(() => mlClient.stopDatafeed(...p), 'ml_stop_ad_datafeed', p); }, async updateDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); return auditLogger.wrapTask( () => mlClient.updateDataFrameAnalytics(...p), - 'update_dfa_job', + 'ml_update_dfa_job', p ); }, async updateDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return auditLogger.wrapTask(() => mlClient.updateDatafeed(...p), 'update_ad_datafeed', p); + return auditLogger.wrapTask(() => mlClient.updateDatafeed(...p), 'ml_update_ad_datafeed', p); }, async updateFilter(...p: Parameters) { - return auditLogger.wrapTask(() => mlClient.updateFilter(...p), 'update_filter', p); + return auditLogger.wrapTask(() => mlClient.updateFilter(...p), 'ml_update_filter', p); }, async updateJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.updateJob(...p), 'update_ad_job', p); + return auditLogger.wrapTask(() => mlClient.updateJob(...p), 'ml_update_ad_job', p); }, async resetJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return auditLogger.wrapTask(() => mlClient.resetJob(...p), 'reset_ad_job', p); + return auditLogger.wrapTask(() => mlClient.resetJob(...p), 'ml_reset_ad_job', p); }, async updateModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); From 551d4c531e99604e11df89137bed1e513a0d52d4 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 10 Oct 2024 18:19:36 +0100 Subject: [PATCH 12/14] updating asciidoc --- docs/user/security/audit-logging.asciidoc | 158 ++++++++++++++++++++-- 1 file changed, 149 insertions(+), 9 deletions(-) diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index 0ddc830e4ed49..5ec3d667a94e5 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -17,20 +17,20 @@ by cluster-wide privileges. For more information on enabling audit logging in Audit logs are **disabled** by default. To enable this functionality, you must set `xpack.security.audit.enabled` to `true` in `kibana.yml`. -You can optionally configure audit logs location, file/rolling file appenders and +You can optionally configure audit logs location, file/rolling file appenders and ignore filters using <>. ============================================================================ [[xpack-security-ecs-audit-logging]] ==== Audit events -Refer to the table of events that can be logged for auditing purposes. +Refer to the table of events that can be logged for auditing purposes. Each event is broken down into <>, <>, <> and <> fields to make it easy to filter, query and aggregate the resulting logs. The <> field can be used to correlate multiple events that originate from the same request. -Refer to <> for a table of fields that get logged with audit event. +Refer to <> for a table of fields that get logged with audit event. [NOTE] ============================================================================ @@ -116,6 +116,42 @@ Refer to the corresponding {es} logs for potential write errors. .1+| `case_user_action_create_case` | `success` | User has created a case. +.2+| `ml_put_ad_job` +| `success` | Creating anomaly detection job. +| `failure` | Failed to create anomaly detection job. + +.2+| `ml_put_ad_datafeed` +| `success` | Creating anomaly detection datafeed. +| `failure` | Failed to create anomaly detection datafeed. + +.2+| `ml_put_calendar` +| `success` | Creating calendar. +| `failure` | Failed to create calendar. + +.2+| `ml_put_calendar_job` +| `success` | Adding job to calendar. +| `failure` | Failed to add job to calendar. + +.2+| `ml_post_calendar_events` +| `success` | Adding events to calendar. +| `failure` | Failed to add events to calendar. + +.2+| `ml_forecast` +| `success` | Creating anomaly detection forecast. +| `failure` | Failed to create anomaly detection forecast. + +.2+| `ml_put_filter` +| `success` | Creating filter. +| `failure` | Failed to create filter. + +.2+| `ml_put_dfa_job` +| `success` | Creating data frame analytics job. +| `failure` | Failed to create data frame analytics job. + +.2+| `ml_put_trained_model` +| `success` | Creating trained model. +| `failure` | Failed to create trained model. + 3+a| ====== Type: change @@ -234,6 +270,66 @@ Refer to the corresponding {es} logs for potential write errors. .1+| `case_user_action_update_case_title` | `success` | User has updated the case title. +.2+| `ml_open_ad_job` +| `success` | Opening anomaly detection job. +| `failure` | Failed to open anomaly detection job. + +.2+| `ml_close_ad_job` +| `success` | Closing anomaly detection job. +| `failure` | Failed to close anomaly detection job. + +.2+| `ml_start_ad_datafeed` +| `success` | Starting anomaly detection datafeed. +| `failure` | Failed to start anomaly detection datafeed. + +.2+| `ml_stop_ad_datafeed` +| `success` | Stopping anomaly detection datafeed. +| `failure` | Failed to stop anomaly detection datafeed. + +.2+| `ml_update_ad_job` +| `success` | Updating anomaly detection job. +| `failure` | Failed to update anomaly detection job. + +.2+| `ml_reset_ad_job` +| `success` | Resetting anomaly detection job. +| `failure` | Failed to reset anomaly detection job. + +.2+| `ml_revert_ad_snapshot` +| `success` | Reverting anomaly detection snapshot. +| `failure` | Failed to revert anomaly detection snapshot. + +.2+| `ml_update_ad_datafeed` +| `success` | Updating anomaly detection datafeed. +| `failure` | Failed to update anomaly detection datafeed. + +.2+| `ml_update_filter` +| `success` | Updating filter. +| `failure` | Failed to update filter. + +.2+| `ml_start_dfa_job` +| `success` | Starting data frame analytics job. +| `failure` | Failed to start data frame analytics job. + +.2+| `ml_stop_dfa_job` +| `success` | Stopping data frame analytics job. +| `failure` | Failed to stop data frame analytics job. + +.2+| `ml_update_dfa_job` +| `success` | Updating data frame analytics job. +| `failure` | Failed to update data frame analytics job. + +.2+| `ml_start_trained_model_deployment` +| `success` | Starting trained model deployment. +| `failure` | Failed to start trained model deployment. + +.2+| `ml_stop_trained_model_deployment` +| `success` | Stopping trained model deployment. +| `failure` | Failed to stop trained model deployment. + +.2+| `ml_update_trained_model_deployment` +| `success` | Updating trained model deployment. +| `failure` | Failed to update trained model deployment. + 3+a| ====== Type: deletion @@ -289,6 +385,46 @@ Refer to the corresponding {es} logs for potential write errors. .1+| `case_user_action_delete_case_tags` | `success` | User has removed tags from a case. +.2+| `ml_delete_ad_job` +| `success` | Deleting anomaly detection job. +| `failure` | Failed to delete anomaly detection job. + +.2+| `ml_delete_model_snapshot` +| `success` | Deleting model snapshot. +| `failure` | Failed to delete model snapshot. + +.2+| `ml_delete_ad_datafeed` +| `success` | Deleting anomaly detection datafeed. +| `failure` | Failed to delete anomaly detection datafeed. + +.2+| `ml_delete_calendar` +| `success` | Deleting calendar. +| `failure` | Failed to delete calendar. + +.2+| `ml_delete_calendar_job` +| `success` | Removing job from calendar. +| `failure` | Failed to remove job from calendar. + +.2+| `ml_delete_calendar_event` +| `success` | Deleting calendar event. +| `failure` | Failed to delete calendar event. + +.2+| `ml_delete_filter` +| `success` | Deleting filter. +| `failure` | Failed to delete filter. + +.2+| `ml_delete_forecast` +| `success` | Deleting forecast. +| `failure` | Failed to delete forecast. + +.2+| `ml_delete_dfa_job` +| `success` | Deleting data frame analytics job. +| `failure` | Failed to delete data frame analytics job. + +.2+| `ml_delete_trained_model` +| `success` | Deleting trained model. +| `failure` | Failed to delete trained model. + 3+a| ====== Type: access @@ -448,6 +584,10 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | User has accessed the connectors of a case. | `failure` | User is not authorized to access the connectors of a case. +.2+| `ml_infer_trained_model` +| `success` | Inferring using trained model. +| `failure` | Failed to infer using trained model. + 3+a| ===== Category: web @@ -474,12 +614,12 @@ Audit logs are written in JSON using https://www.elastic.co/guide/en/ecs/1.6/ind | *Description* | `@timestamp` -| Time when the event was generated. +| Time when the event was generated. Example: `2016-05-23T08:05:34.853Z` | `message` -| Human readable description of the event. +| Human readable description of the event. 2+a| ===== Event Fields @@ -489,7 +629,7 @@ Example: `2016-05-23T08:05:34.853Z` | [[field-event-action]] `event.action` | The action captured by the event. -Refer to <> for a table of possible actions. +Refer to <> for a table of possible actions. | [[field-event-category]] `event.category` | High level category associated with the event. @@ -513,7 +653,7 @@ Possible values: `deletion` | [[field-event-outcome]] `event.outcome` -a| Denotes whether the event represents a success or failure: +a| Denotes whether the event represents a success or failure: * Any actions that the user is not authorized to perform are logged with outcome: `failure` * Authorized read operations are only logged after successfully fetching the data from {es} with outcome: `success` @@ -553,7 +693,7 @@ Example: `[kibana_admin, reporting_user]` Example: `default` | `kibana.session_id` -| ID of the user session associated with the event. +| ID of the user session associated with the event. Each login attempt results in a unique session id. @@ -604,7 +744,7 @@ Example: `[marketing]` | Error code describing the error. | `error.message` -| Error message. +| Error message. 2+a| ===== HTTP and URL Fields From 26b9675ba9a2435db81a005de08b26b90fc0893d Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 11 Oct 2024 14:31:23 +0100 Subject: [PATCH 13/14] switching some calendar tasks to change type --- docs/user/security/audit-logging.asciidoc | 32 +++++++++---------- .../server/lib/ml_client/ml_audit_logger.ts | 8 ++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index 5ec3d667a94e5..3d1e07c09a964 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -128,14 +128,6 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | Creating calendar. | `failure` | Failed to create calendar. -.2+| `ml_put_calendar_job` -| `success` | Adding job to calendar. -| `failure` | Failed to add job to calendar. - -.2+| `ml_post_calendar_events` -| `success` | Adding events to calendar. -| `failure` | Failed to add events to calendar. - .2+| `ml_forecast` | `success` | Creating anomaly detection forecast. | `failure` | Failed to create anomaly detection forecast. @@ -302,6 +294,22 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | Updating anomaly detection datafeed. | `failure` | Failed to update anomaly detection datafeed. +.2+| `ml_put_calendar_job` +| `success` | Adding job to calendar. +| `failure` | Failed to add job to calendar. + +.2+| `ml_delete_calendar_job` +| `success` | Removing job from calendar. +| `failure` | Failed to remove job from calendar. + +.2+| `ml_post_calendar_events` +| `success` | Adding events to calendar. +| `failure` | Failed to add events to calendar. + +.2+| `ml_delete_calendar_event` +| `success` | Deleting calendar event. +| `failure` | Failed to delete calendar event. + .2+| `ml_update_filter` | `success` | Updating filter. | `failure` | Failed to update filter. @@ -401,14 +409,6 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | Deleting calendar. | `failure` | Failed to delete calendar. -.2+| `ml_delete_calendar_job` -| `success` | Removing job from calendar. -| `failure` | Failed to remove job from calendar. - -.2+| `ml_delete_calendar_event` -| `success` | Deleting calendar event. -| `failure` | Failed to delete calendar event. - .2+| `ml_delete_filter` | `success` | Deleting filter. | `failure` | Failed to delete filter. diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts index e9c780f8b1a4b..84244e21cc93b 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts @@ -244,7 +244,7 @@ export class MlAuditLogger { const jobIds = params.job_id; return { message: `Adding job(s) ${jobIds} to calendar ${calendarId}`, - type: [EVENT_TYPES.creation], + type: [EVENT_TYPES.change], }; } case 'ml_delete_calendar_job': { @@ -253,7 +253,7 @@ export class MlAuditLogger { const jobIds = params.job_id; return { message: `Removing job(s) ${jobIds} from calendar ${calendarId}`, - type: [EVENT_TYPES.deletion], + type: [EVENT_TYPES.change], }; } case 'ml_post_calendar_events': { @@ -263,7 +263,7 @@ export class MlAuditLogger { const eventsCount = (params.body ?? params).events; return { message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, - type: [EVENT_TYPES.creation], + type: [EVENT_TYPES.change], }; } case 'ml_delete_calendar_event': { @@ -272,7 +272,7 @@ export class MlAuditLogger { const eventId = params.event_id; return { message: `Removing event(s) ${eventId} from calendar ${calendarId}`, - type: [EVENT_TYPES.deletion], + type: [EVENT_TYPES.change], }; } case 'ml_put_filter': { From 15a474634f26a7162809b99a15cbab70ab1d892b Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Fri, 11 Oct 2024 14:36:46 +0100 Subject: [PATCH 14/14] updating calendar jobs tasks --- docs/user/security/audit-logging.asciidoc | 16 ++++++++-------- .../ml/server/lib/ml_client/ml_audit_logger.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index 3d1e07c09a964..1ac40bcc7764a 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -128,6 +128,10 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | Creating calendar. | `failure` | Failed to create calendar. +.2+| `ml_post_calendar_events` +| `success` | Adding events to calendar. +| `failure` | Failed to add events to calendar. + .2+| `ml_forecast` | `success` | Creating anomaly detection forecast. | `failure` | Failed to create anomaly detection forecast. @@ -302,14 +306,6 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | Removing job from calendar. | `failure` | Failed to remove job from calendar. -.2+| `ml_post_calendar_events` -| `success` | Adding events to calendar. -| `failure` | Failed to add events to calendar. - -.2+| `ml_delete_calendar_event` -| `success` | Deleting calendar event. -| `failure` | Failed to delete calendar event. - .2+| `ml_update_filter` | `success` | Updating filter. | `failure` | Failed to update filter. @@ -409,6 +405,10 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | Deleting calendar. | `failure` | Failed to delete calendar. +.2+| `ml_delete_calendar_event` +| `success` | Deleting calendar event. +| `failure` | Failed to delete calendar event. + .2+| `ml_delete_filter` | `success` | Deleting filter. | `failure` | Failed to delete filter. diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts index 84244e21cc93b..1d678497cc205 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts @@ -263,7 +263,7 @@ export class MlAuditLogger { const eventsCount = (params.body ?? params).events; return { message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, - type: [EVENT_TYPES.change], + type: [EVENT_TYPES.creation], }; } case 'ml_delete_calendar_event': { @@ -272,7 +272,7 @@ export class MlAuditLogger { const eventId = params.event_id; return { message: `Removing event(s) ${eventId} from calendar ${calendarId}`, - type: [EVENT_TYPES.change], + type: [EVENT_TYPES.deletion], }; } case 'ml_put_filter': {