From efe76121c797379aa4329a94e30fd3506bbbb8e0 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 16 Sep 2020 11:47:37 +0100 Subject: [PATCH] [Alerting] Exempt Alerts pre 7.10 from RBAC on their Action execution until updated (#75563) Marks all Alerts with a `versionApiKeyLastmodified ` field that tracks what version the alert's Api Key was last updated in. We then use this field to exempt legacy alerts (created pre `7.10.0`) in order to use a _dialed down_ version of RBAC which should allow old alerts to continue to function after the upgrade, until they are updates (at which point they will no longer be **Legacy**). More details here: https://github.com/elastic/kibana/issues/74858#issuecomment-688324039 --- x-pack/plugins/actions/README.md | 16 +- .../plugins/actions/server/actions_client.ts | 13 +- .../actions_authorization.test.ts | 38 +- .../authorization/actions_authorization.ts | 57 +- ...should_legacy_rbac_apply_by_source.test.ts | 114 + .../should_legacy_rbac_apply_by_source.ts | 27 + .../server/create_execute_function.test.ts | 47 +- .../actions/server/create_execute_function.ts | 57 +- x-pack/plugins/actions/server/index.ts | 2 + .../server/lib/action_execution_source.ts | 47 + .../actions/server/lib/action_executor.ts | 16 +- x-pack/plugins/actions/server/lib/index.ts | 7 + .../server/lib/task_runner_factory.test.ts | 4 +- .../actions/server/lib/task_runner_factory.ts | 37 +- x-pack/plugins/actions/server/plugin.ts | 62 +- .../actions/server/routes/execute.test.ts | 4 +- .../plugins/actions/server/routes/execute.ts | 2 + .../actions/server/saved_objects/index.ts | 1 + .../alerts/server/alerts_client.test.ts | 46 +- x-pack/plugins/alerts/server/alerts_client.ts | 87 +- .../server/alerts_client_factory.test.ts | 3 + .../alerts/server/alerts_client_factory.ts | 11 +- .../alerts_authorization.mock.ts | 1 + .../authorization/alerts_authorization.ts | 7 +- x-pack/plugins/alerts/server/plugin.ts | 3 + .../alerts/server/saved_objects/mappings.json | 7 + .../server/saved_objects/migrations.test.ts | 48 +- .../alerts/server/saved_objects/migrations.ts | 72 +- .../create_execution_handler.test.ts | 98 +- .../task_runner/create_execution_handler.ts | 9 +- .../server/task_runner/task_runner.test.ts | 58 +- x-pack/plugins/alerts/server/types.ts | 7 +- .../fixtures/plugins/alerts/kibana.json | 4 +- .../fixtures/plugins/alerts/server/plugin.ts | 11 +- .../fixtures/plugins/alerts/server/routes.ts | 124 +- .../common/lib/alert_utils.ts | 38 + .../tests/alerting/index.ts | 3 + .../tests/alerting/rbac_legacy.ts | 229 ++ .../security_and_spaces/tests/index.ts | 75 +- .../es_archives/alerts_legacy/data.json | 491 +++ .../es_archives/alerts_legacy/mappings.json | 2680 +++++++++++++++++ 41 files changed, 4411 insertions(+), 252 deletions(-) create mode 100644 x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts create mode 100644 x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts create mode 100644 x-pack/plugins/actions/server/lib/action_execution_source.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts create mode 100644 x-pack/test/functional/es_archives/alerts_legacy/data.json create mode 100644 x-pack/test/functional/es_archives/alerts_legacy/mappings.json diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index c55b21b2f9029..af29a1d537499 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -280,12 +280,14 @@ The following table describes the properties of the `options` object. | params | The `params` value to give the action type executor. | object | | spaceId | The space id the action is within. | string | | apiKey | The Elasticsearch API key to use for context. (Note: only required and used when security is enabled). | string | +| source | The source of the execution, either an HTTP request or a reference to a Saved Object. | object, optional | ## Example This example makes action `3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5` send an email. The action plugin will load the saved object and find what action type to call with `params`. ```typescript +const request: KibanaRequest = { ... }; const actionsClient = await server.plugins.actions.getActionsClientWithRequest(request); await actionsClient.enqueueExecution({ id: '3c5b2bd4-5424-4e4b-8cf5-c0a58c762cc5', @@ -296,6 +298,7 @@ await actionsClient.enqueueExecution({ subject: 'My email subject', body: 'My email body', }, + source: asHttpRequestExecutionSource(request), }); ``` @@ -305,10 +308,11 @@ This api runs the action and asynchronously returns the result of running the ac The following table describes the properties of the `options` object. -| Property | Description | Type | -| -------- | ---------------------------------------------------- | ------ | -| id | The id of the action you want to execute. | string | -| params | The `params` value to give the action type executor. | object | +| Property | Description | Type | +| -------- | ------------------------------------------------------------------------------------ | ------ | +| id | The id of the action you want to execute. | string | +| params | The `params` value to give the action type executor. | object | +| source | The source of the execution, either an HTTP request or a reference to a Saved Object.| object, optional | ## Example @@ -324,6 +328,10 @@ const result = await actionsClient.execute({ subject: 'My email subject', body: 'My email body', }, + source: asSavedObjectExecutionSource({ + id: '573891ae-8c48-49cb-a197-0cd5ec34a88b', + type: 'alert' + }), }); ``` diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index d46ad3e2e2423..06c9555f3a18d 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -30,6 +30,7 @@ import { } from './create_execute_function'; import { ActionsAuthorization } from './authorization/actions_authorization'; import { ActionType } from '../common'; +import { shouldLegacyRbacApplyBySource } from './authorization/should_legacy_rbac_apply_by_source'; // We are assuming there won't be many actions. This is why we will load // all the actions in advance and assume the total count to not go over 10000. @@ -298,13 +299,19 @@ export class ActionsClient { public async execute({ actionId, params, + source, }: Omit): Promise> { - await this.authorization.ensureAuthorized('execute'); - return this.actionExecutor.execute({ actionId, params, request: this.request }); + if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) { + await this.authorization.ensureAuthorized('execute'); + } + return this.actionExecutor.execute({ actionId, params, source, request: this.request }); } public async enqueueExecution(options: EnqueueExecutionOptions): Promise { - await this.authorization.ensureAuthorized('execute'); + const { source } = options; + if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) { + await this.authorization.ensureAuthorized('execute'); + } return this.executionEnqueuer(this.unsecuredSavedObjectsClient, options); } diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts index 14573161b8d5d..08c4472f8007b 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -9,6 +9,7 @@ import { ActionsAuthorization } from './actions_authorization'; import { actionsAuthorizationAuditLoggerMock } from './audit_logger.mock'; import { ActionsAuthorizationAuditLogger, AuthorizationResult } from './audit_logger'; import { ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { AuthenticatedUser } from '../../../security/server'; const request = {} as KibanaRequest; @@ -19,12 +20,13 @@ const mockAuthorizationAction = (type: string, operation: string) => `${type}/${ function mockSecurity() { const security = securityMock.createSetup(); const authorization = security.authz; + const authentication = security.authc; // typescript is having trouble inferring jest's automocking (authorization.actions.savedObject.get as jest.MockedFunction< typeof authorization.actions.savedObject.get >).mockImplementation(mockAuthorizationAction); authorization.mode.useRbacForRequest.mockReturnValue(true); - return { authorization }; + return { authorization, authentication }; } beforeEach(() => { @@ -192,4 +194,38 @@ describe('ensureAuthorized', () => { ] `); }); + + test('exempts users from requiring privileges to execute actions when shouldUseLegacyRbac is true', async () => { + const { authorization, authentication } = mockSecurity(); + const checkPrivileges: jest.MockedFunction> = jest.fn(); + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + const actionsAuthorization = new ActionsAuthorization({ + request, + authorization, + authentication, + auditLogger, + shouldUseLegacyRbac: true, + }); + + authentication.getCurrentUser.mockReturnValueOnce(({ + username: 'some-user', + } as unknown) as AuthenticatedUser); + + await actionsAuthorization.ensureAuthorized('execute', 'myType'); + + expect(authorization.actions.savedObject.get).not.toHaveBeenCalled(); + expect(checkPrivileges).not.toHaveBeenCalled(); + + expect(auditLogger.actionsAuthorizationSuccess).toHaveBeenCalledTimes(1); + expect(auditLogger.actionsAuthorizationFailure).not.toHaveBeenCalled(); + expect(auditLogger.actionsAuthorizationSuccess.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "some-user", + "execute", + "myType", + ] + `); + }); }); diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index 3ba798ddf1715..bd6e355c2cf9d 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -14,6 +14,15 @@ export interface ConstructorOptions { request: KibanaRequest; auditLogger: ActionsAuthorizationAuditLogger; authorization?: SecurityPluginSetup['authz']; + authentication?: SecurityPluginSetup['authc']; + // In order to support legacy Alerts which predate the introduction of the + // Actions feature in Kibana we need a way of "dialing down" the level of + // authorization for certain opearations. + // Specifically, we want to allow these old alerts and their scheduled + // actions to continue to execute - which requires that we exempt auth on + // `get` for Connectors and `execute` for Action execution when used by + // these legacy alerts + shouldUseLegacyRbac?: boolean; } const operationAlias: Record< @@ -27,33 +36,57 @@ const operationAlias: Record< list: (authorization) => authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'find'), }; +const LEGACY_RBAC_EXEMPT_OPERATIONS = new Set(['get', 'execute']); + export class ActionsAuthorization { private readonly request: KibanaRequest; private readonly authorization?: SecurityPluginSetup['authz']; + private readonly authentication?: SecurityPluginSetup['authc']; private readonly auditLogger: ActionsAuthorizationAuditLogger; + private readonly shouldUseLegacyRbac: boolean; - constructor({ request, authorization, auditLogger }: ConstructorOptions) { + constructor({ + request, + authorization, + authentication, + auditLogger, + shouldUseLegacyRbac = false, + }: ConstructorOptions) { this.request = request; this.authorization = authorization; + this.authentication = authentication; this.auditLogger = auditLogger; + this.shouldUseLegacyRbac = shouldUseLegacyRbac; } public async ensureAuthorized(operation: string, actionTypeId?: string) { const { authorization } = this; if (authorization?.mode?.useRbacForRequest(this.request)) { - const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); - const { hasAllRequested, username } = await checkPrivileges({ - kibana: operationAlias[operation] - ? operationAlias[operation](authorization) - : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation), - }); - if (hasAllRequested) { - this.auditLogger.actionsAuthorizationSuccess(username, operation, actionTypeId); - } else { - throw Boom.forbidden( - this.auditLogger.actionsAuthorizationFailure(username, operation, actionTypeId) + if (this.isOperationExemptDueToLegacyRbac(operation)) { + this.auditLogger.actionsAuthorizationSuccess( + this.authentication?.getCurrentUser(this.request)?.username ?? '', + operation, + actionTypeId ); + } else { + const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); + const { hasAllRequested, username } = await checkPrivileges({ + kibana: operationAlias[operation] + ? operationAlias[operation](authorization) + : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation), + }); + if (hasAllRequested) { + this.auditLogger.actionsAuthorizationSuccess(username, operation, actionTypeId); + } else { + throw Boom.forbidden( + this.auditLogger.actionsAuthorizationFailure(username, operation, actionTypeId) + ); + } } } } + + private isOperationExemptDueToLegacyRbac(operation: string) { + return this.shouldUseLegacyRbac && LEGACY_RBAC_EXEMPT_OPERATIONS.has(operation); + } } diff --git a/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts new file mode 100644 index 0000000000000..03062994adeb6 --- /dev/null +++ b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { shouldLegacyRbacApplyBySource } from './should_legacy_rbac_apply_by_source'; +import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; +import uuid from 'uuid'; +import { asSavedObjectExecutionSource } from '../lib'; + +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +describe(`#shouldLegacyRbacApplyBySource`, () => { + test('should return false if no source is provided', async () => { + expect(await shouldLegacyRbacApplyBySource(unsecuredSavedObjectsClient)).toEqual(false); + }); + + test('should return false if source is not an alert', async () => { + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'action', + id: uuid.v4(), + }) + ) + ).toEqual(false); + }); + + test('should return false if source alert is not marked as legacy', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id })); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(false); + }); + + test('should return true if source alert is marked as legacy', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue( + mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } }) + ); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(true); + }); + + test('should return false if source alert is marked as modern', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue( + mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: '7.10.0' } } }) + ); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(false); + }); + + test('should return false if source alert is marked with a last modified version', async () => { + const id = uuid.v4(); + unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id, attributes: { meta: {} } })); + expect( + await shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient, + asSavedObjectExecutionSource({ + type: 'alert', + id, + }) + ) + ).toEqual(false); + }); +}); + +const mockAlert = (overrides: Record = {}) => ({ + id: '1', + type: 'alert', + attributes: { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: false, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + version: '123', + references: [], + ...overrides, +}); diff --git a/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts new file mode 100644 index 0000000000000..06d5776003ede --- /dev/null +++ b/x-pack/plugins/actions/server/authorization/should_legacy_rbac_apply_by_source.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'src/core/server'; +import { ActionExecutionSource, isSavedObjectExecutionSource } from '../lib'; +import { ALERT_SAVED_OBJECT_TYPE } from '../saved_objects'; + +const LEGACY_VERSION = 'pre-7.10.0'; + +export async function shouldLegacyRbacApplyBySource( + unsecuredSavedObjectsClient: SavedObjectsClientContract, + executionSource?: ActionExecutionSource +): Promise { + return isSavedObjectExecutionSource(executionSource) && + executionSource?.source?.type === ALERT_SAVED_OBJECT_TYPE + ? ( + await unsecuredSavedObjectsClient.get<{ + meta?: { + versionApiKeyLastmodified?: string; + }; + }>(ALERT_SAVED_OBJECT_TYPE, executionSource.source.id) + ).attributes.meta?.versionApiKeyLastmodified === LEGACY_VERSION + : false; +} diff --git a/x-pack/plugins/actions/server/create_execute_function.test.ts b/x-pack/plugins/actions/server/create_execute_function.test.ts index 04d4d92945cdb..7682f01ed769d 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -4,13 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ +import { KibanaRequest } from 'src/core/server'; +import uuid from 'uuid'; import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; import { createExecutionEnqueuerFunction } from './create_execute_function'; import { savedObjectsClientMock } from '../../../../src/core/server/mocks'; import { actionTypeRegistryMock } from './action_type_registry.mock'; +import { + asHttpRequestExecutionSource, + asSavedObjectExecutionSource, +} from './lib/action_execution_source'; const mockTaskManager = taskManagerMock.start(); const savedObjectsClient = savedObjectsClientMock.create(); +const request = {} as KibanaRequest; beforeEach(() => jest.resetAllMocks()); @@ -41,6 +48,7 @@ describe('execute()', () => { params: { baz: false }, spaceId: 'default', apiKey: Buffer.from('123:abc').toString('base64'), + source: asHttpRequestExecutionSource(request), }); expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` @@ -59,11 +67,15 @@ describe('execute()', () => { ] `); expect(savedObjectsClient.get).toHaveBeenCalledWith('action', '123'); - expect(savedObjectsClient.create).toHaveBeenCalledWith('action_task_params', { - actionId: '123', - params: { baz: false }, - apiKey: Buffer.from('123:abc').toString('base64'), - }); + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'action_task_params', + { + actionId: '123', + params: { baz: false }, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + {} + ); }); test('schedules the action with all given parameters with a preconfigured action', async () => { @@ -82,6 +94,8 @@ describe('execute()', () => { }, ], }); + const source = { type: 'alert', id: uuid.v4() }; + savedObjectsClient.get.mockResolvedValueOnce({ id: '123', type: 'action', @@ -101,6 +115,7 @@ describe('execute()', () => { params: { baz: false }, spaceId: 'default', apiKey: Buffer.from('123:abc').toString('base64'), + source: asSavedObjectExecutionSource(source), }); expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` @@ -119,11 +134,23 @@ describe('execute()', () => { ] `); expect(savedObjectsClient.get).not.toHaveBeenCalled(); - expect(savedObjectsClient.create).toHaveBeenCalledWith('action_task_params', { - actionId: '123', - params: { baz: false }, - apiKey: Buffer.from('123:abc').toString('base64'), - }); + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'action_task_params', + { + actionId: '123', + params: { baz: false }, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + { + references: [ + { + id: source.id, + name: 'source', + type: source.type, + }, + ], + } + ); }); test('throws when passing isESOUsingEphemeralEncryptionKey with true as a value', async () => { diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index 85052eef93e05..b226583fade52 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -8,6 +8,8 @@ import { SavedObjectsClientContract } from '../../../../src/core/server'; import { TaskManagerStartContract } from '../../task_manager/server'; import { RawAction, ActionTypeRegistryContract, PreConfiguredAction } from './types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './saved_objects'; +import { ExecuteOptions as ActionExecutorOptions } from './lib/action_executor'; +import { isSavedObjectExecutionSource } from './lib'; interface CreateExecuteFunctionOptions { taskManager: TaskManagerStartContract; @@ -16,15 +18,14 @@ interface CreateExecuteFunctionOptions { preconfiguredActions: PreConfiguredAction[]; } -export interface ExecuteOptions { +export interface ExecuteOptions extends Pick { id: string; - params: Record; spaceId: string; apiKey: string | null; } export type ExecutionEnqueuer = ( - savedObjectsClient: SavedObjectsClientContract, + unsecuredSavedObjectsClient: SavedObjectsClientContract, options: ExecuteOptions ) => Promise; @@ -35,8 +36,8 @@ export function createExecutionEnqueuerFunction({ preconfiguredActions, }: CreateExecuteFunctionOptions) { return async function execute( - savedObjectsClient: SavedObjectsClientContract, - { id, params, spaceId, apiKey }: ExecuteOptions + unsecuredSavedObjectsClient: SavedObjectsClientContract, + { id, params, spaceId, source, apiKey }: ExecuteOptions ) { if (isESOUsingEphemeralEncryptionKey === true) { throw new Error( @@ -44,19 +45,24 @@ export function createExecutionEnqueuerFunction({ ); } - const actionTypeId = await getActionTypeId(id); + const actionTypeId = await getActionTypeId( + unsecuredSavedObjectsClient, + preconfiguredActions, + id + ); if (!actionTypeRegistry.isActionExecutable(id, actionTypeId)) { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } - const actionTaskParamsRecord = await savedObjectsClient.create( + const actionTaskParamsRecord = await unsecuredSavedObjectsClient.create( ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, { actionId: id, params, apiKey, - } + }, + executionSourceAsSavedObjectReferences(source) ); await taskManager.schedule({ @@ -68,15 +74,34 @@ export function createExecutionEnqueuerFunction({ state: {}, scope: ['actions'], }); + }; +} - async function getActionTypeId(actionId: string): Promise { - const pcAction = preconfiguredActions.find((action) => action.id === actionId); - if (pcAction) { - return pcAction.actionTypeId; +function executionSourceAsSavedObjectReferences(executionSource: ActionExecutorOptions['source']) { + return isSavedObjectExecutionSource(executionSource) + ? { + references: [ + { + name: 'source', + ...executionSource.source, + }, + ], } + : {}; +} - const actionSO = await savedObjectsClient.get('action', actionId); - return actionSO.attributes.actionTypeId; - } - }; +async function getActionTypeId( + unsecuredSavedObjectsClient: SavedObjectsClientContract, + preconfiguredActions: PreConfiguredAction[], + actionId: string +): Promise { + const pcAction = preconfiguredActions.find((action) => action.id === actionId); + if (pcAction) { + return pcAction.actionTypeId; + } + + const { + attributes: { actionTypeId }, + } = await unsecuredSavedObjectsClient.get('action', actionId); + return actionTypeId; } diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 31c4d26d1793e..bbe298c0585a3 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -23,6 +23,8 @@ export { } from './types'; export { PluginSetupContract, PluginStartContract } from './plugin'; +export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './lib'; + export const plugin = (initContext: PluginInitializerContext) => new ActionsPlugin(initContext); export const config: PluginConfigDescriptor = { diff --git a/x-pack/plugins/actions/server/lib/action_execution_source.ts b/x-pack/plugins/actions/server/lib/action_execution_source.ts new file mode 100644 index 0000000000000..53f815f070bea --- /dev/null +++ b/x-pack/plugins/actions/server/lib/action_execution_source.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { KibanaRequest, SavedObjectReference } from 'src/core/server'; + +export enum ActionExecutionSourceType { + SAVED_OBJECT = 'SAVED_OBJECT', + HTTP_REQUEST = 'HTTP_REQUEST', +} + +export interface ActionExecutionSource { + type: ActionExecutionSourceType; + source: T; +} +export type HttpRequestExecutionSource = ActionExecutionSource; +export type SavedObjectExecutionSource = ActionExecutionSource>; + +export function asHttpRequestExecutionSource(source: KibanaRequest): HttpRequestExecutionSource { + return { + type: ActionExecutionSourceType.HTTP_REQUEST, + source, + }; +} + +export function asSavedObjectExecutionSource( + source: Omit +): SavedObjectExecutionSource { + return { + type: ActionExecutionSourceType.SAVED_OBJECT, + source, + }; +} + +export function isHttpRequestExecutionSource( + executionSource?: ActionExecutionSource +): executionSource is HttpRequestExecutionSource { + return executionSource?.type === ActionExecutionSourceType.HTTP_REQUEST; +} + +export function isSavedObjectExecutionSource( + executionSource?: ActionExecutionSource +): executionSource is SavedObjectExecutionSource { + return executionSource?.type === ActionExecutionSourceType.SAVED_OBJECT; +} diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 97c08124f5546..a607dc0de0bda 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, KibanaRequest } from '../../../../../src/core/server'; +import { Logger, KibanaRequest } from 'src/core/server'; import { validateParams, validateConfig, validateSecrets } from './validate_with_schema'; import { ActionTypeExecutorResult, @@ -16,15 +16,19 @@ import { } from '../types'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; import { SpacesServiceSetup } from '../../../spaces/server'; -import { EVENT_LOG_ACTIONS, PluginStartContract } from '../plugin'; +import { EVENT_LOG_ACTIONS } from '../plugin'; import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { ActionsClient } from '../actions_client'; +import { ActionExecutionSource } from './action_execution_source'; export interface ActionExecutorContext { logger: Logger; spaces?: SpacesServiceSetup; getServices: GetServicesFunction; - getActionsClientWithRequest: PluginStartContract['getActionsClientWithRequest']; + getActionsClientWithRequest: ( + request: KibanaRequest, + executionSource?: ActionExecutionSource + ) => Promise>; encryptedSavedObjectsClient: EncryptedSavedObjectsClient; actionTypeRegistry: ActionTypeRegistryContract; eventLogger: IEventLogger; @@ -32,10 +36,11 @@ export interface ActionExecutorContext { proxySettings?: ProxySettings; } -export interface ExecuteOptions { +export interface ExecuteOptions { actionId: string; request: KibanaRequest; params: Record; + source?: ActionExecutionSource; } export type ActionExecutorContract = PublicMethodsOf; @@ -61,6 +66,7 @@ export class ActionExecutor { actionId, params, request, + source, }: ExecuteOptions): Promise> { if (!this.isInitialized) { throw new Error('ActionExecutor not initialized'); @@ -88,7 +94,7 @@ export class ActionExecutor { const namespace = spaceId && spaceId !== 'default' ? { namespace: spaceId } : {}; const { actionTypeId, name, config, secrets } = await getActionInfo( - await getActionsClientWithRequest(request), + await getActionsClientWithRequest(request, source), encryptedSavedObjectsClient, preconfiguredActions, actionId, diff --git a/x-pack/plugins/actions/server/lib/index.ts b/x-pack/plugins/actions/server/lib/index.ts index f03b6de1fc5fb..e97875b91cf33 100644 --- a/x-pack/plugins/actions/server/lib/index.ts +++ b/x-pack/plugins/actions/server/lib/index.ts @@ -15,3 +15,10 @@ export { ActionTypeDisabledReason, isErrorThatHandlesItsOwnResponse, } from './errors'; +export { + ActionExecutionSource, + asSavedObjectExecutionSource, + isSavedObjectExecutionSource, + asHttpRequestExecutionSource, + isHttpRequestExecutionSource, +} from './action_execution_source'; diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts index 78522682054e1..18cbd9f9c5fad 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.test.ts @@ -71,13 +71,13 @@ const taskRunnerFactoryInitializerParams = { logger: loggingSystemMock.create().get(), encryptedSavedObjectsClient: mockedEncryptedSavedObjectsClient, getBasePath: jest.fn().mockReturnValue(undefined), - getScopedSavedObjectsClient: jest.fn().mockReturnValue(services.savedObjectsClient), + getUnsecuredSavedObjectsClient: jest.fn().mockReturnValue(services.savedObjectsClient), }; beforeEach(() => { jest.resetAllMocks(); actionExecutorInitializerParams.getServices.mockReturnValue(services); - taskRunnerFactoryInitializerParams.getScopedSavedObjectsClient.mockReturnValue( + taskRunnerFactoryInitializerParams.getUnsecuredSavedObjectsClient.mockReturnValue( services.savedObjectsClient ); }); diff --git a/x-pack/plugins/actions/server/lib/task_runner_factory.ts b/x-pack/plugins/actions/server/lib/task_runner_factory.ts index 10a8501e856d2..aeeeb4ed7d520 100644 --- a/x-pack/plugins/actions/server/lib/task_runner_factory.ts +++ b/x-pack/plugins/actions/server/lib/task_runner_factory.ts @@ -4,9 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { pick } from 'lodash'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { map, fromNullable, getOrElse } from 'fp-ts/lib/Option'; +import { + Logger, + SavedObjectsClientContract, + KibanaRequest, + SavedObjectReference, +} from 'src/core/server'; import { ActionExecutorContract } from './action_executor'; import { ExecutorError } from './executor_error'; -import { Logger, CoreStart, KibanaRequest } from '../../../../../src/core/server'; import { RunContext } from '../../../task_manager/server'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; import { ActionTypeDisabledError } from './errors'; @@ -18,6 +26,7 @@ import { ActionTypeExecutorResult, } from '../types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { asSavedObjectExecutionSource } from './action_execution_source'; export interface TaskRunnerContext { logger: Logger; @@ -25,7 +34,7 @@ export interface TaskRunnerContext { encryptedSavedObjectsClient: EncryptedSavedObjectsClient; spaceIdToNamespace: SpaceIdToNamespaceFunction; getBasePath: GetBasePathFunction; - getScopedSavedObjectsClient: CoreStart['savedObjects']['getScopedClient']; + getUnsecuredSavedObjectsClient: (request: KibanaRequest) => SavedObjectsClientContract; } export class TaskRunnerFactory { @@ -56,7 +65,7 @@ export class TaskRunnerFactory { encryptedSavedObjectsClient, spaceIdToNamespace, getBasePath, - getScopedSavedObjectsClient, + getUnsecuredSavedObjectsClient, } = this.taskRunnerContext!; return { @@ -66,6 +75,7 @@ export class TaskRunnerFactory { const { attributes: { actionId, params, apiKey }, + references, } = await encryptedSavedObjectsClient.getDecryptedAsInternalUser( ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, actionTaskParamsId, @@ -100,6 +110,7 @@ export class TaskRunnerFactory { params, actionId, request: fakeRequest, + ...getSourceFromReferences(references), }); } catch (e) { if (e instanceof ActionTypeDisabledError) { @@ -121,8 +132,14 @@ export class TaskRunnerFactory { // Cleanup action_task_params object now that we're done with it try { - const savedObjectsClient = getScopedSavedObjectsClient(fakeRequest); - await savedObjectsClient.delete(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, actionTaskParamsId); + // If the request has reached this far we can assume the user is allowed to run clean up + // We would idealy secure every operation but in order to support clean up of legacy alerts + // we allow this operation in an unsecured manner + // Once support for legacy alert RBAC is dropped, this can be secured + await getUnsecuredSavedObjectsClient(fakeRequest).delete( + ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + actionTaskParamsId + ); } catch (e) { // Log error only, we shouldn't fail the task because of an error here (if ever there's retry logic) logger.error( @@ -133,3 +150,13 @@ export class TaskRunnerFactory { }; } } + +function getSourceFromReferences(references: SavedObjectReference[]) { + return pipe( + fromNullable(references.find((ref) => ref.name === 'source')), + map((source) => ({ + source: asSavedObjectExecutionSource(pick(source, 'id', 'type')), + })), + getOrElse(() => ({})) + ); +} diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 592ca93ef5a16..97cefafad4385 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -65,10 +65,13 @@ import { setupSavedObjects, ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + ALERT_SAVED_OBJECT_TYPE, } from './saved_objects'; import { ACTIONS_FEATURE } from './feature'; import { ActionsAuthorization } from './authorization/actions_authorization'; import { ActionsAuthorizationAuditLogger } from './authorization/audit_logger'; +import { ActionExecutionSource } from './lib/action_execution_source'; +import { shouldLegacyRbacApplyBySource } from './authorization/should_legacy_rbac_apply_by_source'; const EVENT_LOG_PROVIDER = 'actions'; export const EVENT_LOG_ACTIONS = { @@ -109,7 +112,11 @@ export interface ActionsPluginsStart { taskManager: TaskManagerStartContract; } -const includedHiddenTypes = [ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE]; +const includedHiddenTypes = [ + ACTION_SAVED_OBJECT_TYPE, + ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + ALERT_SAVED_OBJECT_TYPE, +]; export class ActionsPlugin implements Plugin, PluginStartContract> { private readonly kibanaIndex: Promise; @@ -265,29 +272,39 @@ export class ActionsPlugin implements Plugin, Plugi isESOUsingEphemeralEncryptionKey, preconfiguredActions, instantiateAuthorization, + getUnsecuredSavedObjectsClient, } = this; const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient({ includedHiddenTypes, }); - const getActionsClientWithRequest = async (request: KibanaRequest) => { + const getActionsClientWithRequest = async ( + request: KibanaRequest, + source?: ActionExecutionSource + ) => { if (isESOUsingEphemeralEncryptionKey === true) { throw new Error( `Unable to create actions client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml` ); } + + const unsecuredSavedObjectsClient = getUnsecuredSavedObjectsClient( + core.savedObjects, + request + ); + return new ActionsClient({ - unsecuredSavedObjectsClient: core.savedObjects.getScopedClient(request, { - excludedWrappers: ['security'], - includedHiddenTypes, - }), + unsecuredSavedObjectsClient, actionTypeRegistry: actionTypeRegistry!, defaultKibanaIndex: await kibanaIndex, scopedClusterClient: core.elasticsearch.legacy.client.asScoped(request), preconfiguredActions, request, - authorization: instantiateAuthorization(request), + authorization: instantiateAuthorization( + request, + await shouldLegacyRbacApplyBySource(unsecuredSavedObjectsClient, source) + ), actionExecutor: actionExecutor!, executionEnqueuer: createExecutionEnqueuerFunction({ taskManager: plugins.taskManager, @@ -298,8 +315,13 @@ export class ActionsPlugin implements Plugin, Plugi }); }; + // Ensure the public API cannot be used to circumvent authorization + // using our legacy exemption mechanism + const secureGetActionsClientWithRequest = (request: KibanaRequest) => + getActionsClientWithRequest(request); + this.eventLogService!.registerSavedObjectProvider('action', (request) => { - const client = getActionsClientWithRequest(request); + const client = secureGetActionsClientWithRequest(request); return async (type: string, id: string) => (await client).get({ id }); }); @@ -335,10 +357,8 @@ export class ActionsPlugin implements Plugin, Plugi encryptedSavedObjectsClient, getBasePath: this.getBasePath, spaceIdToNamespace: this.spaceIdToNamespace, - getScopedSavedObjectsClient: (request: KibanaRequest) => - core.savedObjects.getScopedClient(request, { - includedHiddenTypes, - }), + getUnsecuredSavedObjectsClient: (request: KibanaRequest) => + this.getUnsecuredSavedObjectsClient(core.savedObjects, request), }); scheduleActionsTelemetry(this.telemetryLogger, plugins.taskManager); @@ -353,15 +373,29 @@ export class ActionsPlugin implements Plugin, Plugi getActionsAuthorizationWithRequest(request: KibanaRequest) { return instantiateAuthorization(request); }, - getActionsClientWithRequest, + getActionsClientWithRequest: secureGetActionsClientWithRequest, preconfiguredActions, }; } - private instantiateAuthorization = (request: KibanaRequest) => { + private getUnsecuredSavedObjectsClient = ( + savedObjects: CoreStart['savedObjects'], + request: KibanaRequest + ) => + savedObjects.getScopedClient(request, { + excludedWrappers: ['security'], + includedHiddenTypes, + }); + + private instantiateAuthorization = ( + request: KibanaRequest, + shouldUseLegacyRbac: boolean = false + ) => { return new ActionsAuthorization({ request, + shouldUseLegacyRbac, authorization: this.security?.authz, + authentication: this.security?.authc, auditLogger: new ActionsAuthorizationAuditLogger( this.security?.audit.getLogger(ACTIONS_FEATURE.id) ), diff --git a/x-pack/plugins/actions/server/routes/execute.test.ts b/x-pack/plugins/actions/server/routes/execute.test.ts index b668e3460828a..e0c83711818ad 100644 --- a/x-pack/plugins/actions/server/routes/execute.test.ts +++ b/x-pack/plugins/actions/server/routes/execute.test.ts @@ -8,7 +8,7 @@ import { executeActionRoute } from './execute'; import { httpServiceMock } from 'src/core/server/mocks'; import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; -import { verifyApiAccess, ActionTypeDisabledError } from '../lib'; +import { verifyApiAccess, ActionTypeDisabledError, asHttpRequestExecutionSource } from '../lib'; import { actionsClientMock } from '../actions_client.mock'; import { ActionTypeExecutorResult } from '../types'; @@ -61,6 +61,7 @@ describe('executeActionRoute', () => { params: { someData: 'data', }, + source: asHttpRequestExecutionSource(req), }); expect(res.ok).toHaveBeenCalled(); @@ -97,6 +98,7 @@ describe('executeActionRoute', () => { expect(actionsClient.execute).toHaveBeenCalledWith({ actionId: '1', params: {}, + source: asHttpRequestExecutionSource(req), }); expect(res.ok).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/actions/server/routes/execute.ts b/x-pack/plugins/actions/server/routes/execute.ts index f15a117106210..8191b6946d332 100644 --- a/x-pack/plugins/actions/server/routes/execute.ts +++ b/x-pack/plugins/actions/server/routes/execute.ts @@ -15,6 +15,7 @@ import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from import { ActionTypeExecutorResult } from '../types'; import { BASE_ACTION_API_PATH } from '../../common'; +import { asHttpRequestExecutionSource } from '../lib/action_execution_source'; const paramSchema = schema.object({ id: schema.string(), @@ -51,6 +52,7 @@ export const executeActionRoute = (router: IRouter, licenseState: ILicenseState) const body: ActionTypeExecutorResult = await actionsClient.execute({ params, actionId: id, + source: asHttpRequestExecutionSource(req), }); return body ? res.ok({ diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts index 54f186acc1ba5..afc51ea4842f5 100644 --- a/x-pack/plugins/actions/server/saved_objects/index.ts +++ b/x-pack/plugins/actions/server/saved_objects/index.ts @@ -9,6 +9,7 @@ import mappings from './mappings.json'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; export const ACTION_SAVED_OBJECT_TYPE = 'action'; +export const ALERT_SAVED_OBJECT_TYPE = 'alert'; export const ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE = 'action_task_params'; export function setupSavedObjects( diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts index b20e6c6be2ebf..4b5af942024c0 100644 --- a/x-pack/plugins/alerts/server/alerts_client.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client.test.ts @@ -33,6 +33,7 @@ const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); const authorization = alertsAuthorizationMock.create(); const actionsAuthorization = actionsAuthorizationMock.create(); +const kibanaVersion = 'v7.10.0'; const alertsClientParams: jest.Mocked = { taskManager, alertTypeRegistry, @@ -48,6 +49,7 @@ const alertsClientParams: jest.Mocked = { encryptedSavedObjectsClient: encryptedSavedObjects, getActionsClient: jest.fn(), getEventLogClient: jest.fn(), + kibanaVersion, }; beforeEach(() => { @@ -374,6 +376,9 @@ describe('create()', () => { "createdAt": "2019-02-12T21:01:22.479Z", "createdBy": "elastic", "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "muteAll": false, "mutedInstanceIds": Array [], "name": "abc", @@ -426,10 +431,10 @@ describe('create()', () => { expect(unsecuredSavedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); expect(unsecuredSavedObjectsClient.update.mock.calls[0][1]).toEqual('1'); expect(unsecuredSavedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "scheduledTaskId": "task-123", - } - `); + Object { + "scheduledTaskId": "task-123", + } + `); }); test('creates an alert with multiple actions', async () => { @@ -999,6 +1004,9 @@ describe('create()', () => { createdAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', enabled: true, + meta: { + versionApiKeyLastmodified: 'v7.10.0', + }, schedule: { interval: '10s' }, throttle: null, muteAll: false, @@ -1112,6 +1120,9 @@ describe('create()', () => { createdAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', enabled: false, + meta: { + versionApiKeyLastmodified: 'v7.10.0', + }, schedule: { interval: '10s' }, throttle: null, muteAll: false, @@ -1237,6 +1248,9 @@ describe('enable()', () => { alertTypeId: 'myType', consumer: 'myApp', enabled: true, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, updatedBy: 'elastic', apiKey: null, apiKeyOwner: null, @@ -1322,6 +1336,9 @@ describe('enable()', () => { alertTypeId: 'myType', consumer: 'myApp', enabled: true, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, apiKey: Buffer.from('123:abc').toString('base64'), apiKeyOwner: 'elastic', updatedBy: 'elastic', @@ -1487,6 +1504,9 @@ describe('disable()', () => { apiKey: null, apiKeyOwner: null, enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, scheduledTaskId: null, updatedBy: 'elastic', actions: [ @@ -1527,6 +1547,9 @@ describe('disable()', () => { apiKey: null, apiKeyOwner: null, enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, scheduledTaskId: null, updatedBy: 'elastic', actions: [ @@ -3203,6 +3226,9 @@ describe('update()', () => { "apiKeyOwner": null, "consumer": "myApp", "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "name": "abc", "params": Object { "bar": true, @@ -3360,6 +3386,9 @@ describe('update()', () => { "apiKeyOwner": "elastic", "consumer": "myApp", "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "name": "abc", "params": Object { "bar": true, @@ -3511,6 +3540,9 @@ describe('update()', () => { "apiKeyOwner": null, "consumer": "myApp", "enabled": false, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, "name": "abc", "params": Object { "bar": true, @@ -4196,6 +4228,9 @@ describe('updateApiKey()', () => { }, }, ], + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, }, { version: '123' } ); @@ -4232,6 +4267,9 @@ describe('updateApiKey()', () => { }, }, ], + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, }, { version: '123' } ); diff --git a/x-pack/plugins/alerts/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client.ts index f8da17f4bf089..0a08ca848c73d 100644 --- a/x-pack/plugins/alerts/server/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client.ts @@ -12,6 +12,7 @@ import { SavedObjectsClientContract, SavedObjectReference, SavedObject, + PluginInitializerContext, } from 'src/core/server'; import { esKuery } from '../../../../src/plugins/data/server'; import { ActionsClient, ActionsAuthorization } from '../../actions/server'; @@ -71,6 +72,7 @@ export interface ConstructorOptions { invalidateAPIKey: (params: InvalidateAPIKeyParams) => Promise; getActionsClient: () => Promise; getEventLogClient: () => Promise; + kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; } export interface MuteOptions extends IndexType { @@ -157,7 +159,8 @@ export class AlertsClient { private readonly getActionsClient: () => Promise; private readonly actionsAuthorization: ActionsAuthorization; private readonly getEventLogClient: () => Promise; - encryptedSavedObjectsClient: EncryptedSavedObjectsClient; + private readonly encryptedSavedObjectsClient: EncryptedSavedObjectsClient; + private readonly kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; constructor({ alertTypeRegistry, @@ -174,6 +177,7 @@ export class AlertsClient { getActionsClient, actionsAuthorization, getEventLogClient, + kibanaVersion, }: ConstructorOptions) { this.logger = logger; this.getUserName = getUserName; @@ -189,6 +193,7 @@ export class AlertsClient { this.getActionsClient = getActionsClient; this.actionsAuthorization = actionsAuthorization; this.getEventLogClient = getEventLogClient; + this.kibanaVersion = kibanaVersion; } public async create({ data, options }: CreateOptions): Promise { @@ -222,10 +227,14 @@ export class AlertsClient { muteAll: false, mutedInstanceIds: [], }; - const createdAlert = await this.unsecuredSavedObjectsClient.create('alert', rawAlert, { - ...options, - references, - }); + const createdAlert = await this.unsecuredSavedObjectsClient.create( + 'alert', + this.updateMeta(rawAlert), + { + ...options, + references, + } + ); if (data.enabled) { let scheduledTask; try { @@ -373,7 +382,7 @@ export class AlertsClient { } public async delete({ id }: { id: string }) { - let taskIdToRemove: string | undefined; + let taskIdToRemove: string | undefined | null; let apiKeyToInvalidate: string | null = null; let attributes: RawAlert; @@ -482,14 +491,14 @@ export class AlertsClient { const updatedObject = await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, ...data, ...apiKeyAttributes, params: validatedAlertTypeParams as RawAlert['params'], actions, updatedBy: username, - }, + }), { version, references, @@ -547,7 +556,7 @@ export class AlertsClient { WriteOperations.UpdateApiKey ); - if (attributes.actions.length) { + if (attributes.actions.length && !this.authorization.shouldUseLegacyAuthorization(attributes)) { await this.actionsAuthorization.ensureAuthorized('execute'); } @@ -555,14 +564,14 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, ...this.apiKeyAsAlertAttributes( await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)), username ), updatedBy: username, - }, + }), { version } ); @@ -625,7 +634,7 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, enabled: true, ...this.apiKeyAsAlertAttributes( @@ -635,7 +644,7 @@ export class AlertsClient { username ), updatedBy: username, - }, + }), { version } ); const scheduledTask = await this.scheduleAlert(id, attributes.alertTypeId); @@ -681,14 +690,14 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', id, - { + this.updateMeta({ ...attributes, enabled: false, scheduledTaskId: null, apiKey: null, apiKeyOwner: null, updatedBy: await this.getUserName(), - }, + }), { version } ); @@ -713,11 +722,15 @@ export class AlertsClient { await this.actionsAuthorization.ensureAuthorized('execute'); } - await this.unsecuredSavedObjectsClient.update('alert', id, { - muteAll: true, - mutedInstanceIds: [], - updatedBy: await this.getUserName(), - }); + await this.unsecuredSavedObjectsClient.update( + 'alert', + id, + this.updateMeta({ + muteAll: true, + mutedInstanceIds: [], + updatedBy: await this.getUserName(), + }) + ); } public async unmuteAll({ id }: { id: string }) { @@ -732,11 +745,15 @@ export class AlertsClient { await this.actionsAuthorization.ensureAuthorized('execute'); } - await this.unsecuredSavedObjectsClient.update('alert', id, { - muteAll: false, - mutedInstanceIds: [], - updatedBy: await this.getUserName(), - }); + await this.unsecuredSavedObjectsClient.update( + 'alert', + id, + this.updateMeta({ + muteAll: false, + mutedInstanceIds: [], + updatedBy: await this.getUserName(), + }) + ); } public async muteInstance({ alertId, alertInstanceId }: MuteOptions) { @@ -761,10 +778,10 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', alertId, - { + this.updateMeta({ mutedInstanceIds, updatedBy: await this.getUserName(), - }, + }), { version } ); } @@ -795,11 +812,10 @@ export class AlertsClient { await this.unsecuredSavedObjectsClient.update( 'alert', alertId, - { + this.updateMeta({ updatedBy: await this.getUserName(), - mutedInstanceIds: mutedInstanceIds.filter((id: string) => id !== alertInstanceId), - }, + }), { version } ); } @@ -859,7 +875,7 @@ export class AlertsClient { private getPartialAlertFromRaw( id: string, - { createdAt, ...rawAlert }: Partial, + { createdAt, meta, scheduledTaskId, ...rawAlert }: Partial, updatedAt: SavedObject['updated_at'] = createdAt, references: SavedObjectReference[] | undefined ): PartialAlert { @@ -874,6 +890,7 @@ export class AlertsClient { : [], ...(updatedAt ? { updatedAt: new Date(updatedAt) } : {}), ...(createdAt ? { createdAt: new Date(createdAt) } : {}), + ...(scheduledTaskId ? { scheduledTaskId } : {}), }; } @@ -941,6 +958,14 @@ export class AlertsClient { private generateAPIKeyName(alertTypeId: string, alertName: string) { return truncate(`Alerting: ${alertTypeId}/${trim(alertName)}`, { length: 256 }); } + + private updateMeta>(alertAttributes: T): T { + if (alertAttributes.hasOwnProperty('apiKey') || alertAttributes.hasOwnProperty('apiKeyOwner')) { + alertAttributes.meta = alertAttributes.meta ?? {}; + alertAttributes.meta.versionApiKeyLastmodified = this.kibanaVersion; + } + return alertAttributes; + } } function parseDate(dateString: string | undefined, propertyName: string, defaultValue: Date): Date { diff --git a/x-pack/plugins/alerts/server/alerts_client_factory.test.ts b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts index 99e81344715a5..ac91d689798c9 100644 --- a/x-pack/plugins/alerts/server/alerts_client_factory.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts @@ -44,6 +44,7 @@ const alertsClientFactoryParams: jest.Mocked = { actions: actionsMock.createStart(), features, eventLog: eventLogMock.createStart(), + kibanaVersion: '7.10.0', }; const fakeRequest = ({ app: {}, @@ -126,6 +127,7 @@ test('creates an alerts client with proper constructor arguments when security i createAPIKey: expect.any(Function), invalidateAPIKey: expect.any(Function), encryptedSavedObjectsClient: alertsClientFactoryParams.encryptedSavedObjectsClient, + kibanaVersion: '7.10.0', }); }); @@ -169,6 +171,7 @@ test('creates an alerts client with proper constructor arguments', async () => { encryptedSavedObjectsClient: alertsClientFactoryParams.encryptedSavedObjectsClient, getActionsClient: expect.any(Function), getEventLogClient: expect.any(Function), + kibanaVersion: '7.10.0', }); }); diff --git a/x-pack/plugins/alerts/server/alerts_client_factory.ts b/x-pack/plugins/alerts/server/alerts_client_factory.ts index 83202424c9773..eccd810391307 100644 --- a/x-pack/plugins/alerts/server/alerts_client_factory.ts +++ b/x-pack/plugins/alerts/server/alerts_client_factory.ts @@ -4,11 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { + KibanaRequest, + Logger, + SavedObjectsServiceStart, + PluginInitializerContext, +} from 'src/core/server'; import { PluginStartContract as ActionsPluginStartContract } from '../../actions/server'; import { AlertsClient } from './alerts_client'; import { ALERTS_FEATURE_ID } from '../common'; import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types'; -import { KibanaRequest, Logger, SavedObjectsServiceStart } from '../../../../src/core/server'; import { InvalidateAPIKeyParams, SecurityPluginSetup } from '../../security/server'; import { EncryptedSavedObjectsClient } from '../../encrypted_saved_objects/server'; import { TaskManagerStartContract } from '../../task_manager/server'; @@ -30,6 +35,7 @@ export interface AlertsClientFactoryOpts { actions: ActionsPluginStartContract; features: FeaturesPluginStart; eventLog: IEventLogClientService; + kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; } export class AlertsClientFactory { @@ -45,6 +51,7 @@ export class AlertsClientFactory { private actions!: ActionsPluginStartContract; private features!: FeaturesPluginStart; private eventLog!: IEventLogClientService; + private kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; public initialize(options: AlertsClientFactoryOpts) { if (this.isInitialized) { @@ -62,6 +69,7 @@ export class AlertsClientFactory { this.actions = options.actions; this.features = options.features; this.eventLog = options.eventLog; + this.kibanaVersion = options.kibanaVersion; } public create(request: KibanaRequest, savedObjects: SavedObjectsServiceStart): AlertsClient { @@ -80,6 +88,7 @@ export class AlertsClientFactory { return new AlertsClient({ spaceId, + kibanaVersion: this.kibanaVersion, logger: this.logger, taskManager: this.taskManager, alertTypeRegistry: this.alertTypeRegistry, diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts index d7705f834ad41..3728daa946d5b 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.mock.ts @@ -14,6 +14,7 @@ const createAlertsAuthorizationMock = () => { ensureAuthorized: jest.fn(), filterByAlertTypeAuthorization: jest.fn(), getFindAuthorizationFilter: jest.fn(), + shouldUseLegacyAuthorization: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts index 20b9fecd601e6..17691baa25af8 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts @@ -8,12 +8,13 @@ import Boom from 'boom'; import { map, mapValues, fromPairs, has } from 'lodash'; import { KibanaRequest } from 'src/core/server'; import { ALERTS_FEATURE_ID } from '../../common'; -import { AlertTypeRegistry } from '../types'; +import { AlertTypeRegistry, RawAlert } from '../types'; import { SecurityPluginSetup } from '../../../security/server'; import { RegistryAlertType } from '../alert_type_registry'; import { PluginStartContract as FeaturesPluginStart } from '../../../features/server'; import { AlertsAuthorizationAuditLogger, ScopeType } from './audit_logger'; import { Space } from '../../../spaces/server'; +import { LEGACY_LAST_MODIFIED_VERSION } from '../saved_objects/migrations'; import { asFiltersByAlertTypeAndConsumer } from './alerts_authorization_kuery'; import { KueryNode } from '../../../../../src/plugins/data/server'; @@ -111,6 +112,10 @@ export class AlertsAuthorization { ); } + public shouldUseLegacyAuthorization(alert: RawAlert): boolean { + return alert.meta?.versionApiKeyLastmodified === LEGACY_LAST_MODIFIED_VERSION; + } + private shouldCheckAuthorization(): boolean { return this.authorization?.mode?.useRbacForRequest(this.request) ?? false; } diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index 4f9b1f7c22e6d..8f09d55c9a0e0 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -109,6 +109,7 @@ export class AlertingPlugin { private readonly alertsClientFactory: AlertsClientFactory; private readonly telemetryLogger: Logger; private readonly kibanaIndex: Promise; + private readonly kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; private eventLogService?: IEventLogService; private eventLogger?: IEventLogger; @@ -123,6 +124,7 @@ export class AlertingPlugin { map((config: SharedGlobalConfig) => config.kibana.index) ) .toPromise(); + this.kibanaVersion = initializerContext.env.packageInfo.version; } public async setup( @@ -241,6 +243,7 @@ export class AlertingPlugin { actions: plugins.actions, features: plugins.features, eventLog: plugins.eventLog, + kibanaVersion: this.kibanaVersion, }); const getAlertsClientWithRequest = (request: KibanaRequest) => { diff --git a/x-pack/plugins/alerts/server/saved_objects/mappings.json b/x-pack/plugins/alerts/server/saved_objects/mappings.json index a7e85febf2446..8440b963975ff 100644 --- a/x-pack/plugins/alerts/server/saved_objects/mappings.json +++ b/x-pack/plugins/alerts/server/saved_objects/mappings.json @@ -76,6 +76,13 @@ }, "mutedInstanceIds": { "type": "keyword" + }, + "meta": { + "properties": { + "versionApiKeyLastmodified": { + "type": "keyword" + } + } } } } diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts index 46fa2bcd512ff..1c1261ae3fa08 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts @@ -21,15 +21,18 @@ describe('7.10.0', () => { ); }); - test('changes nothing on alerts by other plugins', () => { + test('marks alerts as legacy', () => { const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; const alert = getMockData({}); - expect(migration710(alert, { log })).toMatchObject(alert); - - expect(encryptedSavedObjectsSetup.createMigration).toHaveBeenCalledWith( - expect.any(Function), - expect.any(Function) - ); + expect(migration710(alert, { log })).toMatchObject({ + ...alert, + attributes: { + ...alert.attributes, + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, + }, + }); }); test('migrates the consumer for metrics', () => { @@ -42,6 +45,26 @@ describe('7.10.0', () => { attributes: { ...alert.attributes, consumer: 'infrastructure', + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, + }, + }); + }); + + test('migrates the consumer for siem', () => { + const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; + const alert = getMockData({ + consumer: 'securitySolution', + }); + expect(migration710(alert, { log })).toMatchObject({ + ...alert, + attributes: { + ...alert.attributes, + consumer: 'siem', + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, }, }); }); @@ -56,6 +79,9 @@ describe('7.10.0', () => { attributes: { ...alert.attributes, consumer: 'alerts', + meta: { + versionApiKeyLastmodified: 'pre-7.10.0', + }, }, }); }); @@ -64,9 +90,9 @@ describe('7.10.0', () => { describe('7.10.0 migrates with failure', () => { beforeEach(() => { jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockRejectedValueOnce( - new Error(`Can't migrate!`) as never - ); + encryptedSavedObjectsSetup.createMigration.mockImplementationOnce(() => () => { + throw new Error(`Can't migrate!`); + }); }); test('should show the proper exception', () => { @@ -82,7 +108,7 @@ describe('7.10.0 migrates with failure', () => { }, }); expect(log.error).toHaveBeenCalledWith( - `encryptedSavedObject migration failed for alert ${alert.id} with error: migrationFunc is not a function`, + `encryptedSavedObject 7.10.0 migration failed for alert ${alert.id} with error: Can't migrate!`, { alertDocument: { ...alert, diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.ts index 30570eeb0a453..c88f4d786c212 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.ts @@ -11,54 +11,54 @@ import { } from '../../../../../src/core/server'; import { RawAlert } from '../types'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; +import { + APP_ID as SIEM_APP_ID, + SERVER_APP_ID as SIEM_SERVER_APP_ID, +} from '../../../security_solution/common/constants'; + +export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0'; export function getMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationMap { - const alertsMigration = changeAlertingConsumer(encryptedSavedObjects, 'alerting', 'alerts'); - - const infrastructureMigration = changeAlertingConsumer( - encryptedSavedObjects, - 'metrics', - 'infrastructure' - ); + const migrationWhenRBACWasIntroduced = markAsLegacyAndChangeConsumer(encryptedSavedObjects); return { - '7.10.0': (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { - if (doc.attributes.consumer === 'alerting') { - return executeMigration(doc, context, alertsMigration); - } else if (doc.attributes.consumer === 'metrics') { - return executeMigration(doc, context, infrastructureMigration); - } - return doc; - }, + '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), }; } -function executeMigration( - doc: SavedObjectUnsanitizedDoc, - context: SavedObjectMigrationContext, - migrationFunc: SavedObjectMigrationFn +function executeMigrationWithErrorHandling( + migrationFunc: SavedObjectMigrationFn, + version: string ) { - try { - return migrationFunc(doc, context); - } catch (ex) { - context.log.error( - `encryptedSavedObject migration failed for alert ${doc.id} with error: ${ex.message}`, - { alertDocument: doc } - ); - } - return doc; + return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { + try { + return migrationFunc(doc, context); + } catch (ex) { + context.log.error( + `encryptedSavedObject ${version} migration failed for alert ${doc.id} with error: ${ex.message}`, + { alertDocument: doc } + ); + } + return doc; + }; } -function changeAlertingConsumer( - encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, - from: string, - to: string +const consumersToChange: Map = new Map( + Object.entries({ + alerting: 'alerts', + metrics: 'infrastructure', + [SIEM_APP_ID]: SIEM_SERVER_APP_ID, + }) +); +function markAsLegacyAndChangeConsumer( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationFn { return encryptedSavedObjects.createMigration( function shouldBeMigrated(doc): doc is SavedObjectUnsanitizedDoc { - return doc.attributes.consumer === from; + // migrate all documents in 7.10 in order to add the "meta" RBAC field + return true; }, (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc => { const { @@ -68,7 +68,11 @@ function changeAlertingConsumer( ...doc, attributes: { ...doc.attributes, - consumer: consumer === from ? to : consumer, + consumer: consumersToChange.get(consumer) ?? consumer, + // mark any alert predating 7.10 as a legacy alert + meta: { + versionApiKeyLastmodified: LEGACY_LAST_MODIFIED_VERSION, + }, }, }; } diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts index 677040d8174e3..2f0754d34492f 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts @@ -10,6 +10,7 @@ import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { KibanaRequest } from 'kibana/server'; +import { asSavedObjectExecutionSource } from '../../../actions/server'; const alertType: AlertType = { id: 'test', @@ -79,20 +80,27 @@ test('enqueues execution per selected action', async () => { ).toHaveBeenCalledWith(createExecutionHandlerParams.request); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", + "contextVal": "My goes here", + "foo": true, + "stateVal": "My goes here", + }, + "source": Object { + "source": Object { "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My goes here", - }, - "spaceId": "default", + "type": "alert", }, - ] - `); + "type": "SAVED_OBJECT", + }, + "spaceId": "default", + }, + ] + `); const eventLogger = createExecutionHandlerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); @@ -161,6 +169,10 @@ test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => contextVal: 'My other goes here', stateVal: 'My other goes here', }, + source: asSavedObjectExecutionSource({ + id: '1', + type: 'alert', + }), spaceId: 'default', apiKey: createExecutionHandlerParams.apiKey, }); @@ -231,20 +243,27 @@ test('context attribute gets parameterized', async () => { }); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", + "contextVal": "My context-val goes here", + "foo": true, + "stateVal": "My goes here", + }, + "source": Object { + "source": Object { "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", - "contextVal": "My context-val goes here", - "foo": true, - "stateVal": "My goes here", - }, - "spaceId": "default", + "type": "alert", }, - ] - `); + "type": "SAVED_OBJECT", + }, + "spaceId": "default", + }, + ] + `); }); test('state attribute gets parameterized', async () => { @@ -257,20 +276,27 @@ test('state attribute gets parameterized', async () => { }); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", + "contextVal": "My goes here", + "foo": true, + "stateVal": "My state-val goes here", + }, + "source": Object { + "source": Object { "id": "1", - "params": Object { - "alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here", - "contextVal": "My goes here", - "foo": true, - "stateVal": "My state-val goes here", - }, - "spaceId": "default", + "type": "alert", }, - ] - `); + "type": "SAVED_OBJECT", + }, + "spaceId": "default", + }, + ] + `); }); test(`logs an error when action group isn't part of actionGroups available for the alertType`, async () => { diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index bf074e2c60ee3..f873b0178ece9 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -7,7 +7,10 @@ import { map } from 'lodash'; import { Logger, KibanaRequest } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; -import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; +import { + PluginStartContract as ActionsPluginStartContract, + asSavedObjectExecutionSource, +} from '../../../actions/server'; import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; import { @@ -97,6 +100,10 @@ export function createExecutionHandler({ params: action.params, spaceId, apiKey, + source: asSavedObjectExecutionSource({ + id: alertId, + type: 'alert', + }), }); const namespace = spaceId === 'default' ? {} : { namespace: spaceId }; diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index 58b1fa4a123e1..801d30b6406ee 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -211,17 +211,24 @@ describe('Task Runner', () => { await taskRunner.run(); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "id": "1", - "params": Object { - "foo": true, - }, - "spaceId": undefined, - }, - ] - `); + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "foo": true, + }, + "source": Object { + "source": Object { + "id": "1", + "type": "alert", + }, + "type": "SAVED_OBJECT", + }, + "spaceId": undefined, + }, + ] + `); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); @@ -351,17 +358,24 @@ describe('Task Runner', () => { }); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "id": "1", - "params": Object { - "foo": true, - }, - "spaceId": undefined, - }, - ] - `); + Array [ + Object { + "apiKey": "MTIzOmFiYw==", + "id": "1", + "params": Object { + "foo": true, + }, + "source": Object { + "source": Object { + "id": "1", + "type": "alert", + }, + "type": "SAVED_OBJECT", + }, + "spaceId": undefined, + }, + ] + `); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 20943ba28885c..8d568e8b7ecd1 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -111,6 +111,10 @@ export interface RawAlertAction extends SavedObjectAttributes { params: AlertActionParams; } +export interface AlertMeta extends SavedObjectAttributes { + versionApiKeyLastmodified?: string; +} + export type PartialAlert = Pick & Partial>; export interface RawAlert extends SavedObjectAttributes { @@ -122,7 +126,7 @@ export interface RawAlert extends SavedObjectAttributes { schedule: SavedObjectAttributes; actions: RawAlertAction[]; params: SavedObjectAttributes; - scheduledTaskId?: string; + scheduledTaskId?: string | null; createdBy: string | null; updatedBy: string | null; createdAt: string; @@ -131,6 +135,7 @@ export interface RawAlert extends SavedObjectAttributes { throttle: string | null; muteAll: boolean; mutedInstanceIds: string[]; + meta?: AlertMeta; } export type AlertInfoParams = Pick< diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json index 083386480c540..df61fde6518d0 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/kibana.json @@ -3,8 +3,8 @@ "version": "1.0.0", "kibanaVersion": "kibana", "configPath": ["xpack"], - "requiredPlugins": ["taskManager", "features", "actions", "alerts"], - "optionalPlugins": ["spaces"], + "requiredPlugins": ["taskManager", "features", "actions", "alerts", "encryptedSavedObjects"], + "optionalPlugins": ["security", "spaces"], "server": true, "ui": false } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts index f9fc3d647abd3..525f99587672a 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/plugin.ts @@ -12,11 +12,15 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../.. import { defineAlertTypes } from './alert_types'; import { defineActionTypes } from './action_types'; import { defineRoutes } from './routes'; +import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server'; +import { SecurityPluginSetup } from '../../../../../../../plugins/security/server'; export interface FixtureSetupDeps { features: FeaturesPluginSetup; actions: ActionsPluginSetup; alerts: AlertingPluginSetup; + spaces?: SpacesPluginSetup; + security?: SecurityPluginSetup; } export interface FixtureStartDeps { @@ -24,7 +28,10 @@ export interface FixtureStartDeps { } export class FixturePlugin implements Plugin { - public setup(core: CoreSetup, { features, actions, alerts }: FixtureSetupDeps) { + public setup( + core: CoreSetup, + { features, actions, alerts, spaces, security }: FixtureSetupDeps + ) { features.registerKibanaFeature({ id: 'alertsFixture', name: 'Alerts', @@ -97,7 +104,7 @@ export class FixturePlugin implements Plugin, + { spaces, security }: Partial +) { const router = core.http.createRouter(); + router.put( + { + path: '/api/alerts_fixture/{id}/replace_api_key', + validate: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object({ + spaceId: schema.maybe(schema.string()), + }), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + const { id } = req.params; + + if (!security) { + return res.ok({ + body: {}, + }); + } + + const [{ savedObjects }, { encryptedSavedObjects }] = await core.getStartServices(); + const encryptedSavedObjectsWithAlerts = await encryptedSavedObjects.getClient({ + includedHiddenTypes: ['alert'], + }); + const savedObjectsWithAlerts = await savedObjects.getScopedClient(req, { + excludedWrappers: ['security', 'spaces'], + includedHiddenTypes: ['alert'], + }); + + let namespace: string | undefined; + if (spaces && req.body.spaceId) { + namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId); + } + + const user = await security.authc.getCurrentUser(req); + if (!user) { + return res.internalError({}); + } + + // Create an API key using the new grant API - in this case the Kibana system user is creating the + // API key for the user, instead of having the user create it themselves, which requires api_key + // privileges + const createAPIKeyResult = await security.authc.grantAPIKeyAsInternalUser(req, { + name: `alert:migrated-to-7.10:${user.username}`, + role_descriptors: {}, + }); + + if (!createAPIKeyResult) { + return res.internalError({}); + } + + const result = await savedObjectsWithAlerts.update( + 'alert', + id, + { + ...( + await encryptedSavedObjectsWithAlerts.getDecryptedAsInternalUser( + 'alert', + id, + { + namespace, + } + ) + ).attributes, + apiKey: Buffer.from(`${createAPIKeyResult.id}:${createAPIKeyResult.api_key}`).toString( + 'base64' + ), + apiKeyOwner: user.username, + }, + { + namespace, + } + ); + return res.ok({ body: result }); + } + ); + router.put( { path: '/api/alerts_fixture/saved_object/{type}/{id}', @@ -54,4 +142,38 @@ export function defineRoutes(core: CoreSetup) { return res.ok({ body: result }); } ); + + router.put( + { + path: '/api/alerts_fixture/{id}/reschedule_task', + validate: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object({ + runAt: schema.string(), + }), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + const { id } = req.params; + const { runAt } = req.body; + + const [{ savedObjects }] = await core.getStartServices(); + const savedObjectsWithTasksAndAlerts = await savedObjects.getScopedClient(req, { + includedHiddenTypes: ['task', 'alert'], + }); + const alert = await savedObjectsWithTasksAndAlerts.get('alert', id); + const result = await savedObjectsWithTasksAndAlerts.update( + 'task', + alert.attributes.scheduledTaskId!, + { runAt } + ); + return res.ok({ body: result }); + } + ); } diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index a9e5d0c832c8c..797769fd64471 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -8,6 +8,7 @@ import { Space, User } from '../types'; import { ObjectRemover } from './object_remover'; import { getUrlPrefix } from './space_test_utils'; import { ES_TEST_INDEX_NAME } from './es_test_index_tool'; +import { getTestAlertData } from './get_test_alert_data'; export interface AlertUtilsOpts { user?: User; @@ -23,6 +24,10 @@ export interface CreateAlertWithActionOpts { overwrites?: Record; reference: string; } +export interface CreateNoopAlertOpts { + objectRemover?: ObjectRemover; + overwrites?: Record; +} interface UpdateAlwaysFiringAction { alertId: string; @@ -265,6 +270,39 @@ export class AlertUtils { } return response; } + + public replaceApiKeys(id: string) { + let request = this.supertestWithoutAuth + .put(`/api/alerts_fixture/${id}/replace_api_key`) + .set('kbn-xsrf', 'foo'); + if (this.user) { + request = request.auth(this.user.username, this.user.password); + } + return request.send({ spaceId: this.space.id }); + } + + public async createNoopAlert({ objectRemover, overwrites = {} }: CreateNoopAlertOpts) { + const objRemover = objectRemover || this.objectRemover; + + if (!objRemover) { + throw new Error('objectRemover is required'); + } + + let request = this.supertestWithoutAuth + .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo'); + if (this.user) { + request = request.auth(this.user.username, this.user.password); + } + const response = await request.send({ + ...getTestAlertData(), + ...overwrites, + }); + if (response.statusCode === 200) { + objRemover.add(this.space.id, response.body.id, 'alert', 'alerts'); + } + return response; + } } export function getConsumerUnauthorizedErrorMessage( diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts index b03a3c8ccf6af..fa0130780cb69 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts @@ -25,5 +25,8 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./update')); loadTestFile(require.resolve('./update_api_key')); loadTestFile(require.resolve('./alerts')); + + // note that this test will destroy existing spaces + loadTestFile(require.resolve('./rbac_legacy')); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts new file mode 100644 index 0000000000000..513b7fc449065 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { UserAtSpaceScenarios, Superuser } from '../../scenarios'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { ESTestIndexTool, getUrlPrefix, ObjectRemover, AlertUtils } from '../../../common/lib'; +import { setupSpacesAndUsers } from '..'; + +// eslint-disable-next-line import/no-default-export +export default function alertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const securityService = getService('security'); + const spacesService = getService('spaces'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esTestIndexTool = new ESTestIndexTool(es, retry); + + const MIGRATED_ACTION_ID = '17f38826-5a8d-4a76-975a-b496e7fffe0b'; + const MIGRATED_ALERT_ID: Record = { + space_1_all_alerts_none_actions: '6ee9630a-a20e-44af-9465-217a3717d2ab', + space_1_all_with_restricted_fixture: '5cc59319-74ee-4edc-8646-a79ea91067cd', + space_1_all: 'd41a6abb-b93b-46df-a80a-926221ea847c', + global_read: '362e362b-a137-4aa2-9434-43e3d0d84a34', + superuser: 'b384be60-ec53-4b26-857e-0253ee55b277', + }; + + describe('alerts', () => { + const authorizationIndex = '.kibana-test-authorization'; + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + await esTestIndexTool.destroy(); + await esArchiver.load('alerts_legacy'); + await esTestIndexTool.setup(); + await es.indices.create({ index: authorizationIndex }); + await setupSpacesAndUsers(spacesService, securityService); + }); + + after(async () => { + await esTestIndexTool.destroy(); + await es.indices.delete({ index: authorizationIndex }); + await esArchiver.unload('alerts_legacy'); + }); + + for (const scenario of UserAtSpaceScenarios) { + const { user, space } = scenario; + + describe(scenario.id, () => { + let alertUtils: AlertUtils; + + before(async () => { + alertUtils = new AlertUtils({ + user, + space, + supertestWithoutAuth, + indexRecordActionId: MIGRATED_ACTION_ID, + objectRemover, + }); + }); + + it('should schedule actions on legacy alerts', async () => { + const reference = `alert:migrated-to-7.10:${user.username}`; + const migratedAlertId = MIGRATED_ALERT_ID[user.username]; + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + // These cases are not relevant as we're testing the migration of alerts which + // were valid pre 7.10.0 and which become invalid after the introduction of RBAC in 7.10.0 + // these cases were invalid pre 7.10.0 and remain invalid post 7.10.0 + break; + case 'space_1_all at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + await ensureLegacyAlertHasBeenMigrated(migratedAlertId); + + await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId); + + await ensureAlertIsRunning(); + + await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId); + + // update alert as user with privileges - so it is no longer a legacy alert + const updatedKeyResponse = await alertUtils.getUpdateApiKeyRequest(migratedAlertId); + expect(updatedKeyResponse.statusCode).to.eql(204); + + await ensureAlertIsRunning(); + break; + case 'global_read at space1': + await ensureLegacyAlertHasBeenMigrated(migratedAlertId); + + await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId); + + await ensureAlertIsRunning(); + + await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId); + + // attempt to update alert as user with no Alerts privileges - as it is no longer a legacy alert + // this should fail, as the user doesn't have the `updateApiKey` privilege for Alerts + const failedUpdateKeyDueToAlertsPrivilegesResponse = await alertUtils.getUpdateApiKeyRequest( + migratedAlertId + ); + + expect(failedUpdateKeyDueToAlertsPrivilegesResponse.statusCode).to.eql(403); + expect(failedUpdateKeyDueToAlertsPrivilegesResponse.body).to.eql({ + error: 'Forbidden', + message: + 'Unauthorized to updateApiKey a "test.always-firing" alert for "alertsFixture"', + statusCode: 403, + }); + break; + case 'space_1_all_alerts_none_actions at space1': + await ensureLegacyAlertHasBeenMigrated(migratedAlertId); + + await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId); + + await ensureAlertIsRunning(); + + await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId); + + // attempt to update alert as user with no Actions privileges - as it is no longer a legacy alert + // this should fail, as the user doesn't have the `execute` privilege for Actions + const failedUpdateKeyDueToActionsPrivilegesResponse = await alertUtils.getUpdateApiKeyRequest( + migratedAlertId + ); + + expect(failedUpdateKeyDueToActionsPrivilegesResponse.statusCode).to.eql(403); + expect(failedUpdateKeyDueToActionsPrivilegesResponse.body).to.eql({ + error: 'Forbidden', + message: 'Unauthorized to execute actions', + statusCode: 403, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + + async function ensureLegacyAlertHasBeenMigrated(alertId: string) { + const getResponse = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${alertId}`) + .auth(user.username, user.password); + expect(getResponse.status).to.eql(200); + } + + async function updateMigratedAlertToUseApiKeyOfCurrentUser(alertId: string) { + // swap out api key to run as the current user + const swapResponse = await alertUtils.replaceApiKeys(alertId); + expect(swapResponse.statusCode).to.eql(200); + // ensure the alert is till marked as legacy despite the update of the Api key + // this is important as proper update *should* update the legacy status of the alert + // and we want to ensure we don't accidentally introduce a change that might break our support of legacy alerts + expect(swapResponse.body.id).to.eql(alertId); + expect(swapResponse.body.attributes.meta.versionApiKeyLastmodified).to.eql( + 'pre-7.10.0' + ); + + // loading the archive likely caused the task to fail so ensure it's rescheduled to run in 2 seconds, + // otherwise this test will stall for 5 minutes + // no other attributes are touched, only runAt, so unless it would have ran when runAt expired, it + // won't run now + await supertest + .put(`${getUrlPrefix(space.id)}/api/alerts_fixture/${alertId}/reschedule_task`) + .set('kbn-xsrf', 'foo') + .send({ + runAt: getRunAt(2000), + }) + .expect(200); + } + + async function ensureAlertIsRunning() { + // ensure the alert still runs and that it can schedule actions + const numberOfAlertExecutions = ( + await esTestIndexTool.search('alert:test.always-firing', reference) + ).hits.total.value; + + const numberOfActionExecutions = ( + await esTestIndexTool.search('action:test.index-record', reference) + ).hits.total.value; + + // wait for alert to execute and for its action to be scheduled and run + await retry.try(async () => { + const alertSearchResult = await esTestIndexTool.search( + 'alert:test.always-firing', + reference + ); + + const actionSearchResult = await esTestIndexTool.search( + 'action:test.index-record', + reference + ); + + expect(alertSearchResult.hits.total.value).to.be.greaterThan(numberOfAlertExecutions); + expect(actionSearchResult.hits.total.value).to.be.greaterThan( + numberOfActionExecutions + ); + }); + } + + async function updateAlertSoThatItIsNoLongerLegacy(alertId: string) { + // update the alert as super user (to avoid privilege limitations) so that it is no longer a legacy alert + await alertUtils.updateAlwaysFiringAction({ + alertId, + actionId: MIGRATED_ACTION_ID, + user: Superuser, + reference, + overwrites: { + name: 'Updated Alert', + schedule: { interval: '2s' }, + throttle: '2s', + }, + }); + } + }); + }); + } + }); +} + +function getRunAt(delayInMs: number) { + const runAt = new Date(); + runAt.setMilliseconds(new Date().getMilliseconds() + delayInMs); + return runAt.toISOString(); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts index f544ef2d771eb..7daa223dc2d43 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/index.ts @@ -8,6 +8,47 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; import { isCustomRoleSpecification } from '../../common/types'; import { Spaces, Users } from '../scenarios'; +export async function setupSpacesAndUsers( + spacesService: ReturnType, + securityService: ReturnType +) { + for (const space of Spaces) { + await spacesService.create(space); + } + + for (const user of Users) { + const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; + + await securityService.user.create(user.username, { + password: user.password, + full_name: user.fullName, + roles: roles.map((role) => role.name), + }); + + for (const role of roles) { + if (isCustomRoleSpecification(role)) { + await securityService.role.create(role.name, { + kibana: role.kibana, + elasticsearch: role.elasticsearch, + }); + } + } + } +} + +export async function tearDownUsers(securityService: ReturnType) { + for (const user of Users) { + await securityService.user.delete(user.username); + + const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; + for (const role of roles) { + if (isCustomRoleSpecification(role)) { + await securityService.role.delete(role.name); + } + } + } +} + // eslint-disable-next-line import/no-default-export export default function alertingApiIntegrationTests({ loadTestFile, @@ -21,41 +62,11 @@ export default function alertingApiIntegrationTests({ this.tags('ciGroup5'); before(async () => { - for (const space of Spaces) { - await spacesService.create(space); - } - - for (const user of Users) { - const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; - - await securityService.user.create(user.username, { - password: user.password, - full_name: user.fullName, - roles: roles.map((role) => role.name), - }); - - for (const role of roles) { - if (isCustomRoleSpecification(role)) { - await securityService.role.create(role.name, { - kibana: role.kibana, - elasticsearch: role.elasticsearch, - }); - } - } - } + await setupSpacesAndUsers(spacesService, securityService); }); after(async () => { - for (const user of Users) { - await securityService.user.delete(user.username); - - const roles = [...(user.role ? [user.role] : []), ...(user.roles ? user.roles : [])]; - for (const role of roles) { - if (isCustomRoleSpecification(role)) { - await securityService.role.delete(role.name); - } - } - } + await tearDownUsers(securityService); await esArchiver.unload('empty_kibana'); }); diff --git a/x-pack/test/functional/es_archives/alerts_legacy/data.json b/x-pack/test/functional/es_archives/alerts_legacy/data.json new file mode 100644 index 0000000000000..770e8e7c15617 --- /dev/null +++ b/x-pack/test/functional/es_archives/alerts_legacy/data.json @@ -0,0 +1,491 @@ +{ + "type": "doc", + "value": { + "id": "space1:action:17f38826-5a8d-4a76-975a-b496e7fffe0b", + "index": ".kibana_1", + "source": { + "action": { + "actionTypeId": "test.index-record", + "config": { + "unencrypted": "This value shouldn't get encrypted" + }, + "name": "My action", + "secrets": "/ZW1x3z3KWzPB0H3ociZNVBeWJGQytz2P5OEUCmz4ahO7S8709DDJ+uC9ztGRig7e73GtCgT6XU42Cn8l8rm90INusLWf2oXaXbdGctjMDtTXv5S3b75YdEwIHClhLTLswmraGQHUwMsTMe+EVh9gUhwGsjDLTQAiDqsDonMto8XkTdtyP6C5axn" + }, + "namespace": "space1", + "references": [ + ], + "type": "action", + "updated_at": "2020-09-04T11:50:50.815Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:6ee9630a-a20e-44af-9465-217a3717d2ab", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:space_1_all_alerts_none_actions" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "5kB2ao8TYvwpj+CGsgnAmWvzvuPCt9Wu6q4d4Y56rTKbqO352RjiRG8dbvEzKcRveiMjfKYFOXs6dlh8cMIRBrBnF7z19+A1sq+RhrvMiqXFxbN+udBEwtUXNjgoaFH5Ajvm0t5Yg3f4XSGhaWc/n2URo6eXq/OkVsp/xjHs1rfGow5dojDEtotkFuvzPLstH2mBImYLwwejvw==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:51:04.584Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:space_1_all_alerts_none_actions" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e9c069d0-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:51:05.222Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e9c069d0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "params": "{\"alertId\":\"6ee9630a-a20e-44af-9465-217a3717d2ab\",\"spaceId\":\"space1\"}", + "retryAt": null, + "runAt": "2020-09-04T11:51:05.197Z", + "scheduledAt": "2020-09-04T11:51:05.197Z", + "scope": [ + "alerting" + ], + "startedAt": null, + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.197Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:b384be60-ec53-4b26-857e-0253ee55b277", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:superuser" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "Aj+qLwjCZ/WUeMUmLbe9xsf3/O4YAGGYg063WHGoPdftE7WKRxhn45ZUGu4SZ45BAloAsX113sCjIjhSwyVFTHG41lv0sGDLheviV/FwxmNnUDwX8Bn5PcbFw6n+eRsgBgXXC1qgNjhd1SfuEPd1BfmtjT58NLPN6G5NMKRAeWA7Tp5U25Aw6JYaPXwyFV5sfNhWi21pLs2fJQ==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:50:54.493Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:superuser" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e39a02f0-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:50:54.902Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e39a02f0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "ownerId": null, + "params": "{\"alertId\":\"b384be60-ec53-4b26-857e-0253ee55b277\",\"spaceId\":\"space1\"}", + "retryAt": null, + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:54.879Z", + "scope": [ + "alerting" + ], + "startedAt": null, + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:04.273Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:5cc59319-74ee-4edc-8646-a79ea91067cd", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:space_1_all_with_restricted_fixture" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "OFpEg7T+jq0nVoGkqHEN5W0fgcEu4LmCXCz6F5EzQ8mgxr2+270/nqv2+zSTnkef7rK2Xz2MDmQsdrRrsAFb5ItFb5YnR/zfqbq0+QBlvgqChKORE1+ggI1oQsYnjDtPWOqMwbUUNGYQXdyUZVdRqeeoYq847NgSo5UBoCJgEZ9ah1gzPA6XYvzbBtJdZ+QgCjfuXsrBSlImhQ==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:51:02.571Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:space_1_all_with_restricted_fixture" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e8885f00-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:51:03.175Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e8885f00-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"5cc59319-74ee-4edc-8646-a79ea91067cd\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:03.152Z", + "scheduledAt": "2020-09-04T11:51:03.152Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "running", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:d41a6abb-b93b-46df-a80a-926221ea847c", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:space_1_all" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "5qbDONVFRacd8IukMksh11NZ8OFxSN31sJRgIdRw4GHME2qKqyqczHz/CDANEjKF78M98yc5xSr8vNnco2PDOHTD7YfMdnlrl1/lOdIkJ/IM8YxGBCLZfk5hRx/dD3X84ZtSEZ2hYWjAemFKtD5GpTWtczhkx9jRgUMbPgBzrhyEmDJoHMQQSOEpIJKmQpAcpHGsE5/+SVIcGQ==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:50:58.533Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:space_1_all" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e616c2c0-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:50:59.083Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e616c2c0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"d41a6abb-b93b-46df-a80a-926221ea847c\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:59.052Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space1:alert:362e362b-a137-4aa2-9434-43e3d0d84a34", + "index": ".kibana_1", + "source": { + "alert": { + "actions": [ + { + "actionRef": "action_0", + "actionTypeId": "test.index-record", + "group": "default", + "params": { + "index": ".kibana-alerting-test-data", + "message": "alertId: {{alertId}},\nalertName: {{alertName}},\nspaceId: {{spaceId}},\ntags: {{tags}},\nalertInstanceId: {{alertInstanceId}},\ninstanceContextValue: {{context.instanceContextValue}},\ninstanceStateValue: {{state.instanceStateValue}}", + "reference": "alert:migrated-to-7.10:global_read" + } + } + ], + "alertTypeId": "test.always-firing", + "apiKey": "asFMeAR9MRRO5bwGiA5z3arqWOs9ZVPHYTvLL2S9JFRZSsA88yauEPxHeri3UpHWqLktDbOSpR4C1bmIQySDt1VOpe13JzGC/Z+/S0I9PaFQ8oHQ2VqjQ26j9hxuS+B9sXtAOhgYu2WBQw1ugeW2Eo86c9wpFux4p3SbKiNhwyC/9WUR5qI63cBnvRs8Zi8ePOMdnTJoiBzduw==", + "apiKeyOwner": "superuser", + "consumer": "alertsFixture", + "createdAt": "2020-09-04T11:50:56.513Z", + "createdBy": "superuser", + "enabled": true, + "muteAll": false, + "mutedInstanceIds": [ + ], + "name": "abc", + "params": { + "index": ".kibana-alerting-test-data", + "reference": "alert:migrated-to-7.10:global_read" + }, + "schedule": { + "interval": "2s" + }, + "scheduledTaskId": "e4df5430-eea4-11ea-a285-352ee3aecffa", + "tags": [ + "tag-A", + "tag-B" + ], + "throttle": "2s", + "updatedBy": "superuser" + }, + "migrationVersion": { + "alert": "7.9.0" + }, + "namespace": "space1", + "references": [ + { + "id": "17f38826-5a8d-4a76-975a-b496e7fffe0b", + "name": "action_0", + "type": "action" + } + ], + "type": "alert", + "updated_at": "2020-09-04T11:50:57.048Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e4df5430-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"362e362b-a137-4aa2-9434-43e3d0d84a34\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:57.011Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "running", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json new file mode 100644 index 0000000000000..68a60bec854c9 --- /dev/null +++ b/x-pack/test/functional/es_archives/alerts_legacy/mappings.json @@ -0,0 +1,2680 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "6e96ac5e648f57523879661ea72525b7", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "ee7356e3d77d357fe62a10350eed4b3c", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "43b8830d5d0df85a6823d290885fc9fd", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "cases": "32aa96a6d3855ddda53010ae2048ac22", + "cases-comments": "c2061fb929f585df57425102fa928b4b", + "cases-configure": "42711cbb311976c0687853f4c1354572", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "dashboard": "74eb4b909f81222fa1ddeaba2881a37e", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", + "epm-packages": "8f6e0b09ea0374c4ffe98c3755373cff", + "exception-list": "497afa2f881a675d72d58e20057f3d8b", + "exception-list-agnostic": "497afa2f881a675d72d58e20057f3d8b", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "fleet-agent-actions": "e520c855577170c24481be05c3ae14ec", + "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", + "fleet-agents": "6012d61d15e72564e47fc3402332756e", + "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "45915a1ad866812242df474eb0479052", + "infrastructure-ui-source": "2b2809653635caf490c93f090502d04c", + "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", + "ingest-outputs": "8aa988c376e65443fefc26f1075e93a3", + "ingest-package-policies": "f74dfe498e1849267cda41580b2be110", + "ingest_manager_settings": "012cf278ec84579495110bb827d1ed09", + "inventory-view": "88fc7e12fd1b45b6f0787323ce4f18d2", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "52346cfec69ff7b47d5f0c12361a2797", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "4a05b35c3a3a58fbc72dd0202dc3487f", + "maps-telemetry": "5ef305b18111b77789afefbd36b66171", + "metrics-explorer-view": "a8df1d270ee48c969d22d23812d08187", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "7f9e077078cab612f6a58e3bfdedb71a", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", + "siem-ui-timeline": "94bc38c7a421d15fbfe8ea565370a421", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "44d6bd48a1a653bcb60ea01614b9e3c9", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "params": { + "enabled": false, + "type": "object" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "app_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad-template": { + "dynamic": "false", + "properties": { + "help": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "properties": { + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { + "properties": { + "action": { + "type": "keyword" + }, + "action_at": { + "type": "date" + }, + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "index": false, + "type": "keyword" + }, + "created": { + "index": false, + "type": "date" + }, + "decodedSha256": { + "index": false, + "type": "keyword" + }, + "decodedSize": { + "index": false, + "type": "long" + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "index": false, + "type": "long" + }, + "encryptionAlgorithm": { + "index": false, + "type": "keyword" + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "ids": { + "index": false, + "type": "keyword" + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "index": false, + "type": "keyword" + } + } + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "enabled": false, + "type": "object" + }, + "installed_es": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "installed_kibana": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "internal": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "removable": { + "type": "boolean" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "fleet-agent-actions": { + "properties": { + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "binary" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agent-events": { + "properties": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "policy_id": { + "type": "keyword" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "current_error_events": { + "index": false, + "type": "text" + }, + "default_api_key": { + "type": "binary" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_checkin_status": { + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "packages": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "shared_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "unenrolled_at": { + "type": "date" + }, + "unenrollment_started_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, + "version": { + "type": "keyword" + } + } + }, + "fleet-enrollment-api-keys": { + "properties": { + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "properties": { + "description": { + "type": "text" + }, + "fields": { + "properties": { + "container": { + "type": "keyword" + }, + "host": { + "type": "keyword" + }, + "pod": { + "type": "keyword" + }, + "tiebreaker": { + "type": "keyword" + }, + "timestamp": { + "type": "keyword" + } + } + }, + "inventoryDefaultView": { + "type": "keyword" + }, + "logAlias": { + "type": "keyword" + }, + "logColumns": { + "properties": { + "fieldColumn": { + "properties": { + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "messageColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "timestampColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "metricAlias": { + "type": "keyword" + }, + "metricsExplorerDefaultView": { + "type": "keyword" + }, + "name": { + "type": "text" + } + } + }, + "ingest-agent-policies": { + "properties": { + "description": { + "type": "text" + }, + "is_default": { + "type": "boolean" + }, + "monitoring_enabled": { + "index": false, + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package_policies": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "index": false, + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "enabled": false, + "properties": { + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "streams": { + "properties": { + "compiled_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "index": false, + "type": "boolean" + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_url": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, + "inventory-view": { + "properties": { + "accountId": { + "type": "keyword" + }, + "autoBounds": { + "type": "boolean" + }, + "autoReload": { + "type": "boolean" + }, + "boundsOverride": { + "properties": { + "max": { + "type": "integer" + }, + "min": { + "type": "integer" + } + } + }, + "customMetrics": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "label": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "customOptions": { + "properties": { + "field": { + "type": "keyword" + }, + "text": { + "type": "keyword" + } + }, + "type": "nested" + }, + "filterQuery": { + "properties": { + "expression": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + } + } + }, + "groupBy": { + "properties": { + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + }, + "legend": { + "properties": { + "palette": { + "type": "keyword" + }, + "reverseColors": { + "type": "boolean" + }, + "steps": { + "type": "long" + } + } + }, + "metric": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "label": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "nodeType": { + "type": "keyword" + }, + "region": { + "type": "keyword" + }, + "sort": { + "properties": { + "by": { + "type": "keyword" + }, + "direction": { + "type": "keyword" + } + } + }, + "time": { + "type": "long" + }, + "view": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "expression": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "enabled": false, + "type": "object" + }, + "metrics-explorer-view": { + "properties": { + "chartOptions": { + "properties": { + "stack": { + "type": "boolean" + }, + "type": { + "type": "keyword" + }, + "yAxisMode": { + "type": "keyword" + } + } + }, + "currentTimerange": { + "properties": { + "from": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "to": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "options": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "filterQuery": { + "type": "keyword" + }, + "forceInterval": { + "type": "boolean" + }, + "groupBy": { + "type": "keyword" + }, + "limit": { + "type": "integer" + }, + "metrics": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "color": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + }, + "source": { + "type": "keyword" + } + } + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "alert": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "config": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "index": false, + "type": "text" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "properties": { + "errorMessage": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "indexName": { + "type": "keyword" + }, + "lastCompletedStep": { + "type": "long" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } + } + } + } + }, + "reindexTaskId": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "uptime-dynamic-settings": { + "dynamic": "false", + "type": "object" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "savedSearchRefName": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "index": false, + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "index": false, + "type": "text" + } + } + }, + "workplace_search_telemetry": { + "dynamic": "false", + "type": "object" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".kibana_task_manager": { + } + }, + "index": ".kibana_task_manager_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "task": "235412e52d09e7165fac8a67a43ad6b4", + "type": "2f4316de49999235636386fe51dc06c1", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0" + } + }, + "dynamic": "strict", + "properties": { + "migrationVersion": { + "dynamic": "true", + "properties": { + "task": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "task": { + "properties": { + "attempts": { + "type": "integer" + }, + "ownerId": { + "type": "keyword" + }, + "params": { + "type": "text" + }, + "retryAt": { + "type": "date" + }, + "runAt": { + "type": "date" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledAt": { + "type": "date" + }, + "scope": { + "type": "keyword" + }, + "startedAt": { + "type": "date" + }, + "state": { + "type": "text" + }, + "status": { + "type": "keyword" + }, + "taskType": { + "type": "keyword" + }, + "user": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file