From 1352e529d45bd24c208403bc74bfdb6df4776bd9 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 10:30:33 +0300 Subject: [PATCH 01/45] Add isSystemAction to ActionResult type and default it to false --- .../server/action_type_registry.test.ts | 1 + .../actions/server/actions_client.test.ts | 22 +++++++++++++++++++ .../plugins/actions/server/actions_client.ts | 6 +++++ .../server/create_execute_function.test.ts | 6 +++++ .../create_unsecured_execute_function.test.ts | 7 ++++++ .../server/lib/action_executor.test.ts | 2 ++ .../lib/is_connector_deprecated.test.ts | 3 ++- x-pack/plugins/actions/server/plugin.ts | 1 + .../alert_history_es_index.ts | 1 + .../actions/server/routes/create.test.ts | 5 +++++ .../plugins/actions/server/routes/create.ts | 2 ++ .../plugins/actions/server/routes/get.test.ts | 5 +++++ x-pack/plugins/actions/server/routes/get.ts | 2 ++ .../plugins/actions/server/routes/get_all.ts | 2 ++ .../server/routes/legacy/create.test.ts | 3 +++ .../actions/server/routes/legacy/get.test.ts | 4 ++++ .../server/routes/legacy/update.test.ts | 3 +++ .../actions/server/routes/update.test.ts | 4 ++++ .../plugins/actions/server/routes/update.ts | 2 ++ .../action_task_params_migrations.test.ts | 1 + x-pack/plugins/actions/server/types.ts | 1 + .../server/usage/actions_telemetry.test.ts | 5 +++++ 22 files changed, 87 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 73911db9cece4..589ff24c77420 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -45,6 +45,7 @@ describe('actionTypeRegistry', () => { secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ], }; diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index e09e3adf93140..14a295cfec325 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -385,6 +385,7 @@ describe('create()', () => { expect(result).toEqual({ id: '1', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, name: 'my name', actionTypeId: 'my-action-type', @@ -532,6 +533,7 @@ describe('create()', () => { expect(result).toEqual({ id: '1', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, name: 'my name', actionTypeId: 'my-action-type', @@ -722,6 +724,7 @@ describe('create()', () => { }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -799,6 +802,7 @@ describe('get()', () => { }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -860,6 +864,7 @@ describe('get()', () => { }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -950,6 +955,7 @@ describe('get()', () => { expect(result).toEqual({ id: '1', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }); expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); @@ -983,6 +989,7 @@ describe('get()', () => { }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -999,6 +1006,7 @@ describe('get()', () => { actionTypeId: '.slack', isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); @@ -1057,6 +1065,7 @@ describe('getAll()', () => { secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -1200,6 +1209,7 @@ describe('getAll()', () => { secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -1214,6 +1224,7 @@ describe('getAll()', () => { { id: '1', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, name: 'test', config: { @@ -1226,6 +1237,7 @@ describe('getAll()', () => { id: 'testPreconfigured', actionTypeId: '.slack', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'test', referencedByCount: 2, @@ -1283,6 +1295,7 @@ describe('getBulk()', () => { secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -1420,6 +1433,7 @@ describe('getBulk()', () => { secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -1438,6 +1452,7 @@ describe('getBulk()', () => { }, id: 'testPreconfigured', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'test', secrets: {}, @@ -1450,6 +1465,7 @@ describe('getBulk()', () => { id: '1', isMissingSecrets: false, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, name: 'test', }, @@ -1479,6 +1495,7 @@ describe('getOAuthAccessToken()', () => { actionTypeId: '.slack', secrets: {}, isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'test', config: { @@ -1982,6 +1999,7 @@ describe('update()', () => { expect(result).toEqual({ id: 'my-action', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, actionTypeId: 'my-action-type', isMissingSecrets: false, @@ -2060,6 +2078,7 @@ describe('update()', () => { expect(result).toEqual({ id: 'my-action', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, actionTypeId: 'my-action-type', isMissingSecrets: true, @@ -2217,6 +2236,7 @@ describe('update()', () => { expect(result).toEqual({ id: 'my-action', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, actionTypeId: 'my-action-type', isMissingSecrets: true, @@ -2703,6 +2723,7 @@ describe('isPreconfigured()', () => { }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', @@ -2742,6 +2763,7 @@ describe('isPreconfigured()', () => { }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'test', config: { foo: 'bar', diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 3efae4952692a..7905b7a0da6df 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -260,6 +260,7 @@ export class ActionsClient { name: result.attributes.name, config: result.attributes.config, isPreconfigured: false, + isSystemAction: false, isDeprecated: isConnectorDeprecated(result.attributes), }; } @@ -357,6 +358,7 @@ export class ActionsClient { name: result.attributes.name as string, config: result.attributes.config as Record, isPreconfigured: false, + isSystemAction: false, isDeprecated: isConnectorDeprecated(result.attributes), }; } @@ -394,6 +396,7 @@ export class ActionsClient { actionTypeId: preconfiguredActionsList.actionTypeId, name: preconfiguredActionsList.name, isPreconfigured: true, + isSystemAction: false, isDeprecated: isConnectorDeprecated(preconfiguredActionsList), }; } @@ -414,6 +417,7 @@ export class ActionsClient { name: result.attributes.name, config: result.attributes.config, isPreconfigured: false, + isSystemAction: false, isDeprecated: isConnectorDeprecated(result.attributes), }; } @@ -460,6 +464,7 @@ export class ActionsClient { name: preconfiguredAction.name, isPreconfigured: true, isDeprecated: isConnectorDeprecated(preconfiguredAction), + isSystemAction: false, })), ].sort((a, b) => a.name.localeCompare(b.name)); return await injectExtraFindData(this.kibanaIndices, this.scopedClusterClient, mergedResult); @@ -893,6 +898,7 @@ function actionFromSavedObject( ...savedObject.attributes, isPreconfigured: false, isDeprecated, + isSystemAction: 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 e4787101b1cde..ec746ee62862b 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -254,6 +254,7 @@ describe('execute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -333,6 +334,7 @@ describe('execute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -519,6 +521,7 @@ describe('execute()', () => { name: 'Slack #xyz', secrets: {}, isPreconfigured: true, + isSystemAction: false, isDeprecated: false, }, ], @@ -829,6 +832,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -925,6 +929,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -1143,6 +1148,7 @@ describe('bulkExecute()', () => { secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ], }); diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts index cd50be7558fea..8e75a889c3410 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts @@ -32,6 +32,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -137,6 +138,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -254,6 +256,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -395,6 +398,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -430,6 +434,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -466,6 +471,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, @@ -475,6 +481,7 @@ describe('bulkExecute()', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, name: 'x', secrets: {}, }, diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 8323428395e88..9b2e2f96390c7 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -64,6 +64,7 @@ actionExecutor.initialize({ }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ], }); @@ -988,6 +989,7 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f }, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ], }); diff --git a/x-pack/plugins/actions/server/lib/is_connector_deprecated.test.ts b/x-pack/plugins/actions/server/lib/is_connector_deprecated.test.ts index 8d2a698563b0c..8306421f76f79 100644 --- a/x-pack/plugins/actions/server/lib/is_connector_deprecated.test.ts +++ b/x-pack/plugins/actions/server/lib/is_connector_deprecated.test.ts @@ -14,7 +14,8 @@ describe('isConnectorDeprecated', () => { name: 'Test', config: { apiUrl: 'http://example.com', usesTableApi: false }, secrets: { username: 'test', password: 'test' }, - isPreconfigured: false as const, + isPreconfigured: false, + isSystemAction: false, }; it('returns false if the config is not defined', () => { diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 8e44714129557..ba4fec697b0c7 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -253,6 +253,7 @@ export class ActionsPlugin implements Plugin { id: AlertHistoryEsIndexConnectorId, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, config: { index: AlertHistoryDefaultIndexName, }, diff --git a/x-pack/plugins/actions/server/routes/create.test.ts b/x-pack/plugins/actions/server/routes/create.test.ts index dc06a119ca2fa..2161e68e3706b 100644 --- a/x-pack/plugins/actions/server/routes/create.test.ts +++ b/x-pack/plugins/actions/server/routes/create.test.ts @@ -41,6 +41,7 @@ describe('createActionRoute', () => { isPreconfigured: false, isDeprecated: false, isMissingSecrets: false, + isSystemAction: false, }; const createApiResult = { @@ -49,11 +50,13 @@ describe('createActionRoute', () => { 'isPreconfigured', 'isDeprecated', 'isMissingSecrets', + 'isSystemAction', ]), connector_type_id: createResult.actionTypeId, is_preconfigured: createResult.isPreconfigured, is_deprecated: createResult.isDeprecated, is_missing_secrets: createResult.isMissingSecrets, + is_system_action: createResult.isSystemAction, }; const actionsClient = actionsClientMock.create(); @@ -113,6 +116,7 @@ describe('createActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments( @@ -153,6 +157,7 @@ describe('createActionRoute', () => { isMissingSecrets: false, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments( diff --git a/x-pack/plugins/actions/server/routes/create.ts b/x-pack/plugins/actions/server/routes/create.ts index c55661ce7b921..c3928e72d042b 100644 --- a/x-pack/plugins/actions/server/routes/create.ts +++ b/x-pack/plugins/actions/server/routes/create.ts @@ -35,6 +35,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ isPreconfigured, isDeprecated, isMissingSecrets, + isSystemAction, ...res }) => ({ ...res, @@ -42,6 +43,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, }); export const createActionRoute = ( diff --git a/x-pack/plugins/actions/server/routes/get.test.ts b/x-pack/plugins/actions/server/routes/get.test.ts index 7f9736ad7dfb2..76dda602945c9 100644 --- a/x-pack/plugins/actions/server/routes/get.test.ts +++ b/x-pack/plugins/actions/server/routes/get.test.ts @@ -40,6 +40,7 @@ describe('getActionRoute', () => { isPreconfigured: false, isDeprecated: false, isMissingSecrets: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -62,6 +63,7 @@ describe('getActionRoute', () => { "is_deprecated": false, "is_missing_secrets": false, "is_preconfigured": false, + "is_system_action": false, "name": "action name", }, } @@ -79,6 +81,7 @@ describe('getActionRoute', () => { is_preconfigured: false, is_deprecated: false, is_missing_secrets: false, + is_system_action: false, }, }); }); @@ -99,6 +102,7 @@ describe('getActionRoute', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments( @@ -134,6 +138,7 @@ describe('getActionRoute', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments( diff --git a/x-pack/plugins/actions/server/routes/get.ts b/x-pack/plugins/actions/server/routes/get.ts index 207ed694f2985..82aad7d371c72 100644 --- a/x-pack/plugins/actions/server/routes/get.ts +++ b/x-pack/plugins/actions/server/routes/get.ts @@ -21,6 +21,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ isPreconfigured, isMissingSecrets, isDeprecated, + isSystemAction, ...res }) => ({ ...res, @@ -28,6 +29,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, }); export const getActionRoute = ( diff --git a/x-pack/plugins/actions/server/routes/get_all.ts b/x-pack/plugins/actions/server/routes/get_all.ts index 61da031db99d4..dec70257313e1 100644 --- a/x-pack/plugins/actions/server/routes/get_all.ts +++ b/x-pack/plugins/actions/server/routes/get_all.ts @@ -19,6 +19,7 @@ const rewriteBodyRes: RewriteResponseCase = (results) => { isDeprecated, referencedByCount, isMissingSecrets, + isSystemAction, ...res }) => ({ ...res, @@ -27,6 +28,7 @@ const rewriteBodyRes: RewriteResponseCase = (results) => { is_deprecated: isDeprecated, referenced_by_count: referencedByCount, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, }) ); }; diff --git a/x-pack/plugins/actions/server/routes/legacy/create.test.ts b/x-pack/plugins/actions/server/routes/legacy/create.test.ts index 656c24d118d30..0d285244342a6 100644 --- a/x-pack/plugins/actions/server/routes/legacy/create.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/create.test.ts @@ -48,6 +48,7 @@ describe('createActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -105,6 +106,7 @@ describe('createActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); @@ -134,6 +136,7 @@ describe('createActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments({ actionsClient }, {}); diff --git a/x-pack/plugins/actions/server/routes/legacy/get.test.ts b/x-pack/plugins/actions/server/routes/legacy/get.test.ts index a79185a4ea882..be5cc1c819e3a 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get.test.ts @@ -47,6 +47,7 @@ describe('getActionRoute', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -68,6 +69,7 @@ describe('getActionRoute', () => { "id": "1", "isDeprecated": false, "isPreconfigured": false, + "isSystemAction": false, "name": "action name", }, } @@ -97,6 +99,7 @@ describe('getActionRoute', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments( @@ -132,6 +135,7 @@ describe('getActionRoute', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }); const [context, req, res] = mockHandlerArguments( diff --git a/x-pack/plugins/actions/server/routes/legacy/update.test.ts b/x-pack/plugins/actions/server/routes/legacy/update.test.ts index 49bb0ff2b86e0..9158b4165a660 100644 --- a/x-pack/plugins/actions/server/routes/legacy/update.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/update.test.ts @@ -47,6 +47,7 @@ describe('updateActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -105,6 +106,7 @@ describe('updateActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -149,6 +151,7 @@ describe('updateActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); diff --git a/x-pack/plugins/actions/server/routes/update.test.ts b/x-pack/plugins/actions/server/routes/update.test.ts index d22d16798376e..ac9f3dc45f660 100644 --- a/x-pack/plugins/actions/server/routes/update.test.ts +++ b/x-pack/plugins/actions/server/routes/update.test.ts @@ -39,6 +39,7 @@ describe('updateActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -67,6 +68,7 @@ describe('updateActionRoute', () => { config: { foo: true }, is_preconfigured: false, is_deprecated: false, + is_system_action: false, }, }); @@ -106,6 +108,7 @@ describe('updateActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); @@ -150,6 +153,7 @@ describe('updateActionRoute', () => { config: { foo: true }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const actionsClient = actionsClientMock.create(); diff --git a/x-pack/plugins/actions/server/routes/update.ts b/x-pack/plugins/actions/server/routes/update.ts index b0d233c506767..a44c8ea7bb5b9 100644 --- a/x-pack/plugins/actions/server/routes/update.ts +++ b/x-pack/plugins/actions/server/routes/update.ts @@ -31,6 +31,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ isPreconfigured, isMissingSecrets, isDeprecated, + isSystemAction, ...res }) => ({ ...res, @@ -38,6 +39,7 @@ const rewriteBodyRes: RewriteResponseCase = ({ is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, }); export const updateActionRoute = ( diff --git a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts index 3b19a9feb15a6..7ef21c8fc84bd 100644 --- a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts @@ -28,6 +28,7 @@ const preconfiguredActions = [ secrets: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ]; diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 4b568b949bef5..061cdd2db3d90 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -81,6 +81,7 @@ export interface ActionResult Date: Wed, 28 Jun 2023 16:25:47 +0300 Subject: [PATCH 02/45] Disallow registering system actions --- .../plugins/actions/server/action_type_registry.ts | 12 ++++++++++++ x-pack/plugins/actions/server/types.ts | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 09f7fa5370fc0..8404bafbd8994 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -104,6 +104,18 @@ export class ActionTypeRegistry { Params extends ActionTypeParams = ActionTypeParams, ExecutorResultData = void >(actionType: ActionType) { + // TODO: Remove when system action are supported + if (actionType.isSystemAction) { + throw new Error( + i18n.translate( + 'xpack.actions.actionTypeRegistry.register.systemActionsNotSupportedErrorMessage', + { + defaultMessage: 'System actions are not supported', + } + ) + ); + } + if (this.has(actionType.id)) { throw new Error( i18n.translate( diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 061cdd2db3d90..54f5f8327be65 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -140,9 +140,8 @@ export interface ActionType< secrets: ValidatorType; connector?: (config: Config, secrets: Secrets) => string | null; }; - + isSystemAction?: boolean; renderParameterTemplates?: RenderParameterTemplates; - executor: ExecutorType; } From dc1a2c8692df8762e1582e230ce7950f8929e6d4 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 18:18:32 +0300 Subject: [PATCH 03/45] Add test --- .../server/action_type_registry.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 589ff24c77420..595461b137b59 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -216,6 +216,26 @@ describe('actionTypeRegistry', () => { }); expect(actionTypeRegistryParams.licensing.featureUsage.register).not.toHaveBeenCalled(); }); + + test('does not allows registering system actions', () => { + const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + + expect(() => + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + isSystemAction: true, + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }) + ).toThrowErrorMatchingInlineSnapshot(`"System actions are not supported"`); + }); }); describe('get()', () => { From bb7e9a57c56480ca600670935ca42585b78fd557 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 18:27:20 +0300 Subject: [PATCH 04/45] Fix alerting types --- .../server/rules_client/tests/bulk_edit.test.ts | 1 + .../server/rules_client/tests/create.test.ts | 8 ++++++++ .../alerting/server/rules_client/tests/lib.ts | 3 +++ .../server/rules_client/tests/update.test.ts | 14 ++++++++++++++ 4 files changed, 26 insertions(+) diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index 8a4978e78f28e..1f6e86945b1b5 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -161,6 +161,7 @@ describe('bulkEdit()', () => { isMissingSecrets: false, name: 'email connector', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }, ]); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index 7e21881410e74..8de37e55421e1 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -132,6 +132,7 @@ describe('create()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); taskManager.schedule.mockResolvedValue({ @@ -740,6 +741,7 @@ describe('create()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -756,6 +758,7 @@ describe('create()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -912,6 +915,7 @@ describe('create()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -928,6 +932,7 @@ describe('create()', () => { name: 'another email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: 'preconfigured', @@ -944,6 +949,7 @@ describe('create()', () => { name: 'preconfigured email connector', isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ]); actionsClient.isPreconfigured.mockReset(); @@ -2658,6 +2664,7 @@ describe('create()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( @@ -3163,6 +3170,7 @@ describe('create()', () => { name: 'Slack connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts index 337c09e087243..25afdedf54d3c 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts @@ -77,6 +77,7 @@ export function getBeforeSetup( { id: '1', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, actionTypeId: 'test', name: 'test', @@ -87,6 +88,7 @@ export function getBeforeSetup( { id: '2', isPreconfigured: false, + isSystemAction: false, isDeprecated: false, actionTypeId: 'test2', name: 'test2', @@ -98,6 +100,7 @@ export function getBeforeSetup( id: 'testPreconfigured', actionTypeId: '.slack', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'test', }, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 775df48e8fc02..51717f8bb7839 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -151,6 +151,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); rulesClientParams.getActionsClient.mockResolvedValue(actionsClient); @@ -200,6 +201,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -216,6 +218,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -476,6 +479,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -492,6 +496,7 @@ describe('update()', () => { name: 'another email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: 'preconfigured', @@ -508,6 +513,7 @@ describe('update()', () => { name: 'preconfigured email connector', isPreconfigured: true, isDeprecated: false, + isSystemAction: false, }, ]); actionsClient.isPreconfigured.mockReset(); @@ -1438,6 +1444,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -1454,6 +1461,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -2024,6 +2032,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -2040,6 +2049,7 @@ describe('update()', () => { name: 'another connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); @@ -2098,6 +2108,7 @@ describe('update()', () => { name: 'slack connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); actionsClient.isPreconfigured.mockReset(); @@ -2259,6 +2270,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '2', @@ -2275,6 +2287,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -2610,6 +2623,7 @@ describe('update()', () => { name: 'email connector', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ From 774b389ffebbd0028a7e1fe786c4ddf233ae19e8 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 18:49:57 +0300 Subject: [PATCH 05/45] Fix integration tests --- .../group2/tests/actions/connector_types/cases_webhook.ts | 2 ++ .../spaces_only/tests/actions/create.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts index 154b693ad6533..de21333275327 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts @@ -112,6 +112,7 @@ export default function casesWebhookTest({ getService }: FtrProviderContext) { name: 'A casesWebhook action', connector_type_id: '.cases-webhook', is_missing_secrets: false, + is_system_action: false, config: simulatorConfig, }); @@ -126,6 +127,7 @@ export default function casesWebhookTest({ getService }: FtrProviderContext) { name: 'A casesWebhook action', connector_type_id: '.cases-webhook', is_missing_secrets: false, + is_system_action: false, config: simulatorConfig, }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts index bf5318509bf99..a7239343b2c61 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts @@ -43,6 +43,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { name: 'My action', connector_type_id: 'test.index-record', is_missing_secrets: false, + is_system_action: false, config: { unencrypted: `This value shouldn't get encrypted`, }, @@ -104,6 +105,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { name: 'My action', actionTypeId: 'test.index-record', isMissingSecrets: false, + isSystemAction: false, config: { unencrypted: `This value shouldn't get encrypted`, }, From 83ee74702594c4e2ea1c669c242aa331e249fb32 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 19:45:38 +0300 Subject: [PATCH 06/45] Fix triggers_actions_ui types --- x-pack/plugins/cases/public/common/mock/connectors.ts | 5 +++++ .../public/components/all_cases/use_cases_columns.test.tsx | 1 + .../components/configure_cases/connectors_dropdown.test.tsx | 1 + x-pack/plugins/cases/public/components/connectors/mock.ts | 1 + x-pack/plugins/cases/public/components/utils.test.ts | 1 + .../application/lib/action_connector_api/connectors.ts | 2 ++ .../public/application/lib/action_connector_api/create.ts | 2 ++ .../application/lib/action_connector_api/update.test.ts | 2 ++ .../public/application/lib/action_connector_api/update.ts | 2 ++ .../application/lib/check_action_type_enabled.test.tsx | 2 ++ .../public/application/lib/value_validators.test.ts | 3 +++ .../sections/action_connector_form/action_type_form.test.tsx | 3 +++ .../action_connector_form/connectors_selection.test.tsx | 1 + .../edit_connector_flyout/index.test.tsx | 1 + .../components/actions_connectors_list.test.tsx | 1 + x-pack/plugins/triggers_actions_ui/public/types.ts | 1 + 16 files changed, 29 insertions(+) diff --git a/x-pack/plugins/cases/public/common/mock/connectors.ts b/x-pack/plugins/cases/public/common/mock/connectors.ts index 4d733c4e660a8..3485af438a4d9 100644 --- a/x-pack/plugins/cases/public/common/mock/connectors.ts +++ b/x-pack/plugins/cases/public/common/mock/connectors.ts @@ -19,6 +19,7 @@ export const connectorsMock: ActionConnector[] = [ apiUrl: 'https://instance1.service-now.com', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }, { @@ -30,6 +31,7 @@ export const connectorsMock: ActionConnector[] = [ orgId: '201', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }, { @@ -40,6 +42,7 @@ export const connectorsMock: ActionConnector[] = [ apiUrl: 'https://instance.atlassian.ne', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }, { @@ -50,6 +53,7 @@ export const connectorsMock: ActionConnector[] = [ apiUrl: 'https://instance1.service-now.com', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }, { @@ -61,6 +65,7 @@ export const connectorsMock: ActionConnector[] = [ usesTableApi: true, }, isPreconfigured: false, + isSystemAction: false, isDeprecated: true, }, ]; diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx index 07163b1e1eb4f..42a5ea4c1d1fc 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx @@ -791,6 +791,7 @@ describe('useCasesColumns ', () => { name: 'None', config: {}, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }, ]} diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx index b659e5b187520..d36ba676357cf 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx @@ -252,6 +252,7 @@ describe('ConnectorsDropdown', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]} />, diff --git a/x-pack/plugins/cases/public/components/connectors/mock.ts b/x-pack/plugins/cases/public/components/connectors/mock.ts index c4739002e6f8c..ef2b1c08eeb52 100644 --- a/x-pack/plugins/cases/public/components/connectors/mock.ts +++ b/x-pack/plugins/cases/public/components/connectors/mock.ts @@ -14,6 +14,7 @@ export const connector = { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; export const swimlaneConnector = { diff --git a/x-pack/plugins/cases/public/components/utils.test.ts b/x-pack/plugins/cases/public/components/utils.test.ts index 3866b2503d63f..6bef0b8585d4b 100644 --- a/x-pack/plugins/cases/public/components/utils.test.ts +++ b/x-pack/plugins/cases/public/components/utils.test.ts @@ -28,6 +28,7 @@ describe('Utils', () => { config: { usesTableApi: false }, secrets: {}, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts index c08232ca5eea5..60f1e25295686 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts @@ -25,6 +25,7 @@ const transformConnector: RewriteRequestCase< is_deprecated: isDeprecated, referenced_by_count: referencedByCount, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, ...res }) => ({ actionTypeId, @@ -32,6 +33,7 @@ const transformConnector: RewriteRequestCase< isDeprecated, referencedByCount, isMissingSecrets, + isSystemAction, ...res, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts index 287c27a1c0e3f..1256497a5d448 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts @@ -27,6 +27,7 @@ const rewriteBodyRes: RewriteRequestCase< is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, ...res }) => ({ ...res, @@ -34,6 +35,7 @@ const rewriteBodyRes: RewriteRequestCase< isPreconfigured, isDeprecated, isMissingSecrets, + isSystemAction, }); export async function createActionConnector({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts index 957f682d1fd6c..1030e3375aa07 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts @@ -20,6 +20,7 @@ describe('updateActionConnector', () => { connector_type_id: 'te/st', is_preconfigured: false, is_deprecated: false, + is_system_action: false, name: 'My test', config: {}, secrets: {}, @@ -31,6 +32,7 @@ describe('updateActionConnector', () => { actionTypeId: 'te/st', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, name: 'My test', config: {}, secrets: {}, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts index 34aa1e127ad95..7b65b53443198 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts @@ -20,6 +20,7 @@ const rewriteBodyRes: RewriteRequestCase< is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, ...res }) => ({ ...res, @@ -27,6 +28,7 @@ const rewriteBodyRes: RewriteRequestCase< isPreconfigured, isDeprecated, isMissingSecrets, + isSystemAction, }); export async function updateActionConnector({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx index 7152f5455a72c..e2dc91214e783 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx @@ -102,6 +102,7 @@ describe('checkActionFormActionTypeEnabled', () => { actionTypeId: '1', id: 'test1', isPreconfigured: true, + isSystemAction: false, isDeprecated: true, name: 'test', referencedByCount: 0, @@ -111,6 +112,7 @@ describe('checkActionFormActionTypeEnabled', () => { id: 'test2', isPreconfigured: true, isDeprecated: true, + isSystemAction: false, name: 'test', referencedByCount: 0, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts index 4fdd9fe0893ab..97c9ba686435a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts @@ -113,6 +113,7 @@ describe('getConnectorWithInvalidatedFields', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const secretsErrors = { webhookUrl: ['Webhook URL is required.'] }; const configErrors = {}; @@ -130,6 +131,7 @@ describe('getConnectorWithInvalidatedFields', () => { config: {} as any, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const secretsErrors = {}; const configErrors = { apiUrl: ['apiUrl is required'] }; @@ -149,6 +151,7 @@ describe('getConnectorWithInvalidatedFields', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; const secretsErrors = { webhookUrl: ['Webhook URL must start with https://.'] }; const configErrors = {}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index 171636d060c9e..807e5454f094f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -556,6 +556,7 @@ function getActionTypeForm({ id: 'test', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, name: 'test name', secrets: {}, }; @@ -579,6 +580,7 @@ function getActionTypeForm({ id: 'test', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, name: 'test name', secrets: {}, }, @@ -588,6 +590,7 @@ function getActionTypeForm({ actionTypeId: '.server-log', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, config: {}, secrets: {}, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx index 97742e22a9ae4..230f81e153162 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx @@ -69,6 +69,7 @@ describe('connectors_selection', () => { id: 'testId', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, name: 'test pagerduty', secrets: {}, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx index e13465b356f8e..eed385f7ef11f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx @@ -40,6 +40,7 @@ const connector: ActionConnector = { isDeprecated: false, isPreconfigured: false, isMissingSecrets: false, + isSystemAction: false, }; describe('EditConnectorFlyout', () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index ce9e4e638b573..90f4bcc1e6d13 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -318,6 +318,7 @@ describe('actions_connectors_list', () => { description: `My test ${index}`, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, config: {}, })) diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 923f3847df078..3e4006e64ca7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -278,6 +278,7 @@ export interface ActionConnectorProps { config: Config; isPreconfigured: boolean; isDeprecated: boolean; + isSystemAction: boolean; isMissingSecrets?: boolean; } From d2b8e670f9f0d9ce0cbb12f33103b980e6230cb0 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 19:50:27 +0300 Subject: [PATCH 07/45] Fix cases types --- x-pack/plugins/cases/common/api/connectors/connector.ts | 1 + x-pack/plugins/cases/public/components/connectors/mock.ts | 1 + .../connectors/servicenow/use_get_choices.test.tsx | 1 + x-pack/plugins/cases/server/client/cases/utils.test.ts | 1 + x-pack/plugins/cases/server/client/configure/client.test.ts | 6 ++++++ 5 files changed, 10 insertions(+) diff --git a/x-pack/plugins/cases/common/api/connectors/connector.ts b/x-pack/plugins/cases/common/api/connectors/connector.ts index 66e5c5ada902b..ebdc1d5db9bb6 100644 --- a/x-pack/plugins/cases/common/api/connectors/connector.ts +++ b/x-pack/plugins/cases/common/api/connectors/connector.ts @@ -102,6 +102,7 @@ const ActionConnectorResultRt = rt.intersection([ name: rt.string, isDeprecated: rt.boolean, isPreconfigured: rt.boolean, + isSystemAction: rt.boolean, referencedByCount: rt.number, }), rt.exact(rt.partial({ config: rt.record(rt.string, rt.unknown), isMissingSecrets: rt.boolean })), diff --git a/x-pack/plugins/cases/public/components/connectors/mock.ts b/x-pack/plugins/cases/public/components/connectors/mock.ts index ef2b1c08eeb52..c66f11ebacff7 100644 --- a/x-pack/plugins/cases/public/components/connectors/mock.ts +++ b/x-pack/plugins/cases/public/components/connectors/mock.ts @@ -32,6 +32,7 @@ export const swimlaneConnector = { }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; export const issues = [ diff --git a/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx b/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx index c8182ce970595..06141f14dbeb8 100644 --- a/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/servicenow/use_get_choices.test.tsx @@ -30,6 +30,7 @@ const connector = { name: 'ServiceNow', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, config: { apiUrl: 'https://dev94428.service-now.com/', }, diff --git a/x-pack/plugins/cases/server/client/cases/utils.test.ts b/x-pack/plugins/cases/server/client/cases/utils.test.ts index 1efea16801c17..089af7f31c698 100644 --- a/x-pack/plugins/cases/server/client/cases/utils.test.ts +++ b/x-pack/plugins/cases/server/client/cases/utils.test.ts @@ -85,6 +85,7 @@ describe('utils', () => { }, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }; it('creates an external incident correctly for Jira', async () => { diff --git a/x-pack/plugins/cases/server/client/configure/client.test.ts b/x-pack/plugins/cases/server/client/configure/client.test.ts index e344273b51baa..1c2590251b90a 100644 --- a/x-pack/plugins/cases/server/client/configure/client.test.ts +++ b/x-pack/plugins/cases/server/client/configure/client.test.ts @@ -72,6 +72,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -81,6 +82,8 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, + referencedByCount: 1, }, { @@ -90,6 +93,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, ]; @@ -135,6 +139,7 @@ describe('client', () => { config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, ]); @@ -181,6 +186,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, ]); From 31f1f396c28bf8eb61e88ea1cbc8c3f157b8f41a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 19:58:13 +0300 Subject: [PATCH 08/45] Fix cases tests --- .../server/action_type_registry.test.ts | 2 +- .../common/api/connectors/connector.test.ts | 2 ++ .../server/client/configure/client.test.ts | 7 +++++ .../spaces_only/tests/actions/get_all.ts | 28 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 595461b137b59..613438579e13e 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -226,7 +226,7 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], - isSystemAction: true, + isSystemAction: false, validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, diff --git a/x-pack/plugins/cases/common/api/connectors/connector.test.ts b/x-pack/plugins/cases/common/api/connectors/connector.test.ts index 33208ab722252..39640b1607024 100644 --- a/x-pack/plugins/cases/common/api/connectors/connector.test.ts +++ b/x-pack/plugins/cases/common/api/connectors/connector.test.ts @@ -160,6 +160,7 @@ describe('Connector', () => { referencedByCount: 0, config: { foo: 'bar' }, isMissingSecrets: false, + isSystemAction: false, }, { id: 'test-2', @@ -167,6 +168,7 @@ describe('Connector', () => { name: 'My connector 2', isDeprecated: false, isPreconfigured: false, + isSystemAction: false, referencedByCount: 0, }, ]; diff --git a/x-pack/plugins/cases/server/client/configure/client.test.ts b/x-pack/plugins/cases/server/client/configure/client.test.ts index 1c2590251b90a..43e19a799b5e6 100644 --- a/x-pack/plugins/cases/server/client/configure/client.test.ts +++ b/x-pack/plugins/cases/server/client/configure/client.test.ts @@ -114,6 +114,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -122,6 +123,7 @@ describe('client', () => { name: '2', config: {}, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, referencedByCount: 1, }, @@ -152,6 +154,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -161,6 +164,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -169,6 +173,7 @@ describe('client', () => { name: 'sn-preconfigured', config: {}, isPreconfigured: true, + isSystemAction: false, isDeprecated: false, referencedByCount: 1, }, @@ -199,6 +204,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -208,6 +214,7 @@ describe('client', () => { config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, ]); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index b2b208843c60e..c34c314ade4c3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -53,6 +53,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.index', is_preconfigured: true, is_deprecated: false, + is_system_action: false, referenced_by_count: 0, }, { @@ -62,6 +63,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'My action', connector_type_id: 'test.index-record', is_missing_secrets: false, + is_system_action: false, config: { unencrypted: `This value shouldn't get encrypted`, }, @@ -72,6 +74,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'notification-email', is_deprecated: false, is_preconfigured: true, + is_system_action: false, name: 'Notification Email Connector', referenced_by_count: 0, }, @@ -79,6 +82,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'preconfigured-es-index-action', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: '.index', name: 'preconfigured_es_index_action', referenced_by_count: 0, @@ -88,6 +92,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow', is_deprecated: true, is_preconfigured: true, + is_system_action: false, name: 'ServiceNow#xyz', referenced_by_count: 0, }, @@ -96,6 +101,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow-default', is_preconfigured: true, is_deprecated: true, + is_system_action: false, name: 'ServiceNow#xyz', referenced_by_count: 0, }, @@ -104,6 +110,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { is_preconfigured: true, is_deprecated: false, connector_type_id: '.slack', + is_system_action: false, name: 'Slack#xyz', referenced_by_count: 0, }, @@ -112,6 +119,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { is_preconfigured: true, is_deprecated: false, connector_type_id: 'system-abc-action-type', + is_system_action: false, name: 'SystemABC', referenced_by_count: 0, }, @@ -120,6 +128,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { is_preconfigured: true, is_deprecated: false, connector_type_id: 'test.index-record', + is_system_action: false, name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, }, @@ -168,6 +177,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.index', is_preconfigured: true, is_deprecated: false, + is_system_action: false, referenced_by_count: 0, }, { @@ -175,6 +185,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'notification-email', is_deprecated: false, is_preconfigured: true, + is_system_action: false, name: 'Notification Email Connector', referenced_by_count: 0, }, @@ -182,6 +193,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'preconfigured-es-index-action', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: '.index', name: 'preconfigured_es_index_action', referenced_by_count: 0, @@ -191,6 +203,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow', is_deprecated: true, is_preconfigured: true, + is_system_action: false, name: 'ServiceNow#xyz', referenced_by_count: 0, }, @@ -199,6 +212,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow-default', is_preconfigured: true, is_deprecated: true, + is_system_action: false, name: 'ServiceNow#xyz', referenced_by_count: 0, }, @@ -206,6 +220,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-slack1', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: '.slack', name: 'Slack#xyz', referenced_by_count: 0, @@ -214,6 +229,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'custom-system-abc-connector', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: 'system-abc-action-type', name: 'SystemABC', referenced_by_count: 0, @@ -222,6 +238,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'preconfigured.test.index-record', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, @@ -230,6 +247,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-test-email', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: '.email', name: 'TestEmail#xyz', referenced_by_count: 0, @@ -271,6 +289,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Alert history Elasticsearch index', actionTypeId: '.index', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, referencedByCount: 0, }, @@ -278,6 +297,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: createdAction.id, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, name: 'My action', actionTypeId: 'test.index-record', isMissingSecrets: false, @@ -291,6 +311,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'notification-email', isDeprecated: false, isPreconfigured: true, + isSystemAction: false, name: 'Notification Email Connector', referencedByCount: 0, }, @@ -299,6 +320,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, isDeprecated: false, actionTypeId: '.index', + isSystemAction: false, name: 'preconfigured_es_index_action', referencedByCount: 0, }, @@ -307,6 +329,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow', isDeprecated: true, isPreconfigured: true, + isSystemAction: false, name: 'ServiceNow#xyz', referencedByCount: 0, }, @@ -315,6 +338,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow-default', isPreconfigured: true, isDeprecated: true, + isSystemAction: false, name: 'ServiceNow#xyz', referencedByCount: 0, }, @@ -323,6 +347,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, isDeprecated: false, actionTypeId: '.slack', + isSystemAction: false, name: 'Slack#xyz', referencedByCount: 0, }, @@ -331,6 +356,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, isDeprecated: false, actionTypeId: 'system-abc-action-type', + isSystemAction: false, name: 'SystemABC', referencedByCount: 0, }, @@ -339,6 +365,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, isDeprecated: false, actionTypeId: 'test.index-record', + isSystemAction: false, name: 'Test:_Preconfigured_Index_Record', referencedByCount: 0, }, @@ -347,6 +374,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { isPreconfigured: true, isDeprecated: false, actionTypeId: '.email', + isSystemAction: false, name: 'TestEmail#xyz', referencedByCount: 0, }, From 85ca6a4730de13c9b46f5b12ee468c5ea71dc5ec Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 20:54:40 +0300 Subject: [PATCH 09/45] Fix integration tests --- .../actions/connector_types/cases_webhook.ts | 4 +-- .../actions/connector_types/d3security.ts | 1 + .../tests/actions/connector_types/email.ts | 8 ++++++ .../tests/actions/connector_types/es_index.ts | 4 +++ .../tests/actions/connector_types/gen_ai.ts | 1 + .../tests/actions/connector_types/jira.ts | 2 ++ .../tests/actions/connector_types/opsgenie.ts | 1 + .../actions/connector_types/pagerduty.ts | 2 ++ .../actions/connector_types/resilient.ts | 2 ++ .../actions/connector_types/server_log.ts | 2 ++ .../connector_types/servicenow_itom.ts | 4 +++ .../connector_types/servicenow_itsm.ts | 4 +++ .../actions/connector_types/servicenow_sir.ts | 4 +++ .../actions/connector_types/slack_api.ts | 2 ++ .../actions/connector_types/slack_webhook.ts | 2 ++ .../tests/actions/connector_types/swimlane.ts | 2 ++ .../tests/actions/connector_types/tines.ts | 1 + .../tests/actions/connector_types/torq.ts | 1 + .../tests/actions/connector_types/webhook.ts | 4 +++ .../tests/actions/connector_types/xmatters.ts | 2 ++ .../group2/tests/actions/create.ts | 2 ++ .../group2/tests/actions/get.ts | 2 ++ .../group2/tests/actions/get_all.ts | 26 +++++++++++++++++++ .../actions/sub_action_framework/index.ts | 1 + .../group2/tests/actions/update.ts | 1 + .../actions/connector_types/stack/es_index.ts | 4 +++ .../spaces_only/tests/actions/get.ts | 6 +++++ .../spaces_only/tests/actions/get_all.ts | 1 + .../tests/actions/type_not_enabled.ts | 2 ++ .../spaces_only/tests/actions/update.ts | 2 ++ 30 files changed, 98 insertions(+), 2 deletions(-) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts index de21333275327..23410b83d10ab 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts @@ -108,11 +108,11 @@ export default function casesWebhookTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A casesWebhook action', connector_type_id: '.cases-webhook', is_missing_secrets: false, - is_system_action: false, config: simulatorConfig, }); @@ -123,11 +123,11 @@ export default function casesWebhookTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A casesWebhook action', connector_type_id: '.cases-webhook', is_missing_secrets: false, - is_system_action: false, config: simulatorConfig, }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/d3security.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/d3security.ts index 19bbdfc4daacb..55d53c00f59df 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/d3security.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/d3security.ts @@ -72,6 +72,7 @@ export default function d3SecurityTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name, connector_type_id: connectorTypeId, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts index a6c889ff6e556..cfffe90126fac 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts @@ -45,6 +45,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdActionId, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -71,6 +72,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -377,6 +379,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -403,6 +406,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -443,6 +447,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -469,6 +474,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -514,6 +520,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdMSExchangeActionId, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', @@ -542,6 +549,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An email action', connector_type_id: '.email', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts index fed3acba1147e..46287db208b87 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts @@ -41,6 +41,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An index action', connector_type_id: '.index', @@ -61,6 +62,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'An index action', @@ -86,6 +88,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(createdActionWithIndex).to.eql({ id: createdActionWithIndex.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An index action with index config', connector_type_id: '.index', @@ -106,6 +109,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(fetchedActionWithIndex).to.eql({ id: fetchedActionWithIndex.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An index action with index config', connector_type_id: '.index', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gen_ai.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gen_ai.ts index 828e19470f4fe..45068d82b7c28 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gen_ai.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gen_ai.ts @@ -82,6 +82,7 @@ export default function genAiTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name, connector_type_id: connectorTypeId, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts index 87f69ffabbbb6..11588f48a48de 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts @@ -75,6 +75,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A jira action', connector_type_id: '.jira', @@ -92,6 +93,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A jira action', connector_type_id: '.jira', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts index 1b9f001a2232c..fb73f6a95089e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts @@ -55,6 +55,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An opsgenie action', connector_type_id: '.opsgenie', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts index 6533690762260..81a3c2ef966b9 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts @@ -61,6 +61,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A pagerduty action', connector_type_id: '.pagerduty', @@ -79,6 +80,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A pagerduty action', connector_type_id: '.pagerduty', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts index 3543c6a35c91d..8425de3b900ec 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts @@ -78,6 +78,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An IBM Resilient action', connector_type_id: '.resilient', @@ -95,6 +96,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An IBM Resilient action', connector_type_id: '.resilient', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts index b8b12d6aac764..a5f14e512ddb1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts @@ -30,6 +30,7 @@ export default function serverLogTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'A server.log action', @@ -46,6 +47,7 @@ export default function serverLogTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A server.log action', connector_type_id: '.server-log', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts index 35cc75fbd08b5..e9e70d879618b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts @@ -111,6 +111,7 @@ export default function serviceNowITOMTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-itom', @@ -131,6 +132,7 @@ export default function serviceNowITOMTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-itom', @@ -163,6 +165,7 @@ export default function serviceNowITOMTest({ getService }: FtrProviderContext) { expect(createdConnector).to.eql({ id: createdConnector.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-itom', @@ -183,6 +186,7 @@ export default function serviceNowITOMTest({ getService }: FtrProviderContext) { expect(fetchedConnector).to.eql({ id: fetchedConnector.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-itom', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts index 3833c39dd4618..96f0694ef794b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts @@ -120,6 +120,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow', @@ -141,6 +142,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow', @@ -174,6 +176,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { expect(createdConnector).to.eql({ id: createdConnector.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow', @@ -195,6 +198,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { expect(fetchedConnector).to.eql({ id: fetchedConnector.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts index 7afb13b444eb7..2094ddd71cd7d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts @@ -132,6 +132,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-sir', @@ -153,6 +154,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-sir', @@ -186,6 +188,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { expect(createdConnector).to.eql({ id: createdConnector.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-sir', @@ -207,6 +210,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { expect(fetchedConnector).to.eql({ id: fetchedConnector.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A servicenow action', connector_type_id: '.servicenow-sir', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts index 12c17d2a7a4f9..1a974eda92e71 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts @@ -29,6 +29,7 @@ export default function slackTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'A slack api action', @@ -45,6 +46,7 @@ export default function slackTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'A slack api action', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts index d9c32ccd643b0..4941bec1844ca 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts @@ -58,6 +58,7 @@ export default function slackTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'A slack action', @@ -74,6 +75,7 @@ export default function slackTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'A slack action', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts index f0684db81f9cf..5d6bb9e9b8145 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts @@ -151,6 +151,7 @@ export default function swimlaneTest({ getService }: FtrProviderContext) { id: createdAction.id, is_missing_secrets: false, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A swimlane action', }); @@ -164,6 +165,7 @@ export default function swimlaneTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'A swimlane action', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts index 4a745716ed08f..99ba90f78dbeb 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts @@ -83,6 +83,7 @@ export default function tinesTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name, connector_type_id: connectorTypeId, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/torq.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/torq.ts index 2dfbc3e8de643..257378b406da5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/torq.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/torq.ts @@ -88,6 +88,7 @@ export default function torqTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A Torq action', connector_type_id: '.torq', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts index c021733224a1c..c7a8f1c1b2524 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts @@ -117,6 +117,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A generic Webhook action', connector_type_id: '.webhook', @@ -136,6 +137,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A generic Webhook action', connector_type_id: '.webhook', @@ -170,6 +172,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A generic Webhook action', connector_type_id: '.webhook', @@ -208,6 +211,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'A generic Webhook action', connector_type_id: '.webhook', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xmatters.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xmatters.ts index 38bc73a3ad452..4efacbf78f951 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xmatters.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/xmatters.ts @@ -61,6 +61,7 @@ export default function xmattersTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An xmatters action', connector_type_id: '.xmatters', @@ -95,6 +96,7 @@ export default function xmattersTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'An xmatters action', connector_type_id: '.xmatters', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts index dce40323e9e1c..4c9db37fe3543 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts @@ -60,6 +60,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ id: response.body.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'My action', @@ -296,6 +297,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ id: predefinedId, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'My action', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts index f58481bb94412..3b04af22d2e96 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts @@ -63,6 +63,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, connector_type_id: 'test.index-record', is_deprecated: false, is_missing_secrets: false, @@ -151,6 +152,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { connector_type_id: '.slack', name: 'Slack#xyz', is_preconfigured: true, + is_system_action: false, is_deprecated: false, }); break; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts index 1cf5b7bb30347..37475d7f191ca 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts @@ -70,6 +70,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'My action', connector_type_id: 'test.index-record', @@ -83,6 +84,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.email', id: 'notification-email', is_deprecated: false, + is_system_action: false, is_preconfigured: true, name: 'Notification Email Connector', referenced_by_count: 0, @@ -90,6 +92,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'preconfigured-es-index-action', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.index', name: 'preconfigured_es_index_action', @@ -99,6 +102,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', id: 'my-deprecated-servicenow', is_preconfigured: true, + is_system_action: false, is_deprecated: true, name: 'ServiceNow#xyz', referenced_by_count: 0, @@ -107,6 +111,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', id: 'my-deprecated-servicenow-default', is_preconfigured: true, + is_system_action: false, is_deprecated: true, name: 'ServiceNow#xyz', referenced_by_count: 0, @@ -114,6 +119,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'my-slack1', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.slack', name: 'Slack#xyz', @@ -122,6 +128,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'custom-system-abc-connector', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: 'system-abc-action-type', name: 'SystemABC', @@ -130,6 +137,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'preconfigured.test.index-record', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', @@ -138,6 +146,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'my-test-email', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.email', name: 'TestEmail#xyz', @@ -221,6 +230,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, name: 'My action', connector_type_id: 'test.index-record', @@ -235,12 +245,14 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'notification-email', is_deprecated: false, is_preconfigured: true, + is_system_action: false, name: 'Notification Email Connector', referenced_by_count: 0, }, { id: 'preconfigured-es-index-action', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.index', name: 'preconfigured_es_index_action', @@ -251,6 +263,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow', is_deprecated: true, is_preconfigured: true, + is_system_action: false, name: 'ServiceNow#xyz', referenced_by_count: 0, }, @@ -258,6 +271,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', id: 'my-deprecated-servicenow-default', is_preconfigured: true, + is_system_action: false, is_deprecated: true, name: 'ServiceNow#xyz', referenced_by_count: 0, @@ -265,6 +279,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'my-slack1', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.slack', name: 'Slack#xyz', @@ -273,6 +288,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'custom-system-abc-connector', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: 'system-abc-action-type', name: 'SystemABC', @@ -281,6 +297,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'preconfigured.test.index-record', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', @@ -289,6 +306,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'my-test-email', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.email', name: 'TestEmail#xyz', @@ -350,12 +368,14 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'notification-email', is_deprecated: false, is_preconfigured: true, + is_system_action: false, name: 'Notification Email Connector', referenced_by_count: 0, }, { id: 'preconfigured-es-index-action', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.index', name: 'preconfigured_es_index_action', @@ -365,6 +385,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', id: 'my-deprecated-servicenow', is_preconfigured: true, + is_system_action: false, is_deprecated: true, name: 'ServiceNow#xyz', referenced_by_count: 0, @@ -373,6 +394,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', id: 'my-deprecated-servicenow-default', is_preconfigured: true, + is_system_action: false, is_deprecated: true, name: 'ServiceNow#xyz', referenced_by_count: 0, @@ -380,6 +402,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'my-slack1', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.slack', name: 'Slack#xyz', @@ -388,6 +411,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'custom-system-abc-connector', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: 'system-abc-action-type', name: 'SystemABC', @@ -396,6 +420,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'preconfigured.test.index-record', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: 'test.index-record', name: 'Test:_Preconfigured_Index_Record', @@ -404,6 +429,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { { id: 'my-test-email', is_preconfigured: true, + is_system_action: false, is_deprecated: false, connector_type_id: '.email', name: 'TestEmail#xyz', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts index 8b79bd2227f8d..8bc8bc6a89757 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts @@ -91,6 +91,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { expect(res.body).to.eql({ id: res.body.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, is_missing_secrets: false, name: 'My sub connector', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts index 0e4214a9138f6..07d2e6064bc0b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts @@ -73,6 +73,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, connector_type_id: 'test.index-record', is_missing_secrets: false, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts index aa3b2b6780dc8..e25775755eb00 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts @@ -57,6 +57,7 @@ export default function indexTest({ getService }: FtrProviderContext) { id: createdConnector.id, is_preconfigured: false, is_deprecated: false, + is_system_action: false, name: 'An index connector', connector_type_id: '.index', is_missing_secrets: false, @@ -78,6 +79,7 @@ export default function indexTest({ getService }: FtrProviderContext) { is_preconfigured: false, is_deprecated: false, is_missing_secrets: false, + is_system_action: false, name: 'An index connector', connector_type_id: '.index', config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null }, @@ -102,6 +104,7 @@ export default function indexTest({ getService }: FtrProviderContext) { id: createdConnectorWithIndex.id, is_preconfigured: false, is_deprecated: false, + is_system_action: false, name: 'An index connector with index config', connector_type_id: '.index', is_missing_secrets: false, @@ -122,6 +125,7 @@ export default function indexTest({ getService }: FtrProviderContext) { id: fetchedConnectorWithIndex.id, is_preconfigured: false, is_deprecated: false, + is_system_action: false, name: 'An index connector with index config', connector_type_id: '.index', is_missing_secrets: false, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts index 6d923452faac5..68d4512145118 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -42,6 +42,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { is_preconfigured: false, is_deprecated: false, is_missing_secrets: false, + is_system_action: false, connector_type_id: 'test.index-record', name: 'My action', config: { @@ -83,6 +84,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { id: 'my-slack1', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: '.slack', name: 'Slack#xyz', }); @@ -95,6 +97,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow', is_preconfigured: true, is_deprecated: true, + is_system_action: false, connector_type_id: '.servicenow', name: 'ServiceNow#xyz', }); @@ -107,6 +110,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { id: 'my-deprecated-servicenow-default', is_preconfigured: true, is_deprecated: true, + is_system_action: false, connector_type_id: '.servicenow', name: 'ServiceNow#xyz', }); @@ -136,6 +140,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { id: createdAction.id, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, actionTypeId: 'test.index-record', isMissingSecrets: false, name: 'My action', @@ -178,6 +183,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { id: 'my-slack1', isPreconfigured: true, isDeprecated: false, + isSystemAction: false, actionTypeId: '.slack', name: 'Slack#xyz', }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index c34c314ade4c3..d4a43da31894e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -136,6 +136,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { id: 'my-test-email', is_preconfigured: true, is_deprecated: false, + is_system_action: false, connector_type_id: '.email', name: 'TestEmail#xyz', referenced_by_count: 0, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts index 0ab9baad0b554..d0f7c8752d47c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts @@ -63,6 +63,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) id: 'uuid-actionId', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, isMissingSecrets: false, name: 'an action created before test.not-enabled was disabled', }); @@ -93,6 +94,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) isPreconfigured: false, isDeprecated: false, isMissingSecrets: false, + isSystemAction: false, name: 'an action created before test.not-enabled was disabled', }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts index 5f6dffcc3c3e6..d6f41e3037ad2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts @@ -51,6 +51,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { .expect(200, { id: createdAction.id, is_preconfigured: false, + is_system_action: false, is_deprecated: false, connector_type_id: 'test.index-record', is_missing_secrets: false, @@ -215,6 +216,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { id: createdAction.id, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, actionTypeId: 'test.index-record', isMissingSecrets: false, name: 'My action updated', From b070bd9bb3182b87466fd335cbdd0bcaf6d59f56 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 28 Jun 2023 23:52:17 +0300 Subject: [PATCH 10/45] Fix cases tests --- .../plugins/actions/server/action_type_registry.test.ts | 2 +- .../tests/trial/configure/get_connectors.ts | 7 +++++++ .../spaces_only/tests/trial/configure/get_connectors.ts | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 613438579e13e..595461b137b59 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -226,7 +226,7 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], - isSystemAction: false, + isSystemAction: true, validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts index bd24b8852b716..e75fed4f399b7 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/configure/get_connectors.ts @@ -82,6 +82,7 @@ export default ({ getService }: FtrProviderContext): void => { updateIncidentUrl: 'http://some.non.existent.com/{{{external.system.id}}}', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -95,6 +96,7 @@ export default ({ getService }: FtrProviderContext): void => { projectKey: 'pkey', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -107,6 +109,7 @@ export default ({ getService }: FtrProviderContext): void => { actionTypeId: '.servicenow', id: 'preconfigured-servicenow', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'preconfigured-servicenow', referencedByCount: 0, @@ -120,6 +123,7 @@ export default ({ getService }: FtrProviderContext): void => { orgId: 'pkey', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -137,6 +141,7 @@ export default ({ getService }: FtrProviderContext): void => { userIdentifierValue: null, }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -154,6 +159,7 @@ export default ({ getService }: FtrProviderContext): void => { jwtKeyId: 'def', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -171,6 +177,7 @@ export default ({ getService }: FtrProviderContext): void => { userIdentifierValue: null, }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, diff --git a/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts b/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts index c7b6f03d3526e..5ddc3df660142 100644 --- a/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts +++ b/x-pack/test/cases_api_integration/spaces_only/tests/trial/configure/get_connectors.ts @@ -116,6 +116,7 @@ export default ({ getService }: FtrProviderContext): void => { updateIncidentUrl: 'http://some.non.existent.com/{{{external.system.id}}}', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -129,6 +130,7 @@ export default ({ getService }: FtrProviderContext): void => { projectKey: 'pkey', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -141,6 +143,7 @@ export default ({ getService }: FtrProviderContext): void => { actionTypeId: '.servicenow', id: 'preconfigured-servicenow', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'preconfigured-servicenow', referencedByCount: 0, @@ -154,6 +157,7 @@ export default ({ getService }: FtrProviderContext): void => { orgId: 'pkey', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -171,6 +175,7 @@ export default ({ getService }: FtrProviderContext): void => { userIdentifierValue: null, }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -188,6 +193,7 @@ export default ({ getService }: FtrProviderContext): void => { jwtKeyId: 'def', }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -205,6 +211,7 @@ export default ({ getService }: FtrProviderContext): void => { userIdentifierValue: null, }, isPreconfigured: false, + isSystemAction: false, isDeprecated: false, isMissingSecrets: false, referencedByCount: 0, @@ -270,6 +277,7 @@ export default ({ getService }: FtrProviderContext): void => { actionTypeId: '.servicenow', id: 'preconfigured-servicenow', isPreconfigured: true, + isSystemAction: false, isDeprecated: false, name: 'preconfigured-servicenow', referencedByCount: 0, From b50a3e0e7fd0a133927eef18e15b7a3070cdbf80 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 29 Jun 2023 11:44:22 +0300 Subject: [PATCH 11/45] Fix types from other plugins --- .../logic/export/get_export_all.test.ts | 2 ++ .../logic/export/get_export_by_object_ids.test.ts | 2 ++ .../import_rule_action_connectors.test.ts | 2 ++ .../rule_management/utils/utils.test.ts | 11 +++++++++++ .../connector_types/es_index/es_index_params.test.tsx | 2 ++ .../public/connector_types/gen_ai/params.test.tsx | 1 + .../public/connector_types/jira/jira_params.test.tsx | 1 + .../connector_types/lib/servicenow/helpers.test.ts | 1 + .../lib/servicenow/servicenow_connectors.tsx | 1 + .../lib/servicenow/use_choices.test.tsx | 1 + .../lib/servicenow/use_get_choices.test.tsx | 1 + .../public/connector_types/opsgenie/params.test.tsx | 1 + .../resilient/resilient_params.test.tsx | 1 + .../servicenow_itom/servicenow_itom_params.test.tsx | 1 + .../servicenow_itsm/servicenow_itsm_params.test.tsx | 1 + .../servicenow_sir/servicenow_sir_params.test.tsx | 1 + .../connector_types/swimlane/swimlane_params.test.tsx | 1 + .../public/apps/synthetics/state/settings/api.ts | 2 ++ .../lib/action_connector_api/connectors.ts | 3 ++- .../application/lib/action_connector_api/create.ts | 2 +- .../application/lib/action_connector_api/update.ts | 2 +- .../action_connector_form/action_type_form.test.tsx | 6 +++--- .../connectors_selection.test.tsx | 2 +- .../edit_connector_flyout/index.tsx | 2 +- x-pack/plugins/triggers_actions_ui/public/types.ts | 8 ++++++++ .../uptime/public/legacy_uptime/state/api/alerts.ts | 2 ++ 26 files changed, 52 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index c86387f26f50a..55f797e73ebc3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -39,6 +39,7 @@ const connectors = [ config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -47,6 +48,7 @@ const connectors = [ name: 'Email (preconfigured)', config: {}, isPreconfigured: true, + isSystemAction: false, isDeprecated: false, referencedByCount: 1, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index 35151f1b4b1d6..79aa4bfd3d88e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -38,6 +38,7 @@ const connectors = [ config: {}, isPreconfigured: false, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, { @@ -47,6 +48,7 @@ const connectors = [ config: {}, isPreconfigured: true, isDeprecated: false, + isSystemAction: false, referencedByCount: 1, }, ]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts index 54574c85f7036..19b4597f408df 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/action_connectors/import_rule_action_connectors.test.ts @@ -317,6 +317,7 @@ describe('importRuleActionConnectors', () => { id: 'cabc78e0-9031-11ed-b076-53cc4d57aaf1', referencedByCount: 1, isDeprecated: false, + isSystemAction: false, }, ]); const actionsImporter2 = core.savedObjects.getImporter; @@ -358,6 +359,7 @@ describe('importRuleActionConnectors', () => { id: 'cabc78e0-9031-11ed-b076-53cc4d57aaf1', referencedByCount: 1, isDeprecated: false, + isSystemAction: false, }, ]); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts index 8e82965f2b5f9..89699de1316ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.test.ts @@ -874,6 +874,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); const [errors, output] = await getInvalidConnectors(rules, clients.actionsClient); @@ -918,6 +919,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '789', @@ -926,6 +928,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); const [errors, output] = await getInvalidConnectors(rules, clients.actionsClient); @@ -976,6 +979,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '789', @@ -984,6 +988,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); const [errors, output] = await getInvalidConnectors(rules, clients.actionsClient); @@ -1041,6 +1046,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '789', @@ -1049,6 +1055,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); const [errors, output] = await getInvalidConnectors(rules, clients.actionsClient); @@ -1108,6 +1115,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '789', @@ -1116,6 +1124,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); const [errors, output] = await getInvalidConnectors(rules, clients.actionsClient); @@ -1216,6 +1225,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, { id: '789', @@ -1224,6 +1234,7 @@ describe('utils', () => { name: 'name', isPreconfigured: false, isDeprecated: false, + isSystemAction: false, }, ]); const [errors, output] = await getInvalidConnectors(rules, clients.actionsClient); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.test.tsx index 44a6133cbefda..6118efcfdb12c 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/es_index/es_index_params.test.tsx @@ -33,6 +33,7 @@ const actionConnector = { }, id: 'es index connector', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, name: 'test name', secrets: {}, @@ -45,6 +46,7 @@ const preconfiguredActionConnector = { }, id: AlertHistoryEsIndexConnectorId, isPreconfigured: true, + isSystemAction: false as const, isDeprecated: false, name: 'Alert history Elasticsearch index', secrets: {}, diff --git a/x-pack/plugins/stack_connectors/public/connector_types/gen_ai/params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/gen_ai/params.test.tsx index f37ab8b39d573..ef418bd16965d 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/gen_ai/params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/gen_ai/params.test.tsx @@ -65,6 +65,7 @@ describe('Gen AI Params Fields renders', () => { id: 'test', actionTypeId: '.gen-ai', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, name: 'My GenAI Connector', config: { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/jira/jira_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/jira/jira_params.test.tsx index 3865da8ead3a7..6deb820876b96 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/jira/jira_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/jira/jira_params.test.tsx @@ -50,6 +50,7 @@ const connector: ActionConnector = { name: 'Test', isPreconfigured: false, isDeprecated: false, + isSystemAction: false as const, }; const editAction = jest.fn(); const defaultProps = { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.test.ts index 7700014e58021..17996ccf7fe65 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.test.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/helpers.test.ts @@ -23,6 +23,7 @@ const deprecatedConnector: ActionConnector = { name: 'Test', isPreconfigured: false, isDeprecated: true, + isSystemAction: false as const, }; const validConnector = { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.tsx index f667847f6dd29..c08e97902746a 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/servicenow_connectors.tsx @@ -131,6 +131,7 @@ const ServiceNowConnectorFields: React.FC = ({ ...connectorToUpdate, isDeprecated, isPreconfigured: false, + isSystemAction: false, actionTypeId, }); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.test.tsx index d3584cc38a202..721897ece7266 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_choices.test.tsx @@ -27,6 +27,7 @@ const actionConnector = { actionTypeId: '.servicenow', name: 'ServiceNow ITSM', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, config: { apiUrl: 'https://dev94428.service-now.com/', diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.test.tsx index b4d204c117b50..38ea6d55b4e17 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/use_get_choices.test.tsx @@ -28,6 +28,7 @@ const actionConnector = { actionTypeId: '.servicenow', name: 'ServiceNow ITSM', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, config: { apiUrl: 'https://dev94428.service-now.com/', diff --git a/x-pack/plugins/stack_connectors/public/connector_types/opsgenie/params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/opsgenie/params.test.tsx index 7a65a8b5fd790..562fd66d031dd 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/opsgenie/params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/opsgenie/params.test.tsx @@ -45,6 +45,7 @@ describe('OpsgenieParamFields', () => { actionTypeId: '.test', name: 'Test', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/resilient/resilient_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/resilient/resilient_params.test.tsx index c6417660720f7..178cb86389d18 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/resilient/resilient_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/resilient/resilient_params.test.tsx @@ -39,6 +39,7 @@ const connector = { actionTypeId: '.test', name: 'Test', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itom/servicenow_itom_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itom/servicenow_itom_params.test.tsx index 5ac0c36b0c7fc..cc67186790be7 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itom/servicenow_itom_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itom/servicenow_itom_params.test.tsx @@ -41,6 +41,7 @@ const connector: ActionConnector = { actionTypeId: '.test', name: 'Test', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itsm/servicenow_itsm_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itsm/servicenow_itsm_params.test.tsx index 344cd73208fb6..5925724b7b8a9 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itsm/servicenow_itsm_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/servicenow_itsm/servicenow_itsm_params.test.tsx @@ -46,6 +46,7 @@ const connector: ActionConnector = { actionTypeId: '.test', name: 'Test', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/servicenow_sir/servicenow_sir_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/servicenow_sir/servicenow_sir_params.test.tsx index a368c1564725d..8a62b6f09a758 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/servicenow_sir/servicenow_sir_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/servicenow_sir/servicenow_sir_params.test.tsx @@ -48,6 +48,7 @@ const connector: ActionConnector = { actionTypeId: '.test', name: 'Test', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/swimlane/swimlane_params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/swimlane/swimlane_params.test.tsx index 302e5c80af15c..a8575912ddc38 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/swimlane/swimlane_params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/swimlane/swimlane_params.test.tsx @@ -36,6 +36,7 @@ describe('SwimlaneParamsFields renders', () => { actionTypeId: '.test', name: 'Test', isPreconfigured: false, + isSystemAction: false as const, isDeprecated: false, }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts index 02efa016475c2..b7ae7884b178a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts @@ -61,6 +61,7 @@ export const fetchConnectors = async (): Promise => { is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, ...res }) => ({ ...res, @@ -69,6 +70,7 @@ export const fetchConnectors = async (): Promise => { isDeprecated, isPreconfigured, isMissingSecrets, + isSystemAction, }) ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts index 60f1e25295686..6938654b294ac 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts @@ -41,5 +41,6 @@ export async function loadAllActions({ http }: { http: HttpSetup }): Promise[0]>( `${BASE_ACTION_API_PATH}/connectors` ); - return rewriteResponseRes(res); + + return rewriteResponseRes(res) as ActionConnector[]; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts index 1256497a5d448..32efba66d56be 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts @@ -49,5 +49,5 @@ export async function createActionConnector({ `${BASE_ACTION_API_PATH}/connector`, { body: JSON.stringify(rewriteBodyRequest(connector)) } ); - return rewriteBodyRes(res); + return rewriteBodyRes(res) as ActionConnector; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts index 7b65b53443198..c35378ebad17e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts @@ -51,5 +51,5 @@ export async function updateActionConnector({ } ); - return rewriteBodyRes(res); + return rewriteBodyRes(res) as ActionConnector; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index 807e5454f094f..46dafc3c4fc03 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -556,7 +556,7 @@ function getActionTypeForm({ id: 'test', isPreconfigured: false, isDeprecated: false, - isSystemAction: false, + isSystemAction: false as const, name: 'test name', secrets: {}, }; @@ -580,7 +580,7 @@ function getActionTypeForm({ id: 'test', isPreconfigured: false, isDeprecated: false, - isSystemAction: false, + isSystemAction: false as const, name: 'test name', secrets: {}, }, @@ -590,7 +590,7 @@ function getActionTypeForm({ actionTypeId: '.server-log', isPreconfigured: false, isDeprecated: false, - isSystemAction: false, + isSystemAction: false as const, config: {}, secrets: {}, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx index 230f81e153162..64a19d253740c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connectors_selection.test.tsx @@ -69,7 +69,7 @@ describe('connectors_selection', () => { id: 'testId', isPreconfigured: false, isDeprecated: false, - isSystemAction: false, + isSystemAction: false as const, name: 'test pagerduty', secrets: {}, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.tsx index e00349cecd62b..343e0b01c4782 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.tsx @@ -260,7 +260,7 @@ const EditConnectorFlyoutComponent: React.FC = ({ /> {selectedTab === EditConnectorTabs.Configuration ? ( - !connector.isPreconfigured ? ( + !connector.isPreconfigured && !connector.isSystemAction ? ( <> {isEdit && ( <> diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 3e4006e64ca7f..607ceef218508 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -287,6 +287,7 @@ export type PreConfiguredActionConnector = Omit< 'config' | 'secrets' > & { isPreconfigured: true; + isSystemAction: false; }; export type UserConfiguredActionConnector = ActionConnectorProps< @@ -294,10 +295,17 @@ export type UserConfiguredActionConnector = ActionConnectorProp Secrets > & { isPreconfigured: false; + isSystemAction: false; +}; + +export type SystemAction = Omit, 'config' | 'secrets'> & { + isSystemAction: true; + isPreconfigured: false; }; export type ActionConnector, Secrets = Record> = | PreConfiguredActionConnector + | SystemAction | UserConfiguredActionConnector; export type ActionConnectorWithoutId< diff --git a/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts b/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts index 9355cea954f98..8c623e914516b 100644 --- a/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts +++ b/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts @@ -38,6 +38,7 @@ export const fetchConnectors = async (): Promise => { is_preconfigured: isPreconfigured, is_deprecated: isDeprecated, is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, ...res }) => ({ ...res, @@ -46,6 +47,7 @@ export const fetchConnectors = async (): Promise => { isDeprecated, isPreconfigured, isMissingSecrets, + isSystemAction, }) ); }; From 1caf8742bceed23afd7196ad4f368b6416e83881 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 29 Jun 2023 11:55:50 +0300 Subject: [PATCH 12/45] Rename preconfiguredConnectors to inMemoryConnectors --- .../server/action_type_registry.test.ts | 2 +- .../actions/server/action_type_registry.ts | 13 +++-- .../actions/server/actions_client.test.ts | 30 ++++++------ .../plugins/actions/server/actions_client.ts | 44 ++++++++--------- .../server/create_execute_function.test.ts | 36 +++++++------- .../actions/server/create_execute_function.ts | 30 ++++++------ .../create_unsecured_execute_function.test.ts | 12 ++--- .../create_unsecured_execute_function.ts | 23 ++++----- x-pack/plugins/actions/server/index.ts | 2 +- .../server/lib/action_executor.test.ts | 6 +-- .../actions/server/lib/action_executor.ts | 18 +++---- .../server/lib/is_connector_deprecated.ts | 6 +-- .../server/lib/task_runner_factory.test.ts | 2 +- x-pack/plugins/actions/server/mocks.ts | 2 +- x-pack/plugins/actions/server/plugin.test.ts | 6 +-- x-pack/plugins/actions/server/plugin.ts | 48 +++++++++---------- .../alert_history_es_index.ts | 4 +- .../action_task_params_migrations.test.ts | 26 +++++----- .../action_task_params_migrations.ts | 18 +++---- .../actions/server/saved_objects/index.ts | 6 +-- x-pack/plugins/actions/server/types.ts | 2 +- .../actions/server/usage/actions_telemetry.ts | 18 +++---- x-pack/plugins/actions/server/usage/task.ts | 16 +++---- 23 files changed, 183 insertions(+), 187 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 595461b137b59..a36732c2c7e91 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -36,7 +36,7 @@ describe('actionTypeRegistry', () => { ), actionsConfigUtils: mockedActionsConfig, licenseState: mockedLicenseState, - preconfiguredActions: [ + inMemoryConnectors: [ { actionTypeId: 'foo', config: {}, diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 8404bafbd8994..7e9adcc99ac34 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -14,7 +14,7 @@ import { ActionsConfigurationUtilities } from './actions_config'; import { getActionTypeFeatureUsageName, TaskRunnerFactory, ILicenseState } from './lib'; import { ActionType, - PreConfiguredAction, + InMemoryConnector, ActionTypeConfig, ActionTypeSecrets, ActionTypeParams, @@ -26,7 +26,7 @@ export interface ActionTypeRegistryOpts { taskRunnerFactory: TaskRunnerFactory; actionsConfigUtils: ActionsConfigurationUtilities; licenseState: ILicenseState; - preconfiguredActions: PreConfiguredAction[]; + inMemoryConnectors: InMemoryConnector[]; } export class ActionTypeRegistry { @@ -35,7 +35,7 @@ export class ActionTypeRegistry { private readonly taskRunnerFactory: TaskRunnerFactory; private readonly actionsConfigUtils: ActionsConfigurationUtilities; private readonly licenseState: ILicenseState; - private readonly preconfiguredActions: PreConfiguredAction[]; + private readonly inMemoryConnectors: InMemoryConnector[]; private readonly licensing: LicensingPluginSetup; constructor(constructorParams: ActionTypeRegistryOpts) { @@ -43,7 +43,7 @@ export class ActionTypeRegistry { this.taskRunnerFactory = constructorParams.taskRunnerFactory; this.actionsConfigUtils = constructorParams.actionsConfigUtils; this.licenseState = constructorParams.licenseState; - this.preconfiguredActions = constructorParams.preconfiguredActions; + this.inMemoryConnectors = constructorParams.inMemoryConnectors; this.licensing = constructorParams.licensing; } @@ -89,9 +89,8 @@ export class ActionTypeRegistry { return ( actionTypeEnabled || (!actionTypeEnabled && - this.preconfiguredActions.find( - (preconfiguredAction) => preconfiguredAction.id === actionId - ) !== undefined) + this.inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === actionId) !== + undefined) ); } diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 14a295cfec325..8ae4aeebabc8e 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -133,7 +133,7 @@ beforeEach(() => { ), actionsConfigUtils: actionsConfigMock.create(), licenseState: mockedLicenseState, - preconfiguredActions: [], + inMemoryConnectors: [], }; actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionsClient = new ActionsClient({ @@ -142,7 +142,7 @@ beforeEach(() => { unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, - preconfiguredActions: [], + inMemoryConnectors: [], actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -594,7 +594,7 @@ describe('create()', () => { ), actionsConfigUtils: localConfigUtils, licenseState: licenseStateMock.create(), - preconfiguredActions: [], + inMemoryConnectors: [], }; actionTypeRegistry = new ActionTypeRegistry(localActionTypeRegistryParams); @@ -604,7 +604,7 @@ describe('create()', () => { unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, - preconfiguredActions: [], + inMemoryConnectors: [], actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -715,7 +715,7 @@ describe('create()', () => { unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, - preconfiguredActions: [ + inMemoryConnectors: [ { id: preDefinedId, actionTypeId: 'my-action-type', @@ -793,7 +793,7 @@ describe('get()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: 'my-action-type', @@ -855,7 +855,7 @@ describe('get()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: 'my-action-type', @@ -980,7 +980,7 @@ describe('get()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', @@ -1058,7 +1058,7 @@ describe('getAll()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', @@ -1202,7 +1202,7 @@ describe('getAll()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', @@ -1288,7 +1288,7 @@ describe('getBulk()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', @@ -1426,7 +1426,7 @@ describe('getBulk()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', @@ -1489,7 +1489,7 @@ describe('getOAuthAccessToken()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: '.slack', @@ -2714,7 +2714,7 @@ describe('isPreconfigured()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: 'my-action-type', @@ -2754,7 +2754,7 @@ describe('isPreconfigured()', () => { bulkExecutionEnqueuer, request, authorization: authorization as unknown as ActionsAuthorization, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'testPreconfigured', actionTypeId: 'my-action-type', diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 7905b7a0da6df..5366d71e30106 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -45,7 +45,7 @@ import { ActionResult, FindActionResult, RawAction, - PreConfiguredAction, + InMemoryConnector, ActionTypeExecutorResult, ConnectorTokenClientContract, } from './types'; @@ -115,7 +115,7 @@ interface ConstructorOptions { scopedClusterClient: IScopedClusterClient; actionTypeRegistry: ActionTypeRegistry; unsecuredSavedObjectsClient: SavedObjectsClientContract; - preconfiguredActions: PreConfiguredAction[]; + inMemoryConnectors: InMemoryConnector[]; actionExecutor: ActionExecutorContract; executionEnqueuer: ExecutionEnqueuer; ephemeralExecutionEnqueuer: ExecutionEnqueuer; @@ -139,7 +139,7 @@ export class ActionsClient { private readonly scopedClusterClient: IScopedClusterClient; private readonly unsecuredSavedObjectsClient: SavedObjectsClientContract; private readonly actionTypeRegistry: ActionTypeRegistry; - private readonly preconfiguredActions: PreConfiguredAction[]; + private readonly inMemoryConnectors: InMemoryConnector[]; private readonly actionExecutor: ActionExecutorContract; private readonly request: KibanaRequest; private readonly authorization: ActionsAuthorization; @@ -157,7 +157,7 @@ export class ActionsClient { kibanaIndices, scopedClusterClient, unsecuredSavedObjectsClient, - preconfiguredActions, + inMemoryConnectors, actionExecutor, executionEnqueuer, ephemeralExecutionEnqueuer, @@ -174,7 +174,7 @@ export class ActionsClient { this.unsecuredSavedObjectsClient = unsecuredSavedObjectsClient; this.scopedClusterClient = scopedClusterClient; this.kibanaIndices = kibanaIndices; - this.preconfiguredActions = preconfiguredActions; + this.inMemoryConnectors = inMemoryConnectors; this.actionExecutor = actionExecutor; this.executionEnqueuer = executionEnqueuer; this.ephemeralExecutionEnqueuer = ephemeralExecutionEnqueuer; @@ -196,7 +196,7 @@ export class ActionsClient { }: CreateOptions): Promise { const id = options?.id || SavedObjectsUtils.generateId(); - if (this.preconfiguredActions.some((preconfiguredAction) => preconfiguredAction.id === id)) { + if (this.inMemoryConnectors.some((inMemoryConnector) => inMemoryConnector.id === id)) { throw Boom.badRequest( i18n.translate('xpack.actions.serverSideErrors.predefinedIdConnectorAlreadyExists', { defaultMessage: 'This {id} already exist in preconfigured action.', @@ -273,7 +273,7 @@ export class ActionsClient { await this.authorization.ensureAuthorized('update'); if ( - this.preconfiguredActions.find((preconfiguredAction) => preconfiguredAction.id === id) !== + this.inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === id) !== undefined ) { throw new PreconfiguredActionDisabledModificationError( @@ -380,10 +380,10 @@ export class ActionsClient { throw error; } - const preconfiguredActionsList = this.preconfiguredActions.find( - (preconfiguredAction) => preconfiguredAction.id === id + const inMemoryConnectorsList = this.inMemoryConnectors.find( + (inMemoryConnector) => inMemoryConnector.id === id ); - if (preconfiguredActionsList !== undefined) { + if (inMemoryConnectorsList !== undefined) { this.auditLogger?.log( connectorAuditEvent({ action: ConnectorAuditAction.GET, @@ -393,11 +393,11 @@ export class ActionsClient { return { id, - actionTypeId: preconfiguredActionsList.actionTypeId, - name: preconfiguredActionsList.name, + actionTypeId: inMemoryConnectorsList.actionTypeId, + name: inMemoryConnectorsList.name, isPreconfigured: true, isSystemAction: false, - isDeprecated: isConnectorDeprecated(preconfiguredActionsList), + isDeprecated: isConnectorDeprecated(inMemoryConnectorsList), }; } @@ -458,12 +458,12 @@ export class ActionsClient { const mergedResult = [ ...savedObjectsActions, - ...this.preconfiguredActions.map((preconfiguredAction) => ({ - id: preconfiguredAction.id, - actionTypeId: preconfiguredAction.actionTypeId, - name: preconfiguredAction.name, + ...this.inMemoryConnectors.map((inMemoryConnector) => ({ + id: inMemoryConnector.id, + actionTypeId: inMemoryConnector.actionTypeId, + name: inMemoryConnector.name, isPreconfigured: true, - isDeprecated: isConnectorDeprecated(preconfiguredAction), + isDeprecated: isConnectorDeprecated(inMemoryConnector), isSystemAction: false, })), ].sort((a, b) => a.name.localeCompare(b.name)); @@ -491,8 +491,8 @@ export class ActionsClient { const actionResults = new Array(); for (const actionId of ids) { - const action = this.preconfiguredActions.find( - (preconfiguredAction) => preconfiguredAction.id === actionId + const action = this.inMemoryConnectors.find( + (inMemoryConnector) => inMemoryConnector.id === actionId ); if (action !== undefined) { actionResults.push(action); @@ -633,7 +633,7 @@ export class ActionsClient { await this.authorization.ensureAuthorized('delete'); if ( - this.preconfiguredActions.find((preconfiguredAction) => preconfiguredAction.id === id) !== + this.inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === id) !== undefined ) { throw new PreconfiguredActionDisabledModificationError( @@ -765,7 +765,7 @@ export class ActionsClient { } public isPreconfigured(connectorId: string): boolean { - return !!this.preconfiguredActions.find((preconfigured) => preconfigured.id === connectorId); + return !!this.inMemoryConnectors.find((preconfigured) => preconfigured.id === connectorId); } public async getGlobalExecutionLogWithAuth({ 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 ec746ee62862b..55aa1758a70b7 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -32,7 +32,7 @@ describe('execute()', () => { taskManager: mockTaskManager, actionTypeRegistry, isESOCanEncrypt: true, - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.get.mockResolvedValueOnce({ id: '123', @@ -103,7 +103,7 @@ describe('execute()', () => { taskManager: mockTaskManager, actionTypeRegistry, isESOCanEncrypt: true, - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.get.mockResolvedValueOnce({ id: '123', @@ -176,7 +176,7 @@ describe('execute()', () => { taskManager: mockTaskManager, actionTypeRegistry, isESOCanEncrypt: true, - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.get.mockResolvedValueOnce({ id: '123', @@ -247,7 +247,7 @@ describe('execute()', () => { taskManager: mockTaskManager, actionTypeRegistry: actionTypeRegistryMock.create(), isESOCanEncrypt: true, - preconfiguredActions: [ + inMemoryConnectors: [ { id: '123', actionTypeId: 'mock-action-preconfigured', @@ -327,7 +327,7 @@ describe('execute()', () => { taskManager: mockTaskManager, actionTypeRegistry: actionTypeRegistryMock.create(), isESOCanEncrypt: true, - preconfiguredActions: [ + inMemoryConnectors: [ { id: '123', actionTypeId: 'mock-action-preconfigured', @@ -428,7 +428,7 @@ describe('execute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: false, actionTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredActions: [], + inMemoryConnectors: [], }); await expect( executeFn(savedObjectsClient, { @@ -449,7 +449,7 @@ describe('execute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: true, actionTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.get.mockResolvedValueOnce({ id: '123', @@ -481,7 +481,7 @@ describe('execute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: true, actionTypeRegistry: mockedActionTypeRegistry, - preconfiguredActions: [], + inMemoryConnectors: [], }); mockedActionTypeRegistry.ensureActionTypeEnabled.mockImplementation(() => { throw new Error('Fail'); @@ -513,7 +513,7 @@ describe('execute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: true, actionTypeRegistry: mockedActionTypeRegistry, - preconfiguredActions: [ + inMemoryConnectors: [ { actionTypeId: 'mock-action', config: {}, @@ -562,7 +562,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, actionTypeRegistry, isESOCanEncrypt: true, - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ @@ -650,7 +650,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, actionTypeRegistry, isESOCanEncrypt: true, - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ @@ -741,7 +741,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, actionTypeRegistry, isESOCanEncrypt: true, - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ @@ -825,7 +825,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, actionTypeRegistry: actionTypeRegistryMock.create(), isESOCanEncrypt: true, - preconfiguredActions: [ + inMemoryConnectors: [ { id: '123', actionTypeId: 'mock-action-preconfigured', @@ -922,7 +922,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, actionTypeRegistry: actionTypeRegistryMock.create(), isESOCanEncrypt: true, - preconfiguredActions: [ + inMemoryConnectors: [ { id: '123', actionTypeId: 'mock-action-preconfigured', @@ -1040,7 +1040,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: false, actionTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredActions: [], + inMemoryConnectors: [], }); await expect( executeFn(savedObjectsClient, [ @@ -1063,7 +1063,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: true, actionTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredActions: [], + inMemoryConnectors: [], }); savedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ @@ -1101,7 +1101,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: true, actionTypeRegistry: mockedActionTypeRegistry, - preconfiguredActions: [], + inMemoryConnectors: [], }); mockedActionTypeRegistry.ensureActionTypeEnabled.mockImplementation(() => { throw new Error('Fail'); @@ -1139,7 +1139,7 @@ describe('bulkExecute()', () => { taskManager: mockTaskManager, isESOCanEncrypt: true, actionTypeRegistry: mockedActionTypeRegistry, - preconfiguredActions: [ + inMemoryConnectors: [ { actionTypeId: 'mock-action', config: {}, diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index c33bcc6923d8a..5f4f4a72a7e64 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -10,7 +10,7 @@ import { RunNowResult, TaskManagerStartContract } from '@kbn/task-manager-plugin import { RawAction, ActionTypeRegistryContract, - PreConfiguredAction, + InMemoryConnector, ActionTaskExecutorParams, } from './types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './constants/saved_objects'; @@ -21,7 +21,7 @@ interface CreateExecuteFunctionOptions { taskManager: TaskManagerStartContract; isESOCanEncrypt: boolean; actionTypeRegistry: ActionTypeRegistryContract; - preconfiguredActions: PreConfiguredAction[]; + inMemoryConnectors: InMemoryConnector[]; } export interface ExecuteOptions @@ -39,7 +39,7 @@ interface ActionTaskParams } export interface GetConnectorsResult { - connector: PreConfiguredAction | RawAction; + connector: InMemoryConnector | RawAction; isPreconfigured: boolean; id: string; } @@ -58,7 +58,7 @@ export function createExecutionEnqueuerFunction({ taskManager, actionTypeRegistry, isESOCanEncrypt, - preconfiguredActions, + inMemoryConnectors, }: CreateExecuteFunctionOptions): ExecutionEnqueuer { return async function execute( unsecuredSavedObjectsClient: SavedObjectsClientContract, @@ -81,7 +81,7 @@ export function createExecutionEnqueuerFunction({ const { action, isPreconfigured } = await getAction( unsecuredSavedObjectsClient, - preconfiguredActions, + inMemoryConnectors, id ); validateCanActionBeUsed(action); @@ -139,7 +139,7 @@ export function createBulkExecutionEnqueuerFunction({ taskManager, actionTypeRegistry, isESOCanEncrypt, - preconfiguredActions, + inMemoryConnectors, }: CreateExecuteFunctionOptions): BulkExecutionEnqueuer { return async function execute( unsecuredSavedObjectsClient: SavedObjectsClientContract, @@ -157,7 +157,7 @@ export function createBulkExecutionEnqueuerFunction({ const connectorIds = [...new Set(actionsToExecute.map((action) => action.id))]; const connectors = await getConnectors( unsecuredSavedObjectsClient, - preconfiguredActions, + inMemoryConnectors, connectorIds ); connectors.forEach((c) => { @@ -229,13 +229,13 @@ export function createBulkExecutionEnqueuerFunction({ export function createEphemeralExecutionEnqueuerFunction({ taskManager, actionTypeRegistry, - preconfiguredActions, + inMemoryConnectors, }: CreateExecuteFunctionOptions): ExecutionEnqueuer { return async function execute( unsecuredSavedObjectsClient: SavedObjectsClientContract, { id, params, spaceId, source, consumer, apiKey, executionId }: ExecuteOptions ): Promise { - const { action } = await getAction(unsecuredSavedObjectsClient, preconfiguredActions, id); + const { action } = await getAction(unsecuredSavedObjectsClient, inMemoryConnectors, id); validateCanActionBeUsed(action); const { actionTypeId } = action; @@ -266,7 +266,7 @@ export function createEphemeralExecutionEnqueuerFunction({ }; } -function validateCanActionBeUsed(action: PreConfiguredAction | RawAction) { +function validateCanActionBeUsed(action: InMemoryConnector | RawAction) { const { name, isMissingSecrets } = action; if (isMissingSecrets) { throw new Error( @@ -290,10 +290,10 @@ function executionSourceAsSavedObjectReferences(executionSource: ActionExecutorO async function getAction( unsecuredSavedObjectsClient: SavedObjectsClientContract, - preconfiguredActions: PreConfiguredAction[], + inMemoryConnectors: InMemoryConnector[], actionId: string -): Promise<{ action: PreConfiguredAction | RawAction; isPreconfigured: boolean }> { - const pcAction = preconfiguredActions.find((action) => action.id === actionId); +): Promise<{ action: InMemoryConnector | RawAction; isPreconfigured: boolean }> { + const pcAction = inMemoryConnectors.find((action) => action.id === actionId); if (pcAction) { return { action: pcAction, isPreconfigured: true }; } @@ -304,14 +304,14 @@ async function getAction( async function getConnectors( unsecuredSavedObjectsClient: SavedObjectsClientContract, - preconfiguredConnectors: PreConfiguredAction[], + inMemoryConnectors: InMemoryConnector[], connectorIds: string[] ): Promise { const result: GetConnectorsResult[] = []; const connectorIdsToFetch = []; for (const connectorId of connectorIds) { - const pcConnector = preconfiguredConnectors.find((connector) => connector.id === connectorId); + const pcConnector = inMemoryConnectors.find((connector) => connector.id === connectorId); if (pcConnector) { result.push({ connector: pcConnector, isPreconfigured: true, id: connectorId }); } else { diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts index 8e75a889c3410..9eeb10e541f00 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts @@ -25,7 +25,7 @@ describe('bulkExecute()', () => { const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ taskManager: mockTaskManager, connectorTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredConnectors: [ + inMemoryConnectors: [ { id: '123', actionTypeId: '.email', @@ -131,7 +131,7 @@ describe('bulkExecute()', () => { const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ taskManager: mockTaskManager, connectorTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredConnectors: [ + inMemoryConnectors: [ { id: '123', actionTypeId: '.email', @@ -249,7 +249,7 @@ describe('bulkExecute()', () => { const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ taskManager: mockTaskManager, connectorTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredConnectors: [ + inMemoryConnectors: [ { id: '123', actionTypeId: '.email', @@ -391,7 +391,7 @@ describe('bulkExecute()', () => { const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ taskManager: mockTaskManager, connectorTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredConnectors: [ + inMemoryConnectors: [ { id: '123', actionTypeId: '.email', @@ -427,7 +427,7 @@ describe('bulkExecute()', () => { const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ taskManager: mockTaskManager, connectorTypeRegistry: mockedConnectorTypeRegistry, - preconfiguredConnectors: [ + inMemoryConnectors: [ { id: '123', actionTypeId: '.email', @@ -464,7 +464,7 @@ describe('bulkExecute()', () => { const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ taskManager: mockTaskManager, connectorTypeRegistry: actionTypeRegistryMock.create(), - preconfiguredConnectors: [ + inMemoryConnectors: [ { id: '123', actionTypeId: '.email', diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts index b1ad90d9093eb..1db64a3e16121 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts @@ -9,7 +9,7 @@ import { ISavedObjectsRepository, SavedObjectsBulkResponse } from '@kbn/core/ser import { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; import { ActionTypeRegistryContract as ConnectorTypeRegistryContract, - PreConfiguredAction as PreconfiguredConnector, + InMemoryConnector, } from './types'; import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './constants/saved_objects'; import { ExecuteOptions as ActionExecutorOptions } from './lib/action_executor'; @@ -21,7 +21,7 @@ const ALLOWED_CONNECTOR_TYPE_IDS = ['.email']; interface CreateBulkUnsecuredExecuteFunctionOptions { taskManager: TaskManagerStartContract; connectorTypeRegistry: ConnectorTypeRegistryContract; - preconfiguredConnectors: PreconfiguredConnector[]; + inMemoryConnectors: InMemoryConnector[]; } export interface ExecuteOptions @@ -42,7 +42,7 @@ export type BulkUnsecuredExecutionEnqueuer = ( export function createBulkUnsecuredExecutionEnqueuerFunction({ taskManager, connectorTypeRegistry, - preconfiguredConnectors, + inMemoryConnectors, }: CreateBulkUnsecuredExecuteFunctionOptions): BulkUnsecuredExecutionEnqueuer { return async function execute( internalSavedObjectsRepository: ISavedObjectsRepository, @@ -51,24 +51,21 @@ export function createBulkUnsecuredExecutionEnqueuerFunction({ const connectorTypeIds: Record = {}; const connectorIds = [...new Set(actionsToExecute.map((action) => action.id))]; - const notPreconfiguredConnectors = connectorIds.filter( - (connectorId) => - preconfiguredConnectors.find((connector) => connector.id === connectorId) == null + const notInMemoryConnectors = connectorIds.filter( + (connectorId) => inMemoryConnectors.find((connector) => connector.id === connectorId) == null ); - if (notPreconfiguredConnectors.length > 0) { + if (notInMemoryConnectors.length > 0) { throw new Error( - `${notPreconfiguredConnectors.join( + `${notInMemoryConnectors.join( ',' )} are not preconfigured connectors and can't be scheduled for unsecured actions execution` ); } - const connectors: PreconfiguredConnector[] = connectorIds - .map((connectorId) => - preconfiguredConnectors.find((pConnector) => pConnector.id === connectorId) - ) - .filter(Boolean) as PreconfiguredConnector[]; + const connectors: InMemoryConnector[] = connectorIds + .map((connectorId) => inMemoryConnectors.find((pConnector) => pConnector.id === connectorId)) + .filter(Boolean) as InMemoryConnector[]; connectors.forEach((connector) => { const { id, actionTypeId } = connector; diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 15f3c2fa7e20a..3deddfc9c16c6 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -22,7 +22,7 @@ export type { ActionResult, ActionTypeExecutorOptions, ActionType, - PreConfiguredAction, + InMemoryConnector, ActionsApiRequestHandlerContext, FindActionResult, } from './types'; diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 9b2e2f96390c7..31f1ab76cb0e9 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -51,7 +51,7 @@ actionExecutor.initialize({ actionTypeRegistry, encryptedSavedObjectsClient, eventLogger, - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'preconfigured', name: 'Preconfigured', @@ -958,7 +958,7 @@ test('throws an error when passing isESOCanEncrypt with value of false', async ( actionTypeRegistry, encryptedSavedObjectsClient, eventLogger: eventLoggerMock.create(), - preconfiguredActions: [], + inMemoryConnectors: [], }); await expect( customActionExecutor.execute(executeParams) @@ -976,7 +976,7 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f actionTypeRegistry, encryptedSavedObjectsClient, eventLogger: eventLoggerMock.create(), - preconfiguredActions: [ + inMemoryConnectors: [ { id: 'preconfigured', name: 'Preconfigured', diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 272d61943ac2b..d5f6e9c3b14ea 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -25,7 +25,7 @@ import { ActionTypeExecutorRawResult, ActionTypeRegistryContract, GetServicesFunction, - PreConfiguredAction, + InMemoryConnector, RawAction, ValidatorServices, } from '../types'; @@ -46,7 +46,7 @@ export interface ActionExecutorContext { encryptedSavedObjectsClient: EncryptedSavedObjectsClient; actionTypeRegistry: ActionTypeRegistryContract; eventLogger: IEventLogger; - preconfiguredActions: PreConfiguredAction[]; + inMemoryConnectors: InMemoryConnector[]; } export interface TaskInfo { @@ -118,7 +118,7 @@ export class ActionExecutor { encryptedSavedObjectsClient, actionTypeRegistry, eventLogger, - preconfiguredActions, + inMemoryConnectors, security, } = this.actionExecutorContext!; @@ -129,7 +129,7 @@ export class ActionExecutor { const actionInfo = await getActionInfoInternal( this.isESOCanEncrypt, encryptedSavedObjectsClient, - preconfiguredActions, + inMemoryConnectors, actionId, namespace.namespace ); @@ -341,7 +341,7 @@ export class ActionExecutor { source?: ActionExecutionSource; consumer?: string; }) { - const { spaces, encryptedSavedObjectsClient, preconfiguredActions, eventLogger } = + const { spaces, encryptedSavedObjectsClient, inMemoryConnectors, eventLogger } = this.actionExecutorContext!; const spaceId = spaces && spaces.getSpaceId(request); @@ -350,7 +350,7 @@ export class ActionExecutor { this.actionInfo = await getActionInfoInternal( this.isESOCanEncrypt, encryptedSavedObjectsClient, - preconfiguredActions, + inMemoryConnectors, actionId, namespace.namespace ); @@ -405,13 +405,13 @@ interface ActionInfo { async function getActionInfoInternal( isESOCanEncrypt: boolean, encryptedSavedObjectsClient: EncryptedSavedObjectsClient, - preconfiguredActions: PreConfiguredAction[], + inMemoryConnectors: InMemoryConnector[], actionId: string, namespace: string | undefined ): Promise { // check to see if it's a pre-configured action first - const pcAction = preconfiguredActions.find( - (preconfiguredAction) => preconfiguredAction.id === actionId + const pcAction = inMemoryConnectors.find( + (inMemoryConnector) => inMemoryConnector.id === actionId ); if (pcAction) { return { diff --git a/x-pack/plugins/actions/server/lib/is_connector_deprecated.ts b/x-pack/plugins/actions/server/lib/is_connector_deprecated.ts index db5138cddc53f..4ebdeec7728eb 100644 --- a/x-pack/plugins/actions/server/lib/is_connector_deprecated.ts +++ b/x-pack/plugins/actions/server/lib/is_connector_deprecated.ts @@ -6,10 +6,10 @@ */ import { isPlainObject } from 'lodash'; -import { PreConfiguredAction, RawAction } from '../types'; +import { InMemoryConnector, RawAction } from '../types'; -export type ConnectorWithOptionalDeprecation = Omit & - Pick, 'isDeprecated'>; +export type ConnectorWithOptionalDeprecation = Omit & + Pick, 'isDeprecated'>; const isObject = (obj: unknown): obj is Record => isPlainObject(obj); 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 eae6cd5bc06c2..fb764b2b6f74c 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 @@ -86,7 +86,7 @@ const actionExecutorInitializerParams = { getActionsClientWithRequest: jest.fn(async () => actionsClientMock.create()), encryptedSavedObjectsClient: mockedEncryptedSavedObjectsClient, eventLogger, - preconfiguredActions: [], + inMemoryConnectors: [], }; const taskRunnerFactoryInitializerParams = { spaceIdToNamespace, diff --git a/x-pack/plugins/actions/server/mocks.ts b/x-pack/plugins/actions/server/mocks.ts index d14c082f96905..14ae64391177f 100644 --- a/x-pack/plugins/actions/server/mocks.ts +++ b/x-pack/plugins/actions/server/mocks.ts @@ -45,7 +45,7 @@ const createStartMock = () => { getActionsAuthorizationWithRequest: jest .fn() .mockReturnValue(actionsAuthorizationMock.create()), - preconfiguredActions: [], + inMemoryConnectors: [], renderActionParameterTemplates: jest.fn(), }; return mock; diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 0136410920c66..a1a58bf14cb13 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -390,7 +390,7 @@ describe('Actions Plugin', () => { const pluginStart = await plugin.start(coreStart, pluginsStart); - expect(pluginStart.preconfiguredActions.length).toEqual(1); + expect(pluginStart.inMemoryConnectors.length).toEqual(1); expect(pluginStart.isActionExecutable('preconfiguredServerLog', '.server-log')).toBe(true); }); @@ -414,7 +414,7 @@ describe('Actions Plugin', () => { const pluginStart = await plugin.start(coreStart, pluginsStart); - expect(pluginStart.preconfiguredActions.length).toEqual(2); + expect(pluginStart.inMemoryConnectors.length).toEqual(2); expect( pluginStart.isActionExecutable('preconfigured-alert-history-es-index', '.index') ).toBe(true); @@ -438,7 +438,7 @@ describe('Actions Plugin', () => { await plugin.setup(coreSetup as any, pluginsSetup); const pluginStart = await plugin.start(coreStart, pluginsStart); - expect(pluginStart.preconfiguredActions.length).toEqual(0); + expect(pluginStart.inMemoryConnectors.length).toEqual(0); expect(context.logger.get().warn).toHaveBeenCalledWith( `Preconfigured connectors cannot have the id "${AlertHistoryEsIndexConnectorId}" because this is a reserved id.` ); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index ba4fec697b0c7..85cacdcd7a5a6 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -60,7 +60,7 @@ import { import { Services, ActionType, - PreConfiguredAction, + InMemoryConnector, ActionTypeConfig, ActionTypeSecrets, ActionTypeParams, @@ -147,7 +147,7 @@ export interface PluginStartContract { getActionsAuthorizationWithRequest(request: KibanaRequest): PublicMethodsOf; - preconfiguredActions: PreConfiguredAction[]; + inMemoryConnectors: InMemoryConnector[]; getUnsecuredActionsClient(): IUnsecuredActionsClient; @@ -200,7 +200,7 @@ export class ActionsPlugin implements Plugin()) ); this.telemetryLogger = initContext.logger.get('usage'); - this.preconfiguredActions = []; + this.inMemoryConnectors = []; this.inMemoryMetrics = new InMemoryMetrics(initContext.logger.get('in_memory_metrics')); } @@ -244,7 +244,7 @@ export class ActionsPlugin implements Plugin { - return !!this.preconfiguredActions.find( - (preconfigured) => preconfigured.id === connectorId + return !!this.inMemoryConnectors.find( + (inMemoryConnector) => inMemoryConnector.id === connectorId ); }, getSubActionConnectorClass: () => SubActionConnector, @@ -384,7 +384,7 @@ export class ActionsPlugin implements Plugin renderActionParameterTemplates(actionTypeRegistry, ...args), }; @@ -595,7 +595,7 @@ export class ActionsPlugin implements Plugin { +export function getAlertHistoryEsIndex(): Readonly { return Object.freeze({ name: i18n.translate('xpack.actions.alertHistoryEsIndexConnector.name', { defaultMessage: 'Alert history Elasticsearch index', diff --git a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts index 7ef21c8fc84bd..80e3461e12529 100644 --- a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts @@ -19,7 +19,7 @@ import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; const context = migrationMocks.createContext(); const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); -const preconfiguredActions = [ +const inMemoryConnectors = [ { actionTypeId: 'foo', config: {}, @@ -41,7 +41,7 @@ describe('successful migrations', () => { describe('7.16.0', () => { test('adds actionId to references array if actionId is not preconfigured', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData(); const migratedActionTaskParam = migration716(actionTaskParam, context); @@ -59,7 +59,7 @@ describe('successful migrations', () => { test('does not add actionId to references array if actionId is preconfigured', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({ actionId: 'my-slack1' }); const migratedActionTaskParam = migration716(actionTaskParam, context); @@ -71,7 +71,7 @@ describe('successful migrations', () => { test('handles empty relatedSavedObjects array', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({ relatedSavedObjects: [] }); const migratedActionTaskParam = migration716(actionTaskParam, context); @@ -93,7 +93,7 @@ describe('successful migrations', () => { test('adds actionId and relatedSavedObjects to references array', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({ relatedSavedObjects: [ @@ -136,7 +136,7 @@ describe('successful migrations', () => { test('only adds relatedSavedObjects to references array if action is preconfigured', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({ actionId: 'my-slack1', @@ -175,7 +175,7 @@ describe('successful migrations', () => { test('adds actionId and multiple relatedSavedObjects to references array', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({ relatedSavedObjects: [ @@ -233,7 +233,7 @@ describe('successful migrations', () => { test('does not overwrite existing references', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData( { @@ -290,7 +290,7 @@ describe('successful migrations', () => { test('does not overwrite existing references if relatedSavedObjects is undefined', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({}, [ { @@ -319,7 +319,7 @@ describe('successful migrations', () => { test('does not overwrite existing references if relatedSavedObjects is empty', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData({ relatedSavedObjects: [] }, [ { @@ -373,7 +373,7 @@ describe('handles errors during migrations', () => { describe('7.16.0 throws if migration fails', () => { test('should show the proper exception', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( - getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0'] + getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); const actionTaskParam = getMockData(); expect(() => { @@ -394,12 +394,12 @@ describe('handles errors during migrations', () => { describe('isPreconfiguredAction()', () => { test('returns true if actionId is preconfigured action', () => { expect( - isPreconfiguredAction(getMockData({ actionId: 'my-slack1' }), preconfiguredActions) + isPreconfiguredAction(getMockData({ actionId: 'my-slack1' }), inMemoryConnectors) ).toEqual(true); }); test('returns false if actionId is not preconfigured action', () => { - expect(isPreconfiguredAction(getMockData(), preconfiguredActions)).toEqual(false); + expect(isPreconfiguredAction(getMockData(), inMemoryConnectors)).toEqual(false); }); }); diff --git a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts index b0778d680e70e..c686fa55d9fe6 100644 --- a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts +++ b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts @@ -15,7 +15,7 @@ import { } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import type { IsMigrationNeededPredicate } from '@kbn/encrypted-saved-objects-plugin/server'; -import { ActionTaskParams, PreConfiguredAction } from '../types'; +import { ActionTaskParams, InMemoryConnector } from '../types'; import { RelatedSavedObjects } from '../lib/related_saved_objects'; interface ActionTaskParamsLogMeta extends LogMeta { @@ -40,12 +40,12 @@ function createEsoMigration( export function getActionTaskParamsMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, - preconfiguredActions: PreConfiguredAction[] + inMemoryConnectors: InMemoryConnector[] ): SavedObjectMigrationMap { const migrationActionTaskParamsSixteen = createEsoMigration( encryptedSavedObjects, (doc): doc is SavedObjectUnsanitizedDoc => true, - pipeMigrations(getUseSavedObjectReferencesFn(preconfiguredActions)) + pipeMigrations(getUseSavedObjectReferencesFn(inMemoryConnectors)) ); const migrationActionsTaskParams800 = createEsoMigration( @@ -88,20 +88,20 @@ function executeMigrationWithErrorHandling( export function isPreconfiguredAction( doc: SavedObjectUnsanitizedDoc, - preconfiguredActions: PreConfiguredAction[] + inMemoryConnectors: InMemoryConnector[] ): boolean { - return !!preconfiguredActions.find((action) => action.id === doc.attributes.actionId); + return !!inMemoryConnectors.find((action) => action.id === doc.attributes.actionId); } -function getUseSavedObjectReferencesFn(preconfiguredActions: PreConfiguredAction[]) { +function getUseSavedObjectReferencesFn(inMemoryConnectors: InMemoryConnector[]) { return (doc: SavedObjectUnsanitizedDoc) => { - return useSavedObjectReferences(doc, preconfiguredActions); + return useSavedObjectReferences(doc, inMemoryConnectors); }; } function useSavedObjectReferences( doc: SavedObjectUnsanitizedDoc, - preconfiguredActions: PreConfiguredAction[] + inMemoryConnectors: InMemoryConnector[] ): SavedObjectUnsanitizedDoc { const { attributes: { actionId, relatedSavedObjects }, @@ -111,7 +111,7 @@ function useSavedObjectReferences( const newReferences: SavedObjectReference[] = []; const relatedSavedObjectRefs: RelatedSavedObjects = []; - if (!isPreconfiguredAction(doc, preconfiguredActions)) { + if (!isPreconfiguredAction(doc, inMemoryConnectors)) { newReferences.push({ id: actionId, name: 'actionRef', diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts index 5b38062265196..d5125ecc134cd 100644 --- a/x-pack/plugins/actions/server/saved_objects/index.ts +++ b/x-pack/plugins/actions/server/saved_objects/index.ts @@ -16,7 +16,7 @@ import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-serve import { actionMappings, actionTaskParamsMappings, connectorTokenMappings } from './mappings'; import { getActionsMigrations } from './actions_migrations'; import { getActionTaskParamsMigrations } from './action_task_params_migrations'; -import { PreConfiguredAction, RawAction } from '../types'; +import { InMemoryConnector, RawAction } from '../types'; import { getImportWarnings } from './get_import_warnings'; import { transformConnectorsForExport } from './transform_connectors_for_export'; import { ActionTypeRegistry } from '../action_type_registry'; @@ -31,7 +31,7 @@ export function setupSavedObjects( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, actionTypeRegistry: ActionTypeRegistry, taskManagerIndex: string, - preconfiguredActions: PreConfiguredAction[] + inMemoryConnectors: InMemoryConnector[] ) { savedObjects.registerType({ name: ACTION_SAVED_OBJECT_TYPE, @@ -79,7 +79,7 @@ export function setupSavedObjects( namespaceType: 'multiple-isolated', convertToMultiNamespaceTypeVersion: '8.0.0', mappings: actionTaskParamsMappings, - migrations: getActionTaskParamsMigrations(encryptedSavedObjects, preconfiguredActions), + migrations: getActionTaskParamsMigrations(encryptedSavedObjects, inMemoryConnectors), excludeOnUpgrade: async ({ readonlyEsClient }) => { const oldestIdleActionTask = await getOldestIdleActionTask( readonlyEsClient, diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 54f5f8327be65..4e078af3868f6 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -84,7 +84,7 @@ export interface ActionResult extends ActionResult { diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.ts index 6e2b4ebbfff80..649f1109311af 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.ts @@ -13,13 +13,13 @@ import { parseActionRunOutcomeByConnectorTypesBucket, } from './lib/parse_connector_type_bucket'; import { AlertHistoryEsIndexConnectorId } from '../../common'; -import { ActionResult, PreConfiguredAction } from '../types'; +import { ActionResult, InMemoryConnector } from '../types'; export async function getTotalCount( esClient: ElasticsearchClient, kibanaIndex: string, logger: Logger, - preconfiguredActions?: PreConfiguredAction[] + inMemoryConnectors?: InMemoryConnector[] ) { const scriptedMetric = { scripted_metric: { @@ -74,9 +74,9 @@ export async function getTotalCount( }, {} ); - if (preconfiguredActions && preconfiguredActions.length) { - for (const preconfiguredAction of preconfiguredActions) { - const actionTypeId = replaceFirstAndLastDotSymbols(preconfiguredAction.actionTypeId); + if (inMemoryConnectors && inMemoryConnectors.length) { + for (const inMemoryConnector of inMemoryConnectors) { + const actionTypeId = replaceFirstAndLastDotSymbols(inMemoryConnector.actionTypeId); countByType[actionTypeId] = countByType[actionTypeId] || 0; countByType[actionTypeId]++; } @@ -87,7 +87,7 @@ export async function getTotalCount( Object.keys(aggs).reduce( (total: number, key: string) => parseInt(aggs[key], 10) + total, 0 - ) + (preconfiguredActions?.length ?? 0), + ) + (inMemoryConnectors?.length ?? 0), countByType, }; } catch (err) { @@ -109,7 +109,7 @@ export async function getInUseTotalCount( kibanaIndex: string, logger: Logger, referenceType?: string, - preconfiguredActions?: PreConfiguredAction[] + inMemoryConnectors?: InMemoryConnector[] ): Promise<{ hasErrors: boolean; errorMessage?: string; @@ -363,9 +363,9 @@ export async function getInUseTotalCount( if (actionRef === `preconfigured:${AlertHistoryEsIndexConnectorId}`) { preconfiguredAlertHistoryConnectors++; } - if (preconfiguredActions && actionTypeId === '__email') { + if (inMemoryConnectors && actionTypeId === '__email') { const preconfiguredConnectorId = actionRef.split(':')[1]; - const service = (preconfiguredActions.find( + const service = (inMemoryConnectors.find( (preconfConnector) => preconfConnector.id === preconfiguredConnectorId )?.config?.service ?? 'other') as string; const currentCount = diff --git a/x-pack/plugins/actions/server/usage/task.ts b/x-pack/plugins/actions/server/usage/task.ts index 7faf443019c6b..ef71d4ff46e3c 100644 --- a/x-pack/plugins/actions/server/usage/task.ts +++ b/x-pack/plugins/actions/server/usage/task.ts @@ -12,7 +12,7 @@ import { TaskManagerStartContract, IntervalSchedule, } from '@kbn/task-manager-plugin/server'; -import { PreConfiguredAction } from '../types'; +import { InMemoryConnector } from '../types'; import { getTotalCount, getInUseTotalCount, getExecutionsPerDayCount } from './actions_telemetry'; export const TELEMETRY_TASK_TYPE = 'actions_telemetry'; @@ -24,10 +24,10 @@ export function initializeActionsTelemetry( logger: Logger, taskManager: TaskManagerSetupContract, core: CoreSetup, - preconfiguredActions: PreConfiguredAction[], + inMemoryConnectors: InMemoryConnector[], eventLogIndex: string ) { - registerActionsTelemetryTask(logger, taskManager, core, preconfiguredActions, eventLogIndex); + registerActionsTelemetryTask(logger, taskManager, core, inMemoryConnectors, eventLogIndex); } export function scheduleActionsTelemetry(logger: Logger, taskManager: TaskManagerStartContract) { @@ -38,14 +38,14 @@ function registerActionsTelemetryTask( logger: Logger, taskManager: TaskManagerSetupContract, core: CoreSetup, - preconfiguredActions: PreConfiguredAction[], + inMemoryConnectors: InMemoryConnector[], eventLogIndex: string ) { taskManager.registerTaskDefinitions({ [TELEMETRY_TASK_TYPE]: { title: 'Actions usage fetch task', timeout: '5m', - createTaskRunner: telemetryTaskRunner(logger, core, preconfiguredActions, eventLogIndex), + createTaskRunner: telemetryTaskRunner(logger, core, inMemoryConnectors, eventLogIndex), }, }); } @@ -67,7 +67,7 @@ async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContra export function telemetryTaskRunner( logger: Logger, core: CoreSetup, - preconfiguredActions: PreConfiguredAction[], + inMemoryConnectors: InMemoryConnector[], eventLogIndex: string ) { return ({ taskInstance }: RunContext) => { @@ -89,8 +89,8 @@ export function telemetryTaskRunner( const actionIndex = await getActionIndex(); const esClient = await getEsClient(); return Promise.all([ - getTotalCount(esClient, actionIndex, logger, preconfiguredActions), - getInUseTotalCount(esClient, actionIndex, logger, undefined, preconfiguredActions), + getTotalCount(esClient, actionIndex, logger, inMemoryConnectors), + getInUseTotalCount(esClient, actionIndex, logger, undefined, inMemoryConnectors), getExecutionsPerDayCount(esClient, eventLogIndex, logger), ]).then(([totalAggegations, totalInUse, totalExecutionsPerDay]) => { const hasErrors = From 2aabeed2fbc739272599fb58b3965964797d0b9d Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 29 Jun 2023 12:35:19 +0300 Subject: [PATCH 13/45] Create system actions --- x-pack/plugins/actions/common/types.ts | 1 + .../server/action_type_registry.test.ts | 2 +- .../actions/server/action_type_registry.ts | 3 +- .../server/create_system_actions.test.ts | 49 +++++ .../actions/server/create_system_actions.ts | 27 +++ .../server/lib/ensure_sufficient_license.ts | 3 +- x-pack/plugins/actions/server/plugin.test.ts | 181 ++++++++++++------ x-pack/plugins/actions/server/plugin.ts | 26 ++- x-pack/plugins/actions/server/types.ts | 2 +- 9 files changed, 219 insertions(+), 75 deletions(-) create mode 100644 x-pack/plugins/actions/server/create_system_actions.test.ts create mode 100644 x-pack/plugins/actions/server/create_system_actions.ts diff --git a/x-pack/plugins/actions/common/types.ts b/x-pack/plugins/actions/common/types.ts index 275a757597d28..c3e1b35777fff 100644 --- a/x-pack/plugins/actions/common/types.ts +++ b/x-pack/plugins/actions/common/types.ts @@ -22,6 +22,7 @@ export interface ActionType { enabledInLicense: boolean; minimumLicenseRequired: LicenseType; supportedFeatureIds: string[]; + isSystemActionType: boolean; } export enum InvalidEmailReason { diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index a36732c2c7e91..e9079517eb708 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -226,7 +226,7 @@ describe('actionTypeRegistry', () => { name: 'My action type', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], - isSystemAction: true, + isSystemActionType: true, validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 7e9adcc99ac34..a54aadd2e465a 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -104,7 +104,7 @@ export class ActionTypeRegistry { ExecutorResultData = void >(actionType: ActionType) { // TODO: Remove when system action are supported - if (actionType.isSystemAction) { + if (actionType.isSystemActionType) { throw new Error( i18n.translate( 'xpack.actions.actionTypeRegistry.register.systemActionsNotSupportedErrorMessage', @@ -213,6 +213,7 @@ export class ActionTypeRegistry { enabledInConfig: this.actionsConfigUtils.isActionTypeEnabled(actionTypeId), enabledInLicense: !!this.licenseState.isLicenseValidForActionType(actionType).isValid, supportedFeatureIds: actionType.supportedFeatureIds, + isSystemActionType: !!actionType.isSystemActionType, })); } diff --git a/x-pack/plugins/actions/server/create_system_actions.test.ts b/x-pack/plugins/actions/server/create_system_actions.test.ts new file mode 100644 index 0000000000000..fd591f2c16429 --- /dev/null +++ b/x-pack/plugins/actions/server/create_system_actions.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createSystemConnectors } from './create_system_actions'; + +const actionTypes = [ + { + id: 'action-type', + name: 'My action type', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic' as const, + supportedFeatureIds: ['alerting'], + isSystemActionType: false, + }, + { + id: 'system-action-type-2', + name: 'My system action type', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic' as const, + supportedFeatureIds: ['alerting'], + isSystemActionType: true, + }, +]; + +describe('createSystemConnectors', () => { + it('creates the system actions correctly', () => { + expect(createSystemConnectors(actionTypes)).toEqual([ + { + id: 'system-connector-system-action-type-2', + actionTypeId: 'system-action-type-2', + name: 'system-connector-system-action-type-2', + secrets: {}, + config: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ]); + }); +}); diff --git a/x-pack/plugins/actions/server/create_system_actions.ts b/x-pack/plugins/actions/server/create_system_actions.ts new file mode 100644 index 0000000000000..90a13104ff76d --- /dev/null +++ b/x-pack/plugins/actions/server/create_system_actions.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ActionType } from '../common'; +import { InMemoryConnector } from './types'; + +export const createSystemConnectors = (actionTypes: ActionType[]): InMemoryConnector[] => { + const systemActionTypes = actionTypes.filter((actionType) => actionType.isSystemActionType); + + const systemConnectors: InMemoryConnector[] = systemActionTypes.map((systemActionType) => ({ + id: `system-connector-${systemActionType.id}`, + actionTypeId: systemActionType.id, + name: `system-connector-${systemActionType.id}`, + isMissingSecrets: false, + config: {}, + secrets: {}, + isDeprecated: false, + isPreconfigured: false, + isSystemAction: true, + })); + + return systemConnectors; +}; diff --git a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts index e7d781a7218bd..9c28621034c61 100644 --- a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts +++ b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts @@ -9,9 +9,10 @@ import { LICENSE_TYPE } from '@kbn/licensing-plugin/common/types'; import { ActionType } from '../types'; import { ActionTypeConfig, ActionTypeSecrets, ActionTypeParams } from '../types'; -const CASE_ACTION_TYPE_ID = '.case'; +const CASE_ACTION_TYPE_ID = '.cases'; const ServerLogActionTypeId = '.server-log'; const IndexActionTypeId = '.index'; + const ACTIONS_SCOPED_WITHIN_STACK = new Set([ ServerLogActionTypeId, IndexActionTypeId, diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index a1a58bf14cb13..44418835a1609 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -323,7 +323,7 @@ describe('Actions Plugin', () => { }); }); - describe('Preconfigured connectors', () => { + describe('inMemoryConnectors', () => { function getConfig(overrides = {}) { return { enabled: true, @@ -370,78 +370,135 @@ describe('Actions Plugin', () => { }; } - it('should handle preconfigured actions', async () => { - setup(getConfig()); - // coreMock.createSetup doesn't support Plugin generics - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); - pluginSetup.registerType({ - id: '.server-log', - name: 'Server log', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, + describe('Preconfigured connectors', () => { + it('should handle preconfigured actions', async () => { + setup(getConfig()); + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + pluginSetup.registerType({ + id: '.server-log', + name: 'Server log', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + const pluginStart = await plugin.start(coreStart, pluginsStart); + + expect(pluginStart.inMemoryConnectors.length).toEqual(1); + expect(pluginStart.isActionExecutable('preconfiguredServerLog', '.server-log')).toBe( + true + ); }); - const pluginStart = await plugin.start(coreStart, pluginsStart); + it('should handle preconfiguredAlertHistoryEsIndex = true', async () => { + setup(getConfig({ preconfiguredAlertHistoryEsIndex: true })); - expect(pluginStart.inMemoryConnectors.length).toEqual(1); - expect(pluginStart.isActionExecutable('preconfiguredServerLog', '.server-log')).toBe(true); - }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + pluginSetup.registerType({ + id: '.index', + name: 'ES Index', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); - it('should handle preconfiguredAlertHistoryEsIndex = true', async () => { - setup(getConfig({ preconfiguredAlertHistoryEsIndex: true })); + const pluginStart = await plugin.start(coreStart, pluginsStart); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); - pluginSetup.registerType({ - id: '.index', - name: 'ES Index', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, + expect(pluginStart.inMemoryConnectors.length).toEqual(2); + expect( + pluginStart.isActionExecutable('preconfigured-alert-history-es-index', '.index') + ).toBe(true); }); - const pluginStart = await plugin.start(coreStart, pluginsStart); - - expect(pluginStart.inMemoryConnectors.length).toEqual(2); - expect( - pluginStart.isActionExecutable('preconfigured-alert-history-es-index', '.index') - ).toBe(true); + it('should not allow preconfigured connector with same ID as AlertHistoryEsIndexConnectorId', async () => { + setup( + getConfig({ + preconfigured: { + [AlertHistoryEsIndexConnectorId]: { + actionTypeId: '.index', + name: 'clashing preconfigured index connector', + config: {}, + secrets: {}, + }, + }, + }) + ); + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await plugin.setup(coreSetup as any, pluginsSetup); + const pluginStart = await plugin.start(coreStart, pluginsStart); + + expect(pluginStart.inMemoryConnectors.length).toEqual(0); + expect(context.logger.get().warn).toHaveBeenCalledWith( + `Preconfigured connectors cannot have the id "${AlertHistoryEsIndexConnectorId}" because this is a reserved id.` + ); + }); }); - it('should not allow preconfigured connector with same ID as AlertHistoryEsIndexConnectorId', async () => { - setup( - getConfig({ - preconfigured: { - [AlertHistoryEsIndexConnectorId]: { - actionTypeId: '.index', - name: 'clashing preconfigured index connector', - config: {}, - secrets: {}, - }, + // TODO: Unskip when registering system actions is allowed + describe.skip('System actions', () => { + it('should handle system actions', async () => { + setup(getConfig()); + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + + pluginSetup.registerType({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, }, - }) - ); - // coreMock.createSetup doesn't support Plugin generics - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await plugin.setup(coreSetup as any, pluginsSetup); - const pluginStart = await plugin.start(coreStart, pluginsStart); + isSystemActionType: true, + executor, + }); - expect(pluginStart.inMemoryConnectors.length).toEqual(0); - expect(context.logger.get().warn).toHaveBeenCalledWith( - `Preconfigured connectors cannot have the id "${AlertHistoryEsIndexConnectorId}" because this is a reserved id.` - ); + const pluginStart = await plugin.start(coreStart, pluginsStart); + + // inMemoryConnectors holds both preconfigure and system connectors + expect(pluginStart.inMemoryConnectors.length).toEqual(2); + expect(pluginStart.inMemoryConnectors).toEqual([ + { + id: 'preconfiguredServerLog', + actionTypeId: '.server-log', + name: 'preconfigured-server-log', + config: {}, + secrets: {}, + isDeprecated: false, + isPreconfigured: true, + isSystemAction: false, + }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ]); + expect(pluginStart.isActionExecutable('preconfiguredServerLog', '.cases')).toBe(true); + }); }); }); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 85cacdcd7a5a6..8fe4fe5928a50 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -106,6 +106,7 @@ import { UnsecuredActionsClient, } from './unsecured_actions_client/unsecured_actions_client'; import { createBulkUnsecuredExecutionEnqueuerFunction } from './create_unsecured_execute_function'; +import { createSystemConnectors } from './create_system_actions'; export interface PluginSetupContract { registerType< @@ -200,7 +201,7 @@ export class ActionsPlugin implements Plugin @@ -416,7 +424,7 @@ export class ActionsPlugin implements Plugin renderActionParameterTemplates(actionTypeRegistry, ...args), }; diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 4e078af3868f6..d51af5a37f812 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -140,7 +140,7 @@ export interface ActionType< secrets: ValidatorType; connector?: (config: Config, secrets: Secrets) => string | null; }; - isSystemAction?: boolean; + isSystemActionType?: boolean; renderParameterTemplates?: RenderParameterTemplates; executor: ExecutorType; } From 3cf469a026c50cb930d50022a4d29a7b34458e1b Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 30 Jun 2023 12:56:15 +0300 Subject: [PATCH 14/45] Prevent create/update/delete of system actions --- .../server/action_type_registry.test.ts | 98 +++- .../actions/server/action_type_registry.ts | 18 +- .../actions/server/actions_client.test.ts | 459 +++++++++++++++++- .../plugins/actions/server/actions_client.ts | 87 +++- x-pack/plugins/actions/server/plugin.test.ts | 3 +- .../apps/synthetics/state/settings/api.ts | 2 + 6 files changed, 607 insertions(+), 60 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index e9079517eb708..d1c6989e39f17 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -217,7 +217,7 @@ describe('actionTypeRegistry', () => { expect(actionTypeRegistryParams.licensing.featureUsage.register).not.toHaveBeenCalled(); }); - test('does not allows registering system actions', () => { + test('allows registering system actions', () => { const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); expect(() => @@ -234,7 +234,7 @@ describe('actionTypeRegistry', () => { }, executor, }) - ).toThrowErrorMatchingInlineSnapshot(`"System actions are not supported"`); + ).not.toThrow(); }); }); @@ -302,6 +302,7 @@ describe('actionTypeRegistry', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]); expect(mockedActionsConfig.isActionTypeEnabled).toHaveBeenCalled(); @@ -345,11 +346,46 @@ describe('actionTypeRegistry', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]); expect(mockedActionsConfig.isActionTypeEnabled).toHaveBeenCalled(); expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalled(); }); + + test('sets the isSystemActionType correctly for system actions', () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const actionTypes = actionTypeRegistry.list(); + + expect(actionTypes).toEqual([ + { + id: '.cases', + name: 'Cases', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + isSystemActionType: true, + }, + ]); + }); }); describe('has()', () => { @@ -567,4 +603,62 @@ describe('actionTypeRegistry', () => { expect(result).toEqual(['foo']); }); }); + + describe('isSystemActionType()', () => { + it('should return true if the action type is a system action type', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const result = registry.isSystemActionType('.cases'); + expect(result).toBe(true); + }); + + it('should return false if the action type is not a system action type', () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: 'foo', + name: 'Foo', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + const allTypes = registry.getAllTypes(); + expect(allTypes.length).toBe(1); + + const result = registry.isSystemActionType('foo'); + expect(result).toBe(false); + }); + + it('should return false if the action type does not exists', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + const allTypes = registry.getAllTypes(); + expect(allTypes.length).toBe(0); + + const result = registry.isSystemActionType('not-exist'); + expect(result).toBe(false); + }); + }); }); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index a54aadd2e465a..0c14ba028ea90 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -94,6 +94,12 @@ export class ActionTypeRegistry { ); } + /** + * Returns true if the action type is a system action type + */ + public isSystemActionType = (actionTypeId: string): boolean => + Boolean(this.actionTypes.get(actionTypeId)?.isSystemActionType); + /** * Registers an action type to the action type registry */ @@ -103,18 +109,6 @@ export class ActionTypeRegistry { Params extends ActionTypeParams = ActionTypeParams, ExecutorResultData = void >(actionType: ActionType) { - // TODO: Remove when system action are supported - if (actionType.isSystemActionType) { - throw new Error( - i18n.translate( - 'xpack.actions.actionTypeRegistry.register.systemActionsNotSupportedErrorMessage', - { - defaultMessage: 'System actions are not supported', - } - ) - ); - } - if (this.has(actionType.id)) { throw new Error( i18n.translate( diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 8ae4aeebabc8e..d272f776c991d 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -758,6 +758,35 @@ describe('create()', () => { `"This mySuperRadTestPreconfiguredId already exist in preconfigured action."` ); }); + + it('throws when creating a system connector', async () => { + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + await expect( + actionsClient.create({ + action: { + name: 'my name', + actionTypeId: '.cases', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"System action creation is forbidden. Action type: .cases."` + ); + }); }); describe('get()', () => { @@ -1011,6 +1040,50 @@ describe('get()', () => { }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); }); + + it('return system action with id', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + const result = await actionsClient.get({ id: 'system-connector-.cases' }); + + expect(result).toEqual({ + id: 'system-connector-.cases', + actionTypeId: '.cases', + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + name: 'system-connector-.cases', + }); + + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + }); }); describe('getAll()', () => { @@ -1158,7 +1231,7 @@ describe('getAll()', () => { }); }); - test('calls unsecuredSavedObjectsClient with parameters', async () => { + test('calls unsecuredSavedObjectsClient with parameters and returns inMemoryConnectors correctly', async () => { const expectedResult = { total: 1, per_page: 10, @@ -1186,6 +1259,7 @@ describe('getAll()', () => { aggregations: { '1': { doc_count: 6 }, testPreconfigured: { doc_count: 2 }, + 'system-connector-.cases': { doc_count: 2 }, }, } ); @@ -1215,31 +1289,51 @@ describe('getAll()', () => { foo: 'bar', }, }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, ], connectorTokenClient: connectorTokenClientMock.create(), getEventLogClient, }); + const result = await actionsClient.getAll(); + expect(result).toEqual([ { - id: '1', + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', isPreconfigured: false, - isSystemAction: false, isDeprecated: false, + isSystemAction: true, + referencedByCount: 2, + }, + { + id: '1', name: 'test', - config: { - foo: 'bar', - }, isMissingSecrets: false, + config: { foo: 'bar' }, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, referencedByCount: 6, }, { id: 'testPreconfigured', actionTypeId: '.slack', + name: 'test', isPreconfigured: true, - isSystemAction: false, isDeprecated: false, - name: 'test', + isSystemAction: false, referencedByCount: 2, }, ]); @@ -1386,7 +1480,7 @@ describe('getBulk()', () => { }); }); - test('calls getBulk unsecuredSavedObjectsClient with parameters', async () => { + test('calls getBulk unsecuredSavedObjectsClient with parameters and return inMemoryConnectors correctly', async () => { unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ { @@ -1410,6 +1504,7 @@ describe('getBulk()', () => { aggregations: { '1': { doc_count: 6 }, testPreconfigured: { doc_count: 2 }, + 'system-connector-.cases': { doc_count: 2 }, }, } ); @@ -1439,35 +1534,59 @@ describe('getBulk()', () => { foo: 'bar', }, }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, ], connectorTokenClient: connectorTokenClientMock.create(), getEventLogClient, }); - const result = await actionsClient.getBulk(['1', 'testPreconfigured']); + + const result = await actionsClient.getBulk([ + '1', + 'testPreconfigured', + 'system-connector-.cases', + ]); + expect(result).toEqual([ { - actionTypeId: '.slack', - config: { - foo: 'bar', - }, id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: {}, isPreconfigured: true, - isSystemAction: false, isDeprecated: false, + isSystemAction: false, name: 'test', + config: { foo: 'bar' }, + }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, }, { - actionTypeId: 'test', - config: { - foo: 'bar', - }, id: '1', + actionTypeId: 'test', + name: 'test', + config: { foo: 'bar' }, isMissingSecrets: false, isPreconfigured: false, - isSystemAction: false, isDeprecated: false, - name: 'test', + isSystemAction: false, }, ]); }); @@ -1842,6 +1961,83 @@ describe('delete()', () => { ] `); }); + + it('throws when trying to delete a system connector', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + inMemoryConnectors: [ + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await expect( + actionsClient.delete({ id: 'system-connector-.cases' }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"System action system-connector-.cases is not allowed to delete."` + ); + }); + + it('throws when trying to delete a preconfigured connector', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await expect( + actionsClient.delete({ id: 'testPreconfigured' }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Preconfigured action testPreconfigured is not allowed to delete."` + ); + }); }); describe('update()', () => { @@ -2318,6 +2514,97 @@ describe('update()', () => { }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); + + it('throws when trying to update a system connector', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + inMemoryConnectors: [ + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await expect( + actionsClient.update({ + id: 'system-connector-.cases', + action: { + name: 'my name', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"System action system-connector-.cases is not allowed to update."` + ); + }); + + it('throws when trying to update a preconfigured connector', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await expect( + actionsClient.update({ + id: 'testPreconfigured', + action: { + name: 'my name', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Preconfigured action testPreconfigured is not allowed to update."` + ); + }); }); describe('execute()', () => { @@ -2701,7 +2988,7 @@ describe('isActionTypeEnabled()', () => { }); describe('isPreconfigured()', () => { - test('should return true if connector id is in list of preconfigured connectors', () => { + test('should return true if the connector is a preconfigured connector', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -2729,6 +3016,17 @@ describe('isPreconfigured()', () => { foo: 'bar', }, }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, ], connectorTokenClient: new ConnectorTokenClient({ unsecuredSavedObjectsClient: savedObjectsClientMock.create(), @@ -2741,7 +3039,7 @@ describe('isPreconfigured()', () => { expect(actionsClient.isPreconfigured('testPreconfigured')).toEqual(true); }); - test('should return false if connector id is not in list of preconfigured connectors', () => { + test('should return false if the connector is not preconfigured connector', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -2769,6 +3067,17 @@ describe('isPreconfigured()', () => { foo: 'bar', }, }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, ], connectorTokenClient: new ConnectorTokenClient({ unsecuredSavedObjectsClient: savedObjectsClientMock.create(), @@ -2782,6 +3091,110 @@ describe('isPreconfigured()', () => { }); }); +describe('isSystemAction()', () => { + test('should return true if the connector is a system connectors', () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: new ConnectorTokenClient({ + unsecuredSavedObjectsClient: savedObjectsClientMock.create(), + encryptedSavedObjectsClient: encryptedSavedObjectsMock.createClient(), + logger, + }), + getEventLogClient, + }); + + expect(actionsClient.isSystemAction('system-connector-.cases')).toEqual(true); + }); + + test('should return false if connector id is not in list of preconfigured connectors', () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'system-connector-.cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: new ConnectorTokenClient({ + unsecuredSavedObjectsClient: savedObjectsClientMock.create(), + encryptedSavedObjectsClient: encryptedSavedObjectsMock.createClient(), + logger, + }), + getEventLogClient, + }); + + expect(actionsClient.isSystemAction(uuidv4())).toEqual(false); + }); +}); + describe('getGlobalExecutionLogWithAuth()', () => { const opts: GetGlobalExecutionLogParams = { dateStart: '2023-01-09T08:55:56-08:00', diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 5366d71e30106..fe54f29b2efde 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -194,9 +194,24 @@ export class ActionsClient { action: { actionTypeId, name, config, secrets }, options, }: CreateOptions): Promise { + if (this.actionTypeRegistry.isSystemActionType(actionTypeId)) { + throw Boom.badRequest( + i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { + defaultMessage: 'System action creation is forbidden. Action type: {actionTypeId}.', + values: { + actionTypeId, + }, + }) + ); + } + const id = options?.id || SavedObjectsUtils.generateId(); - if (this.inMemoryConnectors.some((inMemoryConnector) => inMemoryConnector.id === id)) { + if ( + this.inMemoryConnectors.some( + (inMemoryConnector) => inMemoryConnector.isPreconfigured && inMemoryConnector.id === id + ) + ) { throw Boom.badRequest( i18n.translate('xpack.actions.serverSideErrors.predefinedIdConnectorAlreadyExists', { defaultMessage: 'This {id} already exist in preconfigured action.', @@ -272,10 +287,20 @@ export class ActionsClient { try { await this.authorization.ensureAuthorized('update'); - if ( - this.inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === id) !== - undefined - ) { + const inMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + + if (inMemoryConnector?.isSystemAction) { + throw Boom.badRequest( + i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { + defaultMessage: 'System action {id} is not allowed to update.', + values: { + id, + }, + }) + ); + } + + if (inMemoryConnector?.isPreconfigured) { throw new PreconfiguredActionDisabledModificationError( i18n.translate('xpack.actions.serverSideErrors.predefinedActionUpdateDisabled', { defaultMessage: 'Preconfigured action {id} is not allowed to update.', @@ -380,10 +405,9 @@ export class ActionsClient { throw error; } - const inMemoryConnectorsList = this.inMemoryConnectors.find( - (inMemoryConnector) => inMemoryConnector.id === id - ); - if (inMemoryConnectorsList !== undefined) { + const inMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + + if (inMemoryConnector !== undefined) { this.auditLogger?.log( connectorAuditEvent({ action: ConnectorAuditAction.GET, @@ -393,11 +417,11 @@ export class ActionsClient { return { id, - actionTypeId: inMemoryConnectorsList.actionTypeId, - name: inMemoryConnectorsList.name, - isPreconfigured: true, - isSystemAction: false, - isDeprecated: isConnectorDeprecated(inMemoryConnectorsList), + actionTypeId: inMemoryConnector.actionTypeId, + name: inMemoryConnector.name, + isPreconfigured: inMemoryConnector.isPreconfigured, + isSystemAction: inMemoryConnector.isSystemAction, + isDeprecated: isConnectorDeprecated(inMemoryConnector), }; } @@ -462,9 +486,9 @@ export class ActionsClient { id: inMemoryConnector.id, actionTypeId: inMemoryConnector.actionTypeId, name: inMemoryConnector.name, - isPreconfigured: true, + isPreconfigured: inMemoryConnector.isPreconfigured, isDeprecated: isConnectorDeprecated(inMemoryConnector), - isSystemAction: false, + isSystemAction: inMemoryConnector.isSystemAction, })), ].sort((a, b) => a.name.localeCompare(b.name)); return await injectExtraFindData(this.kibanaIndices, this.scopedClusterClient, mergedResult); @@ -490,10 +514,12 @@ export class ActionsClient { } const actionResults = new Array(); + for (const actionId of ids) { const action = this.inMemoryConnectors.find( (inMemoryConnector) => inMemoryConnector.id === actionId ); + if (action !== undefined) { actionResults.push(action); } @@ -531,6 +557,7 @@ export class ActionsClient { } actionResults.push(actionFromSavedObject(action, isConnectorDeprecated(action.attributes))); } + return actionResults; } @@ -632,10 +659,20 @@ export class ActionsClient { try { await this.authorization.ensureAuthorized('delete'); - if ( - this.inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === id) !== - undefined - ) { + const inMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + + if (inMemoryConnector?.isSystemAction) { + throw Boom.badRequest( + i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { + defaultMessage: 'System action {id} is not allowed to delete.', + values: { + id, + }, + }) + ); + } + + if (inMemoryConnector?.isPreconfigured) { throw new PreconfiguredActionDisabledModificationError( i18n.translate('xpack.actions.serverSideErrors.predefinedActionDeleteDisabled', { defaultMessage: 'Preconfigured action {id} is not allowed to delete.', @@ -765,7 +802,15 @@ export class ActionsClient { } public isPreconfigured(connectorId: string): boolean { - return !!this.inMemoryConnectors.find((preconfigured) => preconfigured.id === connectorId); + return !!this.inMemoryConnectors.find( + (connector) => connector.isPreconfigured && connector.id === connectorId + ); + } + + public isSystemAction(connectorId: string): boolean { + return !!this.inMemoryConnectors.find( + (connector) => connector.isSystemAction && connector.id === connectorId + ); } public async getGlobalExecutionLogWithAuth({ diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 44418835a1609..8882103222705 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -448,8 +448,7 @@ describe('Actions Plugin', () => { }); }); - // TODO: Unskip when registering system actions is allowed - describe.skip('System actions', () => { + describe('System actions', () => { it('should handle system actions', async () => { setup(getConfig()); // coreMock.createSetup doesn't support Plugin generics diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts index b7ae7884b178a..da8855f531ef9 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts @@ -85,6 +85,7 @@ export const fetchActionTypes = async (): Promise => { enabled_in_license: enabledInLicense, minimum_license_required: minimumLicenseRequired, supported_feature_ids: supportedFeatureIds, + is_system_action_type: isSystemActionType, ...res }: AsApiContract) => ({ ...res, @@ -92,6 +93,7 @@ export const fetchActionTypes = async (): Promise => { enabledInLicense, minimumLicenseRequired, supportedFeatureIds, + isSystemActionType, }) ); }; From 8c1246c775d29f74bf5157fd6ead0ff0d0ce3591 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 30 Jun 2023 14:19:43 +0300 Subject: [PATCH 15/45] Fix types --- x-pack/plugins/actions/server/action_type_registry.mock.ts | 1 + x-pack/plugins/actions/server/actions_client.mock.ts | 1 + .../plugins/actions/server/routes/connector_types.test.ts | 6 ++++++ x-pack/plugins/actions/server/routes/connector_types.ts | 2 ++ .../actions/server/routes/legacy/list_action_types.test.ts | 4 ++++ 5 files changed, 14 insertions(+) diff --git a/x-pack/plugins/actions/server/action_type_registry.mock.ts b/x-pack/plugins/actions/server/action_type_registry.mock.ts index 922fc39ada8a2..532b192001e7a 100644 --- a/x-pack/plugins/actions/server/action_type_registry.mock.ts +++ b/x-pack/plugins/actions/server/action_type_registry.mock.ts @@ -17,6 +17,7 @@ const createActionTypeRegistryMock = () => { ensureActionTypeEnabled: jest.fn(), isActionTypeEnabled: jest.fn(), isActionExecutable: jest.fn(), + isSystemActionType: jest.fn(), getUtils: jest.fn(), }; return mocked; diff --git a/x-pack/plugins/actions/server/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client.mock.ts index b80914c309b25..3fb08e5008793 100644 --- a/x-pack/plugins/actions/server/actions_client.mock.ts +++ b/x-pack/plugins/actions/server/actions_client.mock.ts @@ -27,6 +27,7 @@ const createActionsClientMock = () => { listTypes: jest.fn(), isActionTypeEnabled: jest.fn(), isPreconfigured: jest.fn(), + isSystemAction: jest.fn(), getGlobalExecutionKpiWithAuth: jest.fn(), getGlobalExecutionLogWithAuth: jest.fn(), }; diff --git a/x-pack/plugins/actions/server/routes/connector_types.test.ts b/x-pack/plugins/actions/server/routes/connector_types.test.ts index 8a4639cf3f4a2..4b1ce8189c2a1 100644 --- a/x-pack/plugins/actions/server/routes/connector_types.test.ts +++ b/x-pack/plugins/actions/server/routes/connector_types.test.ts @@ -42,6 +42,7 @@ describe('connectorTypesRoute', () => { enabledInLicense: true, minimumLicenseRequired: 'gold' as LicenseType, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; @@ -57,6 +58,7 @@ describe('connectorTypesRoute', () => { "enabled_in_config": true, "enabled_in_license": true, "id": "1", + "is_system_action_type": false, "minimum_license_required": "gold", "name": "name", "supported_feature_ids": Array [ @@ -101,6 +103,7 @@ describe('connectorTypesRoute', () => { enabledInLicense: true, supportedFeatureIds: ['alerting'], minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, }, ]; @@ -124,6 +127,7 @@ describe('connectorTypesRoute', () => { "enabled_in_config": true, "enabled_in_license": true, "id": "1", + "is_system_action_type": false, "minimum_license_required": "gold", "name": "name", "supported_feature_ids": Array [ @@ -175,6 +179,7 @@ describe('connectorTypesRoute', () => { enabledInLicense: true, supportedFeatureIds: ['alerting'], minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, }, ]; @@ -217,6 +222,7 @@ describe('connectorTypesRoute', () => { enabledInLicense: true, supportedFeatureIds: ['alerting'], minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, }, ]; diff --git a/x-pack/plugins/actions/server/routes/connector_types.ts b/x-pack/plugins/actions/server/routes/connector_types.ts index 6d014a4eba61c..39c24b6c178c8 100644 --- a/x-pack/plugins/actions/server/routes/connector_types.ts +++ b/x-pack/plugins/actions/server/routes/connector_types.ts @@ -23,6 +23,7 @@ const rewriteBodyRes: RewriteResponseCase = (results) => { enabledInLicense, minimumLicenseRequired, supportedFeatureIds, + isSystemActionType, ...res }) => ({ ...res, @@ -30,6 +31,7 @@ const rewriteBodyRes: RewriteResponseCase = (results) => { enabled_in_license: enabledInLicense, minimum_license_required: minimumLicenseRequired, supported_feature_ids: supportedFeatureIds, + is_system_action_type: isSystemActionType, }) ); }; diff --git a/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts b/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts index 045ad01719e93..4674bbb9936a6 100644 --- a/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts +++ b/x-pack/plugins/actions/server/routes/legacy/list_action_types.test.ts @@ -50,6 +50,7 @@ describe('listActionTypesRoute', () => { enabledInLicense: true, minimumLicenseRequired: 'gold' as LicenseType, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; @@ -65,6 +66,7 @@ describe('listActionTypesRoute', () => { "enabledInConfig": true, "enabledInLicense": true, "id": "1", + "isSystemActionType": false, "minimumLicenseRequired": "gold", "name": "name", "supportedFeatureIds": Array [ @@ -99,6 +101,7 @@ describe('listActionTypesRoute', () => { enabledInLicense: true, minimumLicenseRequired: 'gold' as LicenseType, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; @@ -141,6 +144,7 @@ describe('listActionTypesRoute', () => { enabledInLicense: true, minimumLicenseRequired: 'gold' as LicenseType, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; From 52ef0e5103282fda1ad32927fb4df8f62f60996f Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 30 Jun 2023 21:13:14 +0300 Subject: [PATCH 16/45] Fix types --- .../impl/connectorland/connector_selector/index.tsx | 1 + .../impl/connectorland/connector_setup/index.tsx | 1 + x-pack/plugins/actions/server/actions_client.test.ts | 4 ---- x-pack/plugins/cases/public/common/mock/connectors.ts | 6 ++++++ x-pack/plugins/cases/server/client/configure/client.test.ts | 4 ++++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx index c9c514a4f4254..010b4be5ad4b0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx @@ -50,6 +50,7 @@ export const ConnectorSelector: React.FC = React.memo( enabledInLicense: true, minimumLicenseRequired: 'platinum', supportedFeatureIds: ['general'], + isSystemActionType: false, id: '.gen-ai', name: 'Generative AI', enabled: true, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx index 66c58e2b9ac61..c430355051ce0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_setup/index.tsx @@ -92,6 +92,7 @@ export const useConnectorSetup = ({ actionTypes?.find((at) => at.id === GEN_AI_CONNECTOR_ID) ?? { enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, minimumLicenseRequired: 'platinum', supportedFeatureIds: ['general'], id: '.gen-ai', diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index f69152a954973..7083d08973149 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -1315,7 +1315,6 @@ describe('getAll()', () => { isPreconfigured: false, isSystemAction: false, isDeprecated: false, - isSystemAction: true, referencedByCount: 2, }, { @@ -1335,7 +1334,6 @@ describe('getAll()', () => { isPreconfigured: true, isSystemAction: false, isDeprecated: false, - isSystemAction: false, referencedByCount: 2, }, ]); @@ -1566,7 +1564,6 @@ describe('getBulk()', () => { isPreconfigured: true, isSystemAction: false, isDeprecated: false, - isSystemAction: false, name: 'test', config: { foo: 'bar' }, }, @@ -1590,7 +1587,6 @@ describe('getBulk()', () => { isPreconfigured: false, isSystemAction: false, isDeprecated: false, - isSystemAction: false, }, ]); }); diff --git a/x-pack/plugins/cases/public/common/mock/connectors.ts b/x-pack/plugins/cases/public/common/mock/connectors.ts index 3485af438a4d9..f1ba88deedd14 100644 --- a/x-pack/plugins/cases/public/common/mock/connectors.ts +++ b/x-pack/plugins/cases/public/common/mock/connectors.ts @@ -79,6 +79,7 @@ export const actionTypesMock: ActionTypeConnector[] = [ enabledInConfig: true, enabledInLicense: true, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, { id: '.index', @@ -88,6 +89,7 @@ export const actionTypesMock: ActionTypeConnector[] = [ enabledInConfig: true, enabledInLicense: true, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, { id: '.servicenow', @@ -97,6 +99,7 @@ export const actionTypesMock: ActionTypeConnector[] = [ enabledInConfig: true, enabledInLicense: true, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, { id: '.jira', @@ -106,6 +109,7 @@ export const actionTypesMock: ActionTypeConnector[] = [ enabledInConfig: true, enabledInLicense: true, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, { id: '.resilient', @@ -115,6 +119,7 @@ export const actionTypesMock: ActionTypeConnector[] = [ enabledInConfig: true, enabledInLicense: true, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, { id: '.servicenow-sir', @@ -124,6 +129,7 @@ export const actionTypesMock: ActionTypeConnector[] = [ enabledInConfig: true, enabledInLicense: true, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, ]; diff --git a/x-pack/plugins/cases/server/client/configure/client.test.ts b/x-pack/plugins/cases/server/client/configure/client.test.ts index 43e19a799b5e6..f87cfa40c4765 100644 --- a/x-pack/plugins/cases/server/client/configure/client.test.ts +++ b/x-pack/plugins/cases/server/client/configure/client.test.ts @@ -34,6 +34,7 @@ describe('client', () => { enabledInLicense: true, minimumLicenseRequired: 'basic' as const, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, { id: '.servicenow', @@ -43,6 +44,7 @@ describe('client', () => { enabledInLicense: true, minimumLicenseRequired: 'basic' as const, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, { id: '.unsupported', @@ -52,6 +54,7 @@ describe('client', () => { enabledInLicense: true, minimumLicenseRequired: 'basic' as const, supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, { id: '.swimlane', @@ -61,6 +64,7 @@ describe('client', () => { enabledInLicense: false, minimumLicenseRequired: 'basic' as const, supportedFeatureIds: ['alerting', 'cases'], + isSystemActionType: false, }, ]; From d7631611b4b4aac0090e785b2f5ca4eca1654374 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 1 Jul 2023 13:16:38 +0300 Subject: [PATCH 17/45] Remove cases from basic --- .../actions/server/lib/ensure_sufficient_license.test.ts | 3 --- .../actions/server/lib/ensure_sufficient_license.ts | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts index 06e5380c04f97..5cd2fec223ffc 100644 --- a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts +++ b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.test.ts @@ -32,9 +32,6 @@ describe('ensureSufficientLicense()', () => { }); it('allows licenses below gold for allowed connectors', () => { - expect(() => - ensureSufficientLicense({ ...sampleActionType, id: '.case', minimumLicenseRequired: 'basic' }) - ).not.toThrow(); expect(() => ensureSufficientLicense({ ...sampleActionType, diff --git a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts index 9c28621034c61..5d1988cdd8fa0 100644 --- a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts +++ b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts @@ -9,15 +9,10 @@ import { LICENSE_TYPE } from '@kbn/licensing-plugin/common/types'; import { ActionType } from '../types'; import { ActionTypeConfig, ActionTypeSecrets, ActionTypeParams } from '../types'; -const CASE_ACTION_TYPE_ID = '.cases'; const ServerLogActionTypeId = '.server-log'; const IndexActionTypeId = '.index'; -const ACTIONS_SCOPED_WITHIN_STACK = new Set([ - ServerLogActionTypeId, - IndexActionTypeId, - CASE_ACTION_TYPE_ID, -]); +const ACTIONS_SCOPED_WITHIN_STACK = new Set([ServerLogActionTypeId, IndexActionTypeId]); export function ensureSufficientLicense< Config extends ActionTypeConfig, From 5c3245e9753868c8eef892c125d5796d7cdd8205 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 1 Jul 2023 13:21:57 +0300 Subject: [PATCH 18/45] Convert preconfigured to in-memory --- .../server/action_type_registry.test.ts | 34 +- .../actions/server/action_type_registry.ts | 14 +- .../actions/server/actions_client.test.ts | 243 ++++-- .../plugins/actions/server/actions_client.ts | 52 +- .../server/create_execute_function.test.ts | 446 +++++++++++ .../actions/server/create_execute_function.ts | 27 +- .../server/create_system_actions.test.ts | 2 +- .../actions/server/create_system_actions.ts | 2 +- .../create_unsecured_execute_function.test.ts | 724 +++++++++--------- .../create_unsecured_execute_function.ts | 2 +- .../server/lib/action_executor.test.ts | 331 ++++++++ .../actions/server/lib/action_executor.ts | 23 +- .../lib/action_task_params_utils.test.ts | 4 +- .../server/lib/action_task_params_utils.ts | 6 +- ...ate_action_event_log_record_object.test.ts | 4 +- .../create_action_event_log_record_object.ts | 6 +- x-pack/plugins/actions/server/plugin.test.ts | 4 +- x-pack/plugins/actions/server/plugin.ts | 3 +- .../server/routes/connector_types.test.ts | 2 + .../action_task_params_migrations.test.ts | 25 +- .../action_task_params_migrations.ts | 4 +- .../task_runner/execution_handler.test.ts | 2 +- 22 files changed, 1457 insertions(+), 503 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index d1c6989e39f17..60b1cf75bc79f 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -47,6 +47,16 @@ describe('actionTypeRegistry', () => { isDeprecated: false, isSystemAction: false, }, + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, ], }; }); @@ -360,7 +370,7 @@ describe('actionTypeRegistry', () => { actionTypeRegistry.register({ id: '.cases', name: 'Cases', - minimumLicenseRequired: 'basic', + minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], validate: { config: { schema: schema.object({}) }, @@ -380,7 +390,7 @@ describe('actionTypeRegistry', () => { enabled: true, enabledInConfig: true, enabledInLicense: true, - minimumLicenseRequired: 'basic', + minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], isSystemActionType: true, }, @@ -414,6 +424,7 @@ describe('actionTypeRegistry', () => { describe('isActionTypeEnabled', () => { let actionTypeRegistry: ActionTypeRegistry; + const fooActionType: ActionType = { id: 'foo', name: 'Foo', @@ -429,9 +440,17 @@ describe('actionTypeRegistry', () => { }, }; + const systemActionType: ActionType = { + ...fooActionType, + id: 'system-action-type', + name: 'System action type', + isSystemActionType: true, + }; + beforeEach(() => { actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register(fooActionType); + actionTypeRegistry.register(systemActionType); }); test('should call isActionTypeEnabled of the actions config', async () => { @@ -453,6 +472,15 @@ describe('actionTypeRegistry', () => { expect(actionTypeRegistry.isActionExecutable('my-slack1', 'foo')).toEqual(true); }); + test('should return true when isActionTypeEnabled is false and isLicenseValidForActionType is true and it has system connectors', async () => { + mockedActionsConfig.isActionTypeEnabled.mockReturnValue(false); + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + + expect(actionTypeRegistry.isActionExecutable('my-slack1', 'system-action-type')).toEqual( + true + ); + }); + test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => { mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); actionTypeRegistry.isActionTypeEnabled('foo'); @@ -611,7 +639,7 @@ describe('actionTypeRegistry', () => { registry.register({ id: '.cases', name: 'Cases', - minimumLicenseRequired: 'basic', + minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], validate: { config: { schema: schema.object({}) }, diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 31edcf4c22419..1b133af7584b6 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -78,7 +78,7 @@ export class ActionTypeRegistry { } /** - * Returns true if action type is enabled or it is a preconfigured action type. + * Returns true if action type is enabled or it is an in memory action type. */ public isActionExecutable( actionId: string, @@ -109,18 +109,6 @@ export class ActionTypeRegistry { Params extends ActionTypeParams = ActionTypeParams, ExecutorResultData = void >(actionType: ActionType) { - // TODO: Remove when system action are supported - if (actionType.isSystemAction) { - throw new Error( - i18n.translate( - 'xpack.actions.actionTypeRegistry.register.systemActionsNotSupportedErrorMessage', - { - defaultMessage: 'System actions are not supported', - } - ) - ); - } - if (this.has(actionType.id)) { throw new Error( i18n.translate( diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 7083d08973149..465e3f7ff4f85 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -763,7 +763,7 @@ describe('create()', () => { actionTypeRegistry.register({ id: '.cases', name: 'Cases', - minimumLicenseRequired: 'basic', + minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], validate: { config: { schema: schema.object({}) }, @@ -787,6 +787,47 @@ describe('create()', () => { `"System action creation is forbidden. Action type: .cases."` ); }); + + it('throws when creating a system connector where the action type is not registered by a system connector exists in the in-memory list', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await expect( + actionsClient.create({ + action: { + name: 'my name', + actionTypeId: '.cases', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Action type \\".cases\\" is not registered."`); + }); }); describe('get()', () => { @@ -847,6 +888,40 @@ describe('get()', () => { expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); }); + test('ensures user is authorised to get a system action', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + await actionsClient.get({ id: 'system-connector-.cases' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + }); + test('throws when user is not authorised to get the type of action', async () => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', @@ -914,6 +989,48 @@ describe('get()', () => { expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); }); + + test('throws when user is not authorised to get a system action', async () => { + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to get a "system-connector-.cases" action`) + ); + + await expect( + actionsClient.get({ id: 'system-connector-.cases' }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to get a "system-connector-.cases" action]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + }); }); describe('auditLogger', () => { @@ -1058,7 +1175,7 @@ describe('get()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -1079,7 +1196,7 @@ describe('get()', () => { isPreconfigured: false, isDeprecated: false, isSystemAction: true, - name: 'system-connector-.cases', + name: 'System action: .cases', }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); @@ -1292,7 +1409,7 @@ describe('getAll()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -1311,9 +1428,9 @@ describe('getAll()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', isPreconfigured: false, - isSystemAction: false, + isSystemAction: true, isDeprecated: false, referencedByCount: 2, }, @@ -1537,7 +1654,7 @@ describe('getBulk()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -1570,7 +1687,7 @@ describe('getBulk()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -1962,7 +2079,7 @@ describe('delete()', () => { `); }); - it('throws when trying to delete a system connector', async () => { + it('throws when trying to delete a preconfigured connector', async () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -1971,15 +2088,18 @@ describe('delete()', () => { kibanaIndices, inMemoryConnectors: [ { - id: 'system-connector-.cases', - actionTypeId: '.cases', - name: 'system-connector-.cases', - config: {}, - secrets: {}, + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, isDeprecated: false, - isMissingSecrets: false, - isPreconfigured: false, - isSystemAction: true, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, }, ], actionExecutor, @@ -1993,13 +2113,13 @@ describe('delete()', () => { }); await expect( - actionsClient.delete({ id: 'system-connector-.cases' }) + actionsClient.delete({ id: 'testPreconfigured' }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"System action system-connector-.cases is not allowed to delete."` + `"Preconfigured action testPreconfigured is not allowed to delete."` ); }); - it('throws when trying to delete a preconfigured connector', async () => { + it('throws when trying to delete a system connector', async () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -2008,18 +2128,15 @@ describe('delete()', () => { kibanaIndices, inMemoryConnectors: [ { - id: 'testPreconfigured', - actionTypeId: 'my-action-type', - secrets: { - test: 'test1', - }, - isPreconfigured: true, + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, isDeprecated: false, - isSystemAction: false, - name: 'test', - config: { - foo: 'bar', - }, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, }, ], actionExecutor, @@ -2033,9 +2150,9 @@ describe('delete()', () => { }); await expect( - actionsClient.delete({ id: 'testPreconfigured' }) + actionsClient.delete({ id: 'system-connector-.cases' }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Preconfigured action testPreconfigured is not allowed to delete."` + `"System action system-connector-.cases is not allowed to delete."` ); }); }); @@ -2515,7 +2632,7 @@ describe('update()', () => { ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); - it('throws when trying to update a system connector', async () => { + it('throws when trying to update a preconfigured connector', async () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -2524,15 +2641,18 @@ describe('update()', () => { kibanaIndices, inMemoryConnectors: [ { - id: 'system-connector-.cases', - actionTypeId: '.cases', - name: 'system-connector-.cases', - config: {}, - secrets: {}, + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, isDeprecated: false, - isMissingSecrets: false, - isPreconfigured: false, - isSystemAction: true, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, }, ], actionExecutor, @@ -2547,7 +2667,7 @@ describe('update()', () => { await expect( actionsClient.update({ - id: 'system-connector-.cases', + id: 'testPreconfigured', action: { name: 'my name', config: {}, @@ -2555,11 +2675,11 @@ describe('update()', () => { }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"System action system-connector-.cases is not allowed to update."` + `"Preconfigured action testPreconfigured is not allowed to update."` ); }); - it('throws when trying to update a preconfigured connector', async () => { + it('throws when trying to update a system connector', async () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -2568,18 +2688,15 @@ describe('update()', () => { kibanaIndices, inMemoryConnectors: [ { - id: 'testPreconfigured', - actionTypeId: 'my-action-type', - secrets: { - test: 'test1', - }, - isPreconfigured: true, + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, isDeprecated: false, - isSystemAction: false, - name: 'test', - config: { - foo: 'bar', - }, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, }, ], actionExecutor, @@ -2594,7 +2711,7 @@ describe('update()', () => { await expect( actionsClient.update({ - id: 'testPreconfigured', + id: 'system-connector-.cases', action: { name: 'my name', config: {}, @@ -2602,7 +2719,7 @@ describe('update()', () => { }, }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Preconfigured action testPreconfigured is not allowed to update."` + `"System action system-connector-.cases is not allowed to update."` ); }); }); @@ -3019,7 +3136,7 @@ describe('isPreconfigured()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -3070,7 +3187,7 @@ describe('isPreconfigured()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -3123,7 +3240,7 @@ describe('isSystemAction()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, @@ -3143,7 +3260,7 @@ describe('isSystemAction()', () => { expect(actionsClient.isSystemAction('system-connector-.cases')).toEqual(true); }); - test('should return false if connector id is not in list of preconfigured connectors', () => { + test('should return false if connector id is not a system action', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, @@ -3174,7 +3291,7 @@ describe('isSystemAction()', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index fe54f29b2efde..9bad5235fc6ab 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -194,7 +194,13 @@ export class ActionsClient { action: { actionTypeId, name, config, secrets }, options, }: CreateOptions): Promise { - if (this.actionTypeRegistry.isSystemActionType(actionTypeId)) { + const id = options?.id || SavedObjectsUtils.generateId(); + const foundInMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + + if ( + this.actionTypeRegistry.isSystemActionType(actionTypeId) || + foundInMemoryConnector?.isSystemAction + ) { throw Boom.badRequest( i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { defaultMessage: 'System action creation is forbidden. Action type: {actionTypeId}.', @@ -205,13 +211,7 @@ export class ActionsClient { ); } - const id = options?.id || SavedObjectsUtils.generateId(); - - if ( - this.inMemoryConnectors.some( - (inMemoryConnector) => inMemoryConnector.isPreconfigured && inMemoryConnector.id === id - ) - ) { + if (foundInMemoryConnector?.isPreconfigured) { throw Boom.badRequest( i18n.translate('xpack.actions.serverSideErrors.predefinedIdConnectorAlreadyExists', { defaultMessage: 'This {id} already exist in preconfigured action.', @@ -287,9 +287,11 @@ export class ActionsClient { try { await this.authorization.ensureAuthorized('update'); - const inMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + const foundInMemoryConnector = this.inMemoryConnectors.find( + (connector) => connector.id === id + ); - if (inMemoryConnector?.isSystemAction) { + if (foundInMemoryConnector?.isSystemAction) { throw Boom.badRequest( i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { defaultMessage: 'System action {id} is not allowed to update.', @@ -300,7 +302,7 @@ export class ActionsClient { ); } - if (inMemoryConnector?.isPreconfigured) { + if (foundInMemoryConnector?.isPreconfigured) { throw new PreconfiguredActionDisabledModificationError( i18n.translate('xpack.actions.serverSideErrors.predefinedActionUpdateDisabled', { defaultMessage: 'Preconfigured action {id} is not allowed to update.', @@ -405,9 +407,9 @@ export class ActionsClient { throw error; } - const inMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + const foundInMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); - if (inMemoryConnector !== undefined) { + if (foundInMemoryConnector !== undefined) { this.auditLogger?.log( connectorAuditEvent({ action: ConnectorAuditAction.GET, @@ -417,11 +419,11 @@ export class ActionsClient { return { id, - actionTypeId: inMemoryConnector.actionTypeId, - name: inMemoryConnector.name, - isPreconfigured: inMemoryConnector.isPreconfigured, - isSystemAction: inMemoryConnector.isSystemAction, - isDeprecated: isConnectorDeprecated(inMemoryConnector), + actionTypeId: foundInMemoryConnector.actionTypeId, + name: foundInMemoryConnector.name, + isPreconfigured: foundInMemoryConnector.isPreconfigured, + isSystemAction: foundInMemoryConnector.isSystemAction, + isDeprecated: isConnectorDeprecated(foundInMemoryConnector), }; } @@ -447,7 +449,7 @@ export class ActionsClient { } /** - * Get all actions with preconfigured list + * Get all actions with in-memory connectors */ public async getAll(): Promise { try { @@ -495,7 +497,7 @@ export class ActionsClient { } /** - * Get bulk actions with preconfigured list + * Get bulk actions with in-memory list */ public async getBulk(ids: string[]): Promise { try { @@ -526,7 +528,7 @@ export class ActionsClient { } // Fetch action objects in bulk - // Excluding preconfigured actions to avoid an not found error, which is already added + // Excluding in-memory actions to avoid an not found error, which is already added const actionSavedObjectsIds = [ ...new Set( ids.filter( @@ -659,9 +661,11 @@ export class ActionsClient { try { await this.authorization.ensureAuthorized('delete'); - const inMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); + const foundInMemoryConnector = this.inMemoryConnectors.find( + (connector) => connector.id === id + ); - if (inMemoryConnector?.isSystemAction) { + if (foundInMemoryConnector?.isSystemAction) { throw Boom.badRequest( i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { defaultMessage: 'System action {id} is not allowed to delete.', @@ -672,7 +676,7 @@ export class ActionsClient { ); } - if (inMemoryConnector?.isPreconfigured) { + if (foundInMemoryConnector?.isPreconfigured) { throw new PreconfiguredActionDisabledModificationError( i18n.translate('xpack.actions.serverSideErrors.predefinedActionDeleteDisabled', { defaultMessage: 'Preconfigured action {id} is not allowed to delete.', 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 55aa1758a70b7..7fb737d2b5341 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -322,6 +322,89 @@ describe('execute()', () => { ); }); + test('schedules the action with all given parameters with a system action', async () => { + const executeFn = createExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + actionTypeRegistry: actionTypeRegistryMock.create(), + isESOCanEncrypt: true, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + const source = { type: 'alert', id: uuidv4() }; + + savedObjectsClient.get.mockResolvedValueOnce({ + id: '123', + type: 'action', + attributes: { + actionTypeId: 'mock-action', + }, + references: [], + }); + + savedObjectsClient.create.mockResolvedValueOnce({ + id: '234', + type: 'action_task_params', + attributes: {}, + references: [], + }); + + await executeFn(savedObjectsClient, { + id: 'system-connector-.cases', + params: { baz: false }, + spaceId: 'default', + executionId: 'system-connector-.casesabc', + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + source: asSavedObjectExecutionSource(source), + }); + + expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "params": Object { + "actionTaskParamsId": "234", + "spaceId": "default", + }, + "scope": Array [ + "actions", + ], + "state": Object {}, + "taskType": "actions:.cases", + }, + ] + `); + expect(savedObjectsClient.get).not.toHaveBeenCalled(); + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'action_task_params', + { + actionId: 'system-connector-.cases', + params: { baz: false }, + executionId: 'system-connector-.casesabc', + source: 'SAVED_OBJECT', + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + }, + { + references: [ + { + id: source.id, + name: 'source', + type: source.type, + }, + ], + } + ); + }); + test('schedules the action with all given parameters with a preconfigured action and relatedSavedObjects', async () => { const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, @@ -423,6 +506,107 @@ describe('execute()', () => { ); }); + test('schedules the action with all given parameters with a system action and relatedSavedObjects', async () => { + const executeFn = createExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + actionTypeRegistry: actionTypeRegistryMock.create(), + isESOCanEncrypt: true, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + const source = { type: 'alert', id: uuidv4() }; + + savedObjectsClient.get.mockResolvedValueOnce({ + id: '123', + type: 'action', + attributes: { + actionTypeId: 'mock-action', + }, + references: [], + }); + savedObjectsClient.create.mockResolvedValueOnce({ + id: '234', + type: 'action_task_params', + attributes: {}, + references: [], + }); + await executeFn(savedObjectsClient, { + id: 'system-connector-.cases', + params: { baz: false }, + spaceId: 'default', + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + source: asSavedObjectExecutionSource(source), + executionId: 'system-connector-.casesabc', + relatedSavedObjects: [ + { + id: 'some-id', + namespace: 'some-namespace', + type: 'some-type', + typeId: 'some-typeId', + }, + ], + }); + expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "params": Object { + "actionTaskParamsId": "234", + "spaceId": "default", + }, + "scope": Array [ + "actions", + ], + "state": Object {}, + "taskType": "actions:.cases", + }, + ] + `); + expect(savedObjectsClient.get).not.toHaveBeenCalled(); + expect(savedObjectsClient.create).toHaveBeenCalledWith( + 'action_task_params', + { + actionId: 'system-connector-.cases', + params: { baz: false }, + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + executionId: 'system-connector-.casesabc', + source: 'SAVED_OBJECT', + relatedSavedObjects: [ + { + id: 'related_some-type_0', + namespace: 'some-namespace', + type: 'some-type', + typeId: 'some-typeId', + }, + ], + }, + { + references: [ + { + id: source.id, + name: 'source', + type: source.type, + }, + { + id: 'some-id', + name: 'related_some-type_0', + type: 'some-type', + }, + ], + } + ); + }); + test('throws when passing isESOCanEncrypt with false as a value', async () => { const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, @@ -553,6 +737,53 @@ describe('execute()', () => { expect(mockedActionTypeRegistry.ensureActionTypeEnabled).not.toHaveBeenCalled(); }); + + test('should skip ensure action type if action type is system action and license is valid', async () => { + const mockedActionTypeRegistry = actionTypeRegistryMock.create(); + const executeFn = createExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + isESOCanEncrypt: true, + actionTypeRegistry: mockedActionTypeRegistry, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + mockedActionTypeRegistry.isActionExecutable.mockImplementation(() => true); + savedObjectsClient.get.mockResolvedValueOnce({ + id: '123', + type: 'action', + attributes: { + actionTypeId: 'mock-action', + }, + references: [], + }); + savedObjectsClient.create.mockResolvedValueOnce({ + id: '234', + type: 'action_task_params', + attributes: {}, + references: [], + }); + + await executeFn(savedObjectsClient, { + id: 'system-connector-.case', + params: { baz: false }, + spaceId: 'default', + executionId: 'system-connector-.caseabc', + apiKey: null, + source: asHttpRequestExecutionSource(request), + }); + + expect(mockedActionTypeRegistry.ensureActionTypeEnabled).not.toHaveBeenCalled(); + }); }); describe('bulkExecute()', () => { @@ -917,6 +1148,103 @@ describe('bulkExecute()', () => { ); }); + test('schedules the action with all given parameters with a system action', async () => { + const executeFn = createBulkExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + actionTypeRegistry: actionTypeRegistryMock.create(), + isESOCanEncrypt: true, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + const source = { type: 'alert', id: uuidv4() }; + + savedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '123', + type: 'action', + attributes: { + actionTypeId: 'mock-action', + }, + references: [], + }, + ], + }); + savedObjectsClient.bulkCreate.mockResolvedValueOnce({ + saved_objects: [ + { + id: '234', + type: 'action_task_params', + attributes: { + actionId: 'system-connector-.cases', + }, + references: [], + }, + ], + }); + await executeFn(savedObjectsClient, [ + { + id: 'system-connector-.cases', + params: { baz: false }, + spaceId: 'default', + executionId: 'system-connector-.casesabc', + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + source: asSavedObjectExecutionSource(source), + }, + ]); + expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "params": Object { + "actionTaskParamsId": "234", + "spaceId": "default", + }, + "scope": Array [ + "actions", + ], + "state": Object {}, + "taskType": "actions:.cases", + }, + ], + ] + `); + expect(savedObjectsClient.get).not.toHaveBeenCalled(); + expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + { + type: 'action_task_params', + attributes: { + actionId: 'system-connector-.cases', + params: { baz: false }, + executionId: 'system-connector-.casesabc', + source: 'SAVED_OBJECT', + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + }, + references: [ + { + id: source.id, + name: 'source', + type: source.type, + }, + ], + }, + ], + { refresh: false } + ); + }); + test('schedules the action with all given parameters with a preconfigured action and relatedSavedObjects', async () => { const executeFn = createBulkExecutionEnqueuerFunction({ taskManager: mockTaskManager, @@ -1035,6 +1363,124 @@ describe('bulkExecute()', () => { ); }); + test('schedules the action with all given parameters with a system action and relatedSavedObjects', async () => { + const executeFn = createBulkExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + actionTypeRegistry: actionTypeRegistryMock.create(), + isESOCanEncrypt: true, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + const source = { type: 'alert', id: uuidv4() }; + + savedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '123', + type: 'action', + attributes: { + actionTypeId: 'mock-action', + }, + references: [], + }, + ], + }); + savedObjectsClient.bulkCreate.mockResolvedValueOnce({ + saved_objects: [ + { + id: '234', + type: 'action_task_params', + attributes: { + actionId: 'system-connector-.cases', + }, + references: [], + }, + ], + }); + await executeFn(savedObjectsClient, [ + { + id: 'system-connector-.cases', + params: { baz: false }, + spaceId: 'default', + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + source: asSavedObjectExecutionSource(source), + executionId: 'system-connector-.casesabc', + relatedSavedObjects: [ + { + id: 'some-id', + namespace: 'some-namespace', + type: 'some-type', + typeId: 'some-typeId', + }, + ], + }, + ]); + expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "params": Object { + "actionTaskParamsId": "234", + "spaceId": "default", + }, + "scope": Array [ + "actions", + ], + "state": Object {}, + "taskType": "actions:.cases", + }, + ], + ] + `); + expect(savedObjectsClient.get).not.toHaveBeenCalled(); + expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + { + type: 'action_task_params', + attributes: { + actionId: 'system-connector-.cases', + params: { baz: false }, + apiKey: Buffer.from('system-connector-.cases:abc').toString('base64'), + executionId: 'system-connector-.casesabc', + source: 'SAVED_OBJECT', + relatedSavedObjects: [ + { + id: 'related_some-type_0', + namespace: 'some-namespace', + type: 'some-type', + typeId: 'some-typeId', + }, + ], + }, + references: [ + { + id: source.id, + name: 'source', + type: source.type, + }, + { + id: 'some-id', + name: 'related_some-type_0', + type: 'some-type', + }, + ], + }, + ], + { refresh: false } + ); + }); + test('throws when passing isESOCanEncrypt with false as a value', async () => { const executeFn = createBulkExecutionEnqueuerFunction({ taskManager: mockTaskManager, diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index 5f4f4a72a7e64..c25db5d7281e7 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -40,7 +40,7 @@ interface ActionTaskParams export interface GetConnectorsResult { connector: InMemoryConnector | RawAction; - isPreconfigured: boolean; + isInMemory: boolean; id: string; } @@ -79,7 +79,7 @@ export function createExecutionEnqueuerFunction({ ); } - const { action, isPreconfigured } = await getAction( + const { action, isInMemory } = await getAction( unsecuredSavedObjectsClient, inMemoryConnectors, id @@ -94,7 +94,7 @@ export function createExecutionEnqueuerFunction({ // Get saved object references from action ID and relatedSavedObjects const { references, relatedSavedObjectWithRefs } = extractSavedObjectReferences( id, - isPreconfigured, + isInMemory, relatedSavedObjects ); const executionSourceReference = executionSourceAsSavedObjectReferences(source); @@ -153,15 +153,16 @@ export function createBulkExecutionEnqueuerFunction({ const actionTypeIds: Record = {}; const spaceIds: Record = {}; - const connectorIsPreconfigured: Record = {}; + const connectorIsInMemory: Record = {}; const connectorIds = [...new Set(actionsToExecute.map((action) => action.id))]; const connectors = await getConnectors( unsecuredSavedObjectsClient, inMemoryConnectors, connectorIds ); + connectors.forEach((c) => { - const { id, connector, isPreconfigured } = c; + const { id, connector, isInMemory } = c; validateCanActionBeUsed(connector); const { actionTypeId } = connector; @@ -170,16 +171,17 @@ export function createBulkExecutionEnqueuerFunction({ } actionTypeIds[id] = actionTypeId; - connectorIsPreconfigured[id] = isPreconfigured; + connectorIsInMemory[id] = isInMemory; }); const actions = actionsToExecute.map((actionToExecute) => { // Get saved object references from action ID and relatedSavedObjects const { references, relatedSavedObjectWithRefs } = extractSavedObjectReferences( actionToExecute.id, - connectorIsPreconfigured[actionToExecute.id], + connectorIsInMemory[actionToExecute.id], actionToExecute.relatedSavedObjects ); + const executionSourceReference = executionSourceAsSavedObjectReferences( actionToExecute.source ); @@ -292,14 +294,14 @@ async function getAction( unsecuredSavedObjectsClient: SavedObjectsClientContract, inMemoryConnectors: InMemoryConnector[], actionId: string -): Promise<{ action: InMemoryConnector | RawAction; isPreconfigured: boolean }> { +): Promise<{ action: InMemoryConnector | RawAction; isInMemory: boolean }> { const pcAction = inMemoryConnectors.find((action) => action.id === actionId); if (pcAction) { - return { action: pcAction, isPreconfigured: true }; + return { action: pcAction, isInMemory: true }; } const { attributes } = await unsecuredSavedObjectsClient.get('action', actionId); - return { action: attributes, isPreconfigured: false }; + return { action: attributes, isInMemory: false }; } async function getConnectors( @@ -312,8 +314,9 @@ async function getConnectors( const connectorIdsToFetch = []; for (const connectorId of connectorIds) { const pcConnector = inMemoryConnectors.find((connector) => connector.id === connectorId); + if (pcConnector) { - result.push({ connector: pcConnector, isPreconfigured: true, id: connectorId }); + result.push({ connector: pcConnector, isInMemory: true, id: connectorId }); } else { connectorIdsToFetch.push(connectorId); } @@ -330,7 +333,7 @@ async function getConnectors( for (const item of bulkGetResult.saved_objects) { if (item.error) throw item.error; result.push({ - isPreconfigured: false, + isInMemory: false, connector: item.attributes, id: item.id, }); diff --git a/x-pack/plugins/actions/server/create_system_actions.test.ts b/x-pack/plugins/actions/server/create_system_actions.test.ts index fd591f2c16429..55b7d43bf5631 100644 --- a/x-pack/plugins/actions/server/create_system_actions.test.ts +++ b/x-pack/plugins/actions/server/create_system_actions.test.ts @@ -36,7 +36,7 @@ describe('createSystemConnectors', () => { { id: 'system-connector-system-action-type-2', actionTypeId: 'system-action-type-2', - name: 'system-connector-system-action-type-2', + name: 'System action: system-action-type-2', secrets: {}, config: {}, isDeprecated: false, diff --git a/x-pack/plugins/actions/server/create_system_actions.ts b/x-pack/plugins/actions/server/create_system_actions.ts index 90a13104ff76d..f6079ea940222 100644 --- a/x-pack/plugins/actions/server/create_system_actions.ts +++ b/x-pack/plugins/actions/server/create_system_actions.ts @@ -14,7 +14,7 @@ export const createSystemConnectors = (actionTypes: ActionType[]): InMemoryConne const systemConnectors: InMemoryConnector[] = systemActionTypes.map((systemActionType) => ({ id: `system-connector-${systemActionType.id}`, actionTypeId: systemActionType.id, - name: `system-connector-${systemActionType.id}`, + name: `System action: ${systemActionType.id}`, isMissingSecrets: false, config: {}, secrets: {}, diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts index 9eeb10e541f00..2bbfab40b7318 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.test.ts @@ -21,58 +21,63 @@ const internalSavedObjectsRepository = savedObjectsRepositoryMock.create(); beforeEach(() => jest.resetAllMocks()); describe('bulkExecute()', () => { - test('schedules the actions with all given parameters with a preconfigured connector', async () => { - const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ - taskManager: mockTaskManager, - connectorTypeRegistry: actionTypeRegistryMock.create(), - inMemoryConnectors: [ - { - id: '123', - actionTypeId: '.email', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - ], - }); + test.each([ + [true, false], + [false, true], + ])( + 'schedules the actions with all given parameters with an in-memory connector: isPreconfigured: %s, isSystemAction: %s', + async (isPreconfigured, isSystemAction) => { + const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + connectorTypeRegistry: actionTypeRegistryMock.create(), + inMemoryConnectors: [ + { + id: '123', + actionTypeId: '.email', + config: {}, + isPreconfigured, + isDeprecated: false, + isSystemAction, + name: 'x', + secrets: {}, + }, + ], + }); - internalSavedObjectsRepository.bulkCreate.mockResolvedValueOnce({ - saved_objects: [ - { - id: '234', - type: 'action_task_params', - attributes: { - actionId: '123', + internalSavedObjectsRepository.bulkCreate.mockResolvedValueOnce({ + saved_objects: [ + { + id: '234', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [], }, - references: [], + { + id: '345', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [], + }, + ], + }); + await executeFn(internalSavedObjectsRepository, [ + { + id: '123', + params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, { - id: '345', - type: 'action_task_params', - attributes: { - actionId: '123', - }, - references: [], + id: '123', + params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), }, - ], - }); - await executeFn(internalSavedObjectsRepository, [ - { - id: '123', - params: { baz: false }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - { - id: '123', - params: { baz: true }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - ]); - expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); - expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` + ]); + expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -101,90 +106,96 @@ describe('bulkExecute()', () => { ] `); - expect(internalSavedObjectsRepository.bulkCreate).toHaveBeenCalledWith([ - { - type: 'action_task_params', - attributes: { - actionId: '123', - params: { baz: false }, - apiKey: null, - source: 'NOTIFICATION', - }, - references: [], - }, - { - type: 'action_task_params', - attributes: { - actionId: '123', - params: { baz: true }, - apiKey: null, - source: 'NOTIFICATION', - }, - references: [], - }, - ]); - }); - - test('schedules the actions with all given parameters with a preconfigured connector and source specified', async () => { - const sourceUuid = uuidv4(); - const source = { type: 'alert', id: sourceUuid }; - const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ - taskManager: mockTaskManager, - connectorTypeRegistry: actionTypeRegistryMock.create(), - inMemoryConnectors: [ - { - id: '123', - actionTypeId: '.email', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - ], - }); - - internalSavedObjectsRepository.bulkCreate.mockResolvedValueOnce({ - saved_objects: [ + expect(internalSavedObjectsRepository.bulkCreate).toHaveBeenCalledWith([ { - id: '234', type: 'action_task_params', attributes: { actionId: '123', + params: { baz: false }, + apiKey: null, + source: 'NOTIFICATION', }, - references: [ - { - id: sourceUuid, - name: 'source', - type: 'alert', - }, - ], + references: [], }, { - id: '345', type: 'action_task_params', attributes: { actionId: '123', + params: { baz: true }, + apiKey: null, + source: 'NOTIFICATION', }, references: [], }, - ], - }); - await executeFn(internalSavedObjectsRepository, [ - { - id: '123', - params: { baz: false }, - source: asSavedObjectExecutionSource(source), - }, - { - id: '123', - params: { baz: true }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - ]); - expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); - expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` + ]); + } + ); + + test.each([ + [true, false], + [false, true], + ])( + 'schedules the actions with all given parameters with an in-memory connector and source specified: isPreconfigured: %s, isSystemAction: %s', + async (isPreconfigured, isSystemAction) => { + const sourceUuid = uuidv4(); + const source = { type: 'alert', id: sourceUuid }; + const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + connectorTypeRegistry: actionTypeRegistryMock.create(), + inMemoryConnectors: [ + { + id: '123', + actionTypeId: '.email', + config: {}, + isPreconfigured, + isDeprecated: false, + isSystemAction, + name: 'x', + secrets: {}, + }, + ], + }); + + internalSavedObjectsRepository.bulkCreate.mockResolvedValueOnce({ + saved_objects: [ + { + id: '234', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [ + { + id: sourceUuid, + name: 'source', + type: 'alert', + }, + ], + }, + { + id: '345', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [], + }, + ], + }); + await executeFn(internalSavedObjectsRepository, [ + { + id: '123', + params: { baz: false }, + source: asSavedObjectExecutionSource(source), + }, + { + id: '123', + params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + ]); + expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -213,63 +224,14 @@ describe('bulkExecute()', () => { ] `); - expect(internalSavedObjectsRepository.bulkCreate).toHaveBeenCalledWith([ - { - type: 'action_task_params', - attributes: { - actionId: '123', - params: { baz: false }, - apiKey: null, - source: 'SAVED_OBJECT', - }, - references: [ - { - id: sourceUuid, - name: 'source', - type: 'alert', - }, - ], - }, - { - type: 'action_task_params', - attributes: { - actionId: '123', - params: { baz: true }, - apiKey: null, - source: 'NOTIFICATION', - }, - references: [], - }, - ]); - }); - - test('schedules the actions with all given parameters with a preconfigured connector and relatedSavedObjects specified', async () => { - const sourceUuid = uuidv4(); - const source = { type: 'alert', id: sourceUuid }; - const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ - taskManager: mockTaskManager, - connectorTypeRegistry: actionTypeRegistryMock.create(), - inMemoryConnectors: [ - { - id: '123', - actionTypeId: '.email', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - ], - }); - - internalSavedObjectsRepository.bulkCreate.mockResolvedValueOnce({ - saved_objects: [ + expect(internalSavedObjectsRepository.bulkCreate).toHaveBeenCalledWith([ { - id: '234', type: 'action_task_params', attributes: { actionId: '123', + params: { baz: false }, + apiKey: null, + source: 'SAVED_OBJECT', }, references: [ { @@ -280,42 +242,97 @@ describe('bulkExecute()', () => { ], }, { - id: '345', type: 'action_task_params', attributes: { actionId: '123', + params: { baz: true }, + apiKey: null, + source: 'NOTIFICATION', }, - references: [ + references: [], + }, + ]); + } + ); + + test.each([ + [true, false], + [false, true], + ])( + 'schedules the actions with all given parameters with an in-memory connector and relatedSavedObjects specified: isPreconfigured: %s, isSystemAction: %s', + async (isPreconfigured, isSystemAction) => { + const sourceUuid = uuidv4(); + const source = { type: 'alert', id: sourceUuid }; + const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + connectorTypeRegistry: actionTypeRegistryMock.create(), + inMemoryConnectors: [ + { + id: '123', + actionTypeId: '.email', + config: {}, + isPreconfigured, + isDeprecated: false, + isSystemAction, + name: 'x', + secrets: {}, + }, + ], + }); + + internalSavedObjectsRepository.bulkCreate.mockResolvedValueOnce({ + saved_objects: [ + { + id: '234', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [ + { + id: sourceUuid, + name: 'source', + type: 'alert', + }, + ], + }, + { + id: '345', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [ + { + id: 'some-id', + name: 'related_some-type_0', + type: 'some-type', + }, + ], + }, + ], + }); + await executeFn(internalSavedObjectsRepository, [ + { + id: '123', + params: { baz: false }, + source: asSavedObjectExecutionSource(source), + }, + { + id: '123', + params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + relatedSavedObjects: [ { id: 'some-id', - name: 'related_some-type_0', + namespace: 'some-namespace', type: 'some-type', }, ], }, - ], - }); - await executeFn(internalSavedObjectsRepository, [ - { - id: '123', - params: { baz: false }, - source: asSavedObjectExecutionSource(source), - }, - { - id: '123', - params: { baz: true }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - relatedSavedObjects: [ - { - id: 'some-id', - namespace: 'some-namespace', - type: 'some-type', - }, - ], - }, - ]); - expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); - expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` + ]); + expect(mockTaskManager.bulkSchedule).toHaveBeenCalledTimes(1); + expect(mockTaskManager.bulkSchedule.mock.calls[0]).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -344,164 +361,183 @@ describe('bulkExecute()', () => { ] `); - expect(internalSavedObjectsRepository.bulkCreate).toHaveBeenCalledWith([ - { - type: 'action_task_params', - attributes: { - actionId: '123', - params: { baz: false }, - apiKey: null, - source: 'SAVED_OBJECT', + expect(internalSavedObjectsRepository.bulkCreate).toHaveBeenCalledWith([ + { + type: 'action_task_params', + attributes: { + actionId: '123', + params: { baz: false }, + apiKey: null, + source: 'SAVED_OBJECT', + }, + references: [ + { + id: sourceUuid, + name: 'source', + type: 'alert', + }, + ], }, - references: [ - { - id: sourceUuid, - name: 'source', - type: 'alert', + { + type: 'action_task_params', + attributes: { + actionId: '123', + params: { baz: true }, + apiKey: null, + source: 'NOTIFICATION', + relatedSavedObjects: [ + { + id: 'related_some-type_0', + namespace: 'some-namespace', + type: 'some-type', + }, + ], }, - ], - }, - { - type: 'action_task_params', - attributes: { - actionId: '123', - params: { baz: true }, - apiKey: null, - source: 'NOTIFICATION', - relatedSavedObjects: [ + references: [ { - id: 'related_some-type_0', - namespace: 'some-namespace', + id: 'some-id', + name: 'related_some-type_0', type: 'some-type', }, ], }, - references: [ + ]); + } + ); + + test.each([ + [true, false], + [false, true], + ])( + 'throws when scheduling action using non in-memory connector: isPreconfigured: %s, isSystemAction: %s', + async (isPreconfigured, isSystemAction) => { + const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + connectorTypeRegistry: actionTypeRegistryMock.create(), + inMemoryConnectors: [ { - id: 'some-id', - name: 'related_some-type_0', - type: 'some-type', + id: '123', + actionTypeId: '.email', + config: {}, + isPreconfigured, + isDeprecated: false, + isSystemAction, + name: 'x', + secrets: {}, }, ], - }, - ]); - }); - - test('throws when scheduling action using non preconfigured connector', async () => { - const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ - taskManager: mockTaskManager, - connectorTypeRegistry: actionTypeRegistryMock.create(), - inMemoryConnectors: [ - { - id: '123', - actionTypeId: '.email', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - ], - }); - await expect( - executeFn(internalSavedObjectsRepository, [ - { - id: '123', - params: { baz: false }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - { - id: 'not-preconfigured', - params: { baz: true }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - ]) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"not-preconfigured are not preconfigured connectors and can't be scheduled for unsecured actions execution"` - ); - }); + }); + await expect( + executeFn(internalSavedObjectsRepository, [ + { + id: '123', + params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + { + id: 'not-preconfigured', + params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + ]) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"not-preconfigured are not in-memory connectors and can't be scheduled for unsecured actions execution"` + ); + } + ); - test('throws when connector type is not enabled', async () => { - const mockedConnectorTypeRegistry = actionTypeRegistryMock.create(); - const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ - taskManager: mockTaskManager, - connectorTypeRegistry: mockedConnectorTypeRegistry, - inMemoryConnectors: [ - { - id: '123', - actionTypeId: '.email', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - ], - }); - mockedConnectorTypeRegistry.ensureActionTypeEnabled.mockImplementation(() => { - throw new Error('Fail'); - }); + test.each([ + [true, false], + [false, true], + ])( + 'throws when connector type is not enabled: isPreconfigured: %s, isSystemAction: %s', + async (isPreconfigured, isSystemAction) => { + const mockedConnectorTypeRegistry = actionTypeRegistryMock.create(); + const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + connectorTypeRegistry: mockedConnectorTypeRegistry, + inMemoryConnectors: [ + { + id: '123', + actionTypeId: '.email', + config: {}, + isPreconfigured, + isDeprecated: false, + isSystemAction, + name: 'x', + secrets: {}, + }, + ], + }); + mockedConnectorTypeRegistry.ensureActionTypeEnabled.mockImplementation(() => { + throw new Error('Fail'); + }); - await expect( - executeFn(internalSavedObjectsRepository, [ - { - id: '123', - params: { baz: false }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - { - id: '123', - params: { baz: true }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - ]) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); - }); + await expect( + executeFn(internalSavedObjectsRepository, [ + { + id: '123', + params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + { + id: '123', + params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + ]) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); + } + ); - test('throws when scheduling action using non allow-listed preconfigured connector', async () => { - const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ - taskManager: mockTaskManager, - connectorTypeRegistry: actionTypeRegistryMock.create(), - inMemoryConnectors: [ - { - id: '123', - actionTypeId: '.email', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - { - id: '456', - actionTypeId: 'not-in-allowlist', - config: {}, - isPreconfigured: true, - isDeprecated: false, - isSystemAction: false, - name: 'x', - secrets: {}, - }, - ], - }); - await expect( - executeFn(internalSavedObjectsRepository, [ - { - id: '123', - params: { baz: false }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - { - id: '456', - params: { baz: true }, - source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), - }, - ]) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"not-in-allowlist actions cannot be scheduled for unsecured actions execution"` - ); - }); + test.each([ + [true, false], + [false, true], + ])( + 'throws when scheduling action using non allow-listed in-memory connector: isPreconfigured: %s, isSystemAction: %s', + async (isPreconfigured, isSystemAction) => { + const executeFn = createBulkUnsecuredExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + connectorTypeRegistry: actionTypeRegistryMock.create(), + inMemoryConnectors: [ + { + id: '123', + actionTypeId: '.email', + config: {}, + isPreconfigured, + isDeprecated: false, + isSystemAction, + name: 'x', + secrets: {}, + }, + { + id: '456', + actionTypeId: 'not-in-allowlist', + config: {}, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'x', + secrets: {}, + }, + ], + }); + await expect( + executeFn(internalSavedObjectsRepository, [ + { + id: '123', + params: { baz: false }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + { + id: '456', + params: { baz: true }, + source: asNotificationExecutionSource({ connectorId: 'abc', requesterId: 'foo' }), + }, + ]) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"not-in-allowlist actions cannot be scheduled for unsecured actions execution"` + ); + } + ); }); diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts index 1db64a3e16121..bd4b947e53248 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts @@ -59,7 +59,7 @@ export function createBulkUnsecuredExecutionEnqueuerFunction({ throw new Error( `${notInMemoryConnectors.join( ',' - )} are not preconfigured connectors and can't be scheduled for unsecured actions execution` + )} are not in-memory connectors and can't be scheduled for unsecured actions execution` ); } diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 31f1ab76cb0e9..af39f914fc720 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -66,6 +66,16 @@ actionExecutor.initialize({ isDeprecated: false, isSystemAction: false, }, + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, ], }); @@ -662,6 +672,133 @@ test('successfully executes with preconfigured connector', async () => { `); }); +test('successfully executes with system connector', async () => { + const actionType: jest.Mocked = { + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.any() }, + secrets: { schema: schema.any() }, + params: { schema: schema.any() }, + }, + executor: jest.fn(), + }; + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + await actionExecutor.execute({ ...executeParams, actionId: 'system-connector-.cases' }); + + expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).not.toHaveBeenCalled(); + + expect(actionTypeRegistry.get).toHaveBeenCalledWith('.cases'); + expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith( + 'system-connector-.cases', + '.cases', + { + notifyUsage: true, + } + ); + + expect(actionType.executor).toHaveBeenCalledWith({ + actionId: 'system-connector-.cases', + services: expect.anything(), + config: {}, + secrets: {}, + params: { foo: true }, + logger: loggerMock, + }); + + expect(loggerMock.debug).toBeCalledWith( + 'executing action .cases:system-connector-.cases: System action: .cases' + ); + expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "event": Object { + "action": "execute-start", + "kind": "action", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "uuid": "2", + }, + "id": "system-connector-.cases", + "name": "System action: .cases", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "system-connector-.cases", + "namespace": "some-namespace", + "rel": "primary", + "space_agnostic": true, + "type": "action", + "type_id": ".cases", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action started: .cases:system-connector-.cases: System action: .cases", + }, + ], + Array [ + Object { + "event": Object { + "action": "execute", + "kind": "action", + "outcome": "success", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "uuid": "2", + }, + "id": "system-connector-.cases", + "name": "System action: .cases", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "system-connector-.cases", + "namespace": "some-namespace", + "rel": "primary", + "space_agnostic": true, + "type": "action", + "type_id": ".cases", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action executed: .cases:system-connector-.cases: System action: .cases", + "user": Object { + "id": "123", + "name": "coolguy", + }, + }, + ], + ] + `); +}); + test('successfully executes as a task', async () => { const actionType: jest.Mocked = { id: 'test', @@ -949,6 +1086,51 @@ test('should not throws an error if actionType is preconfigured', async () => { }); }); +test('should not throws an error if actionType is system action', async () => { + const actionType: jest.Mocked = { + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.any() }, + secrets: { schema: schema.any() }, + params: { schema: schema.any() }, + }, + executor: jest.fn(), + }; + + const actionSavedObject = { + id: '1', + type: 'action', + attributes: { + name: '1', + actionTypeId: '.cases', + config: {}, + secrets: {}, + }, + references: [], + }; + + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(actionSavedObject); + actionTypeRegistry.get.mockReturnValueOnce(actionType); + actionTypeRegistry.ensureActionTypeEnabled.mockImplementationOnce(() => { + throw new Error('not enabled for test'); + }); + actionTypeRegistry.isActionExecutable.mockImplementationOnce(() => true); + await actionExecutor.execute(executeParams); + + expect(actionTypeRegistry.ensureActionTypeEnabled).toHaveBeenCalledTimes(0); + expect(actionType.executor).toHaveBeenCalledWith({ + actionId: '1', + services: expect.anything(), + config: {}, + secrets: {}, + params: { foo: true }, + logger: loggerMock, + }); +}); + test('throws an error when passing isESOCanEncrypt with value of false', async () => { const customActionExecutor = new ActionExecutor({ isESOCanEncrypt: false }); customActionExecutor.initialize({ @@ -1117,6 +1299,155 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f `); }); +test('should not throw error if action is system action and isESOCanEncrypt is false', async () => { + const customActionExecutor = new ActionExecutor({ isESOCanEncrypt: false }); + customActionExecutor.initialize({ + logger: loggingSystemMock.create().get(), + spaces: spacesMock, + getServices: () => services, + actionTypeRegistry, + encryptedSavedObjectsClient, + eventLogger: eventLoggerMock.create(), + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + + const actionType: jest.Mocked = { + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.any() }, + secrets: { schema: schema.any() }, + params: { schema: schema.any() }, + }, + executor: jest.fn(), + }; + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + await actionExecutor.execute({ ...executeParams, actionId: 'system-connector-.cases' }); + + expect(encryptedSavedObjectsClient.getDecryptedAsInternalUser).not.toHaveBeenCalled(); + + expect(actionTypeRegistry.get).toHaveBeenCalledWith('.cases'); + expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith( + 'system-connector-.cases', + '.cases', + { + notifyUsage: true, + } + ); + + expect(actionType.executor).toHaveBeenCalledWith({ + actionId: 'system-connector-.cases', + services: expect.anything(), + config: {}, + secrets: {}, + params: { foo: true }, + logger: loggerMock, + }); + + expect(loggerMock.debug).toBeCalledWith( + 'executing action .cases:system-connector-.cases: System action: .cases' + ); + expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "event": Object { + "action": "execute-start", + "kind": "action", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "uuid": "2", + }, + "id": "system-connector-.cases", + "name": "System action: .cases", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "system-connector-.cases", + "namespace": "some-namespace", + "rel": "primary", + "space_agnostic": true, + "type": "action", + "type_id": ".cases", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action started: .cases:system-connector-.cases: System action: .cases", + }, + ], + Array [ + Object { + "event": Object { + "action": "execute", + "kind": "action", + "outcome": "success", + }, + "kibana": Object { + "action": Object { + "execution": Object { + "uuid": "2", + }, + "id": "system-connector-.cases", + "name": "System action: .cases", + }, + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "123abc", + }, + }, + }, + "saved_objects": Array [ + Object { + "id": "system-connector-.cases", + "namespace": "some-namespace", + "rel": "primary", + "space_agnostic": true, + "type": "action", + "type_id": ".cases", + }, + ], + "space_ids": Array [ + "some-namespace", + ], + }, + "message": "action executed: .cases:system-connector-.cases: System action: .cases", + "user": Object { + "id": "123", + "name": "coolguy", + }, + }, + ], + ] + `); +}); + test('does not log warning when alert executor succeeds', async () => { const executorMock = setupActionExecutorMock(); executorMock.mockResolvedValue({ diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index d5f6e9c3b14ea..4fe18f6ecd5e5 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -186,7 +186,7 @@ export class ActionExecutor { relatedSavedObjects, name, actionExecutionId, - isPreconfigured: this.actionInfo.isPreconfigured, + isInMemory: this.actionInfo.isInMemory, ...(source ? { source } : {}), }); @@ -385,7 +385,7 @@ export class ActionExecutor { ], relatedSavedObjects, actionExecutionId, - isPreconfigured: this.actionInfo.isPreconfigured, + isInMemory: this.actionInfo.isInMemory, ...(source ? { source } : {}), }); @@ -399,7 +399,7 @@ interface ActionInfo { config: unknown; secrets: unknown; actionId: string; - isPreconfigured?: boolean; + isInMemory?: boolean; } async function getActionInfoInternal( @@ -409,18 +409,19 @@ async function getActionInfoInternal( actionId: string, namespace: string | undefined ): Promise { - // check to see if it's a pre-configured action first - const pcAction = inMemoryConnectors.find( + // check to see if it's in memory action first + const inMemoryAction = inMemoryConnectors.find( (inMemoryConnector) => inMemoryConnector.id === actionId ); - if (pcAction) { + + if (inMemoryAction) { return { - actionTypeId: pcAction.actionTypeId, - name: pcAction.name, - config: pcAction.config, - secrets: pcAction.secrets, + actionTypeId: inMemoryAction.actionTypeId, + name: inMemoryAction.name, + config: inMemoryAction.config, + secrets: inMemoryAction.secrets, actionId, - isPreconfigured: true, + isInMemory: true, }; } diff --git a/x-pack/plugins/actions/server/lib/action_task_params_utils.test.ts b/x-pack/plugins/actions/server/lib/action_task_params_utils.test.ts index 98a425ff6fd39..ab2d4305b36d8 100644 --- a/x-pack/plugins/actions/server/lib/action_task_params_utils.test.ts +++ b/x-pack/plugins/actions/server/lib/action_task_params_utils.test.ts @@ -87,13 +87,13 @@ describe('extractSavedObjectReferences()', () => { }); }); - test('correctly skips extracting action id if action is preconfigured', () => { + test('correctly skips extracting action id if action is in-memory', () => { expect(extractSavedObjectReferences('my-action-id', true)).toEqual({ references: [], }); }); - test('correctly extracts related saved object into references array if isPreconfigured is true', () => { + test('correctly extracts related saved object into references array if isInMemory is true', () => { const relatedSavedObjects = [ { id: 'abc', diff --git a/x-pack/plugins/actions/server/lib/action_task_params_utils.ts b/x-pack/plugins/actions/server/lib/action_task_params_utils.ts index 425e8dfb107f2..98288ef4d06c2 100644 --- a/x-pack/plugins/actions/server/lib/action_task_params_utils.ts +++ b/x-pack/plugins/actions/server/lib/action_task_params_utils.ts @@ -12,7 +12,7 @@ export const ACTION_REF_NAME = `actionRef`; export function extractSavedObjectReferences( actionId: string, - isPreconfigured: boolean, + isInMemory: boolean, relatedSavedObjects?: RelatedSavedObjects ): { references: SavedObjectReference[]; @@ -21,8 +21,8 @@ export function extractSavedObjectReferences( const references: SavedObjectReference[] = []; const relatedSavedObjectWithRefs: RelatedSavedObjects = []; - // Add action saved object to reference if it is not preconfigured - if (!isPreconfigured) { + // Add action saved object to reference if it is not in-memory action + if (!isInMemory) { references.push({ id: actionId, name: ACTION_REF_NAME, diff --git a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts index 28c3a96e14507..cb6390a4b3335 100644 --- a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts +++ b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.test.ts @@ -413,7 +413,7 @@ describe('createActionEventLogRecordObject', () => { }); }); - test('created action event "execute" for preconfigured connector with space_agnostic true', async () => { + test('created action event "execute" for in-memory connector with space_agnostic true', async () => { expect( createActionEventLogRecordObject({ actionId: '1', @@ -432,7 +432,7 @@ describe('createActionEventLogRecordObject', () => { }, ], actionExecutionId: '123abc', - isPreconfigured: true, + isInMemory: true, }) ).toStrictEqual({ event: { diff --git a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts index 46dcddd9b55c5..d29cd670c514c 100644 --- a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts +++ b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts @@ -35,7 +35,7 @@ interface CreateActionEventLogRecordParams { relation?: string; }>; relatedSavedObjects?: RelatedSavedObjects; - isPreconfigured?: boolean; + isInMemory?: boolean; source?: ActionExecutionSource; } @@ -51,7 +51,7 @@ export function createActionEventLogRecordObject(params: CreateActionEventLogRec relatedSavedObjects, name, actionExecutionId, - isPreconfigured, + isInMemory, actionId, source, } = params; @@ -81,7 +81,7 @@ export function createActionEventLogRecordObject(params: CreateActionEventLogRec id: so.id, type_id: so.typeId, // set space_agnostic to true for preconfigured connectors - ...(so.type === 'action' && isPreconfigured ? { space_agnostic: isPreconfigured } : {}), + ...(so.type === 'action' && isInMemory ? { space_agnostic: isInMemory } : {}), ...(namespace ? { namespace } : {}), })), ...(spaceId ? { space_ids: [spaceId] } : {}), diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 8882103222705..3406916589048 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -458,7 +458,7 @@ describe('Actions Plugin', () => { pluginSetup.registerType({ id: '.cases', name: 'Cases', - minimumLicenseRequired: 'basic', + minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], validate: { config: { schema: schema.object({}) }, @@ -487,7 +487,7 @@ describe('Actions Plugin', () => { { id: 'system-connector-.cases', actionTypeId: '.cases', - name: 'system-connector-.cases', + name: 'System action: .cases', config: {}, secrets: {}, isDeprecated: false, diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 8fe4fe5928a50..3d5ba29e71fa5 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -363,7 +363,8 @@ export class ActionsPlugin implements Plugin { return !!this.inMemoryConnectors.find( - (inMemoryConnector) => inMemoryConnector.id === connectorId + (inMemoryConnector) => + inMemoryConnector.isPreconfigured && inMemoryConnector.id === connectorId ); }, getSubActionConnectorClass: () => SubActionConnector, diff --git a/x-pack/plugins/actions/server/routes/connector_types.test.ts b/x-pack/plugins/actions/server/routes/connector_types.test.ts index 4b1ce8189c2a1..51fa8c1630d5f 100644 --- a/x-pack/plugins/actions/server/routes/connector_types.test.ts +++ b/x-pack/plugins/actions/server/routes/connector_types.test.ts @@ -79,6 +79,7 @@ describe('connectorTypesRoute', () => { enabled_in_license: true, supported_feature_ids: ['alerting'], minimum_license_required: 'gold', + is_system_action_type: false, }, ], }); @@ -155,6 +156,7 @@ describe('connectorTypesRoute', () => { enabled_in_license: true, supported_feature_ids: ['alerting'], minimum_license_required: 'gold', + is_system_action_type: false, }, ], }); diff --git a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts index 80e3461e12529..798bcbbaacd25 100644 --- a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.test.ts @@ -6,10 +6,7 @@ */ import { v4 as uuidv4 } from 'uuid'; -import { - getActionTaskParamsMigrations, - isPreconfiguredAction, -} from './action_task_params_migrations'; +import { getActionTaskParamsMigrations, isInMemoryAction } from './action_task_params_migrations'; import { ActionTaskParams } from '../types'; import { SavedObjectReference, SavedObjectUnsanitizedDoc } from '@kbn/core/server'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; @@ -39,7 +36,7 @@ describe('successful migrations', () => { }); describe('7.16.0', () => { - test('adds actionId to references array if actionId is not preconfigured', () => { + test('adds actionId to references array if actionId is not in-memory', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); @@ -57,7 +54,7 @@ describe('successful migrations', () => { }); }); - test('does not add actionId to references array if actionId is preconfigured', () => { + test('does not add actionId to references array if actionId is in-memory', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); @@ -134,7 +131,7 @@ describe('successful migrations', () => { }); }); - test('only adds relatedSavedObjects to references array if action is preconfigured', () => { + test('only adds relatedSavedObjects to references array if action is in-memory', () => { const migration716 = SavedObjectsUtils.getMigrationFunction( getActionTaskParamsMigrations(encryptedSavedObjectsSetup, inMemoryConnectors)['7.16.0'] ); @@ -391,15 +388,15 @@ describe('handles errors during migrations', () => { }); }); -describe('isPreconfiguredAction()', () => { - test('returns true if actionId is preconfigured action', () => { - expect( - isPreconfiguredAction(getMockData({ actionId: 'my-slack1' }), inMemoryConnectors) - ).toEqual(true); +describe('isInMemoryAction()', () => { + test('returns true if actionId is in-memory action', () => { + expect(isInMemoryAction(getMockData({ actionId: 'my-slack1' }), inMemoryConnectors)).toEqual( + true + ); }); - test('returns false if actionId is not preconfigured action', () => { - expect(isPreconfiguredAction(getMockData(), inMemoryConnectors)).toEqual(false); + test('returns false if actionId is not in-memory action', () => { + expect(isInMemoryAction(getMockData(), inMemoryConnectors)).toEqual(false); }); }); diff --git a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts index c686fa55d9fe6..3ef9bb0299d7e 100644 --- a/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts +++ b/x-pack/plugins/actions/server/saved_objects/action_task_params_migrations.ts @@ -86,7 +86,7 @@ function executeMigrationWithErrorHandling( }; } -export function isPreconfiguredAction( +export function isInMemoryAction( doc: SavedObjectUnsanitizedDoc, inMemoryConnectors: InMemoryConnector[] ): boolean { @@ -111,7 +111,7 @@ function useSavedObjectReferences( const newReferences: SavedObjectReference[] = []; const relatedSavedObjectRefs: RelatedSavedObjects = []; - if (!isPreconfiguredAction(doc, inMemoryConnectors)) { + if (!isInMemoryAction(doc, inMemoryConnectors)) { newReferences.push({ id: actionId, name: 'actionRef', diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index 2eacd29a39db6..90c7ec1dc095c 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -354,7 +354,7 @@ describe('Execution Handler', () => { }); test('throw error message when action type is disabled', async () => { - mockActionsPlugin.preconfiguredActions = []; + mockActionsPlugin.inMemoryConnectors = []; mockActionsPlugin.isActionExecutable.mockReturnValue(false); mockActionsPlugin.isActionTypeEnabled.mockReturnValue(false); const executionHandler = new ExecutionHandler( From d35a7f9af6d007e75563484c99d82ef59fc2ab63 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 1 Jul 2023 19:49:19 +0300 Subject: [PATCH 19/45] Get in-memory connectors after they have been created in the route context --- x-pack/plugins/actions/server/plugin.test.ts | 175 +++++++++++++------ x-pack/plugins/actions/server/plugin.ts | 19 +- 2 files changed, 140 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 3406916589048..458ff70955c6d 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -29,6 +29,31 @@ const executor: ExecutorType<{}, {}, {}, void> = async (options) => { return { status: 'ok', actionId: options.actionId }; }; +function getConfig(overrides = {}) { + return { + enabled: true, + enabledActionTypes: ['*'], + allowedHosts: ['*'], + preconfiguredAlertHistoryEsIndex: false, + preconfigured: { + preconfiguredServerLog: { + actionTypeId: '.server-log', + name: 'preconfigured-server-log', + config: {}, + secrets: {}, + }, + }, + proxyRejectUnauthorizedCertificates: true, + proxyBypassHosts: undefined, + proxyOnlyHosts: undefined, + rejectUnauthorized: true, + maxResponseContentLength: new ByteSizeValue(1000000), + responseTimeout: moment.duration('60s'), + enableFooterInEmail: true, + ...overrides, + }; +} + describe('Actions Plugin', () => { describe('setup()', () => { let context: PluginInitializerContext; @@ -136,6 +161,106 @@ describe('Actions Plugin', () => { `"Unable to create actions client because the Encrypted Saved Objects plugin is missing encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."` ); }); + + it('the actions client should have the correct in-memory connectors', async () => { + context = coreMock.createPluginInitializerContext(getConfig()); + const pluginWithPreconfiguredConnectors = new ActionsPlugin(context); + + const coreStart = coreMock.createStart(); + const pluginsStart = { + licensing: licensingMock.createStart(), + taskManager: taskManagerMock.createStart(), + encryptedSavedObjects: encryptedSavedObjectsMock.createStart(), + eventLog: eventLogMock.createStart(), + }; + + /** + * 1. In the setup of the actions plugin + * the preconfigured connectors are being + * set up. Also, the action router handler context + * is registered + */ + const pluginSetup = await pluginWithPreconfiguredConnectors.setup(coreSetup, { + ...pluginsSetup, + encryptedSavedObjects: { + ...pluginsSetup.encryptedSavedObjects, + canEncrypt: true, + }, + }); + + /** + * 2. We simulate the registration of + * a system action by another plugin + * in the setup + */ + pluginSetup.registerType({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0]; + + /** + * 3. On start the system actions are being + * created based on the system action types + * that got registered on step 2 + */ + await pluginWithPreconfiguredConnectors.start(coreStart, pluginsStart); + + const actionsContextHandler = (await handler[1]( + { + core: { + savedObjects: { + client: {}, + }, + elasticsearch: { + client: jest.fn(), + }, + }, + } as unknown as RequestHandlerContext, + httpServerMock.createKibanaRequest(), + httpServerMock.createResponseFactory() + )) as unknown as ActionsApiRequestHandlerContext; + + /** + * 4. We verify that the actions client inside + * the router context has the correct system connectors + * that got set up on start (step 3). + */ + // @ts-expect-error: inMemoryConnectors can be accessed + expect(actionsContextHandler.getActionsClient().inMemoryConnectors).toEqual([ + { + id: 'preconfiguredServerLog', + actionTypeId: '.server-log', + name: 'preconfigured-server-log', + config: {}, + secrets: {}, + isDeprecated: false, + isPreconfigured: true, + isSystemAction: false, + }, + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, + isDeprecated: false, + isPreconfigured: false, + isSystemAction: true, + isMissingSecrets: false, + }, + ]); + }); }); describe('registerType()', () => { @@ -199,31 +324,6 @@ describe('Actions Plugin', () => { }); describe('isPreconfiguredConnector', () => { - function getConfig(overrides = {}) { - return { - enabled: true, - enabledActionTypes: ['*'], - allowedHosts: ['*'], - preconfiguredAlertHistoryEsIndex: false, - preconfigured: { - preconfiguredServerLog: { - actionTypeId: '.server-log', - name: 'preconfigured-server-log', - config: {}, - secrets: {}, - }, - }, - proxyRejectUnauthorizedCertificates: true, - proxyBypassHosts: undefined, - proxyOnlyHosts: undefined, - rejectUnauthorized: true, - maxResponseContentLength: new ByteSizeValue(1000000), - responseTimeout: moment.duration('60s'), - enableFooterInEmail: true, - ...overrides, - }; - } - function setup(config: ActionsConfig) { context = coreMock.createPluginInitializerContext(config); plugin = new ActionsPlugin(context); @@ -324,31 +424,6 @@ describe('Actions Plugin', () => { }); describe('inMemoryConnectors', () => { - function getConfig(overrides = {}) { - return { - enabled: true, - enabledActionTypes: ['*'], - allowedHosts: ['*'], - preconfiguredAlertHistoryEsIndex: false, - preconfigured: { - preconfiguredServerLog: { - actionTypeId: '.server-log', - name: 'preconfigured-server-log', - config: {}, - secrets: {}, - }, - }, - proxyRejectUnauthorizedCertificates: true, - proxyBypassHosts: undefined, - proxyOnlyHosts: undefined, - rejectUnauthorized: true, - maxResponseContentLength: new ByteSizeValue(1000000), - responseTimeout: moment.duration('60s'), - enableFooterInEmail: true, - ...overrides, - }; - } - function setup(config: ActionsConfig) { context = coreMock.createPluginInitializerContext(config); plugin = new ActionsPlugin(context); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 3d5ba29e71fa5..25b03b41413ee 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -397,12 +397,14 @@ export class ActionsPlugin implements Plugin this.inMemoryConnectors; + + private setSystemActions = () => { + const systemConnectors = createSystemConnectors(this.actionTypeRegistry?.list() ?? []); + this.inMemoryConnectors = [...this.inMemoryConnectors, ...systemConnectors]; + }; + private createRouteHandlerContext = ( core: CoreSetup ): IContextProvider => { const { actionTypeRegistry, isESOCanEncrypt, - inMemoryConnectors, + getInMemoryConnectors, actionExecutor, instantiateAuthorization, security, @@ -615,7 +624,9 @@ export class ActionsPlugin implements Plugin { From 34ee2ab2338bbb327083f8a986d4ac9bb9fc7978 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 1 Jul 2023 19:51:32 +0300 Subject: [PATCH 20/45] Register test system action in integration tests --- .../alerting_api_integration/common/config.ts | 1 + .../plugins/alerts/server/action_types.ts | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 851bc653d6252..fe84d1b0f50ba 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -64,6 +64,7 @@ const enabledActionTypes = [ 'test.throw', 'test.excluded', 'test.capped', + 'test-system-action', ]; export function createTestConfig(name: string, options: CreateTestConfigOptions) { diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index d04fb52b7637a..d452c68101fdc 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -74,6 +74,7 @@ export function defineActionTypes( actions.registerType(getNoAttemptsRateLimitedActionType()); actions.registerType(getAuthorizationActionType(core)); actions.registerType(getExcludedActionType()); + actions.registerType(getSystemActionType()); /** Sub action framework */ @@ -399,3 +400,29 @@ function getExcludedActionType() { }; return result; } + +function getSystemActionType() { + const result: ActionType<{}, {}, {}> = { + id: 'test-system-action', + name: 'Test system action', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + params: { + schema: schema.any(), + }, + config: { + schema: schema.any(), + }, + secrets: { + schema: schema.any(), + }, + }, + isSystemActionType: true, + async executor({ config, secrets, params, services, actionId }) { + return { status: 'ok', actionId }; + }, + }; + + return result; +} From 86b279cc86dafdc3897a0d332a5348dc210cedd1 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 1 Jul 2023 20:39:22 +0300 Subject: [PATCH 21/45] Add integration tests --- .../plugins/actions/server/actions_client.ts | 27 +++---- .../group2/tests/actions/create.ts | 76 +++++++++++++++++++ .../group2/tests/actions/delete.ts | 37 ++++++++- .../group2/tests/actions/get.ts | 37 +++++++++ .../group2/tests/actions/get_all.ts | 27 +++++++ .../group2/tests/actions/update.ts | 46 ++++++++++- .../spaces_only/tests/actions/create.ts | 34 +++++++++ .../spaces_only/tests/actions/delete.ts | 34 ++++++++- .../spaces_only/tests/actions/get.ts | 40 +++++++++- .../spaces_only/tests/actions/get_all.ts | 27 +++++++ .../actions/schedule_unsecured_action.ts | 4 +- .../spaces_only/tests/actions/update.ts | 52 ++++++++++++- 12 files changed, 417 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 9bad5235fc6ab..6ec173dabb768 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -195,6 +195,20 @@ export class ActionsClient { options, }: CreateOptions): Promise { const id = options?.id || SavedObjectsUtils.generateId(); + + try { + await this.authorization.ensureAuthorized('create', actionTypeId); + } catch (error) { + this.auditLogger?.log( + connectorAuditEvent({ + action: ConnectorAuditAction.CREATE, + savedObject: { type: 'action', id }, + error, + }) + ); + throw error; + } + const foundInMemoryConnector = this.inMemoryConnectors.find((connector) => connector.id === id); if ( @@ -222,19 +236,6 @@ export class ActionsClient { ); } - try { - await this.authorization.ensureAuthorized('create', actionTypeId); - } catch (error) { - this.auditLogger?.log( - connectorAuditEvent({ - action: ConnectorAuditAction.CREATE, - savedObject: { type: 'action', id }, - error, - }) - ); - throw error; - } - const actionType = this.actionTypeRegistry.get(actionTypeId); const configurationUtilities = this.actionTypeRegistry.getUtils(); const validatedActionTypeConfig = validateConfig(actionType, config, { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts index 4c9db37fe3543..e14bb493d7fc6 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts @@ -319,6 +319,82 @@ export default function createActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it(`shouldn't create a preconfigured action with the same id as an existing one`, async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector/custom-system-abc-connector`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'system-abc-action-type', + config: {}, + secrets: {}, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "system-abc-action-type" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'This custom-system-abc-connector already exist in preconfigured action.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`shouldn't create a system action`, async () => { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My system action', + connector_type_id: 'test-system-action', + config: {}, + secrets: {}, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to create a "test-system-action" action', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'System action creation is forbidden. Action type: test-system-action.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts index b0cfffdd2f464..dd8ab65547563 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts @@ -146,7 +146,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { } }); - it(`shouldn't delete action from preconfigured list`, async () => { + it(`shouldn't delete preconfigured action`, async () => { const response = await supertestWithoutAuth .delete(`${getUrlPrefix(space.id)}/api/actions/connector/my-slack1`) .auth(user.username, user.password) @@ -177,6 +177,41 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it(`shouldn't delete system action`, async () => { + const response = await supertestWithoutAuth + .delete( + `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test-system-action` + ) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo'); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to delete actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'System action system-connector-test-system-action is not allowed to delete.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts index 3b04af22d2e96..ac16586f3bb6e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts @@ -160,6 +160,43 @@ export default function getActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it('should handle get system action request appropriately', async () => { + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test-system-action` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to get actions', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + id: 'system-connector-test-system-action', + connector_type_id: 'test-system-action', + name: 'System action: test-system-action', + is_preconfigured: false, + is_system_action: true, + is_deprecated: false, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts index 37475d7f191ca..9e0702d83eb48 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts @@ -125,6 +125,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', referenced_by_count: 0, }, + { + connector_type_id: 'test-system-action', + id: 'system-connector-test-system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test-system-action', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -285,6 +294,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', referenced_by_count: 0, }, + { + connector_type_id: 'test-system-action', + id: 'system-connector-test-system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test-system-action', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -408,6 +426,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', referenced_by_count: 0, }, + { + connector_type_id: 'test-system-action', + id: 'system-connector-test-system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test-system-action', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts index 07d2e6064bc0b..220feb7b3fa34 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts @@ -312,7 +312,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { } }); - it(`shouldn't update action from preconfigured list`, async () => { + it(`shouldn't update a preconfigured action`, async () => { const response = await supertestWithoutAuth .put(`${getUrlPrefix(space.id)}/api/actions/connector/custom-system-abc-connector`) .auth(user.username, user.password) @@ -387,6 +387,50 @@ export default function updateActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it(`shouldn't update a system action`, async () => { + const response = await supertestWithoutAuth + .put( + `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test-system-action` + ) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to update actions', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'System action system-connector-test-system-action is not allowed to update.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts index a7239343b2c61..d0e3e50eaa672 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts @@ -80,6 +80,40 @@ export default function createActionTests({ getService }: FtrProviderContext) { }); }); + it(`shouldn't create a preconfigured action with the same id as an existing one`, async () => { + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/custom-system-abc-connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'system-abc-action-type', + config: {}, + secrets: {}, + }) + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'This custom-system-abc-connector already exist in preconfigured action.', + }); + }); + + it(`shouldn't create a system action`, async () => { + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My system action', + connector_type_id: 'test-system-action', + config: {}, + secrets: {}, + }) + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'System action creation is forbidden. Action type: test-system-action.', + }); + }); + describe('legacy', () => { it('should handle create action request appropriately', async () => { const response = await supertest diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts index 66ae047e5151b..4ac03f2935627 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts @@ -78,7 +78,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { }); }); - it(`shouldn't delete action from preconfigured list`, async () => { + it(`shouldn't delete a preconfigured action`, async () => { await supertest .delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/my-slack1`) .set('kbn-xsrf', 'foo') @@ -89,6 +89,21 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { }); }); + it(`shouldn't delete a system action`, async () => { + await supertest + .delete( + `${getUrlPrefix( + Spaces.space1.id + )}/api/actions/connector/system-connector-test-system-action` + ) + .set('kbn-xsrf', 'foo') + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'System action system-connector-test-system-action is not allowed to delete.', + }); + }); + describe('legacy', () => { it('should handle delete action request appropriately', async () => { const { body: createdAction } = await supertest @@ -150,7 +165,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { }); }); - it(`shouldn't delete action from preconfigured list`, async () => { + it(`shouldn't delete a preconfigured action`, async () => { await supertest .delete(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/my-slack1`) .set('kbn-xsrf', 'foo') @@ -160,6 +175,21 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { message: `Preconfigured action my-slack1 is not allowed to delete.`, }); }); + + it(`shouldn't delete a system action`, async () => { + await supertest + .delete( + `${getUrlPrefix( + Spaces.space1.id + )}/api/actions/action/system-connector-test-system-action` + ) + .set('kbn-xsrf', 'foo') + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'System action system-connector-test-system-action is not allowed to delete.', + }); + }); }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts index 68d4512145118..eaf7a6c434496 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -77,7 +77,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { }); }); - it('should handle get action request from preconfigured list', async () => { + it('should handle get a preconfigured connector', async () => { await supertest .get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/my-slack1`) .expect(200, { @@ -90,7 +90,24 @@ export default function getActionTests({ getService }: FtrProviderContext) { }); }); - it('should handle get action request for deprecated connectors from preconfigured list', async () => { + it('should handle get a system connector', async () => { + await supertest + .get( + `${getUrlPrefix( + Spaces.space1.id + )}/api/actions/connector/system-connector-test-system-action` + ) + .expect(200, { + id: 'system-connector-test-system-action', + connector_type_id: 'test-system-action', + name: 'System action: test-system-action', + is_preconfigured: false, + is_system_action: true, + is_deprecated: false, + }); + }); + + it('should handle get a deprecated connector', async () => { await supertest .get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/my-deprecated-servicenow`) .expect(200, { @@ -176,7 +193,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { }); }); - it('should handle get action request from preconfigured list', async () => { + it('should handle get a preconfigured connector', async () => { await supertest .get(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/my-slack1`) .expect(200, { @@ -188,6 +205,23 @@ export default function getActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', }); }); + + it('should handle get a system connector', async () => { + await supertest + .get( + `${getUrlPrefix( + Spaces.space1.id + )}/api/actions/action/system-connector-test-system-action` + ) + .expect(200, { + id: 'system-connector-test-system-action', + connector_type_id: 'test-system-action', + name: 'System action: test-system-action', + is_preconfigured: false, + is_system_action: true, + is_deprecated: false, + }); + }); }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index d4a43da31894e..528b2a6c59997 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -114,6 +114,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', referenced_by_count: 0, }, + { + connector_type_id: 'test-system-action', + id: 'system-connector-test-system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test-system-action', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -226,6 +235,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', referenced_by_count: 0, }, + { + connector_type_id: 'test-system-action', + id: 'system-connector-test-system-action', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test-system-action', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -352,6 +370,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'Slack#xyz', referencedByCount: 0, }, + { + actionTypeId: 'test-system-action', + id: 'system-connector-test-system-action', + isDeprecated: false, + isPreconfigured: false, + isSystemAction: true, + name: 'System action: test-system-action', + referencedByCount: 0, + }, { id: 'custom-system-abc-connector', isPreconfigured: true, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/schedule_unsecured_action.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/schedule_unsecured_action.ts index 9a5719b7fa700..409e56e7b1fd8 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/schedule_unsecured_action.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/schedule_unsecured_action.ts @@ -149,7 +149,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo ); }); - it('should not allow scheduling action from non preconfigured connectors', async () => { + it('should not allow scheduling action from non in-memory connectors', async () => { const response = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') @@ -184,7 +184,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo .expect(200); expect(result.status).to.eql('error'); expect(result.error).to.eql( - `Error: ${connectorId} are not preconfigured connectors and can't be scheduled for unsecured actions execution` + `Error: ${connectorId} are not in-memory connectors and can't be scheduled for unsecured actions execution` ); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts index d6f41e3037ad2..04122a6d8267b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts @@ -106,7 +106,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }); }); - it(`shouldn't update action from preconfigured list`, async () => { + it(`shouldn't update a preconfigured connector`, async () => { await supertest .put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/custom-system-abc-connector`) .set('kbn-xsrf', 'foo') @@ -126,6 +126,30 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }); }); + it(`shouldn't update a system connector`, async () => { + await supertest + .put( + `${getUrlPrefix( + Spaces.space1.id + )}/api/actions/connector/system-connector-test-system-action` + ) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'System action system-connector-test-system-action is not allowed to update.', + }); + }); + it('should notify feature usage when editing a gold action type', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) @@ -270,7 +294,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }); }); - it(`shouldn't update action from preconfigured list`, async () => { + it(`shouldn't update a preconfigured connector`, async () => { await supertest .put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/custom-system-abc-connector`) .set('kbn-xsrf', 'foo') @@ -290,6 +314,30 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }); }); + it(`shouldn't update a system connector`, async () => { + await supertest + .put( + `${getUrlPrefix( + Spaces.space1.id + )}/api/actions/action/system-connector-test-system-action` + ) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: 'System action system-connector-test-system-action is not allowed to update.', + }); + }); + it('should notify feature usage when editing a gold action type', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) From 57b4d3af1daf856ffc399ec284ecf4c92be025ee Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 2 Jul 2023 14:11:28 +0300 Subject: [PATCH 22/45] Fix i18n --- x-pack/plugins/actions/server/actions_client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 6ec173dabb768..ce82913cd749c 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -294,7 +294,7 @@ export class ActionsClient { if (foundInMemoryConnector?.isSystemAction) { throw Boom.badRequest( - i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { + i18n.translate('xpack.actions.serverSideErrors.systemActionUpdateForbidden', { defaultMessage: 'System action {id} is not allowed to update.', values: { id, @@ -668,7 +668,7 @@ export class ActionsClient { if (foundInMemoryConnector?.isSystemAction) { throw Boom.badRequest( - i18n.translate('xpack.actions.serverSideErrors.systemActionCreationForbidden', { + i18n.translate('xpack.actions.serverSideErrors.systemActionDeletionForbidden', { defaultMessage: 'System action {id} is not allowed to delete.', values: { id, From a16216378ec21854499cdb3365fcceabde1971a2 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 2 Jul 2023 14:17:39 +0300 Subject: [PATCH 23/45] Fix types --- .../lib/action_connector_api/connector_types.test.ts | 2 ++ .../lib/action_connector_api/connector_types.ts | 2 ++ .../public/application/lib/action_type_compare.test.ts | 8 ++++++++ .../application/lib/check_action_type_enabled.test.tsx | 5 +++++ .../action_connector_form/action_type_form.test.tsx | 2 ++ .../action_connector_form/connector_add_modal.test.tsx | 3 +++ .../action_connector_form/connectors_selection.test.tsx | 1 + .../rule_details/components/rule_details.test.tsx | 5 +++++ 8 files changed, 28 insertions(+) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts index 69b8975a9768f..3dbf3e5d20822 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts @@ -37,6 +37,7 @@ describe('loadActionTypes', () => { enabledInLicense: true, supportedFeatureIds: ['alerting'], minimumLicenseRequired: 'basic', + isSystemActionType: false, }, ]; @@ -72,6 +73,7 @@ describe('loadActionTypes', () => { enabledInLicense: true, supportedFeatureIds: ['alerting'], minimumLicenseRequired: 'basic', + isSystemActionType: false, }, ]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts index 191ae96acc8b6..be16cfc65309e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts @@ -19,12 +19,14 @@ const rewriteBodyReq: RewriteRequestCase = ({ enabled_in_license: enabledInLicense, minimum_license_required: minimumLicenseRequired, supported_feature_ids: supportedFeatureIds, + is_system_action_type: isSystemActionType, ...res }: AsApiContract) => ({ enabledInConfig, enabledInLicense, minimumLicenseRequired, supportedFeatureIds, + isSystemActionType, ...res, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_type_compare.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_type_compare.test.ts index ffc6594d59651..8c9e71bb7d03b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_type_compare.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_type_compare.test.ts @@ -18,6 +18,7 @@ test('should sort enabled action types first', async () => { enabled: true, enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, }, { id: '2', @@ -27,6 +28,7 @@ test('should sort enabled action types first', async () => { enabled: false, enabledInConfig: true, enabledInLicense: false, + isSystemActionType: false, }, { id: '3', @@ -36,6 +38,7 @@ test('should sort enabled action types first', async () => { enabled: true, enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, }, { id: '4', @@ -45,6 +48,7 @@ test('should sort enabled action types first', async () => { enabled: true, enabledInConfig: false, enabledInLicense: true, + isSystemActionType: false, }, ]; const result = [...actionTypes].sort(actionTypeCompare); @@ -64,6 +68,7 @@ test('should sort by name when all enabled', async () => { enabled: true, enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, }, { id: '2', @@ -73,6 +78,7 @@ test('should sort by name when all enabled', async () => { enabled: true, enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, }, { id: '3', @@ -82,6 +88,7 @@ test('should sort by name when all enabled', async () => { enabled: true, enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, }, { id: '4', @@ -91,6 +98,7 @@ test('should sort by name when all enabled', async () => { enabled: true, enabledInConfig: false, enabledInLicense: true, + isSystemActionType: false, }, ]; const result = [...actionTypes].sort(actionTypeCompare); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx index e2dc91214e783..2cdd25267de0e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx @@ -29,6 +29,7 @@ describe('checkActionTypeEnabled', () => { enabled: true, enabledInConfig: true, enabledInLicense: true, + isSystemActionType: false, }; expect(checkActionTypeEnabled(actionType)).toMatchInlineSnapshot(` Object { @@ -46,6 +47,7 @@ describe('checkActionTypeEnabled', () => { enabled: false, enabledInConfig: true, enabledInLicense: false, + isSystemActionType: false, }; expect(checkActionTypeEnabled(actionType)).toMatchInlineSnapshot(` Object { @@ -81,6 +83,7 @@ describe('checkActionTypeEnabled', () => { enabled: false, enabledInConfig: false, enabledInLicense: true, + isSystemActionType: false, }; expect(checkActionTypeEnabled(actionType)).toMatchInlineSnapshot(` Object { @@ -127,6 +130,7 @@ describe('checkActionFormActionTypeEnabled', () => { enabled: true, enabledInConfig: false, enabledInLicense: true, + isSystemActionType: false, }; expect(checkActionFormActionTypeEnabled(actionType, preconfiguredConnectors)) @@ -146,6 +150,7 @@ describe('checkActionFormActionTypeEnabled', () => { enabled: true, enabledInConfig: false, enabledInLicense: true, + isSystemActionType: false, }; expect(checkActionFormActionTypeEnabled(actionType, preconfiguredConnectors)) .toMatchInlineSnapshot(` diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index 46dafc3c4fc03..4cfe807ecd728 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -605,6 +605,7 @@ function getActionTypeForm({ enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, '.server-log': { id: '.server-log', @@ -614,6 +615,7 @@ function getActionTypeForm({ enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx index fcc80ace505ff..6efbf023e76d6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx @@ -58,6 +58,7 @@ describe('connector_add_modal', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }; const wrapper = mountWithIntl( @@ -100,6 +101,7 @@ describe('connector_add_modal', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }; const wrapper = mountWithIntl( { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }; const wrapper = mountWithIntl( { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx index b2edd76a39401..3bd45741714a0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.test.tsx @@ -216,6 +216,7 @@ describe('rule_details', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; @@ -259,6 +260,7 @@ describe('rule_details', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, { id: '.email', @@ -268,6 +270,7 @@ describe('rule_details', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; @@ -338,6 +341,7 @@ describe('rule_details', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; ruleTypeRegistry.has.mockReturnValue(true); @@ -468,6 +472,7 @@ describe('rule_details', () => { enabledInLicense: true, minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], + isSystemActionType: false, }, ]; ruleTypeRegistry.has.mockReturnValue(true); From 2ea3c9c45b7192a1afe19fa9a5886bb1ce775b71 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 2 Jul 2023 14:19:37 +0300 Subject: [PATCH 24/45] Fix integration test --- .../spaces_only/tests/actions/get.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts index eaf7a6c434496..87dedc46327e0 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -215,11 +215,11 @@ export default function getActionTests({ getService }: FtrProviderContext) { ) .expect(200, { id: 'system-connector-test-system-action', - connector_type_id: 'test-system-action', + actionTypeId: 'test-system-action', name: 'System action: test-system-action', - is_preconfigured: false, - is_system_action: true, - is_deprecated: false, + isPreconfigured: false, + isSystemAction: true, + isDeprecated: false, }); }); }); From 7362b97da2164211e18c7cafc4aaedd7dbb5b3ce Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 2 Jul 2023 16:14:59 +0300 Subject: [PATCH 25/45] Fixes --- .../connector_types.test.ts | 2 ++ .../legacy_uptime/pages/settings.test.tsx | 1 + .../public/legacy_uptime/state/api/alerts.ts | 2 ++ .../alerting_api_integration/common/config.ts | 2 +- .../plugins/alerts/server/action_types.ts | 2 +- .../group2/tests/actions/create.ts | 6 +++--- .../group2/tests/actions/delete.ts | 4 ++-- .../group2/tests/actions/get.ts | 8 ++++---- .../group2/tests/actions/get_all.ts | 18 +++++++++--------- .../group2/tests/actions/update.ts | 4 ++-- .../spaces_only/tests/actions/create.ts | 4 ++-- .../spaces_only/tests/actions/delete.ts | 8 ++++---- .../spaces_only/tests/actions/get.ts | 16 ++++++++-------- .../spaces_only/tests/actions/get_all.ts | 18 +++++++++--------- .../spaces_only/tests/actions/update.ts | 8 ++++---- 15 files changed, 54 insertions(+), 49 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts index 3dbf3e5d20822..182abc1507a34 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.test.ts @@ -24,6 +24,7 @@ describe('loadActionTypes', () => { enabled_in_license: true, supported_feature_ids: ['alerting'], minimum_license_required: 'basic', + is_system_action_type: false, }, ]; http.get.mockResolvedValueOnce(apiResponseValue); @@ -60,6 +61,7 @@ describe('loadActionTypes', () => { enabled_in_license: true, supported_feature_ids: ['alerting'], minimum_license_required: 'basic', + is_system_action_type: false, }, ]; http.get.mockResolvedValueOnce(apiResponseValue); diff --git a/x-pack/plugins/uptime/public/legacy_uptime/pages/settings.test.tsx b/x-pack/plugins/uptime/public/legacy_uptime/pages/settings.test.tsx index 8a87ff2969974..5c8fb95448fca 100644 --- a/x-pack/plugins/uptime/public/legacy_uptime/pages/settings.test.tsx +++ b/x-pack/plugins/uptime/public/legacy_uptime/pages/settings.test.tsx @@ -24,6 +24,7 @@ describe('settings', () => { minimumLicenseRequired: 'gold', name: 'Slack', supportedFeatureIds: ['uptime'], + isSystemActionType: false, }, ]); }); diff --git a/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts b/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts index 8c623e914516b..9ca6a386997ec 100644 --- a/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts +++ b/x-pack/plugins/uptime/public/legacy_uptime/state/api/alerts.ts @@ -172,6 +172,7 @@ export const fetchActionTypes = async (): Promise => { enabled_in_license: enabledInLicense, minimum_license_required: minimumLicenseRequired, supported_feature_ids: supportedFeatureIds, + is_system_action_type: isSystemActionType, ...res }: AsApiContract) => ({ ...res, @@ -179,6 +180,7 @@ export const fetchActionTypes = async (): Promise => { enabledInLicense, minimumLicenseRequired, supportedFeatureIds, + isSystemActionType, }) ); }; diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index fe84d1b0f50ba..3e48a3b273019 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -64,7 +64,7 @@ const enabledActionTypes = [ 'test.throw', 'test.excluded', 'test.capped', - 'test-system-action', + 'test.system-action', ]; export function createTestConfig(name: string, options: CreateTestConfigOptions) { diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index d452c68101fdc..cddf100205058 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -403,7 +403,7 @@ function getExcludedActionType() { function getSystemActionType() { const result: ActionType<{}, {}, {}> = { - id: 'test-system-action', + id: 'test.system-action', name: 'Test system action', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts index e14bb493d7fc6..6be435b569a97 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/create.ts @@ -365,7 +365,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .send({ name: 'My system action', - connector_type_id: 'test-system-action', + connector_type_id: 'test.system-action', config: {}, secrets: {}, }); @@ -379,7 +379,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 403, error: 'Forbidden', - message: 'Unauthorized to create a "test-system-action" action', + message: 'Unauthorized to create a "test.system-action" action', }); break; case 'superuser at space1': @@ -388,7 +388,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: 'System action creation is forbidden. Action type: test-system-action.', + message: 'System action creation is forbidden. Action type: test.system-action.', }); break; default: diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts index dd8ab65547563..b5b11036a3dfd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/delete.ts @@ -181,7 +181,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { it(`shouldn't delete system action`, async () => { const response = await supertestWithoutAuth .delete( - `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test-system-action` + `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test.system-action` ) .auth(user.username, user.password) .set('kbn-xsrf', 'foo'); @@ -205,7 +205,7 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'System action system-connector-test-system-action is not allowed to delete.', + 'System action system-connector-test.system-action is not allowed to delete.', }); break; default: diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts index ac16586f3bb6e..42cfce82cb2e1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts @@ -164,7 +164,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { it('should handle get system action request appropriately', async () => { const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test-system-action` + `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test.system-action` ) .auth(user.username, user.password); @@ -185,9 +185,9 @@ export default function getActionTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ - id: 'system-connector-test-system-action', - connector_type_id: 'test-system-action', - name: 'System action: test-system-action', + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + name: 'System action: test.system-action', is_preconfigured: false, is_system_action: true, is_deprecated: false, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts index 9e0702d83eb48..bc3444b5a32b3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts @@ -126,12 +126,12 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { referenced_by_count: 0, }, { - connector_type_id: 'test-system-action', - id: 'system-connector-test-system-action', + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', is_deprecated: false, is_preconfigured: false, is_system_action: true, - name: 'System action: test-system-action', + name: 'System action: test.system-action', referenced_by_count: 0, }, { @@ -295,12 +295,12 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { referenced_by_count: 0, }, { - connector_type_id: 'test-system-action', - id: 'system-connector-test-system-action', + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', is_deprecated: false, is_preconfigured: false, is_system_action: true, - name: 'System action: test-system-action', + name: 'System action: test.system-action', referenced_by_count: 0, }, { @@ -427,12 +427,12 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { referenced_by_count: 0, }, { - connector_type_id: 'test-system-action', - id: 'system-connector-test-system-action', + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', is_deprecated: false, is_preconfigured: false, is_system_action: true, - name: 'System action: test-system-action', + name: 'System action: test.system-action', referenced_by_count: 0, }, { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts index 220feb7b3fa34..c27208860fcfa 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/update.ts @@ -391,7 +391,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { it(`shouldn't update a system action`, async () => { const response = await supertestWithoutAuth .put( - `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test-system-action` + `${getUrlPrefix(space.id)}/api/actions/connector/system-connector-test.system-action` ) .auth(user.username, user.password) .set('kbn-xsrf', 'foo') @@ -424,7 +424,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'System action system-connector-test-system-action is not allowed to update.', + 'System action system-connector-test.system-action is not allowed to update.', }); break; default: diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts index d0e3e50eaa672..614c130cc7120 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts @@ -103,14 +103,14 @@ export default function createActionTests({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'foo') .send({ name: 'My system action', - connector_type_id: 'test-system-action', + connector_type_id: 'test.system-action', config: {}, secrets: {}, }) .expect(400, { statusCode: 400, error: 'Bad Request', - message: 'System action creation is forbidden. Action type: test-system-action.', + message: 'System action creation is forbidden. Action type: test.system-action.', }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts index 4ac03f2935627..5d3a5f0c32ecb 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts @@ -94,13 +94,13 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { .delete( `${getUrlPrefix( Spaces.space1.id - )}/api/actions/connector/system-connector-test-system-action` + )}/api/actions/connector/system-connector-test.system-action` ) .set('kbn-xsrf', 'foo') .expect(400, { statusCode: 400, error: 'Bad Request', - message: 'System action system-connector-test-system-action is not allowed to delete.', + message: 'System action system-connector-test.system-action is not allowed to delete.', }); }); @@ -181,13 +181,13 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { .delete( `${getUrlPrefix( Spaces.space1.id - )}/api/actions/action/system-connector-test-system-action` + )}/api/actions/action/system-connector-test.system-action` ) .set('kbn-xsrf', 'foo') .expect(400, { statusCode: 400, error: 'Bad Request', - message: 'System action system-connector-test-system-action is not allowed to delete.', + message: 'System action system-connector-test.system-action is not allowed to delete.', }); }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts index 87dedc46327e0..dc99597a901e1 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -95,12 +95,12 @@ export default function getActionTests({ getService }: FtrProviderContext) { .get( `${getUrlPrefix( Spaces.space1.id - )}/api/actions/connector/system-connector-test-system-action` + )}/api/actions/connector/system-connector-test.system-action` ) .expect(200, { - id: 'system-connector-test-system-action', - connector_type_id: 'test-system-action', - name: 'System action: test-system-action', + id: 'system-connector-test.system-action', + connector_type_id: 'test.system-action', + name: 'System action: test.system-action', is_preconfigured: false, is_system_action: true, is_deprecated: false, @@ -211,12 +211,12 @@ export default function getActionTests({ getService }: FtrProviderContext) { .get( `${getUrlPrefix( Spaces.space1.id - )}/api/actions/action/system-connector-test-system-action` + )}/api/actions/action/system-connector-test.system-action` ) .expect(200, { - id: 'system-connector-test-system-action', - actionTypeId: 'test-system-action', - name: 'System action: test-system-action', + id: 'system-connector-test.system-action', + actionTypeId: 'test.system-action', + name: 'System action: test.system-action', isPreconfigured: false, isSystemAction: true, isDeprecated: false, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index 528b2a6c59997..2366c392c4e4d 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -115,12 +115,12 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { referenced_by_count: 0, }, { - connector_type_id: 'test-system-action', - id: 'system-connector-test-system-action', + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', is_deprecated: false, is_preconfigured: false, is_system_action: true, - name: 'System action: test-system-action', + name: 'System action: test.system-action', referenced_by_count: 0, }, { @@ -236,12 +236,12 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { referenced_by_count: 0, }, { - connector_type_id: 'test-system-action', - id: 'system-connector-test-system-action', + connector_type_id: 'test.system-action', + id: 'system-connector-test.system-action', is_deprecated: false, is_preconfigured: false, is_system_action: true, - name: 'System action: test-system-action', + name: 'System action: test.system-action', referenced_by_count: 0, }, { @@ -371,12 +371,12 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { referencedByCount: 0, }, { - actionTypeId: 'test-system-action', - id: 'system-connector-test-system-action', + actionTypeId: 'test.system-action', + id: 'system-connector-test.system-action', isDeprecated: false, isPreconfigured: false, isSystemAction: true, - name: 'System action: test-system-action', + name: 'System action: test.system-action', referencedByCount: 0, }, { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts index 04122a6d8267b..5dd515395994c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts @@ -131,7 +131,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { .put( `${getUrlPrefix( Spaces.space1.id - )}/api/actions/connector/system-connector-test-system-action` + )}/api/actions/connector/system-connector-test.system-action` ) .set('kbn-xsrf', 'foo') .send({ @@ -146,7 +146,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { .expect(400, { statusCode: 400, error: 'Bad Request', - message: 'System action system-connector-test-system-action is not allowed to update.', + message: 'System action system-connector-test.system-action is not allowed to update.', }); }); @@ -319,7 +319,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { .put( `${getUrlPrefix( Spaces.space1.id - )}/api/actions/action/system-connector-test-system-action` + )}/api/actions/action/system-connector-test.system-action` ) .set('kbn-xsrf', 'foo') .send({ @@ -334,7 +334,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { .expect(400, { statusCode: 400, error: 'Bad Request', - message: 'System action system-connector-test-system-action is not allowed to update.', + message: 'System action system-connector-test.system-action is not allowed to update.', }); }); From a1b5d516cc4236317c353bcdcbd6afd5eee18036 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 3 Jul 2023 14:02:17 +0300 Subject: [PATCH 26/45] Filtered out system connectors in telemetry --- .../server/action_type_registry.test.ts | 6 +- .../actions/server/actions_client.test.ts | 2 +- .../server/create_execute_function.test.ts | 69 +++++++++++++++++-- .../actions/server/create_execute_function.ts | 7 +- .../create_unsecured_execute_function.ts | 4 +- .../create_action_event_log_record_object.ts | 2 +- x-pack/plugins/actions/server/plugin.ts | 2 +- x-pack/plugins/actions/server/usage/task.ts | 18 +++-- 8 files changed, 90 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 60b1cf75bc79f..6988307b01b7e 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -476,9 +476,9 @@ describe('actionTypeRegistry', () => { mockedActionsConfig.isActionTypeEnabled.mockReturnValue(false); mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); - expect(actionTypeRegistry.isActionExecutable('my-slack1', 'system-action-type')).toEqual( - true - ); + expect( + actionTypeRegistry.isActionExecutable('system-connector-.cases', 'system-action-type') + ).toEqual(true); }); test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => { diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 465e3f7ff4f85..1c5ecdc175cc9 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -788,7 +788,7 @@ describe('create()', () => { ); }); - it('throws when creating a system connector where the action type is not registered by a system connector exists in the in-memory list', async () => { + it('throws when creating a system connector where the action type is not registered but a system connector exists in the in-memory list', async () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, 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 7fb737d2b5341..75a1ecf333311 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -346,7 +346,7 @@ describe('execute()', () => { id: '123', type: 'action', attributes: { - actionTypeId: 'mock-action', + actionTypeId: '.cases', }, references: [], }); @@ -530,7 +530,7 @@ describe('execute()', () => { id: '123', type: 'action', attributes: { - actionTypeId: 'mock-action', + actionTypeId: '.cases', }, references: [], }); @@ -762,7 +762,7 @@ describe('execute()', () => { id: '123', type: 'action', attributes: { - actionTypeId: 'mock-action', + actionTypeId: '.cases', }, references: [], }); @@ -1174,7 +1174,7 @@ describe('bulkExecute()', () => { id: '123', type: 'action', attributes: { - actionTypeId: 'mock-action', + actionTypeId: '.cases', }, references: [], }, @@ -1389,7 +1389,7 @@ describe('bulkExecute()', () => { id: '123', type: 'action', attributes: { - actionTypeId: 'mock-action', + actionTypeId: '.cases', }, references: [], }, @@ -1637,4 +1637,63 @@ describe('bulkExecute()', () => { expect(mockedActionTypeRegistry.ensureActionTypeEnabled).not.toHaveBeenCalled(); }); + + test('should skip ensure action type if action type is system action and license is valid', async () => { + const mockedActionTypeRegistry = actionTypeRegistryMock.create(); + const executeFn = createBulkExecutionEnqueuerFunction({ + taskManager: mockTaskManager, + isESOCanEncrypt: true, + actionTypeRegistry: mockedActionTypeRegistry, + inMemoryConnectors: [ + { + actionTypeId: '.cases', + config: {}, + id: 'system-connector-.cases', + name: 'System action: .cases', + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: true, + }, + ], + }); + mockedActionTypeRegistry.isActionExecutable.mockImplementation(() => true); + savedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '123', + type: 'action', + attributes: { + actionTypeId: '.cases', + }, + references: [], + }, + ], + }); + savedObjectsClient.bulkCreate.mockResolvedValueOnce({ + saved_objects: [ + { + id: '234', + type: 'action_task_params', + attributes: { + actionId: '123', + }, + references: [], + }, + ], + }); + + await executeFn(savedObjectsClient, [ + { + id: '123', + params: { baz: false }, + spaceId: 'default', + executionId: '123abc', + apiKey: null, + source: asHttpRequestExecutionSource(request), + }, + ]); + + expect(mockedActionTypeRegistry.ensureActionTypeEnabled).not.toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index c25db5d7281e7..6752b17fd5ffd 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -295,9 +295,10 @@ async function getAction( inMemoryConnectors: InMemoryConnector[], actionId: string ): Promise<{ action: InMemoryConnector | RawAction; isInMemory: boolean }> { - const pcAction = inMemoryConnectors.find((action) => action.id === actionId); - if (pcAction) { - return { action: pcAction, isInMemory: true }; + const inMemoryAction = inMemoryConnectors.find((action) => action.id === actionId); + + if (inMemoryAction) { + return { action: inMemoryAction, isInMemory: true }; } const { attributes } = await unsecuredSavedObjectsClient.get('action', actionId); diff --git a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts index bd4b947e53248..585f442c68e2f 100644 --- a/x-pack/plugins/actions/server/create_unsecured_execute_function.ts +++ b/x-pack/plugins/actions/server/create_unsecured_execute_function.ts @@ -64,7 +64,9 @@ export function createBulkUnsecuredExecutionEnqueuerFunction({ } const connectors: InMemoryConnector[] = connectorIds - .map((connectorId) => inMemoryConnectors.find((pConnector) => pConnector.id === connectorId)) + .map((connectorId) => + inMemoryConnectors.find((inMemoryConnector) => inMemoryConnector.id === connectorId) + ) .filter(Boolean) as InMemoryConnector[]; connectors.forEach((connector) => { diff --git a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts index d29cd670c514c..4f8bf08966c59 100644 --- a/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts +++ b/x-pack/plugins/actions/server/lib/create_action_event_log_record_object.ts @@ -80,7 +80,7 @@ export function createActionEventLogRecordObject(params: CreateActionEventLogRec type: so.type, id: so.id, type_id: so.typeId, - // set space_agnostic to true for preconfigured connectors + // set space_agnostic to true for in-memory connectors ...(so.type === 'action' && isInMemory ? { space_agnostic: isInMemory } : {}), ...(namespace ? { namespace } : {}), })), diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 25b03b41413ee..bc29c3e9ad246 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -308,7 +308,7 @@ export class ActionsPlugin implements Plugin InMemoryConnector[], eventLogIndex: string ) { - registerActionsTelemetryTask(logger, taskManager, core, inMemoryConnectors, eventLogIndex); + registerActionsTelemetryTask(logger, taskManager, core, getInMemoryConnectors, eventLogIndex); } export function scheduleActionsTelemetry(logger: Logger, taskManager: TaskManagerStartContract) { @@ -38,14 +38,14 @@ function registerActionsTelemetryTask( logger: Logger, taskManager: TaskManagerSetupContract, core: CoreSetup, - inMemoryConnectors: InMemoryConnector[], + getInMemoryConnectors: () => InMemoryConnector[], eventLogIndex: string ) { taskManager.registerTaskDefinitions({ [TELEMETRY_TASK_TYPE]: { title: 'Actions usage fetch task', timeout: '5m', - createTaskRunner: telemetryTaskRunner(logger, core, inMemoryConnectors, eventLogIndex), + createTaskRunner: telemetryTaskRunner(logger, core, getInMemoryConnectors, eventLogIndex), }, }); } @@ -67,9 +67,17 @@ async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContra export function telemetryTaskRunner( logger: Logger, core: CoreSetup, - inMemoryConnectors: InMemoryConnector[], + getInMemoryConnectors: () => InMemoryConnector[], eventLogIndex: string ) { + /** + * Filter out system actions from the + * inMemoryConnectors list. + */ + const inMemoryConnectors = getInMemoryConnectors().filter( + (inMemoryConnector) => inMemoryConnector.isPreconfigured + ); + return ({ taskInstance }: RunContext) => { const { state } = taskInstance; const getEsClient = () => From 45c4690a907c0df34d22b25bdfb9828f48f13d5a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 3 Jul 2023 14:54:21 +0300 Subject: [PATCH 27/45] Add the ability to pass additional privileges to the actions authorization model --- .../plugins/actions/server/actions_client.ts | 26 ++++++++-------- .../authorization/actions_authorization.ts | 31 +++++++++++++------ 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index ce82913cd749c..3e398796dd825 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -197,7 +197,7 @@ export class ActionsClient { const id = options?.id || SavedObjectsUtils.generateId(); try { - await this.authorization.ensureAuthorized('create', actionTypeId); + await this.authorization.ensureAuthorized({ operation: 'create', actionTypeId }); } catch (error) { this.auditLogger?.log( connectorAuditEvent({ @@ -286,7 +286,7 @@ export class ActionsClient { */ public async update({ id, action }: UpdateOptions): Promise { try { - await this.authorization.ensureAuthorized('update'); + await this.authorization.ensureAuthorized({ operation: 'update' }); const foundInMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === id @@ -396,7 +396,7 @@ export class ActionsClient { */ public async get({ id }: { id: string }): Promise { try { - await this.authorization.ensureAuthorized('get'); + await this.authorization.ensureAuthorized({ operation: 'get' }); } catch (error) { this.auditLogger?.log( connectorAuditEvent({ @@ -454,7 +454,7 @@ export class ActionsClient { */ public async getAll(): Promise { try { - await this.authorization.ensureAuthorized('get'); + await this.authorization.ensureAuthorized({ operation: 'get' }); } catch (error) { this.auditLogger?.log( connectorAuditEvent({ @@ -502,7 +502,7 @@ export class ActionsClient { */ public async getBulk(ids: string[]): Promise { try { - await this.authorization.ensureAuthorized('get'); + await this.authorization.ensureAuthorized({ operation: 'get' }); } catch (error) { ids.forEach((id) => this.auditLogger?.log( @@ -569,7 +569,7 @@ export class ActionsClient { configurationUtilities: ActionsConfigurationUtilities ) { // Verify that user has edit access - await this.authorization.ensureAuthorized('update'); + await this.authorization.ensureAuthorized({ operation: 'update' }); // Verify that token url is allowed by allowed hosts config try { @@ -660,7 +660,7 @@ export class ActionsClient { */ public async delete({ id }: { id: string }) { try { - await this.authorization.ensureAuthorized('delete'); + await this.authorization.ensureAuthorized({ operation: 'delete' }); const foundInMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === id @@ -730,7 +730,7 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - await this.authorization.ensureAuthorized('execute'); + await this.authorization.ensureAuthorized({ operation: 'execute' }); } else { trackLegacyRBACExemption('execute', this.usageCounter); } @@ -751,7 +751,7 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - await this.authorization.ensureAuthorized('execute'); + await this.authorization.ensureAuthorized({ operation: 'execute' }); } else { trackLegacyRBACExemption('enqueueExecution', this.usageCounter); } @@ -770,7 +770,7 @@ export class ActionsClient { sources ); if (authCounts[AuthorizationMode.RBAC] > 0) { - await this.authorization.ensureAuthorized('execute'); + await this.authorization.ensureAuthorized({ operation: 'execute' }); } if (authCounts[AuthorizationMode.Legacy] > 0) { trackLegacyRBACExemption( @@ -788,7 +788,7 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - await this.authorization.ensureAuthorized('execute'); + await this.authorization.ensureAuthorized({ operation: 'execute' }); } else { trackLegacyRBACExemption('ephemeralEnqueuedExecution', this.usageCounter); } @@ -831,7 +831,7 @@ export class ActionsClient { const authorizationTuple = {} as KueryNode; try { - await this.authorization.ensureAuthorized('get'); + await this.authorization.ensureAuthorized({ operation: 'get' }); } catch (error) { this.auditLogger?.log( connectorAuditEvent({ @@ -891,7 +891,7 @@ export class ActionsClient { const authorizationTuple = {} as KueryNode; try { - await this.authorization.ensureAuthorized('get'); + await this.authorization.ensureAuthorized({ operation: 'get' }); } catch (error) { this.auditLogger?.log( connectorAuditEvent({ diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index f09670c216613..1b08c1b4b177f 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -28,15 +28,15 @@ export interface ConstructorOptions { authorizationMode?: AuthorizationMode; } -const operationAlias: Record< - string, - (authorization: SecurityPluginSetup['authz']) => string | string[] -> = { - execute: (authorization) => [ +const operationAlias: Record string[]> = { + execute: (authorization, additionalPrivileges: string[] = []) => [ authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'get'), authorization.actions.savedObject.get(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), + ...additionalPrivileges, + ], + list: (authorization) => [ + authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'find'), ], - list: (authorization) => authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'find'), }; const LEGACY_RBAC_EXEMPT_OPERATIONS = new Set(['get', 'execute']); @@ -56,15 +56,26 @@ export class ActionsAuthorization { this.authorizationMode = authorizationMode; } - public async ensureAuthorized(operation: string, actionTypeId?: string) { + public async ensureAuthorized({ + operation, + actionTypeId, + additionalPrivileges = [], + }: { + operation: string; + actionTypeId?: string; + additionalPrivileges?: string[]; + }) { const { authorization } = this; if (authorization?.mode?.useRbacForRequest(this.request)) { if (!this.isOperationExemptDueToLegacyRbac(operation)) { const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); + + const privileges = operationAlias[operation] + ? operationAlias[operation](authorization) + : [authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation)]; + const { hasAllRequested } = await checkPrivileges({ - kibana: operationAlias[operation] - ? operationAlias[operation](authorization) - : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation), + kibana: [...privileges, ...additionalPrivileges], }); if (!hasAllRequested) { throw Boom.forbidden( From 2db46a0cf6e5b0f2400f86c5f9de919172a76784 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 3 Jul 2023 19:37:43 +0300 Subject: [PATCH 28/45] Perform system actions RBAC --- .../actions/server/action_type_registry.ts | 12 ++++++++-- .../plugins/actions/server/actions_client.ts | 23 ++++++++++++++++++- .../actions/server/lib/action_executor.ts | 16 +++++++++++++ .../server/lib/task_runner_factory.test.ts | 6 +++-- x-pack/plugins/actions/server/plugin.ts | 3 +++ x-pack/plugins/actions/server/types.ts | 12 ++++++++++ 6 files changed, 67 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 1b133af7584b6..c7242057e7788 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -97,8 +97,16 @@ export class ActionTypeRegistry { /** * Returns true if the action type is a system action type */ - public isSystemActionType = (actionTypeId: string): boolean => - Boolean(this.actionTypes.get(actionTypeId)?.isSystemActionType); + public isSystemActionType(actionTypeId: string): boolean { + return Boolean(this.actionTypes.get(actionTypeId)?.isSystemActionType); + } + + /** + * Returns the kibana privileges of an action type + */ + public getRequiredKibanaPrivileges(actionTypeId: string): string[] { + return this.actionTypes.get(actionTypeId)?.requiredKibanaPrivileges ?? []; + } /** * Registers an action type to the action type registry diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 3e398796dd825..8f06a3b0ddfd7 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -718,6 +718,18 @@ export class ActionsClient { return await this.unsecuredSavedObjectsClient.delete('action', id); } + private getRequiredKibanaPrivileges(actionId: string) { + const inMemoryConnector = this.inMemoryConnectors.find( + (connector) => connector.id === actionId + ); + + const additionalPrivileges = inMemoryConnector?.isSystemAction + ? this.actionTypeRegistry.getRequiredKibanaPrivileges(inMemoryConnector.actionTypeId) + : []; + + return additionalPrivileges; + } + public async execute({ actionId, params, @@ -730,7 +742,8 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - await this.authorization.ensureAuthorized({ operation: 'execute' }); + const additionalPrivileges = this.getRequiredKibanaPrivileges(actionId); + await this.authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } else { trackLegacyRBACExemption('execute', this.usageCounter); } @@ -751,6 +764,10 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { + /** + * For scheduled executions the additional authorization check + * will be performed inside the ActionExecutor at execution time + */ await this.authorization.ensureAuthorized({ operation: 'execute' }); } else { trackLegacyRBACExemption('enqueueExecution', this.usageCounter); @@ -770,6 +787,10 @@ export class ActionsClient { sources ); if (authCounts[AuthorizationMode.RBAC] > 0) { + /** + * For scheduled executions the additional authorization check + * will be performed inside the ActionExecutor at execution time + */ await this.authorization.ensureAuthorized({ operation: 'execute' }); } if (authCounts[AuthorizationMode.Legacy] > 0) { diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 4fe18f6ecd5e5..db30675368e60 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -34,6 +34,7 @@ import { ActionExecutionSource } from './action_execution_source'; import { RelatedSavedObjects } from './related_saved_objects'; import { createActionEventLogRecordObject } from './create_action_event_log_record_object'; import { ActionExecutionError, ActionExecutionErrorReason } from './errors/action_execution_error'; +import type { ActionsAuthorization } from '../authorization/actions_authorization'; // 1,000,000 nanoseconds in 1 millisecond const Millis2Nanos = 1000 * 1000; @@ -47,6 +48,7 @@ export interface ActionExecutorContext { actionTypeRegistry: ActionTypeRegistryContract; eventLogger: IEventLogger; inMemoryConnectors: InMemoryConnector[]; + getActionsAuthorizationWithRequest: (request: KibanaRequest) => ActionsAuthorization; } export interface TaskInfo { @@ -103,6 +105,7 @@ export class ActionExecutor { if (!this.isInitialized) { throw new Error('ActionExecutor not initialized'); } + return withSpan( { name: `execute_action`, @@ -120,11 +123,13 @@ export class ActionExecutor { eventLogger, inMemoryConnectors, security, + getActionsAuthorizationWithRequest, } = this.actionExecutorContext!; const services = getServices(request); const spaceId = spaces && spaces.getSpaceId(request); const namespace = spaceId && spaceId !== 'default' ? { namespace: spaceId } : {}; + const authorization = getActionsAuthorizationWithRequest(request); const actionInfo = await getActionInfoInternal( this.isESOCanEncrypt, @@ -153,8 +158,19 @@ export class ActionExecutor { if (!actionTypeRegistry.isActionExecutable(actionId, actionTypeId, { notifyUsage: true })) { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } + const actionType = actionTypeRegistry.get(actionTypeId); + /** + * Perform additional authorization checks for system actions. + * It will thrown an error in case of failure. + * + */ + if (actionType.isSystemActionType) { + const additionalPrivileges = actionType.requiredKibanaPrivileges ?? []; + authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); + } + const actionLabel = `${actionTypeId}:${actionId}: ${name}`; logger.debug(`executing action ${actionLabel}`); 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 fb764b2b6f74c..a2838ef414270 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 @@ -20,7 +20,7 @@ import { } from '@kbn/core/server/mocks'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/mocks'; import { ActionTypeDisabledError } from './errors'; -import { actionsClientMock } from '../mocks'; +import { actionsAuthorizationMock } from '../mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { IN_MEMORY_METRICS } from '../monitoring'; import { pick } from 'lodash'; @@ -79,15 +79,17 @@ const services = { log: jest.fn(), savedObjectsClient: savedObjectsClientMock.create(), }; + const actionExecutorInitializerParams = { logger: loggingSystemMock.create().get(), getServices: jest.fn().mockReturnValue(services), actionTypeRegistry, - getActionsClientWithRequest: jest.fn(async () => actionsClientMock.create()), + getActionsAuthorizationWithRequest: jest.fn().mockReturnValue(actionsAuthorizationMock.create()), encryptedSavedObjectsClient: mockedEncryptedSavedObjectsClient, eventLogger, inMemoryConnectors: [], }; + const taskRunnerFactoryInitializerParams = { spaceIdToNamespace, actionTypeRegistry, diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index bc29c3e9ad246..85233356ffda8 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -513,6 +513,9 @@ export class ActionsPlugin implements Plugin string | null; }; isSystemActionType?: boolean; + /** + * Additional Kibana privileges to be checked by the actions framework. + * Use it if you want to perform extra authorization checks based on a Kibana feature. + * For example, you can define the privileges a users needs to have to execute + * a Case or OsQuery system action. + * + * The list of the privileges follows the Kibana privileges format usually generated with `security.authz.actions.*.get(...)`. + * + * It only works with system actions and only when executing an action. + * For all other scenarios they will be ignored + */ + requiredKibanaPrivileges?: string[]; renderParameterTemplates?: RenderParameterTemplates; executor: ExecutorType; } From 9ebb327af4463b9e19b2441d353e5a3d20d211d5 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 3 Jul 2023 19:46:14 +0300 Subject: [PATCH 29/45] Fix types and tests --- .../server/action_type_registry.mock.ts | 1 + .../actions/server/actions_client.test.ts | 82 ++++++++++++------- .../actions_authorization.test.ts | 14 ++-- .../server/lib/action_executor.test.ts | 10 ++- .../actions/server/lib/action_executor.ts | 8 +- .../server/rules_client/methods/bulk_edit.ts | 2 +- .../rules_client/methods/bulk_enable.ts | 2 +- .../server/rules_client/methods/enable.ts | 2 +- .../server/rules_client/methods/mute_all.ts | 2 +- .../rules_client/methods/mute_instance.ts | 2 +- .../server/rules_client/methods/run_soon.ts | 2 +- .../server/rules_client/methods/snooze.ts | 2 +- .../server/rules_client/methods/unmute_all.ts | 2 +- .../rules_client/methods/unmute_instance.ts | 2 +- .../server/rules_client/methods/unsnooze.ts | 2 +- .../rules_client/methods/update_api_key.ts | 2 +- .../server/rules_client/tests/enable.test.ts | 2 +- .../rules_client/tests/mute_all.test.ts | 2 +- .../rules_client/tests/mute_instance.test.ts | 2 +- .../rules_client/tests/run_soon.test.ts | 2 +- .../rules_client/tests/unmute_all.test.ts | 2 +- .../tests/unmute_instance.test.ts | 2 +- .../rules_client/tests/update_api_key.test.ts | 2 +- 23 files changed, 90 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.mock.ts b/x-pack/plugins/actions/server/action_type_registry.mock.ts index 532b192001e7a..c169d600793c2 100644 --- a/x-pack/plugins/actions/server/action_type_registry.mock.ts +++ b/x-pack/plugins/actions/server/action_type_registry.mock.ts @@ -19,6 +19,7 @@ const createActionTypeRegistryMock = () => { isActionExecutable: jest.fn(), isSystemActionType: jest.fn(), getUtils: jest.fn(), + getRequiredKibanaPrivileges: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 1c5ecdc175cc9..877ff9f21cec3 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -198,7 +198,10 @@ describe('create()', () => { }, }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('create', 'my-action-type'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'create', + actionTypeId: 'my-action-type', + }); }); test('throws when user is not authorised to create this type of action', async () => { @@ -242,7 +245,10 @@ describe('create()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to create a "my-action-type" action]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('create', 'my-action-type'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'create', + actionTypeId: 'my-action-type', + }); }); }); @@ -847,7 +853,7 @@ describe('get()', () => { await actionsClient.get({ id: '1' }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('ensures user is authorised to get preconfigured type of action', async () => { @@ -885,7 +891,7 @@ describe('get()', () => { await actionsClient.get({ id: 'testPreconfigured' }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('ensures user is authorised to get a system action', async () => { @@ -919,7 +925,7 @@ describe('get()', () => { await actionsClient.get({ id: 'system-connector-.cases' }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to get the type of action', async () => { @@ -943,7 +949,7 @@ describe('get()', () => { `[Error: Unauthorized to get a "my-action-type" action]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to get preconfigured of action', async () => { @@ -987,7 +993,7 @@ describe('get()', () => { `[Error: Unauthorized to get a "my-action-type" action]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to get a system action', async () => { @@ -1029,7 +1035,7 @@ describe('get()', () => { `[Error: Unauthorized to get a "system-connector-.cases" action]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); }); @@ -1270,7 +1276,7 @@ describe('getAll()', () => { test('ensures user is authorised to get the type of action', async () => { await getAllOperation(); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -1282,7 +1288,7 @@ describe('getAll()', () => { `[Error: Unauthorized to get all actions]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); }); @@ -1521,7 +1527,7 @@ describe('getBulk()', () => { test('ensures user is authorised to get the type of action', async () => { await getBulkOperation(); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -1533,7 +1539,7 @@ describe('getBulk()', () => { `[Error: Unauthorized to get all actions]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); }); @@ -1762,7 +1768,7 @@ describe('getOAuthAccessToken()', () => { }, }, }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -1786,7 +1792,7 @@ describe('getOAuthAccessToken()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to update actions]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); }); }); @@ -1809,7 +1815,7 @@ describe('getOAuthAccessToken()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: Token URL must use http or https]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); }); test('throws when tokenUrl does not contain hostname', async () => { @@ -1831,7 +1837,7 @@ describe('getOAuthAccessToken()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: Token URL must contain hostname]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); }); test('throws when tokenUrl is not in allowed hosts', async () => { @@ -1857,7 +1863,7 @@ describe('getOAuthAccessToken()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: URI not allowed]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); expect(configurationUtilities.ensureUriAllowed).toHaveBeenCalledWith( `https://testurl.service-now.com/oauth_token.do` ); @@ -2003,7 +2009,7 @@ describe('delete()', () => { describe('authorization', () => { test('ensures user is authorised to delete actions', async () => { await actionsClient.delete({ id: '1' }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('delete'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'delete' }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -2015,7 +2021,7 @@ describe('delete()', () => { `[Error: Unauthorized to delete all actions]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('delete'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'delete' }); }); test(`deletes any existing authorization tokens`, async () => { @@ -2205,7 +2211,7 @@ describe('update()', () => { describe('authorization', () => { test('ensures user is authorised to update actions', async () => { await updateOperation(); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -2217,7 +2223,7 @@ describe('update()', () => { `[Error: Unauthorized to update all actions]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('update'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'update' }); }); test(`deletes any existing authorization tokens`, async () => { @@ -2737,7 +2743,10 @@ describe('execute()', () => { }, source: asHttpRequestExecutionSource(request), }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + additionalPrivileges: [], + }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -2758,7 +2767,10 @@ describe('execute()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + additionalPrivileges: [], + }); }); test('tracks legacy RBAC', async () => { @@ -2888,7 +2900,9 @@ describe('enqueueExecution()', () => { apiKey: null, source: asHttpRequestExecutionSource(request), }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -2910,7 +2924,9 @@ describe('enqueueExecution()', () => { }) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + }); }); test('tracks legacy RBAC', async () => { @@ -2973,7 +2989,9 @@ describe('bulkEnqueueExecution()', () => { source: asHttpRequestExecutionSource(request), }, ]); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + }); }); test('throws when user is not authorised to create the type of action', async () => { @@ -3005,7 +3023,9 @@ describe('bulkEnqueueExecution()', () => { ]) ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + }); }); test('tracks legacy RBAC', async () => { @@ -3341,7 +3361,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { return AuthorizationMode.RBAC; }); await actionsClient.getGlobalExecutionLogWithAuth(opts); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to access logs', async () => { @@ -3354,7 +3374,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { `[Error: Unauthorized to access logs]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); }); @@ -3396,7 +3416,7 @@ describe('getGlobalExecutionKpiWithAuth()', () => { return AuthorizationMode.RBAC; }); await actionsClient.getGlobalExecutionKpiWithAuth(opts); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); test('throws when user is not authorised to access kpi', async () => { @@ -3409,7 +3429,7 @@ describe('getGlobalExecutionKpiWithAuth()', () => { `[Error: Unauthorized to access kpi]` ); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); }); 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 09310c398bc53..b98c6ffa97e0f 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -42,7 +42,7 @@ describe('ensureAuthorized', () => { request, }); - await actionsAuthorization.ensureAuthorized('create', 'myType'); + await actionsAuthorization.ensureAuthorized({ operation: 'create', actionTypeId: 'myType' }); }); test('is a no-op when the security license is disabled', async () => { @@ -53,7 +53,7 @@ describe('ensureAuthorized', () => { authorization, }); - await actionsAuthorization.ensureAuthorized('create', 'myType'); + await actionsAuthorization.ensureAuthorized({ operation: 'create', actionTypeId: 'myType' }); }); test('ensures the user has privileges to use the operation on the Actions Saved Object type', async () => { @@ -78,11 +78,11 @@ describe('ensureAuthorized', () => { ], }); - await actionsAuthorization.ensureAuthorized('create', 'myType'); + await actionsAuthorization.ensureAuthorized({ operation: 'create', actionTypeId: 'myType' }); expect(authorization.actions.savedObject.get).toHaveBeenCalledWith('action', 'create'); expect(checkPrivileges).toHaveBeenCalledWith({ - kibana: mockAuthorizationAction('action', 'create'), + kibana: [mockAuthorizationAction('action', 'create')], }); }); @@ -108,7 +108,7 @@ describe('ensureAuthorized', () => { ], }); - await actionsAuthorization.ensureAuthorized('execute', 'myType'); + await actionsAuthorization.ensureAuthorized({ operation: 'execute', actionTypeId: 'myType' }); expect(authorization.actions.savedObject.get).toHaveBeenCalledWith( ACTION_SAVED_OBJECT_TYPE, @@ -153,7 +153,7 @@ describe('ensureAuthorized', () => { }); await expect( - actionsAuthorization.ensureAuthorized('create', 'myType') + actionsAuthorization.ensureAuthorized({ operation: 'create', actionTypeId: 'myType' }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Unauthorized to create a \\"myType\\" action"`); }); @@ -174,7 +174,7 @@ describe('ensureAuthorized', () => { username: 'some-user', } as unknown as AuthenticatedUser); - await actionsAuthorization.ensureAuthorized('execute', 'myType'); + await actionsAuthorization.ensureAuthorized({ operation: 'execute', actionTypeId: 'myType' }); expect(authorization.actions.savedObject.get).not.toHaveBeenCalled(); expect(checkPrivileges).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index af39f914fc720..4b99f68200014 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -14,7 +14,7 @@ import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/mocks'; import { spacesServiceMock } from '@kbn/spaces-plugin/server/spaces_service/spaces_service.mock'; import { ActionType } from '../types'; -import { actionsMock } from '../mocks'; +import { actionsAuthorizationMock, actionsMock } from '../mocks'; import { asHttpRequestExecutionSource, asSavedObjectExecutionSource, @@ -43,6 +43,10 @@ const loggerMock: ReturnType = loggingSystemMock.createLogger(); const securityMockStart = securityMock.createStart(); +const getActionsAuthorizationWithRequest = jest + .fn() + .mockReturnValue(actionsAuthorizationMock.create()); + actionExecutor.initialize({ logger: loggerMock, spaces: spacesMock, @@ -51,6 +55,7 @@ actionExecutor.initialize({ actionTypeRegistry, encryptedSavedObjectsClient, eventLogger, + getActionsAuthorizationWithRequest, inMemoryConnectors: [ { id: 'preconfigured', @@ -1141,6 +1146,7 @@ test('throws an error when passing isESOCanEncrypt with value of false', async ( encryptedSavedObjectsClient, eventLogger: eventLoggerMock.create(), inMemoryConnectors: [], + getActionsAuthorizationWithRequest, }); await expect( customActionExecutor.execute(executeParams) @@ -1158,6 +1164,7 @@ test('should not throw error if action is preconfigured and isESOCanEncrypt is f actionTypeRegistry, encryptedSavedObjectsClient, eventLogger: eventLoggerMock.create(), + getActionsAuthorizationWithRequest, inMemoryConnectors: [ { id: 'preconfigured', @@ -1308,6 +1315,7 @@ test('should not throw error if action is system action and isESOCanEncrypt is f actionTypeRegistry, encryptedSavedObjectsClient, eventLogger: eventLoggerMock.create(), + getActionsAuthorizationWithRequest, inMemoryConnectors: [ { actionTypeId: '.cases', diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index db30675368e60..63d51cd59f939 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -159,18 +159,18 @@ export class ActionExecutor { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } - const actionType = actionTypeRegistry.get(actionTypeId); - /** * Perform additional authorization checks for system actions. * It will thrown an error in case of failure. * */ - if (actionType.isSystemActionType) { - const additionalPrivileges = actionType.requiredKibanaPrivileges ?? []; + if (actionTypeRegistry.isSystemActionType(actionTypeId)) { + const additionalPrivileges = actionTypeRegistry.getRequiredKibanaPrivileges(actionTypeId); authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } + const actionType = actionTypeRegistry.get(actionTypeId); + const actionLabel = `${actionTypeId}:${actionId}: ${name}`; logger.debug(`executing action ${actionLabel}`); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts index da812bf079487..b87d71aa275bc 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts @@ -576,7 +576,7 @@ async function ensureAuthorizationForBulkUpdate( const { field } = operation; if (field === 'snoozeSchedule' || field === 'apiKey') { try { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); break; } catch (error) { throw Error(`Rule not authorized for bulk ${field} update - ${error.message}`); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts index 570ad3d5abc56..42b20e65585c1 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts @@ -134,7 +134,7 @@ const bulkEnableRulesWithOCC = async ( try { if (rule.attributes.actions.length) { try { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } catch (error) { throw Error(`Rule not authorized for bulk enable - ${error.message}`); } diff --git a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts index 0cd42f282bcbc..948f254fe462a 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts @@ -55,7 +55,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts b/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts index 4ac6ad207fdc7..dbc00b2135176 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts @@ -37,7 +37,7 @@ async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts b/x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts index 67e78b9851945..5f37988b7b718 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/mute_instance.ts @@ -42,7 +42,7 @@ async function muteInstanceWithOCC( }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts b/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts index d683b5fbafe4f..106f12ef50f6f 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts @@ -23,7 +23,7 @@ export async function runSoon(context: RulesClientContext, { id }: { id: string }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/snooze.ts b/x-pack/plugins/alerting/server/rules_client/methods/snooze.ts index 04585bca002b0..8bfd19bc9c583 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/snooze.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/snooze.ts @@ -62,7 +62,7 @@ async function snoozeWithOCC( }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts b/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts index 80819de2b6cc2..d5cd81b2664d6 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts @@ -40,7 +40,7 @@ async function unmuteAllWithOCC(context: RulesClientContext, { id }: { id: strin }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts b/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts index 714e5c0a4f8e4..86c894a7babd0 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts @@ -47,7 +47,7 @@ async function unmuteInstanceWithOCC( entity: AlertingAuthorizationEntity.Rule, }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/unsnooze.ts b/x-pack/plugins/alerting/server/rules_client/methods/unsnooze.ts index 67e8d76e649b4..59d0ea62eb3ff 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/unsnooze.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/unsnooze.ts @@ -45,7 +45,7 @@ async function unsnoozeWithOCC(context: RulesClientContext, { id, scheduleIds }: }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts b/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts index 4fefb5a8b367e..d8d78264c448b 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts @@ -58,7 +58,7 @@ async function updateApiKeyWithOCC(context: RulesClientContext, { id }: { id: st entity: AlertingAuthorizationEntity.Rule, }); if (attributes.actions.length) { - await context.actionsAuthorization.ensureAuthorized('execute'); + await context.actionsAuthorization.ensureAuthorized({ operation: 'execute' }); } } catch (error) { context.auditLogger?.log( diff --git a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts index d7175e9a47b89..d123469527ad5 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts @@ -148,7 +148,7 @@ describe('enable()', () => { operation: 'enable', ruleTypeId: 'myType', }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); }); test('throws when user is not authorised to enable this type of alert', async () => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts index fde2534ec6e21..0c9c34f1cbabe 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts @@ -135,7 +135,7 @@ describe('muteAll()', () => { operation: 'muteAll', ruleTypeId: 'myType', }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); }); test('throws when user is not authorised to muteAll this type of alert', async () => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts index 1582b84e59da8..2d369bad2ce69 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts @@ -160,7 +160,7 @@ describe('muteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); await rulesClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ entity: 'rule', consumer: 'myApp', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts index 99579a7831b94..080ed8cd44287 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts @@ -115,7 +115,7 @@ describe('runSoon()', () => { operation: 'runSoon', ruleTypeId: 'myType', }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); }); test('throws when user is not authorised to run this type of rule ad hoc', async () => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts index 737974eeba11e..37f3b06f137a7 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts @@ -135,7 +135,7 @@ describe('unmuteAll()', () => { operation: 'unmuteAll', ruleTypeId: 'myType', }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); }); test('throws when user is not authorised to unmuteAll this type of alert', async () => { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts index 75f47210f11d1..f4f8d58f50e32 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts @@ -158,7 +158,7 @@ describe('unmuteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); await rulesClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ entity: 'rule', consumer: 'myApp', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts index 8689e0e7de2ca..c915ccf1fe5c4 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts @@ -355,7 +355,7 @@ describe('updateApiKey()', () => { test('ensures user is authorised to updateApiKey this type of alert under the consumer', async () => { await rulesClient.updateApiKey({ id: '1' }); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute' }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ entity: 'rule', consumer: 'myApp', From 45f02658e36c2a98e8b9c46dd9d390cf2c4ddb3a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 4 Jul 2023 13:55:05 +0300 Subject: [PATCH 30/45] Add unit tests --- .../server/action_type_registry.mock.ts | 2 +- .../server/action_type_registry.test.ts | 65 +++++++++ .../actions/server/action_type_registry.ts | 10 +- .../actions/server/actions_client.test.ts | 128 ++++++++++++++++++ .../plugins/actions/server/actions_client.ts | 8 +- .../actions_authorization.test.ts | 49 +++++++ .../server/lib/action_executor.test.ts | 60 +++++++- .../actions/server/lib/action_executor.ts | 3 +- x-pack/plugins/actions/server/types.ts | 2 +- .../plugins/cases/common/api/saved_object.ts | 29 ++++ 10 files changed, 345 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.mock.ts b/x-pack/plugins/actions/server/action_type_registry.mock.ts index c169d600793c2..d2aeea422b1e5 100644 --- a/x-pack/plugins/actions/server/action_type_registry.mock.ts +++ b/x-pack/plugins/actions/server/action_type_registry.mock.ts @@ -19,7 +19,7 @@ const createActionTypeRegistryMock = () => { isActionExecutable: jest.fn(), isSystemActionType: jest.fn(), getUtils: jest.fn(), - getRequiredKibanaPrivileges: jest.fn(), + getSystemActionRequiredKibanaPrivileges: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 6988307b01b7e..da08610b3950c 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -689,4 +689,69 @@ describe('actionTypeRegistry', () => { expect(result).toBe(false); }); }); + + describe('getSystemActionRequiredKibanaPrivileges()', () => { + it('should get the kibana privileges correctly for system actions', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + kibanaPrivileges: ['cases/create'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const result = registry.getSystemActionRequiredKibanaPrivileges('.cases'); + expect(result).toEqual(['cases/create']); + }); + + it('should return an empty array if the system action does not define any kibana privileges', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + const result = registry.getSystemActionRequiredKibanaPrivileges('.cases'); + expect(result).toEqual([]); + }); + + it('should return an empty array if the action type is not a system action', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: 'foo', + name: 'Foo', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + const result = registry.getSystemActionRequiredKibanaPrivileges('foo'); + expect(result).toEqual([]); + }); + }); }); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index c7242057e7788..92b05271951a2 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -104,8 +104,14 @@ export class ActionTypeRegistry { /** * Returns the kibana privileges of an action type */ - public getRequiredKibanaPrivileges(actionTypeId: string): string[] { - return this.actionTypes.get(actionTypeId)?.requiredKibanaPrivileges ?? []; + public getSystemActionRequiredKibanaPrivileges(actionTypeId: string): string[] { + const actionType = this.actionTypes.get(actionTypeId); + + if (!actionType?.isSystemActionType) { + return []; + } + + return actionType?.kibanaPrivileges ?? []; } /** diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 877ff9f21cec3..7e3f6d4ecc5f7 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -2787,6 +2787,134 @@ describe('execute()', () => { }); expect(trackLegacyRBACExemption as jest.Mock).toBeCalledWith('execute', mockUsageCounter); + expect(authorization.ensureAuthorized).not.toHaveBeenCalled(); + }); + + test('ensures that system actions privileges are being authorized correctly', async () => { + (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { + return AuthorizationMode.RBAC; + }); + + actionsClient = new ActionsClient({ + inMemoryConnectors: [ + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + auditLogger, + usageCounter: mockUsageCounter, + connectorTokenClient, + getEventLogClient, + }); + + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + kibanaPrivileges: ['cases/create'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + await actionsClient.execute({ + actionId: 'system-connector-.cases', + params: {}, + }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + additionalPrivileges: ['cases/create'], + }); + }); + + test('does not authorize kibana privileges for non system actions', async () => { + (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { + return AuthorizationMode.RBAC; + }); + + actionsClient = new ActionsClient({ + inMemoryConnectors: [ + { + id: 'testPreconfigured', + actionTypeId: 'my-action-type', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + isDeprecated: false, + isSystemAction: false, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + auditLogger, + usageCounter: mockUsageCounter, + connectorTokenClient, + getEventLogClient, + }); + + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + kibanaPrivileges: ['cases/create'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + await actionsClient.execute({ + actionId: 'testPreconfigured', + params: {}, + }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + additionalPrivileges: [], + }); }); }); diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 8f06a3b0ddfd7..c72da5b9e49bf 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -718,13 +718,15 @@ export class ActionsClient { return await this.unsecuredSavedObjectsClient.delete('action', id); } - private getRequiredKibanaPrivileges(actionId: string) { + private getSystemActionRequiredKibanaPrivileges(actionId: string) { const inMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === actionId ); const additionalPrivileges = inMemoryConnector?.isSystemAction - ? this.actionTypeRegistry.getRequiredKibanaPrivileges(inMemoryConnector.actionTypeId) + ? this.actionTypeRegistry.getSystemActionRequiredKibanaPrivileges( + inMemoryConnector.actionTypeId + ) : []; return additionalPrivileges; @@ -742,7 +744,7 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - const additionalPrivileges = this.getRequiredKibanaPrivileges(actionId); + const additionalPrivileges = this.getSystemActionRequiredKibanaPrivileges(actionId); await this.authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } else { trackLegacyRBACExemption('execute', this.usageCounter); 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 b98c6ffa97e0f..edd9d04a4170f 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -18,6 +18,7 @@ import { AuthorizationMode } from './get_authorization_mode_by_source'; const request = {} as KibanaRequest; const mockAuthorizationAction = (type: string, operation: string) => `${type}/${operation}`; + function mockSecurity() { const security = securityMock.createSetup(); const authorization = security.authz; @@ -179,4 +180,52 @@ describe('ensureAuthorized', () => { expect(authorization.actions.savedObject.get).not.toHaveBeenCalled(); expect(checkPrivileges).not.toHaveBeenCalled(); }); + + test('checks additional privileges correctly', async () => { + const { authorization } = mockSecurity(); + const checkPrivileges: jest.MockedFunction< + ReturnType + > = jest.fn(); + + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + const actionsAuthorization = new ActionsAuthorization({ + request, + authorization, + }); + + checkPrivileges.mockResolvedValueOnce({ + username: 'some-user', + hasAllRequested: true, + privileges: [ + { + privilege: mockAuthorizationAction('myType', 'execute'), + authorized: true, + }, + ], + }); + + await actionsAuthorization.ensureAuthorized({ + operation: 'execute', + actionTypeId: 'myType', + additionalPrivileges: ['cases/create'], + }); + + expect(authorization.actions.savedObject.get).toHaveBeenCalledWith( + ACTION_SAVED_OBJECT_TYPE, + 'get' + ); + + expect(authorization.actions.savedObject.get).toHaveBeenCalledWith( + ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + 'create' + ); + + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: [ + mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), + mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), + 'cases/create', + ], + }); + }); }); diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 4b99f68200014..62217e6435ac6 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -43,9 +43,8 @@ const loggerMock: ReturnType = loggingSystemMock.createLogger(); const securityMockStart = securityMock.createStart(); -const getActionsAuthorizationWithRequest = jest - .fn() - .mockReturnValue(actionsAuthorizationMock.create()); +const authorizationMock = actionsAuthorizationMock.create(); +const getActionsAuthorizationWithRequest = jest.fn(); actionExecutor.initialize({ logger: loggerMock, @@ -100,6 +99,8 @@ beforeEach(() => { roles: ['superuser'], username: 'coolguy', })); + + getActionsAuthorizationWithRequest.mockReturnValue(authorizationMock); }); test('successfully executes', async () => { @@ -683,6 +684,7 @@ test('successfully executes with system connector', async () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], + isSystemActionType: true, validate: { config: { schema: schema.any() }, secrets: { schema: schema.any() }, @@ -804,6 +806,56 @@ test('successfully executes with system connector', async () => { `); }); +test('successfully authorize system actions', async () => { + const actionType: jest.Mocked = { + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + kibanaPrivileges: ['cases/create'], + isSystemActionType: true, + validate: { + config: { schema: schema.any() }, + secrets: { schema: schema.any() }, + params: { schema: schema.any() }, + }, + executor: jest.fn(), + }; + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + actionTypeRegistry.isSystemActionType.mockReturnValueOnce(true); + actionTypeRegistry.getSystemActionRequiredKibanaPrivileges.mockReturnValueOnce(['cases/create']); + + await actionExecutor.execute({ ...executeParams, actionId: 'system-connector-.cases' }); + + expect(authorizationMock.ensureAuthorized).toBeCalledWith({ + operation: 'execute', + additionalPrivileges: ['cases/create'], + }); +}); + +test('does not authorize non system actions', async () => { + const actionType: jest.Mocked = { + id: 'test', + name: 'Test', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({ bar: schema.string() }) }, + secrets: { schema: schema.object({ apiKey: schema.string() }) }, + params: { schema: schema.object({ foo: schema.boolean() }) }, + }, + executor: jest.fn(), + }; + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + actionTypeRegistry.isSystemActionType.mockReturnValueOnce(false); + + await actionExecutor.execute({ ...executeParams, actionId: 'preconfigured' }); + + expect(authorizationMock.ensureAuthorized).not.toBeCalled(); +}); + test('successfully executes as a task', async () => { const actionType: jest.Mocked = { id: 'test', @@ -1097,6 +1149,7 @@ test('should not throws an error if actionType is system action', async () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], + isSystemActionType: true, validate: { config: { schema: schema.any() }, secrets: { schema: schema.any() }, @@ -1335,6 +1388,7 @@ test('should not throw error if action is system action and isESOCanEncrypt is f name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], + isSystemActionType: true, validate: { config: { schema: schema.any() }, secrets: { schema: schema.any() }, diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 63d51cd59f939..ed9280e491ab7 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -165,7 +165,8 @@ export class ActionExecutor { * */ if (actionTypeRegistry.isSystemActionType(actionTypeId)) { - const additionalPrivileges = actionTypeRegistry.getRequiredKibanaPrivileges(actionTypeId); + const additionalPrivileges = + actionTypeRegistry.getSystemActionRequiredKibanaPrivileges(actionTypeId); authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index b1384eea0ebf2..8501c10856000 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -152,7 +152,7 @@ export interface ActionType< * It only works with system actions and only when executing an action. * For all other scenarios they will be ignored */ - requiredKibanaPrivileges?: string[]; + kibanaPrivileges?: string[]; renderParameterTemplates?: RenderParameterTemplates; executor: ExecutorType; } diff --git a/x-pack/plugins/cases/common/api/saved_object.ts b/x-pack/plugins/cases/common/api/saved_object.ts index 562ca376e20a1..2ccf7a29ca5cb 100644 --- a/x-pack/plugins/cases/common/api/saved_object.ts +++ b/x-pack/plugins/cases/common/api/saved_object.ts @@ -19,3 +19,32 @@ export const NumberFromString = new rt.Type( }), String ); + +export const PaginationSchema = new rt.Type< + { page?: number; perPage?: number }, + { page?: number; perPage?: number }, + unknown +>( + 'Test', + rt.partial({ page: rt.number, perPage: rt.number }).is, + (u, c) => + either.chain(rt.partial({ page: rt.number, perPage: rt.number }).validate(u, c), (params) => { + if (params.page == null && params.perPage) { + return rt.success(params); + } + + const pageAsNumber = params.page ?? 0; + const perPageAsNumber = params.perPage ?? 0; + + if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > 10) { + return rt.failure( + u, + c, + `The number of documents is too high. Paginating through more than ${10} documents is not possible.` + ); + } + + return rt.success(params); + }), + rt.identity +); From e62faae731638b7946fa788ee0195e7d365c9ca7 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 4 Jul 2023 15:04:26 +0300 Subject: [PATCH 31/45] Add integration tests --- .../spaces_only/tests/actions/execute.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index cb10066a50653..21808f0a9d6de 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -340,6 +340,7 @@ export default function ({ getService }: FtrProviderContext) { errorMessage?: string; startMessage?: string; source?: string; + spaceAgnostic?: boolean; } async function validateEventLog(params: ValidateEventLogParams): Promise { @@ -352,6 +353,7 @@ export default function ({ getService }: FtrProviderContext) { startMessage, errorMessage, source, + spaceAgnostic, } = params; const events: IValidatedEvent[] = await retry.try(async () => { @@ -398,6 +400,7 @@ export default function ({ getService }: FtrProviderContext) { id: actionId, namespace: 'space1', type_id: actionTypeId, + ...(spaceAgnostic ? { space_agnostic: true } : {}), }, ]); expect(startExecuteEvent?.kibana?.saved_objects).to.eql(executeEvent?.kibana?.saved_objects); From 15da2b97167350217b914a5dc059537884199079 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 4 Jul 2023 17:29:32 +0300 Subject: [PATCH 32/45] Add integration tests --- .../alerting_api_integration/common/config.ts | 1 + .../plugins/alerts/server/action_types.ts | 36 ++++++++++ .../group2/tests/actions/execute.ts | 69 ++++++++++++++++++- .../security_and_spaces/scenarios.ts | 47 +++++++++++++ .../spaces_only/tests/actions/execute.ts | 50 ++++++++++++++ 5 files changed, 200 insertions(+), 3 deletions(-) diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 3e48a3b273019..69d84e39b10f2 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -65,6 +65,7 @@ const enabledActionTypes = [ 'test.excluded', 'test.capped', 'test.system-action', + 'test.system-action-kibana-privileges', ]; export function createTestConfig(name: string, options: CreateTestConfigOptions) { diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index cddf100205058..cb85881a46990 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -74,7 +74,12 @@ export function defineActionTypes( actions.registerType(getNoAttemptsRateLimitedActionType()); actions.registerType(getAuthorizationActionType(core)); actions.registerType(getExcludedActionType()); + + /** + * System actions + */ actions.registerType(getSystemActionType()); + actions.registerType(getSystemActionTypeWithKibanaPrivileges()); /** Sub action framework */ @@ -426,3 +431,34 @@ function getSystemActionType() { return result; } + +function getSystemActionTypeWithKibanaPrivileges() { + const result: ActionType<{}, {}, {}> = { + id: 'test.system-action-kibana-privileges', + name: 'Test system action with kibana privileges', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + /** + * Requires all access to the case feature + * in Stack management + */ + kibanaPrivileges: ['cases:cases/createCase'], + validate: { + params: { + schema: schema.any(), + }, + config: { + schema: schema.any(), + }, + secrets: { + schema: schema.any(), + }, + }, + isSystemActionType: true, + async executor({ config, secrets, params, services, actionId }) { + return { status: 'ok', actionId }; + }, + }; + + return result; +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index fffe1a70b27bb..76f336734f372 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { IValidatedEvent, nanosToMillis } from '@kbn/event-log-plugin/server'; import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; import { ActionExecutionSourceType } from '@kbn/actions-plugin/server/lib/action_execution_source'; -import { UserAtSpaceScenarios } from '../../../scenarios'; +import { systemActionScenario, UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, ObjectRemover, getEventLog } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -37,7 +37,7 @@ export default function ({ getService }: FtrProviderContext) { await objectRemover.removeAll(); }); - for (const scenario of UserAtSpaceScenarios) { + for (const scenario of [...UserAtSpaceScenarios, systemActionScenario]) { const { user, space } = scenario; describe(scenario.id, () => { it('should handle execute request appropriately', async () => { @@ -85,6 +85,7 @@ export default function ({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(200); expect(response.body).to.be.an('object'); const searchResult = await esTestIndexTool.search( @@ -169,6 +170,7 @@ export default function ({ getService }: FtrProviderContext) { break; case 'global_read at space1': case 'superuser at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(404); expect(response.body).to.eql({ statusCode: 404, @@ -240,6 +242,7 @@ export default function ({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(200); expect(response.body).to.be.an('object'); const searchResult = await esTestIndexTool.search( @@ -294,6 +297,7 @@ export default function ({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(404); expect(response.body).to.eql({ statusCode: 404, @@ -321,6 +325,7 @@ export default function ({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(400); expect(response.body).to.eql({ statusCode: 400, @@ -399,6 +404,7 @@ export default function ({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(200); break; default: @@ -448,6 +454,7 @@ export default function ({ getService }: FtrProviderContext) { case 'global_read at space1': case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': + case 'system_actions at space1': expect(response.statusCode).to.eql(200); searchResult = await esTestIndexTool.search('action:test.authorization', reference); expect(searchResult.body.hits.total.value).to.eql(1); @@ -493,6 +500,51 @@ export default function ({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it('should authenticate correctly system actions with kibana privileges', async () => { + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + const name = 'System action: test.system-action-kibana-privileges'; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/actions/connector/${connectorId}/_execute`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + statusCode: 403, + error: 'Forbidden', + message: 'Unauthorized to execute actions', + }); + break; + case 'superuser at space1': + case 'system_actions at space1': + expect(response.statusCode).to.eql(200); + + await validateEventLog({ + spaceId: space.id, + connectorId, + actionTypeId: 'test.system-action-kibana-privileges', + outcome: 'success', + message: `action executed: test.system-action-kibana-privileges:${connectorId}: ${name}`, + source: ActionExecutionSourceType.HTTP_REQUEST, + spaceAgnostic: true, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); @@ -505,10 +557,20 @@ export default function ({ getService }: FtrProviderContext) { message: string; errorMessage?: string; source?: string; + spaceAgnostic?: boolean; } async function validateEventLog(params: ValidateEventLogParams): Promise { - const { spaceId, connectorId, actionTypeId, outcome, message, errorMessage, source } = params; + const { + spaceId, + connectorId, + actionTypeId, + outcome, + message, + errorMessage, + source, + spaceAgnostic, + } = params; const events: IValidatedEvent[] = await retry.try(async () => { return await getEventLog({ @@ -555,6 +617,7 @@ export default function ({ getService }: FtrProviderContext) { id: connectorId, namespace: 'space1', type_id: actionTypeId, + ...(spaceAgnostic ? { space_agnostic: true } : {}), }, ]); expect(startExecuteEvent?.kibana?.saved_objects).to.eql(executeEvent?.kibana?.saved_objects); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts b/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts index 3e9137e58c5e7..a852657e0b891 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/scenarios.ts @@ -154,6 +154,42 @@ const Space1AllWithRestrictedFixture: User = { }, }; +/** + * This user is needed to test system actions. + * In x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts + * we registered a system action type which requires access to Cases. This user has + * access to Cases only in the Stack Management. The tests use this user to + * execute the system action and verify that the authorization is performed + * as expected + */ +const CasesAll: User = { + username: 'cases_all', + fullName: 'cases_all', + password: 'cases_all', + role: { + name: 'cases_all_role', + elasticsearch: { + indices: [ + { + names: [`${ES_TEST_INDEX_NAME}*`], + privileges: ['all'], + }, + ], + }, + kibana: [ + { + feature: { + generalCases: ['all'], + actions: ['all'], + alertsFixture: ['all'], + alertsRestrictedFixture: ['all'], + }, + spaces: ['*'], + }, + ], + }, +}; + export const Users: User[] = [ NoKibanaPrivileges, Superuser, @@ -161,6 +197,7 @@ export const Users: User[] = [ Space1All, Space1AllWithRestrictedFixture, Space1AllAlertingNoneActions, + CasesAll, ]; const Space1: Space = { @@ -254,6 +291,16 @@ const Space1AllAtSpace2: Space1AllAtSpace2 = { space: Space2, }; +interface SystemActionSpace1 extends Scenario { + id: 'system_actions at space1'; +} + +export const systemActionScenario: SystemActionSpace1 = { + id: 'system_actions at space1', + user: CasesAll, + space: Space1, +}; + export const UserAtSpaceScenarios: [ NoKibanaPrivilegesAtSpace1, SuperuserAtSpace1, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index 21808f0a9d6de..4acb40fcfe3f1 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -329,6 +329,56 @@ export default function ({ getService }: FtrProviderContext) { }); }); }); + + it('should execute system actions correctly', async () => { + const connectorId = 'system-connector-test.system-action'; + const name = 'System action: test.system-action'; + + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${connectorId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }); + + expect(response.status).to.eql(200); + + await validateEventLog({ + spaceId: Spaces.space1.id, + actionId: connectorId, + actionTypeId: 'test.system-action', + outcome: 'success', + message: `action executed: test.system-action:${connectorId}: ${name}`, + startMessage: `action started: test.system-action:${connectorId}: ${name}`, + source: ActionExecutionSourceType.HTTP_REQUEST, + spaceAgnostic: true, + }); + }); + + it('should execute system actions with kibana privileges correctly', async () => { + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + const name = 'System action: test.system-action-kibana-privileges'; + + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector/${connectorId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }); + + expect(response.status).to.eql(200); + + await validateEventLog({ + spaceId: Spaces.space1.id, + actionId: connectorId, + actionTypeId: 'test.system-action-kibana-privileges', + outcome: 'success', + message: `action executed: test.system-action-kibana-privileges:${connectorId}: ${name}`, + startMessage: `action started: test.system-action-kibana-privileges:${connectorId}: ${name}`, + source: ActionExecutionSourceType.HTTP_REQUEST, + spaceAgnostic: true, + }); + }); }); interface ValidateEventLogParams { From eca005d6f0f492ccf293012a17fa832a1ddf0221 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 8 Jul 2023 21:13:26 +0300 Subject: [PATCH 33/45] Fix tests --- .../actions/server/actions_client.test.ts | 76 ------------------- 1 file changed, 76 deletions(-) diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index d4d7c079269e2..bdba86efd47a9 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -928,40 +928,6 @@ describe('get()', () => { expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); - test('ensures user is authorised to get a system action', async () => { - actionsClient = new ActionsClient({ - logger, - actionTypeRegistry, - unsecuredSavedObjectsClient, - scopedClusterClient, - kibanaIndices, - actionExecutor, - executionEnqueuer, - ephemeralExecutionEnqueuer, - bulkExecutionEnqueuer, - request, - authorization: authorization as unknown as ActionsAuthorization, - inMemoryConnectors: [ - { - actionTypeId: '.cases', - config: {}, - id: 'system-connector-.cases', - name: 'System action: .cases', - secrets: {}, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: true, - }, - ], - connectorTokenClient: connectorTokenClientMock.create(), - getEventLogClient, - }); - - await actionsClient.get({ id: 'system-connector-.cases' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); - }); - test('throws when user is not authorised to get the type of action', async () => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', @@ -1071,48 +1037,6 @@ describe('get()', () => { expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'get' }); }); - - test('throws when user is not authorised to get a system action', async () => { - actionsClient = new ActionsClient({ - logger, - actionTypeRegistry, - unsecuredSavedObjectsClient, - scopedClusterClient, - kibanaIndices, - actionExecutor, - executionEnqueuer, - ephemeralExecutionEnqueuer, - bulkExecutionEnqueuer, - request, - authorization: authorization as unknown as ActionsAuthorization, - inMemoryConnectors: [ - { - actionTypeId: '.cases', - config: {}, - id: 'system-connector-.cases', - name: 'System action: .cases', - secrets: {}, - isPreconfigured: false, - isDeprecated: false, - isSystemAction: true, - }, - ], - connectorTokenClient: connectorTokenClientMock.create(), - getEventLogClient, - }); - - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to get a "system-connector-.cases" action`) - ); - - await expect( - actionsClient.get({ id: 'system-connector-.cases' }) - ).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to get a "system-connector-.cases" action]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('get'); - }); }); describe('auditLogger', () => { From 0020ee9f890b3e6f17ba13f79b6b1fb8f280b137 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 9 Jul 2023 14:18:16 +0300 Subject: [PATCH 34/45] Enhancements --- .../server/action_type_registry.mock.ts | 2 +- .../server/action_type_registry.test.ts | 33 ++++++++++--- .../actions/server/action_type_registry.ts | 2 +- .../actions/server/actions_client.test.ts | 6 +-- .../plugins/actions/server/actions_client.ts | 14 +++--- .../actions_authorization.test.ts | 4 +- .../authorization/actions_authorization.ts | 3 +- .../server/lib/action_executor.test.ts | 6 +-- .../actions/server/lib/action_executor.ts | 2 +- .../plugins/cases/common/api/saved_object.ts | 29 ----------- .../group2/tests/actions/execute.ts | 2 +- .../spaces_only/tests/actions/update.ts | 48 ------------------- 12 files changed, 47 insertions(+), 104 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.mock.ts b/x-pack/plugins/actions/server/action_type_registry.mock.ts index d2aeea422b1e5..399bf6ed22684 100644 --- a/x-pack/plugins/actions/server/action_type_registry.mock.ts +++ b/x-pack/plugins/actions/server/action_type_registry.mock.ts @@ -19,7 +19,7 @@ const createActionTypeRegistryMock = () => { isActionExecutable: jest.fn(), isSystemActionType: jest.fn(), getUtils: jest.fn(), - getSystemActionRequiredKibanaPrivileges: jest.fn(), + getSystemActionKibanaPrivileges: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index da08610b3950c..6f745e5a47862 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -690,7 +690,7 @@ describe('actionTypeRegistry', () => { }); }); - describe('getSystemActionRequiredKibanaPrivileges()', () => { + describe('getSystemActionKibanaPrivileges()', () => { it('should get the kibana privileges correctly for system actions', () => { const registry = new ActionTypeRegistry(actionTypeRegistryParams); @@ -699,7 +699,7 @@ describe('actionTypeRegistry', () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['cases/create'], + kibanaPrivileges: ['test/create'], validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, @@ -709,8 +709,8 @@ describe('actionTypeRegistry', () => { executor, }); - const result = registry.getSystemActionRequiredKibanaPrivileges('.cases'); - expect(result).toEqual(['cases/create']); + const result = registry.getSystemActionKibanaPrivileges('.cases'); + expect(result).toEqual(['test/create']); }); it('should return an empty array if the system action does not define any kibana privileges', () => { @@ -730,7 +730,7 @@ describe('actionTypeRegistry', () => { executor, }); - const result = registry.getSystemActionRequiredKibanaPrivileges('.cases'); + const result = registry.getSystemActionKibanaPrivileges('.cases'); expect(result).toEqual([]); }); @@ -750,7 +750,28 @@ describe('actionTypeRegistry', () => { executor, }); - const result = registry.getSystemActionRequiredKibanaPrivileges('foo'); + const result = registry.getSystemActionKibanaPrivileges('foo'); + expect(result).toEqual([]); + }); + + it('should return an empty array if the action type is not a system action but defines kibana privileges', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: 'foo', + name: 'Foo', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + kibanaPrivileges: ['test/create'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + const result = registry.getSystemActionKibanaPrivileges('foo'); expect(result).toEqual([]); }); }); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index eb026b15d3d23..49cc6956d30f1 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -103,7 +103,7 @@ export class ActionTypeRegistry { /** * Returns the kibana privileges of an action type */ - public getSystemActionRequiredKibanaPrivileges(actionTypeId: string): string[] { + public getSystemActionKibanaPrivileges(actionTypeId: string): string[] { const actionType = this.actionTypes.get(actionTypeId); if (!actionType?.isSystemActionType) { diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index bdba86efd47a9..70897b45c2e63 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -2831,7 +2831,7 @@ describe('execute()', () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['cases/create'], + kibanaPrivileges: ['test/create'], validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, @@ -2848,7 +2848,7 @@ describe('execute()', () => { expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ operation: 'execute', - additionalPrivileges: ['cases/create'], + additionalPrivileges: ['test/create'], }); }); @@ -2896,7 +2896,7 @@ describe('execute()', () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['cases/create'], + kibanaPrivileges: ['test/create'], validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 920897b9cf2d6..799b318acc34a 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -718,15 +718,13 @@ export class ActionsClient { return await this.unsecuredSavedObjectsClient.delete('action', id); } - private getSystemActionRequiredKibanaPrivileges(actionId: string) { + private getSystemActionKibanaPrivileges(actionId: string) { const inMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === actionId ); const additionalPrivileges = inMemoryConnector?.isSystemAction - ? this.actionTypeRegistry.getSystemActionRequiredKibanaPrivileges( - inMemoryConnector.actionTypeId - ) + ? this.actionTypeRegistry.getSystemActionKibanaPrivileges(inMemoryConnector.actionTypeId) : []; return additionalPrivileges; @@ -744,7 +742,7 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - const additionalPrivileges = this.getSystemActionRequiredKibanaPrivileges(actionId); + const additionalPrivileges = this.getSystemActionKibanaPrivileges(actionId); await this.authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } else { trackLegacyRBACExemption('execute', this.usageCounter); @@ -768,7 +766,8 @@ export class ActionsClient { ) { /** * For scheduled executions the additional authorization check - * will be performed inside the ActionExecutor at execution time + * for system actions (kibana privileges) will be performed + * inside the ActionExecutor at execution time */ await this.authorization.ensureAuthorized({ operation: 'execute' }); } else { @@ -791,7 +790,8 @@ export class ActionsClient { if (authCounts[AuthorizationMode.RBAC] > 0) { /** * For scheduled executions the additional authorization check - * will be performed inside the ActionExecutor at execution time + * for system actions (kibana privileges) will be performed + * inside the ActionExecutor at execution time */ await this.authorization.ensureAuthorized({ operation: 'execute' }); } 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 edd9d04a4170f..fa65b06777f98 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -207,7 +207,7 @@ describe('ensureAuthorized', () => { await actionsAuthorization.ensureAuthorized({ operation: 'execute', actionTypeId: 'myType', - additionalPrivileges: ['cases/create'], + additionalPrivileges: ['test/create'], }); expect(authorization.actions.savedObject.get).toHaveBeenCalledWith( @@ -224,7 +224,7 @@ describe('ensureAuthorized', () => { kibana: [ mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), - 'cases/create', + 'test/create', ], }); }); diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index 1b08c1b4b177f..34eec819b431b 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -29,10 +29,9 @@ export interface ConstructorOptions { } const operationAlias: Record string[]> = { - execute: (authorization, additionalPrivileges: string[] = []) => [ + execute: (authorization) => [ authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'get'), authorization.actions.savedObject.get(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), - ...additionalPrivileges, ], list: (authorization) => [ authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, 'find'), diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 62217e6435ac6..89edaf962310e 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -812,7 +812,7 @@ test('successfully authorize system actions', async () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['cases/create'], + kibanaPrivileges: ['test/create'], isSystemActionType: true, validate: { config: { schema: schema.any() }, @@ -824,13 +824,13 @@ test('successfully authorize system actions', async () => { actionTypeRegistry.get.mockReturnValueOnce(actionType); actionTypeRegistry.isSystemActionType.mockReturnValueOnce(true); - actionTypeRegistry.getSystemActionRequiredKibanaPrivileges.mockReturnValueOnce(['cases/create']); + actionTypeRegistry.getSystemActionKibanaPrivileges.mockReturnValueOnce(['test/create']); await actionExecutor.execute({ ...executeParams, actionId: 'system-connector-.cases' }); expect(authorizationMock.ensureAuthorized).toBeCalledWith({ operation: 'execute', - additionalPrivileges: ['cases/create'], + additionalPrivileges: ['test/create'], }); }); diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index ed9280e491ab7..7a9bbe64104c6 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -166,7 +166,7 @@ export class ActionExecutor { */ if (actionTypeRegistry.isSystemActionType(actionTypeId)) { const additionalPrivileges = - actionTypeRegistry.getSystemActionRequiredKibanaPrivileges(actionTypeId); + actionTypeRegistry.getSystemActionKibanaPrivileges(actionTypeId); authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } diff --git a/x-pack/plugins/cases/common/api/saved_object.ts b/x-pack/plugins/cases/common/api/saved_object.ts index 2ccf7a29ca5cb..562ca376e20a1 100644 --- a/x-pack/plugins/cases/common/api/saved_object.ts +++ b/x-pack/plugins/cases/common/api/saved_object.ts @@ -19,32 +19,3 @@ export const NumberFromString = new rt.Type( }), String ); - -export const PaginationSchema = new rt.Type< - { page?: number; perPage?: number }, - { page?: number; perPage?: number }, - unknown ->( - 'Test', - rt.partial({ page: rt.number, perPage: rt.number }).is, - (u, c) => - either.chain(rt.partial({ page: rt.number, perPage: rt.number }).validate(u, c), (params) => { - if (params.page == null && params.perPage) { - return rt.success(params); - } - - const pageAsNumber = params.page ?? 0; - const perPageAsNumber = params.perPage ?? 0; - - if (Math.max(pageAsNumber, pageAsNumber * perPageAsNumber) > 10) { - return rt.failure( - u, - c, - `The number of documents is too high. Paginating through more than ${10} documents is not possible.` - ); - } - - return rt.success(params); - }), - rt.identity -); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index 76f336734f372..ea7cd1671d85f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -501,7 +501,7 @@ export default function ({ getService }: FtrProviderContext) { } }); - it('should authenticate correctly system actions with kibana privileges', async () => { + it('should authorize system actions correctly', async () => { const connectorId = 'system-connector-test.system-action-kibana-privileges'; const name = 'System action: test.system-action-kibana-privileges'; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts index 4400afa4bd81c..d64ccc15c4999 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts @@ -150,30 +150,6 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }); }); - it(`shouldn't update a system connector`, async () => { - await supertest - .put( - `${getUrlPrefix( - Spaces.space1.id - )}/api/actions/connector/system-connector-test.system-action` - ) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(400, { - statusCode: 400, - error: 'Bad Request', - message: 'System action system-connector-test.system-action is not allowed to update.', - }); - }); - it('should notify feature usage when editing a gold action type', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) @@ -362,30 +338,6 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }); }); - it(`shouldn't update a system connector`, async () => { - await supertest - .put( - `${getUrlPrefix( - Spaces.space1.id - )}/api/actions/action/system-connector-test.system-action` - ) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action updated', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(400, { - statusCode: 400, - error: 'Bad Request', - message: 'System action system-connector-test.system-action is not allowed to update.', - }); - }); - it('should notify feature usage when editing a gold action type', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) From 356432c0495499684356c2a5e1281bbfb82fbef5 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 9 Jul 2023 14:45:49 +0300 Subject: [PATCH 35/45] Pass params to system actions when getting kibana privileges --- .../server/action_type_registry.test.ts | 50 ++++++++++++- .../actions/server/action_type_registry.ts | 7 +- .../actions/server/actions_client.test.ts | 70 ++++++++++++++++++- .../plugins/actions/server/actions_client.ts | 9 ++- .../server/lib/action_executor.test.ts | 38 +++++++++- .../actions/server/lib/action_executor.ts | 7 +- x-pack/plugins/actions/server/types.ts | 2 +- 7 files changed, 170 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index 6f745e5a47862..d121de83219cf 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -699,7 +699,7 @@ describe('actionTypeRegistry', () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['test/create'], + getKibanaPrivileges: () => ['test/create'], validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, @@ -754,15 +754,36 @@ describe('actionTypeRegistry', () => { expect(result).toEqual([]); }); + it('should return an empty array if the action type is not a system action but registers kibana privileges', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + + registry.register({ + id: 'foo', + name: 'Foo', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }); + + const result = registry.getSystemActionKibanaPrivileges('foo'); + expect(result).toEqual([]); + }); + it('should return an empty array if the action type is not a system action but defines kibana privileges', () => { const registry = new ActionTypeRegistry(actionTypeRegistryParams); + const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); registry.register({ id: 'foo', name: 'Foo', minimumLicenseRequired: 'basic', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['test/create'], + getKibanaPrivileges, validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, @@ -772,7 +793,32 @@ describe('actionTypeRegistry', () => { }); const result = registry.getSystemActionKibanaPrivileges('foo'); + expect(result).toEqual([]); + expect(getKibanaPrivileges).not.toHaveBeenCalled(); + }); + + it('should pass the metadata correctly', () => { + const registry = new ActionTypeRegistry(actionTypeRegistryParams); + const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); + + registry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + getKibanaPrivileges, + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + registry.getSystemActionKibanaPrivileges('.cases', { foo: 'bar' }); + expect(getKibanaPrivileges).toHaveBeenCalledWith({ metadata: { foo: 'bar' } }); }); }); }); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 49cc6956d30f1..a6663c4521bcd 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -103,14 +103,17 @@ export class ActionTypeRegistry { /** * Returns the kibana privileges of an action type */ - public getSystemActionKibanaPrivileges(actionTypeId: string): string[] { + public getSystemActionKibanaPrivileges( + actionTypeId: string, + metadata?: Record + ): string[] { const actionType = this.actionTypes.get(actionTypeId); if (!actionType?.isSystemActionType) { return []; } - return actionType?.kibanaPrivileges ?? []; + return actionType?.getKibanaPrivileges?.({ metadata }) ?? []; } /** diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 70897b45c2e63..e41e5244ce7bd 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -2831,7 +2831,7 @@ describe('execute()', () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['test/create'], + getKibanaPrivileges: () => ['test/create'], validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, @@ -2896,7 +2896,7 @@ describe('execute()', () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['test/create'], + getKibanaPrivileges: () => ['test/create'], validate: { config: { schema: schema.object({}) }, secrets: { schema: schema.object({}) }, @@ -2916,6 +2916,72 @@ describe('execute()', () => { additionalPrivileges: [], }); }); + + test('pass the params to the actionTypeRegistry when authorizing system actions', async () => { + (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { + return AuthorizationMode.RBAC; + }); + + const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); + + actionsClient = new ActionsClient({ + inMemoryConnectors: [ + { + id: 'system-connector-.cases', + actionTypeId: '.cases', + name: 'System action: .cases', + config: {}, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + executionEnqueuer, + ephemeralExecutionEnqueuer, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + auditLogger, + usageCounter: mockUsageCounter, + connectorTokenClient, + getEventLogClient, + }); + + actionTypeRegistry.register({ + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + getKibanaPrivileges, + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + isSystemActionType: true, + executor, + }); + + await actionsClient.execute({ + actionId: 'system-connector-.cases', + params: { foo: 'bar' }, + }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + additionalPrivileges: ['test/create'], + }); + + expect(getKibanaPrivileges).toHaveBeenCalledWith({ metadata: { foo: 'bar' } }); + }); }); test('calls the actionExecutor with the appropriate parameters', async () => { diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 799b318acc34a..df1783b783cdd 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -718,13 +718,16 @@ export class ActionsClient { return await this.unsecuredSavedObjectsClient.delete('action', id); } - private getSystemActionKibanaPrivileges(actionId: string) { + private getSystemActionKibanaPrivileges(actionId: string, metadata?: Record) { const inMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === actionId ); const additionalPrivileges = inMemoryConnector?.isSystemAction - ? this.actionTypeRegistry.getSystemActionKibanaPrivileges(inMemoryConnector.actionTypeId) + ? this.actionTypeRegistry.getSystemActionKibanaPrivileges( + inMemoryConnector.actionTypeId, + metadata + ) : []; return additionalPrivileges; @@ -742,7 +745,7 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - const additionalPrivileges = this.getSystemActionKibanaPrivileges(actionId); + const additionalPrivileges = this.getSystemActionKibanaPrivileges(actionId, params); await this.authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } else { trackLegacyRBACExemption('execute', this.usageCounter); diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 89edaf962310e..a2ba89d85f2d8 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -812,7 +812,7 @@ test('successfully authorize system actions', async () => { name: 'Cases', minimumLicenseRequired: 'platinum', supportedFeatureIds: ['alerting'], - kibanaPrivileges: ['test/create'], + getKibanaPrivileges: () => ['test/create'], isSystemActionType: true, validate: { config: { schema: schema.any() }, @@ -834,6 +834,42 @@ test('successfully authorize system actions', async () => { }); }); +test('pass the params to the actionTypeRegistry when authorizing system actions', async () => { + const actionType: jest.Mocked = { + id: '.cases', + name: 'Cases', + minimumLicenseRequired: 'platinum', + supportedFeatureIds: ['alerting'], + getKibanaPrivileges: () => ['test/create'], + isSystemActionType: true, + validate: { + config: { schema: schema.any() }, + secrets: { schema: schema.any() }, + params: { schema: schema.any() }, + }, + executor: jest.fn(), + }; + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + actionTypeRegistry.isSystemActionType.mockReturnValueOnce(true); + actionTypeRegistry.getSystemActionKibanaPrivileges.mockReturnValueOnce(['test/create']); + + await actionExecutor.execute({ + ...executeParams, + params: { foo: 'bar' }, + actionId: 'system-connector-.cases', + }); + + expect(actionTypeRegistry.getSystemActionKibanaPrivileges).toHaveBeenCalledWith('.cases', { + foo: 'bar', + }); + + expect(authorizationMock.ensureAuthorized).toBeCalledWith({ + operation: 'execute', + additionalPrivileges: ['test/create'], + }); +}); + test('does not authorize non system actions', async () => { const actionType: jest.Mocked = { id: 'test', diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 7a9bbe64104c6..03d1d2ca91d08 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -165,8 +165,11 @@ export class ActionExecutor { * */ if (actionTypeRegistry.isSystemActionType(actionTypeId)) { - const additionalPrivileges = - actionTypeRegistry.getSystemActionKibanaPrivileges(actionTypeId); + const additionalPrivileges = actionTypeRegistry.getSystemActionKibanaPrivileges( + actionTypeId, + params + ); + authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); } diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 8501c10856000..22845a6188792 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -152,7 +152,7 @@ export interface ActionType< * It only works with system actions and only when executing an action. * For all other scenarios they will be ignored */ - kibanaPrivileges?: string[]; + getKibanaPrivileges?: (args?: { metadata?: Record }) => string[]; renderParameterTemplates?: RenderParameterTemplates; executor: ExecutorType; } From 4259f54f1a342df47fc4cbd3319d22addbc7097e Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 9 Jul 2023 14:46:33 +0300 Subject: [PATCH 36/45] Fix integration tests --- .../common/plugins/alerts/server/action_types.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index fe2b8ecbb0074..76513e867daca 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -74,7 +74,6 @@ export function defineActionTypes( actions.registerType(getNoAttemptsRateLimitedActionType()); actions.registerType(getAuthorizationActionType(core)); actions.registerType(getExcludedActionType()); - actions.registerType(getSystemActionType()); /** * System actions @@ -443,7 +442,7 @@ function getSystemActionTypeWithKibanaPrivileges() { * Requires all access to the case feature * in Stack management */ - kibanaPrivileges: ['cases:cases/createCase'], + getKibanaPrivileges: () => ['cases:cases/createCase'], validate: { params: { schema: schema.any(), From c44f49d2b8597e46aeb7a852589545cbf6262347 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 11 Jul 2023 10:41:19 +0300 Subject: [PATCH 37/45] Add integration tests for enqueue --- .../plugins/actions/server/actions_client.ts | 5 +- .../actions/server/lib/action_executor.ts | 66 ++++-- .../lib/errors/action_execution_error.ts | 1 + .../plugins/alerts/server/action_types.ts | 26 ++- .../common/plugins/alerts/server/routes.ts | 4 + .../group2/tests/actions/enqueue.ts | 218 ++++++++++++++++++ .../group2/tests/actions/execute.ts | 39 ---- .../group2/tests/actions/index.ts | 5 +- 8 files changed, 305 insertions(+), 59 deletions(-) create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/enqueue.ts diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index df1783b783cdd..aa57335fbfed5 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -718,9 +718,9 @@ export class ActionsClient { return await this.unsecuredSavedObjectsClient.delete('action', id); } - private getSystemActionKibanaPrivileges(actionId: string, metadata?: Record) { + private getSystemActionKibanaPrivileges(connectorId: string, metadata?: Record) { const inMemoryConnector = this.inMemoryConnectors.find( - (connector) => connector.id === actionId + (connector) => connector.id === connectorId ); const additionalPrivileges = inMemoryConnector?.isSystemAction @@ -772,6 +772,7 @@ export class ActionsClient { * for system actions (kibana privileges) will be performed * inside the ActionExecutor at execution time */ + await this.authorization.ensureAuthorized({ operation: 'execute' }); } else { trackLegacyRBACExemption('enqueueExecution', this.usageCounter); diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 03d1d2ca91d08..1de584043d2e5 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -159,20 +159,6 @@ export class ActionExecutor { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } - /** - * Perform additional authorization checks for system actions. - * It will thrown an error in case of failure. - * - */ - if (actionTypeRegistry.isSystemActionType(actionTypeId)) { - const additionalPrivileges = actionTypeRegistry.getSystemActionKibanaPrivileges( - actionTypeId, - params - ); - - authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); - } - const actionType = actionTypeRegistry.get(actionTypeId); const actionLabel = `${actionTypeId}:${actionId}: ${name}`; @@ -225,6 +211,19 @@ export class ActionExecutor { let rawResult: ActionTypeExecutorRawResult; try { + /** + * Perform additional authorization checks for system actions. + * It will thrown an error in case of failure. + * + */ + await ensureAuthorizedToExecute({ + params, + actionId, + actionTypeId, + actionTypeRegistry, + authorization, + }); + const configurationUtilities = actionTypeRegistry.getUtils(); const { validatedParams, validatedConfig, validatedSecrets } = validateAction( { @@ -250,7 +249,10 @@ export class ActionExecutor { source, }); } catch (err) { - if (err.reason === ActionExecutionErrorReason.Validation) { + if ( + err.reason === ActionExecutionErrorReason.Validation || + err.reason === ActionExecutionErrorReason.Authorization + ) { rawResult = err.result; } else { rawResult = { @@ -519,3 +521,37 @@ function validateAction( }); } } + +interface EnsureAuthorizedToExecuteOpts { + actionId: string; + actionTypeId: string; + params: Record; + actionTypeRegistry: ActionTypeRegistryContract; + authorization: ActionsAuthorization; +} + +const ensureAuthorizedToExecute = async ({ + actionId, + actionTypeId, + params, + actionTypeRegistry, + authorization, +}: EnsureAuthorizedToExecuteOpts) => { + try { + if (actionTypeRegistry.isSystemActionType(actionTypeId)) { + const additionalPrivileges = actionTypeRegistry.getSystemActionKibanaPrivileges( + actionTypeId, + params + ); + + await authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); + } + } catch (error) { + throw new ActionExecutionError(error.message, ActionExecutionErrorReason.Authorization, { + actionId, + status: 'error', + message: error.message, + retry: false, + }); + } +}; diff --git a/x-pack/plugins/actions/server/lib/errors/action_execution_error.ts b/x-pack/plugins/actions/server/lib/errors/action_execution_error.ts index ad43008ef8e20..80ed18f3d3d31 100644 --- a/x-pack/plugins/actions/server/lib/errors/action_execution_error.ts +++ b/x-pack/plugins/actions/server/lib/errors/action_execution_error.ts @@ -9,6 +9,7 @@ import { ActionTypeExecutorResult } from '../../types'; export enum ActionExecutionErrorReason { Validation = 'validation', + Authorization = 'authorization', } export class ActionExecutionError extends Error { diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts index 76513e867daca..a7d5dbc138ea4 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/action_types.ts @@ -433,7 +433,7 @@ function getSystemActionType() { } function getSystemActionTypeWithKibanaPrivileges() { - const result: ActionType<{}, {}, {}> = { + const result: ActionType<{}, {}, { index?: string; reference?: string }> = { id: 'test.system-action-kibana-privileges', name: 'Test system action with kibana privileges', minimumLicenseRequired: 'platinum', @@ -455,7 +455,29 @@ function getSystemActionTypeWithKibanaPrivileges() { }, }, isSystemActionType: true, - async executor({ config, secrets, params, services, actionId }) { + /** + * The executor writes a doc to the + * testing index. The test uses the doc + * to verify that the action is executed + * correctly + */ + async executor({ params, services, actionId }) { + const { index, reference } = params; + + if (index == null || reference == null) { + return { status: 'ok', actionId }; + } + + await services.scopedClusterClient.index({ + index, + refresh: 'wait_for', + body: { + params, + reference, + source: 'action:test.system-action-kibana-privileges', + }, + }); + return { status: 'ok', actionId }; }, }; diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts index 3ee46f4e885fe..423b0e45971e5 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts @@ -350,6 +350,10 @@ export function defineRoutes( }); return res.noContent(); } catch (err) { + if (err.isBoom && err.output.statusCode === 403) { + return res.forbidden({ body: err }); + } + return res.badRequest({ body: err }); } } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/enqueue.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/enqueue.ts new file mode 100644 index 0000000000000..b7266c2f66419 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/enqueue.ts @@ -0,0 +1,218 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; +import { ActionExecutionSourceType } from '@kbn/actions-plugin/server/types'; +import { systemActionScenario, UserAtSpaceScenarios } from '../../../scenarios'; +import { getEventLog, getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const es = getService('es'); + const retry = getService('retry'); + const esTestIndexTool = new ESTestIndexTool(es, retry); + + describe('enqueue', () => { + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + }); + + after(async () => { + await esTestIndexTool.destroy(); + await objectRemover.removeAll(); + }); + + for (const scenario of [...UserAtSpaceScenarios, systemActionScenario]) { + const { user, space } = scenario; + + it(`should handle enqueue request appropriately: ${scenario.id}`, async () => { + const startDate = new Date().toISOString(); + + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const connectorId = createdAction.id; + const name = 'My action'; + const reference = `actions-enqueue-${scenario.id}:${space.id}:${connectorId}`; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerts_fixture/${connectorId}/enqueue_action`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + params: { reference, index: ES_TEST_INDEX_NAME, message: 'Testing 123' }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.status).to.eql(403); + break; + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + case 'system_actions at space1': + expect(response.status).to.eql(204); + + await validateEventLog({ + spaceId: space.id, + connectorId, + outcome: 'success', + message: `action executed: test.index-record:${connectorId}: ${name}`, + source: ActionExecutionSourceType.HTTP_REQUEST, + startDate, + }); + + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 1); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should authorize system actions correctly: ${scenario.id}`, async () => { + const startDate = new Date().toISOString(); + + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + const name = 'System action: test.system-action-kibana-privileges'; + const reference = `actions-enqueue-${scenario.id}:${space.id}:${connectorId}`; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerts_fixture/${connectorId}/enqueue_action`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + params: { index: ES_TEST_INDEX_NAME, reference }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.status).to.eql(403); + break; + /** + * The users in these scenarios have access + * to Actions but do not have access to + * the system action. They should be able to + * enqueue the action but the execution should fail. + */ + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.status).to.eql(204); + + await validateEventLog({ + spaceId: space.id, + connectorId, + outcome: 'failure', + message: `action execution failure: test.system-action-kibana-privileges:${connectorId}: ${name}`, + errorMessage: 'Unauthorized to execute actions', + source: ActionExecutionSourceType.HTTP_REQUEST, + startDate, + }); + break; + /** + * The users in these scenarios have access + * to Actions and to the system action. They should be able to + * enqueue the action and the execution should succeed. + */ + case 'superuser at space1': + case 'system_actions at space1': + expect(response.status).to.eql(204); + + await validateEventLog({ + spaceId: space.id, + connectorId, + outcome: 'success', + message: `action executed: test.system-action-kibana-privileges:${connectorId}: ${name}`, + source: ActionExecutionSourceType.HTTP_REQUEST, + startDate, + }); + + await esTestIndexTool.waitForDocs( + 'action:test.system-action-kibana-privileges', + reference, + 1 + ); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + } + }); + + interface ValidateEventLogParams { + spaceId: string; + connectorId: string; + outcome: string; + message: string; + startDate: string; + errorMessage?: string; + source?: string; + } + + const validateEventLog = async (params: ValidateEventLogParams): Promise => { + const { spaceId, connectorId, outcome, message, startDate, errorMessage, source } = params; + + const events: IValidatedEvent[] = await retry.try(async () => { + const events_ = await getEventLog({ + getService, + spaceId, + type: 'action', + id: connectorId, + provider: 'actions', + actions: new Map([['execute', { gte: 1 }]]), + }); + + const filteredEvents = events_.filter((event) => event!['@timestamp']! >= startDate); + if (filteredEvents.length < 1) throw new Error('no recent events found yet'); + + return filteredEvents; + }); + + expect(events.length).to.be(1); + + const event = events[0]; + + expect(event?.message).to.eql(message); + expect(event?.event?.outcome).to.eql(outcome); + + if (errorMessage) { + expect(event?.error?.message).to.eql(errorMessage); + } + + if (source) { + expect(event?.kibana?.action?.execution?.source).to.eql(source.toLowerCase()); + } + }; +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index ea7cd1671d85f..ea0e87a5ed07c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -583,7 +583,6 @@ export default function ({ getService }: FtrProviderContext) { ['execute-start', { equal: 1 }], ['execute', { equal: 1 }], ]), - // filter: 'event.action:(execute)', }); }); @@ -632,43 +631,5 @@ export default function ({ getService }: FtrProviderContext) { if (errorMessage) { expect(executeEvent?.error?.message).to.eql(errorMessage); } - - // const event = events[0]; - - // const duration = event?.event?.duration; - // const eventStart = Date.parse(event?.event?.start || 'undefined'); - // const eventEnd = Date.parse(event?.event?.end || 'undefined'); - // const dateNow = Date.now(); - - // expect(typeof duration).to.be('number'); - // expect(eventStart).to.be.ok(); - // expect(eventEnd).to.be.ok(); - - // const durationDiff = Math.abs( - // Math.round(duration! / NANOS_IN_MILLIS) - (eventEnd - eventStart) - // ); - - // // account for rounding errors - // expect(durationDiff < 1).to.equal(true); - // expect(eventStart <= eventEnd).to.equal(true); - // expect(eventEnd <= dateNow).to.equal(true); - - // expect(event?.event?.outcome).to.equal(outcome); - - // expect(event?.kibana?.saved_objects).to.eql([ - // { - // rel: 'primary', - // type: 'action', - // id: connectorId, - // type_id: actionTypeId, - // namespace: spaceId, - // }, - // ]); - - // expect(event?.message).to.eql(message); - - // if (errorMessage) { - // expect(event?.error?.message).to.eql(errorMessage); - // } } } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index 4f904f70b67f2..bb26ffd9b8980 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -18,6 +18,7 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide after(async () => { await tearDown(getService); }); + loadTestFile(require.resolve('./connector_types/oauth_access_token')); loadTestFile(require.resolve('./connector_types/cases_webhook')); loadTestFile(require.resolve('./connector_types/jira')); @@ -47,10 +48,12 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./connector_types')); loadTestFile(require.resolve('./update')); + loadTestFile(require.resolve('./enqueue')); /** * Sub action framework */ - // loadTestFile(require.resolve('./sub_action_framework')); + + loadTestFile(require.resolve('./sub_action_framework')); }); } From 4035d11ff76812caf70e6d89a33069174474dc19 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 11 Jul 2023 12:04:38 +0300 Subject: [PATCH 38/45] Add integration tests for bulk_enqueue --- .../plugins/actions/server/actions_client.ts | 1 + .../common/plugins/alerts/server/routes.ts | 52 +++++ .../group2/tests/actions/bulk_enqueue.ts | 209 ++++++++++++++++++ .../group2/tests/actions/execute.ts | 52 ++++- .../group2/tests/actions/index.ts | 1 + 5 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/bulk_enqueue.ts diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index aa57335fbfed5..1b34741c179e6 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -787,6 +787,7 @@ export class ActionsClient { sources.push(option.source); } }); + const authCounts = await getBulkAuthorizationModeBySource( this.unsecuredSavedObjectsClient, sources diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts index 423b0e45971e5..f21894b93b6da 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts @@ -359,6 +359,58 @@ export function defineRoutes( } ); + router.post( + { + path: '/api/alerts_fixture/{id}/bulk_enqueue_actions', + validate: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object({ + params: schema.recordOf(schema.string(), schema.any()), + }), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + try { + const [, { actions, security, spaces }] = await core.getStartServices(); + const actionsClient = await actions.getActionsClientWithRequest(req); + + const createAPIKeyResult = + security && + (await security.authc.apiKeys.grantAsInternalUser(req, { + name: `alerts_fixture:bulk_enqueue_actions:${uuidv4()}`, + role_descriptors: {}, + })); + + await actionsClient.bulkEnqueueExecution([ + { + id: req.params.id, + spaceId: spaces ? spaces.spacesService.getSpaceId(req) : 'default', + executionId: uuidv4(), + apiKey: createAPIKeyResult + ? Buffer.from(`${createAPIKeyResult.id}:${createAPIKeyResult.api_key}`).toString( + 'base64' + ) + : null, + params: req.body.params, + }, + ]); + return res.noContent(); + } catch (err) { + if (err.isBoom && err.output.statusCode === 403) { + return res.forbidden({ body: err }); + } + + return res.badRequest({ body: err }); + } + } + ); + router.post( { path: `/api/alerting_actions_telemetry/run_soon`, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/bulk_enqueue.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/bulk_enqueue.ts new file mode 100644 index 0000000000000..589f31656736c --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/bulk_enqueue.ts @@ -0,0 +1,209 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { IValidatedEvent } from '@kbn/event-log-plugin/server'; +import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; +import { systemActionScenario, UserAtSpaceScenarios } from '../../../scenarios'; +import { getEventLog, getUrlPrefix, ObjectRemover } from '../../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const es = getService('es'); + const retry = getService('retry'); + const esTestIndexTool = new ESTestIndexTool(es, retry); + + describe('bulk_enqueue', () => { + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + }); + + after(async () => { + await esTestIndexTool.destroy(); + await objectRemover.removeAll(); + }); + + for (const scenario of [...UserAtSpaceScenarios, systemActionScenario]) { + const { user, space } = scenario; + + it(`should handle enqueue request appropriately: ${scenario.id}`, async () => { + const startDate = new Date().toISOString(); + + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + + objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + + const connectorId = createdAction.id; + const name = 'My action'; + const reference = `actions-enqueue-${scenario.id}:${space.id}:${connectorId}`; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerts_fixture/${connectorId}/bulk_enqueue_actions`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + params: { reference, index: ES_TEST_INDEX_NAME, message: 'Testing 123' }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.status).to.eql(403); + break; + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + case 'superuser at space1': + case 'system_actions at space1': + expect(response.status).to.eql(204); + + await validateEventLog({ + spaceId: space.id, + connectorId, + outcome: 'success', + message: `action executed: test.index-record:${connectorId}: ${name}`, + startDate, + }); + + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 1); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + + it(`should authorize system actions correctly: ${scenario.id}`, async () => { + const startDate = new Date().toISOString(); + + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + const name = 'System action: test.system-action-kibana-privileges'; + const reference = `actions-enqueue-${scenario.id}:${space.id}:${connectorId}`; + + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(space.id)}/api/alerts_fixture/${connectorId}/bulk_enqueue_actions`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + params: { index: ES_TEST_INDEX_NAME, reference }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all_alerts_none_actions at space1': + case 'space_1_all at space2': + expect(response.status).to.eql(403); + break; + /** + * The users in these scenarios have access + * to Actions but do not have access to + * the system action. They should be able to + * enqueue the action but the execution should fail. + */ + case 'global_read at space1': + case 'space_1_all at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.status).to.eql(204); + + await validateEventLog({ + spaceId: space.id, + connectorId, + outcome: 'failure', + message: `action execution failure: test.system-action-kibana-privileges:${connectorId}: ${name}`, + errorMessage: 'Unauthorized to execute actions', + startDate, + }); + break; + /** + * The users in these scenarios have access + * to Actions and to the system action. They should be able to + * enqueue the action and the execution should succeed. + */ + case 'superuser at space1': + case 'system_actions at space1': + expect(response.status).to.eql(204); + + await validateEventLog({ + spaceId: space.id, + connectorId, + outcome: 'success', + message: `action executed: test.system-action-kibana-privileges:${connectorId}: ${name}`, + startDate, + }); + + await esTestIndexTool.waitForDocs( + 'action:test.system-action-kibana-privileges', + reference, + 1 + ); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + } + }); + + interface ValidateEventLogParams { + spaceId: string; + connectorId: string; + outcome: string; + message: string; + startDate: string; + errorMessage?: string; + } + + const validateEventLog = async (params: ValidateEventLogParams): Promise => { + const { spaceId, connectorId, outcome, message, startDate, errorMessage } = params; + + const events: IValidatedEvent[] = await retry.try(async () => { + const events_ = await getEventLog({ + getService, + spaceId, + type: 'action', + id: connectorId, + provider: 'actions', + actions: new Map([['execute', { gte: 1 }]]), + }); + + const filteredEvents = events_.filter((event) => event!['@timestamp']! >= startDate); + if (filteredEvents.length < 1) throw new Error('no recent events found yet'); + + return filteredEvents; + }); + + expect(events.length).to.be(1); + + const event = events[0]; + + expect(event?.message).to.eql(message); + expect(event?.event?.outcome).to.eql(outcome); + + if (errorMessage) { + expect(event?.error?.message).to.eql(errorMessage); + } + }; +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index ea0e87a5ed07c..f49455edbbdf4 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -502,15 +502,17 @@ export default function ({ getService }: FtrProviderContext) { }); it('should authorize system actions correctly', async () => { + const startDate = new Date().toISOString(); const connectorId = 'system-connector-test.system-action-kibana-privileges'; const name = 'System action: test.system-action-kibana-privileges'; + const reference = `actions-enqueue-${scenario.id}:${space.id}:${connectorId}`; const response = await supertestWithoutAuth .post(`${getUrlPrefix(space.id)}/api/actions/connector/${connectorId}/_execute`) .auth(user.username, user.password) .set('kbn-xsrf', 'foo') .send({ - params: {}, + params: { index: ES_TEST_INDEX_NAME, reference }, }); switch (scenario.id) { @@ -531,15 +533,20 @@ export default function ({ getService }: FtrProviderContext) { case 'system_actions at space1': expect(response.statusCode).to.eql(200); - await validateEventLog({ + await validateSystemEventLog({ spaceId: space.id, connectorId, - actionTypeId: 'test.system-action-kibana-privileges', + startDate, outcome: 'success', message: `action executed: test.system-action-kibana-privileges:${connectorId}: ${name}`, source: ActionExecutionSourceType.HTTP_REQUEST, - spaceAgnostic: true, }); + + await esTestIndexTool.waitForDocs( + 'action:test.system-action-kibana-privileges', + reference, + 1 + ); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -632,4 +639,41 @@ export default function ({ getService }: FtrProviderContext) { expect(executeEvent?.error?.message).to.eql(errorMessage); } } + + const validateSystemEventLog = async ( + params: Omit & { startDate: string } + ): Promise => { + const { spaceId, connectorId, outcome, message, startDate, errorMessage, source } = params; + + const events: IValidatedEvent[] = await retry.try(async () => { + const events_ = await getEventLog({ + getService, + spaceId, + type: 'action', + id: connectorId, + provider: 'actions', + actions: new Map([['execute', { gte: 1 }]]), + }); + + const filteredEvents = events_.filter((event) => event!['@timestamp']! >= startDate); + if (filteredEvents.length < 1) throw new Error('no recent events found yet'); + + return filteredEvents; + }); + + expect(events.length).to.be(1); + + const event = events[0]; + + expect(event?.message).to.eql(message); + expect(event?.event?.outcome).to.eql(outcome); + + if (errorMessage) { + expect(event?.error?.message).to.eql(errorMessage); + } + + if (source) { + expect(event?.kibana?.action?.execution?.source).to.eql(source.toLowerCase()); + } + }; } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index bb26ffd9b8980..675f0c1a02bbc 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -49,6 +49,7 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide loadTestFile(require.resolve('./connector_types')); loadTestFile(require.resolve('./update')); loadTestFile(require.resolve('./enqueue')); + loadTestFile(require.resolve('./bulk_enqueue')); /** * Sub action framework From be7ec6790d972af1243291ef6392e4c1fd8dc60a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 11 Jul 2023 12:14:02 +0300 Subject: [PATCH 39/45] Add integration tests for spaces_only --- .../spaces_only/tests/actions/bulk_enqueue.ts | 90 +++++++++++++++++++ .../spaces_only/tests/actions/enqueue.ts | 20 +++++ .../spaces_only/tests/actions/index.ts | 1 + 3 files changed, 111 insertions(+) create mode 100644 x-pack/test/alerting_api_integration/spaces_only/tests/actions/bulk_enqueue.ts diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/bulk_enqueue.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/bulk_enqueue.ts new file mode 100644 index 0000000000000..af5c91efcda11 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/bulk_enqueue.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { ESTestIndexTool, ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers'; +import { Spaces } from '../../scenarios'; +import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const retry = getService('retry'); + const esTestIndexTool = new ESTestIndexTool(es, retry); + + describe('bulk_enqueue', () => { + const objectRemover = new ObjectRemover(supertest); + + before(async () => { + await esTestIndexTool.destroy(); + await esTestIndexTool.setup(); + }); + after(async () => { + await esTestIndexTool.destroy(); + await objectRemover.removeAll(); + }); + + it('should handle bulk_enqueue request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + connector_type_id: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + const reference = `actions-enqueue-1:${Spaces.space1.id}:${createdAction.id}`; + + const response = await supertest + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts_fixture/${ + createdAction.id + }/bulk_enqueue_actions` + ) + .set('kbn-xsrf', 'foo') + .send({ + params: { reference, index: ES_TEST_INDEX_NAME, message: 'Testing 123' }, + }); + + expect(response.status).to.eql(204); + await esTestIndexTool.waitForDocs('action:test.index-record', reference, 1); + }); + + it('should enqueue system actions correctly', async () => { + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + const reference = `actions-enqueue-1:${Spaces.space1.id}:${connectorId}`; + + const response = await supertest + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts_fixture/${connectorId}/bulk_enqueue_actions` + ) + .set('kbn-xsrf', 'foo') + .send({ + params: { index: ES_TEST_INDEX_NAME, reference }, + }); + + expect(response.status).to.eql(204); + + await esTestIndexTool.waitForDocs( + 'action:test.system-action-kibana-privileges', + reference, + 1 + ); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts index 5934543886b16..238cfa3ae780b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/enqueue.ts @@ -201,5 +201,25 @@ export default function ({ getService }: FtrProviderContext) { expect(total).to.eql(0); }); }); + + it('should enqueue system actions correctly', async () => { + const connectorId = 'system-connector-test.system-action-kibana-privileges'; + const reference = `actions-enqueue-1:${Spaces.space1.id}:${connectorId}`; + + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts_fixture/${connectorId}/enqueue_action`) + .set('kbn-xsrf', 'foo') + .send({ + params: { index: ES_TEST_INDEX_NAME, reference }, + }); + + expect(response.status).to.eql(204); + + await esTestIndexTool.waitForDocs( + 'action:test.system-action-kibana-privileges', + reference, + 1 + ); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts index e2675ca5a4e78..f1d7f59980c23 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts @@ -23,6 +23,7 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo loadTestFile(require.resolve('./monitoring_collection')); loadTestFile(require.resolve('./execute')); loadTestFile(require.resolve('./enqueue')); + loadTestFile(require.resolve('./bulk_enqueue')); loadTestFile(require.resolve('./connector_types/stack/email')); loadTestFile(require.resolve('./connector_types/stack/email_html')); loadTestFile(require.resolve('./connector_types/stack/es_index')); From 2c565b1f1fd0acfe1be951800f11abf5a2be09af Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 11 Jul 2023 12:19:07 +0300 Subject: [PATCH 40/45] Fix integration tests --- .../group2/tests/actions/get_all.ts | 27 +++++++++++++++++++ .../spaces_only/tests/actions/get_all.ts | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts index bc3444b5a32b3..744b1bde95216 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts @@ -134,6 +134,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'System action: test.system-action', referenced_by_count: 0, }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -303,6 +312,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'System action: test.system-action', referenced_by_count: 0, }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -435,6 +453,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'System action: test.system-action', referenced_by_count: 0, }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index 2366c392c4e4d..7f46a2ac625d6 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -123,6 +123,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'System action: test.system-action', referenced_by_count: 0, }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -244,6 +253,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'System action: test.system-action', referenced_by_count: 0, }, + { + connector_type_id: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + is_deprecated: false, + is_preconfigured: false, + is_system_action: true, + name: 'System action: test.system-action-kibana-privileges', + referenced_by_count: 0, + }, { id: 'custom-system-abc-connector', is_preconfigured: true, @@ -379,6 +397,15 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { name: 'System action: test.system-action', referencedByCount: 0, }, + { + actionTypeId: 'test.system-action-kibana-privileges', + id: 'system-connector-test.system-action-kibana-privileges', + isDeprecated: false, + isPreconfigured: false, + isSystemAction: true, + name: 'System action: test.system-action-kibana-privileges', + referencedByCount: 0, + }, { id: 'custom-system-abc-connector', isPreconfigured: true, From 72e9d7278eecaf4a26d05f424e06f17b1c1cc6f5 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 11 Jul 2023 13:14:47 +0300 Subject: [PATCH 41/45] Small improvements --- .../server/action_type_registry.test.ts | 20 ------------------- .../actions/server/action_type_registry.ts | 2 +- .../actions/server/lib/action_executor.ts | 4 ++-- .../group2/tests/actions/execute.ts | 11 ++++++++++ 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index d121de83219cf..83870e2fce855 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -754,26 +754,6 @@ describe('actionTypeRegistry', () => { expect(result).toEqual([]); }); - it('should return an empty array if the action type is not a system action but registers kibana privileges', () => { - const registry = new ActionTypeRegistry(actionTypeRegistryParams); - - registry.register({ - id: 'foo', - name: 'Foo', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - const result = registry.getSystemActionKibanaPrivileges('foo'); - expect(result).toEqual([]); - }); - it('should return an empty array if the action type is not a system action but defines kibana privileges', () => { const registry = new ActionTypeRegistry(actionTypeRegistryParams); const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index a6663c4521bcd..1def5d139cb3d 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -101,7 +101,7 @@ export class ActionTypeRegistry { Boolean(this.actionTypes.get(actionTypeId)?.isSystemActionType); /** - * Returns the kibana privileges of an action type + * Returns the kibana privileges of a system action type */ public getSystemActionKibanaPrivileges( actionTypeId: string, diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 1de584043d2e5..439821c525ffb 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -212,9 +212,9 @@ export class ActionExecutor { let rawResult: ActionTypeExecutorRawResult; try { /** - * Perform additional authorization checks for system actions. + * Ensures correct permissions for execution and + * performs authorization checks for system actions. * It will thrown an error in case of failure. - * */ await ensureAuthorizedToExecute({ params, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts index f49455edbbdf4..8433570e08241 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/execute.ts @@ -516,6 +516,12 @@ export default function ({ getService }: FtrProviderContext) { }); switch (scenario.id) { + /** + * The users in these scenarios may have access + * to Actions but do not have access to + * the system action. They should not be able to + * to execute even if they have access to Actions. + */ case 'no_kibana_privileges at space1': case 'space_1_all_alerts_none_actions at space1': case 'space_1_all at space2': @@ -529,6 +535,11 @@ export default function ({ getService }: FtrProviderContext) { message: 'Unauthorized to execute actions', }); break; + /** + * The users in these scenarios have access + * to Actions and to the system action. They should be able to + * execute. + */ case 'superuser at space1': case 'system_actions at space1': expect(response.statusCode).to.eql(200); From dddcc8eb6e0f03efd736cc9861ffa4c001615566 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 13 Jul 2023 14:11:57 +0300 Subject: [PATCH 42/45] Do not allow setting kibana privileges if it is not a system action type --- .../server/action_type_registry.test.ts | 47 +++++++++---------- .../actions/server/action_type_registry.ts | 13 +++++ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index b8d0f977393f5..bf204185830a8 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -248,6 +248,29 @@ describe('actionTypeRegistry', () => { }) ).not.toThrow(); }); + + test('throws if the kibana privileges are defined but the action type is not a system action type', () => { + const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + + expect(() => + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + getKibanaPrivileges: jest.fn(), + isSystemActionType: false, + validate: { + config: { schema: schema.object({}) }, + secrets: { schema: schema.object({}) }, + params: { schema: schema.object({}) }, + }, + executor, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Kibana privilege authorization is only supported for system action types"` + ); + }); }); describe('get()', () => { @@ -756,30 +779,6 @@ describe('actionTypeRegistry', () => { expect(result).toEqual([]); }); - it('should return an empty array if the action type is not a system action but defines kibana privileges', () => { - const registry = new ActionTypeRegistry(actionTypeRegistryParams); - const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); - - registry.register({ - id: 'foo', - name: 'Foo', - minimumLicenseRequired: 'basic', - supportedFeatureIds: ['alerting'], - getKibanaPrivileges, - validate: { - config: { schema: schema.object({}) }, - secrets: { schema: schema.object({}) }, - params: { schema: schema.object({}) }, - }, - executor, - }); - - const result = registry.getSystemActionKibanaPrivileges('foo'); - - expect(result).toEqual([]); - expect(getKibanaPrivileges).not.toHaveBeenCalled(); - }); - it('should pass the metadata correctly', () => { const registry = new ActionTypeRegistry(actionTypeRegistryParams); const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 32e20081335b6..42968fdbd8f63 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -164,6 +164,19 @@ export class ActionTypeRegistry { ); } + if (!actionType.isSystemActionType && actionType.getKibanaPrivileges) { + throw new Error( + i18n.translate('xpack.actions.actionTypeRegistry.register.invalidKibanaPrivileges', { + defaultMessage: + 'Kibana privilege authorization is only supported for system action types', + values: { + connectorTypeId: actionType.id, + ids: actionType.supportedFeatureIds.join(','), + }, + }) + ); + } + const maxAttempts = this.actionsConfigUtils.getMaxAttempts({ actionTypeId: actionType.id, actionTypeMaxAttempts: actionType.maxAttempts, From 8a9e449d428a24559798dfe098273b4d8f18283b Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 13 Jul 2023 14:44:32 +0300 Subject: [PATCH 43/45] Fix test --- x-pack/plugins/actions/server/lib/action_executor.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index c5a3edc4752f0..d5257f4eb8450 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -183,10 +183,6 @@ export class ActionExecutor { }); } - if (!actionTypeRegistry.isActionExecutable(actionId, actionTypeId)) { - actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); - } - const actionLabel = `${actionTypeId}:${actionId}: ${name}`; logger.debug(`executing action ${actionLabel}`); From 17940872f444c44a96ecee4652f324b40a6299ac Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 13 Jul 2023 14:45:48 +0300 Subject: [PATCH 44/45] Fix i18n --- x-pack/plugins/actions/server/action_type_registry.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 42968fdbd8f63..377d65a8df84a 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -169,10 +169,6 @@ export class ActionTypeRegistry { i18n.translate('xpack.actions.actionTypeRegistry.register.invalidKibanaPrivileges', { defaultMessage: 'Kibana privilege authorization is only supported for system action types', - values: { - connectorTypeId: actionType.id, - ids: actionType.supportedFeatureIds.join(','), - }, }) ); } From 59cfda7a11c94442e4ec41ed4bc8be680ca1d889 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 20 Jul 2023 12:15:10 +0300 Subject: [PATCH 45/45] PR feedback --- x-pack/plugins/actions/server/action_type_registry.test.ts | 4 ++-- x-pack/plugins/actions/server/action_type_registry.ts | 6 +++--- x-pack/plugins/actions/server/actions_client.test.ts | 2 +- x-pack/plugins/actions/server/actions_client.ts | 4 ++-- x-pack/plugins/actions/server/types.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index bf204185830a8..915f5ed047052 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -779,7 +779,7 @@ describe('actionTypeRegistry', () => { expect(result).toEqual([]); }); - it('should pass the metadata correctly', () => { + it('should pass the params correctly', () => { const registry = new ActionTypeRegistry(actionTypeRegistryParams); const getKibanaPrivileges = jest.fn().mockReturnValue(['test/create']); @@ -799,7 +799,7 @@ describe('actionTypeRegistry', () => { }); registry.getSystemActionKibanaPrivileges('.cases', { foo: 'bar' }); - expect(getKibanaPrivileges).toHaveBeenCalledWith({ metadata: { foo: 'bar' } }); + expect(getKibanaPrivileges).toHaveBeenCalledWith({ params: { foo: 'bar' } }); }); }); }); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 377d65a8df84a..5d96b0d6b9761 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -104,9 +104,9 @@ export class ActionTypeRegistry { /** * Returns the kibana privileges of a system action type */ - public getSystemActionKibanaPrivileges( + public getSystemActionKibanaPrivileges( actionTypeId: string, - metadata?: Record + params?: Params ): string[] { const actionType = this.actionTypes.get(actionTypeId); @@ -114,7 +114,7 @@ export class ActionTypeRegistry { return []; } - return actionType?.getKibanaPrivileges?.({ metadata }) ?? []; + return actionType?.getKibanaPrivileges?.({ params }) ?? []; } /** diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index e41e5244ce7bd..de8bcc5ce82f0 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -2980,7 +2980,7 @@ describe('execute()', () => { additionalPrivileges: ['test/create'], }); - expect(getKibanaPrivileges).toHaveBeenCalledWith({ metadata: { foo: 'bar' } }); + expect(getKibanaPrivileges).toHaveBeenCalledWith({ params: { foo: 'bar' } }); }); }); diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 1b34741c179e6..17efbc1b53e3e 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -718,7 +718,7 @@ export class ActionsClient { return await this.unsecuredSavedObjectsClient.delete('action', id); } - private getSystemActionKibanaPrivileges(connectorId: string, metadata?: Record) { + private getSystemActionKibanaPrivileges(connectorId: string, params?: ExecuteOptions['params']) { const inMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === connectorId ); @@ -726,7 +726,7 @@ export class ActionsClient { const additionalPrivileges = inMemoryConnector?.isSystemAction ? this.actionTypeRegistry.getSystemActionKibanaPrivileges( inMemoryConnector.actionTypeId, - metadata + params ) : []; diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 95c08a9e5a39a..5dbf29f96ebb1 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -162,7 +162,7 @@ export interface ActionType< * It only works with system actions and only when executing an action. * For all other scenarios they will be ignored */ - getKibanaPrivileges?: (args?: { metadata?: Record }) => string[]; + getKibanaPrivileges?: (args?: { params?: Params }) => string[]; renderParameterTemplates?: RenderParameterTemplates; executor: ExecutorType; }