From b8626b10d12c738679d85396edb447c2258dd745 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Mon, 27 Apr 2020 23:05:31 -0400 Subject: [PATCH] [Event Log] add rel=primary to saved objects for query targets resolves https://github.com/elastic/kibana/issues/62668 Adds a property named `rel` to the nested saved objects in the event documents, whose value should not be set, or set to `primary`. The query by saved object function changes to only match event documents with that saved objects if it has the `rel: primary` value. This is used to limit searching alerting's executeAction event document with only the alert saved object, and not the action saved object (this document has an alert and action saved object). The alert saved object has the `rel: primary` field set, and the action does not. Previously, those documents were returned with a query of the action saved object. --- .../actions/server/lib/action_executor.ts | 13 +++++++-- .../create_execution_handler.test.ts | 1 + .../task_runner/create_execution_handler.ts | 4 +-- .../server/task_runner/task_runner.test.ts | 7 +++++ .../server/task_runner/task_runner.ts | 22 ++++++++++++-- .../plugins/event_log/generated/mappings.json | 4 +++ x-pack/plugins/event_log/generated/schemas.ts | 1 + x-pack/plugins/event_log/scripts/mappings.js | 6 ++++ .../server/es/cluster_client_adapter.test.ts | 21 ++++++++++++++ .../server/es/cluster_client_adapter.ts | 9 +++++- .../event_log/server/event_logger.test.ts | 29 +++++++++++++++++++ .../plugins/event_log/server/event_logger.ts | 15 +++++++++- x-pack/plugins/event_log/server/index.ts | 8 ++++- x-pack/plugins/event_log/server/types.ts | 2 ++ .../plugins/event_log/server/init_routes.ts | 2 +- .../event_log/public_api_integration.ts | 1 + .../event_log/service_api_integration.ts | 2 +- 17 files changed, 135 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 101e18f2583e3..3e9262c05efac 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -17,7 +17,7 @@ import { import { EncryptedSavedObjectsPluginStart } from '../../../encrypted_saved_objects/server'; import { SpacesServiceSetup } from '../../../spaces/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; -import { IEvent, IEventLogger } from '../../../event_log/server'; +import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; export interface ActionExecutorContext { logger: Logger; @@ -110,7 +110,16 @@ export class ActionExecutor { const actionLabel = `${actionTypeId}:${actionId}: ${name}`; const event: IEvent = { event: { action: EVENT_LOG_ACTIONS.execute }, - kibana: { saved_objects: [{ type: 'action', id: actionId, ...namespace }] }, + kibana: { + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'action', + id: actionId, + ...namespace, + }, + ], + }, }; eventLogger.startTiming(event); diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 0e46ef4919626..a564b87f2ca50 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -95,6 +95,7 @@ test('calls actionsPlugin.execute per selected action', async () => { "saved_objects": Array [ Object { "id": "1", + "rel": "primary", "type": "alert", }, Object { diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 5c3e36b88879d..16fadc8b06cd5 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -9,7 +9,7 @@ import { AlertAction, State, Context, AlertType } from '../types'; import { Logger } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server'; -import { IEventLogger, IEvent } from '../../../event_log/server'; +import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; interface CreateExecutionHandlerOptions { @@ -96,7 +96,7 @@ export function createExecutionHandler({ instance_id: alertInstanceId, }, saved_objects: [ - { type: 'alert', id: alertId, ...namespace }, + { rel: SAVED_OBJECT_REL_PRIMARY, type: 'alert', id: alertId, ...namespace }, { type: 'action', id: action.id, ...namespace }, ], }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 26d8a1d1777c0..35a0018049c33 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -172,6 +172,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -234,6 +235,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -254,6 +256,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -274,6 +277,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, Object { @@ -351,6 +355,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -371,6 +376,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -568,6 +574,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 26970dc6b2b0d..bf005301adc07 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -25,7 +25,7 @@ import { promiseResult, map, Resultable, asOk, asErr, resolveErr } from '../lib/ import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; import { AlertInstances } from '../alert_instance/alert_instance'; import { EVENT_LOG_ACTIONS } from '../plugin'; -import { IEvent, IEventLogger } from '../../../event_log/server'; +import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; const FALLBACK_RETRY_INTERVAL: IntervalSchedule = { interval: '5m' }; @@ -174,7 +174,16 @@ export class TaskRunner { const alertLabel = `${this.alertType.id}:${alertId}: '${name}'`; const event: IEvent = { event: { action: EVENT_LOG_ACTIONS.execute }, - kibana: { saved_objects: [{ type: 'alert', id: alertId, namespace }] }, + kibana: { + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'alert', + id: alertId, + namespace, + }, + ], + }, }; eventLogger.startTiming(event); @@ -393,7 +402,14 @@ function generateNewAndResolvedInstanceEvents(params: GenerateNewAndResolvedInst alerting: { instance_id: id, }, - saved_objects: [{ type: 'alert', id: params.alertId, namespace: params.namespace }], + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'alert', + id: params.alertId, + namespace: params.namespace, + }, + ], }, message, }; diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index f487e9262e50e..0a858969c4f6a 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -86,6 +86,10 @@ }, "saved_objects": { "properties": { + "rel": { + "type": "keyword", + "ignore_above": 1024 + }, "namespace": { "type": "keyword", "ignore_above": 1024 diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 9c923fe77d035..57fe90a8e876e 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -65,6 +65,7 @@ export const EventSchema = schema.maybe( saved_objects: schema.maybe( schema.arrayOf( schema.object({ + rel: ecsString(), namespace: ecsString(), id: ecsString(), type: ecsString(), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 8cc2c74b60e57..fd149d132031e 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -24,6 +24,11 @@ exports.EcsKibanaExtensionsMappings = { saved_objects: { type: 'nested', properties: { + // relation; currently only supports "primary" or not set + rel: { + type: 'keyword', + ignore_above: 1024, + }, // relevant kibana space namespace: { type: 'keyword', @@ -58,6 +63,7 @@ exports.EcsEventLogProperties = [ 'user.name', 'kibana.server_uuid', 'kibana.alerting.instance_id', + 'kibana.saved_objects.rel', 'kibana.saved_objects.namespace', 'kibana.saved_objects.id', 'kibana.saved_objects.name', diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index 470123ada48ea..f8dadc4dce9aa 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -236,6 +236,13 @@ describe('queryEventsBySavedObject', () => { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, { term: { 'kibana.saved_objects.type': { @@ -319,6 +326,13 @@ describe('queryEventsBySavedObject', () => { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, { term: { 'kibana.saved_objects.type': { @@ -388,6 +402,13 @@ describe('queryEventsBySavedObject', () => { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, { term: { 'kibana.saved_objects.type': { diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index 6d5c6b31a637c..a294cb20e6f99 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -7,7 +7,7 @@ import { reject, isUndefined } from 'lodash'; import { SearchResponse, Client } from 'elasticsearch'; import { Logger, ClusterClient } from '../../../../../src/core/server'; -import { IEvent } from '../types'; +import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '../types'; import { FindOptionsType } from '../event_log_client'; export type EsClusterClient = Pick; @@ -155,6 +155,13 @@ export class ClusterClientAdapter { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: SAVED_OBJECT_REL_PRIMARY, + }, + }, + }, { term: { 'kibana.saved_objects.type': { diff --git a/x-pack/plugins/event_log/server/event_logger.test.ts b/x-pack/plugins/event_log/server/event_logger.test.ts index 6a745931420c0..2bda194a65d13 100644 --- a/x-pack/plugins/event_log/server/event_logger.test.ts +++ b/x-pack/plugins/event_log/server/event_logger.test.ts @@ -150,6 +150,35 @@ describe('EventLogger', () => { message = await waitForLogMessage(systemLogger); expect(message).toMatch(/invalid event logged.*action.*undefined.*/); }); + + test('logs warnings when writing invalid events', async () => { + service.registerProviderActions('provider', ['action-a']); + eventLogger = service.getLogger({}); + + eventLogger.logEvent(({ event: { PROVIDER: 'provider' } } as unknown) as IEvent); + let message = await waitForLogMessage(systemLogger); + expect(message).toMatch(/invalid event logged.*provider.*undefined.*/); + + const event: IEvent = { + event: { + provider: 'provider', + action: 'action-a', + }, + kibana: { + saved_objects: [ + { + rel: 'ZZZ-primary', + namespace: 'default', + type: 'event_log_test', + id: '123', + }, + ], + }, + }; + eventLogger.logEvent(event); + message = await waitForLogMessage(systemLogger); + expect(message).toMatch(/invalid rel property.*ZZZ-primary.*/); + }); }); // return the next logged event; throw if not an event diff --git a/x-pack/plugins/event_log/server/event_logger.ts b/x-pack/plugins/event_log/server/event_logger.ts index bcfd7bd45a6f5..1a710a6fa4865 100644 --- a/x-pack/plugins/event_log/server/event_logger.ts +++ b/x-pack/plugins/event_log/server/event_logger.ts @@ -19,6 +19,7 @@ import { ECS_VERSION, EventSchema, } from './types'; +import { SAVED_OBJECT_REL_PRIMARY } from './types'; type SystemLogger = Plugin['systemLogger']; @@ -118,6 +119,8 @@ const RequiredEventSchema = schema.object({ action: schema.string({ minLength: 1 }), }); +const ValidSavedObjectRels = new Set([undefined, SAVED_OBJECT_REL_PRIMARY]); + function validateEvent(eventLogService: IEventLogService, event: IEvent): IValidatedEvent { if (event?.event == null) { throw new Error(`no "event" property`); @@ -137,7 +140,17 @@ function validateEvent(eventLogService: IEventLogService, event: IEvent): IValid } // could throw an error - return EventSchema.validate(event); + const result = EventSchema.validate(event); + + if (result?.kibana?.saved_objects?.length) { + for (const so of result?.kibana?.saved_objects) { + if (!ValidSavedObjectRels.has(so.rel)) { + throw new Error(`invalid rel property in saved_objects: "${so.rel}"`); + } + } + } + + return result; } export const EVENT_LOGGED_PREFIX = `event logged: `; diff --git a/x-pack/plugins/event_log/server/index.ts b/x-pack/plugins/event_log/server/index.ts index b7fa25cb6eb9c..0612b5319c15b 100644 --- a/x-pack/plugins/event_log/server/index.ts +++ b/x-pack/plugins/event_log/server/index.ts @@ -8,6 +8,12 @@ import { PluginInitializerContext } from 'src/core/server'; import { ConfigSchema } from './types'; import { Plugin } from './plugin'; -export { IEventLogService, IEventLogger, IEventLogClientService, IEvent } from './types'; +export { + IEventLogService, + IEventLogger, + IEventLogClientService, + IEvent, + SAVED_OBJECT_REL_PRIMARY, +} from './types'; export const config = { schema: ConfigSchema }; export const plugin = (context: PluginInitializerContext) => new Plugin(context); diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index baf53ef447914..58be6707b0373 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -13,6 +13,8 @@ import { IEvent } from '../generated/schemas'; import { FindOptionsType } from './event_log_client'; import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter'; +export const SAVED_OBJECT_REL_PRIMARY = 'primary'; + export const ConfigSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), logEntries: schema.boolean({ defaultValue: false }), diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts index c5f3e65581df9..9622715e87e55 100644 --- a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts @@ -40,7 +40,7 @@ export const logEventRoute = (router: IRouter, eventLogger: IEventLogger, logger } catch (ex) { logger.info(`log event error: ${ex}`); await context.core.savedObjects.client.create('event_log_test', {}, { id }); - logger.info(`created saved object`); + logger.info(`created saved object ${id}`); } eventLogger.logEvent(event); logger.info(`logged`); diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts index d664357c3ba12..bf76722ada008 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts @@ -222,6 +222,7 @@ export default function({ getService }: FtrProviderContext) { kibana: { saved_objects: [ { + rel: 'primary', namespace: 'default', type: 'event_log_test', id, diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index 2de395308ce74..722420f98f38d 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -100,7 +100,7 @@ export default function({ getService }: FtrProviderContext) { const eventId = '1'; const event: IEvent = { event: { action: 'action1', provider: 'provider4' }, - kibana: { saved_objects: [{ type: 'event_log_test', id: eventId }] }, + kibana: { saved_objects: [{ rel: 'primary', type: 'event_log_test', id: eventId }] }, }; await logTestEvent(eventId, event);