diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index f458ad872f66e..8ed8157e9e3d7 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -518,6 +518,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -675,6 +681,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -827,6 +839,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -955,6 +973,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -1025,6 +1049,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index c372e27cfdc0f..8bbe5b7ed1194 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -589,6 +589,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -746,6 +752,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -898,6 +910,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -1026,6 +1044,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object @@ -1096,6 +1120,12 @@ paths: additionalProperties: false type: object properties: + auth_mode: + description: The authentication mode used for the connector. + enum: + - shared + - per-user + type: string config: additionalProperties: {} type: object diff --git a/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/action/10.2.0.json b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/action/10.2.0.json new file mode 100644 index 0000000000000..46a3248a306b5 --- /dev/null +++ b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/action/10.2.0.json @@ -0,0 +1,108 @@ +{ + "10.1.0": [ + { + "name": "Webhook Connector with authType", + "actionTypeId": ".webhook", + "isMissingSecrets": false, + "config": { + "url": "https://example.com/webhook", + "method": "post", + "authType": "basic" + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false + }, + { + "name": "ServiceNow Connector", + "actionTypeId": ".servicenow", + "isMissingSecrets": false, + "config": { + "apiUrl": "https://instance.service-now.com", + "authType": "apiKey", + "usesTableApi": false + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false + }, + { + "name": "Slack Connector without authType", + "actionTypeId": ".slack", + "isMissingSecrets": false, + "config": { + "webhookUrl": "https://hooks.slack.com/services/xxx" + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false + }, + { + "name": "Email Connector", + "actionTypeId": ".email", + "isMissingSecrets": false, + "config": { + "service": "gmail", + "from": "test@example.com" + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false + } + ], + "10.2.0": [ + { + "name": "Webhook Connector with authType", + "actionTypeId": ".webhook", + "isMissingSecrets": false, + "config": { + "url": "https://example.com/webhook", + "method": "post", + "authType": "basic" + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false, + "authMode": "shared" + }, + { + "name": "ServiceNow Connector", + "actionTypeId": ".servicenow", + "isMissingSecrets": false, + "config": { + "apiUrl": "https://instance.service-now.com", + "authType": "apiKey", + "usesTableApi": false + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false, + "authMode": "shared" + }, + { + "name": "Slack Connector without authType", + "actionTypeId": ".slack", + "isMissingSecrets": false, + "config": { + "webhookUrl": "https://hooks.slack.com/services/xxx" + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false, + "authMode": "shared" + }, + { + "name": "Email Connector", + "actionTypeId": ".email", + "isMissingSecrets": false, + "config": { + "service": "gmail", + "from": "test@example.com" + }, + "secrets": "eyJlbmNyeXB0ZWQiOiJ0cnVlIn0=", + "isPreconfigured": false, + "isSystemAction": false, + "authMode": "shared" + } + ] +} diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 2b9db7b0afede..3fe37c955f84f 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -57,7 +57,7 @@ describe('checking migration metadata changes on all registered SO types', () => expect(hashMap).toMatchInlineSnapshot(` Object { - "action": "f57d48f1be0e7895817648ecc89db8a5b594edc5185ad7c60b5ab743c960311b", + "action": "3ab1aa6b6d32d7704fa2dc5fe34c65d632d869af3736f5d08a761d09b93430e2", "action_task_params": "6751dc8a4707a432bc9b90f5a025f183aefc84bca5ec26c29ce6939b24ea81e4", "ad_hoc_run_params": "9c372f2a8f8b468e9b699a6df633c7f14fab7f13216c9ec160813e75bae56098", "alert": "119624b6025ea6794d2c33e2b41c2e4730d10446430b285691f7638ee6787af5", @@ -237,6 +237,7 @@ describe('checking migration metadata changes on all registered SO types', () => "action|global: 04984aae6011426601f8a2a06278e30080f6da3a", "action|mappings: c4a658c865d4c30b51ae9b49e1dec06d012bc213", "action|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", + "action|10.2.0: 4367848750ed49705e88d5ffbe6229bf25312bd17e0f66f1848ec0031b6667b8", "action|10.1.0: e588c3ab049a6d529d68099cac0cb5b8256603d421f28dd475bbcd2146451d6f", "action|8.3.0: 89bd5d9dfbcd73496bf7ff424f5ed670b074078e", "action|8.0.0: 89bd5d9dfbcd73496bf7ff424f5ed670b074078e", @@ -1324,7 +1325,7 @@ describe('checking migration metadata changes on all registered SO types', () => // WARNING Existing entries' semvers should NEVER be downgraded. Never validate changes if they cause a downgrade. expect(map).toMatchInlineSnapshot(` Object { - "action": "10.1.0", + "action": "10.2.0", "action_task_params": "10.2.0", "ad_hoc_run_params": "10.3.0", "alert": "10.8.0", @@ -1480,7 +1481,7 @@ describe('checking migration metadata changes on all registered SO types', () => // WARNING Existing entries' semvers should NEVER be downgraded. Never validate changes if they cause a downgrade. expect(map).toMatchInlineSnapshot(` Object { - "action": "10.1.0", + "action": "10.2.0", "action_task_params": "10.2.0", "ad_hoc_run_params": "10.3.0", "alert": "10.8.0", diff --git a/x-pack/platform/plugins/shared/actions/common/routes/connector/response/schemas/v1.ts b/x-pack/platform/plugins/shared/actions/common/routes/connector/response/schemas/v1.ts index 57822bb0fb151..ab53dc34bd226 100644 --- a/x-pack/platform/plugins/shared/actions/common/routes/connector/response/schemas/v1.ts +++ b/x-pack/platform/plugins/shared/actions/common/routes/connector/response/schemas/v1.ts @@ -40,6 +40,13 @@ export const connectorResponseSchema = schema.object({ is_connector_type_deprecated: schema.boolean({ meta: { description: 'Indicates whether the connector type is deprecated.' }, }), + auth_mode: schema.maybe( + schema.oneOf([schema.literal('shared'), schema.literal('per-user')], { + meta: { + description: 'The authentication mode used for the connector.', + }, + }) + ), }); const connectorResponseWithReferencesCountSchema = connectorResponseSchema.extends({ diff --git a/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.test.ts b/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.test.ts index ff1e1aa442259..74ac3c3b585c8 100644 --- a/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.test.ts @@ -52,6 +52,8 @@ import type { estypes } from '@elastic/elasticsearch'; import { ConnectorRateLimiter } from '../lib/connector_rate_limiter'; import { getConnectorType } from '../fixtures'; import { createMockInMemoryConnector } from '../application/connector/mocks'; +import { authTypeRegistryMock } from '../auth_types/auth_type_registry.mock'; +import type { AuthTypeRegistry } from '../auth_types/auth_type_registry'; jest.mock('@kbn/core-saved-objects-utils-server', () => { const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); @@ -99,7 +101,7 @@ let actionsClient: ActionsClient; let mockedLicenseState: jest.Mocked; let actionTypeRegistry: ActionTypeRegistry; let actionTypeRegistryParams: ActionTypeRegistryOpts; - +let authTypeRegistry: AuthTypeRegistry; const connectorTokenClient = connectorTokenClientMock.create(); const inMemoryMetrics = inMemoryMetricsMock.create(); @@ -134,9 +136,11 @@ beforeEach(() => { inMemoryConnectors: [], }; actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + authTypeRegistry = authTypeRegistryMock.create() as unknown as AuthTypeRegistry; actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -563,6 +567,7 @@ describe('create()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -642,6 +647,7 @@ describe('create()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -715,6 +721,7 @@ describe('create()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -774,6 +781,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -811,6 +819,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -866,6 +875,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -909,6 +919,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1018,6 +1029,7 @@ describe('get()', () => { expect(result).toContainConnector({ id: '1', isMissingSecrets: false, + authMode: 'shared', }); expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` @@ -1032,6 +1044,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1067,6 +1080,7 @@ describe('get()', () => { isPreconfigured: true, name: 'test', config: undefined, // in memory connectors do not return unless exposeConfig is true + authMode: 'shared', }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); }); @@ -1075,6 +1089,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1106,6 +1121,7 @@ describe('get()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1135,6 +1151,7 @@ describe('get()', () => { id: 'system-connector-.cases', isSystemAction: true, name: 'System action: .cases', + authMode: 'shared', }); }); }); @@ -1172,6 +1189,7 @@ describe('getBulk()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1309,6 +1327,7 @@ describe('getBulk()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1353,6 +1372,7 @@ describe('getBulk()', () => { actionTypeId: '.slack', isPreconfigured: true, name: 'test', + authMode: 'shared', }, { id: '1', @@ -1360,6 +1380,7 @@ describe('getBulk()', () => { name: 'test', config: { foo: 'bar' }, isMissingSecrets: false, + authMode: 'shared', }, ]); }); @@ -1396,6 +1417,7 @@ describe('getBulk()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1464,6 +1486,7 @@ describe('getBulk()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1507,12 +1530,14 @@ describe('getBulk()', () => { id: 'testPreconfigured', isPreconfigured: true, name: 'test', + authMode: 'shared', }, { actionTypeId: '.cases', id: 'system-connector-.cases', isSystemAction: true, name: 'System action: .cases', + authMode: 'shared', }, { actionTypeId: 'test', @@ -1520,6 +1545,7 @@ describe('getBulk()', () => { id: '1', isMissingSecrets: false, name: 'test', + authMode: 'shared', }, ]); }); @@ -1532,6 +1558,7 @@ describe('getOAuthAccessToken()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1940,6 +1967,7 @@ describe('delete()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -1979,6 +2007,7 @@ describe('delete()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -2467,6 +2496,7 @@ describe('update()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -2513,6 +2543,7 @@ describe('update()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -2608,6 +2639,7 @@ describe('execute()', () => { ], logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -2667,6 +2699,7 @@ describe('execute()', () => { ], logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -2722,6 +2755,7 @@ describe('execute()', () => { ], logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -2992,6 +3026,7 @@ describe('isPreconfigured()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -3037,6 +3072,7 @@ describe('isPreconfigured()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -3084,6 +3120,7 @@ describe('isSystemAction()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -3129,6 +3166,7 @@ describe('isSystemAction()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, diff --git a/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.ts b/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.ts index 923225d580488..0569942861c4a 100644 --- a/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.ts +++ b/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client.ts @@ -37,6 +37,7 @@ import type { IExecutionLogResult, } from '../../common'; import type { ActionTypeRegistry } from '../action_type_registry'; +import type { AuthTypeRegistry } from '../auth_types/auth_type_registry'; import type { ActionExecutorContract } from '../lib'; import { parseDate } from '../lib'; import type { @@ -98,6 +99,7 @@ export interface ConstructorOptions { kibanaIndices: string[]; scopedClusterClient: IScopedClusterClient; actionTypeRegistry: ActionTypeRegistry; + authTypeRegistry: AuthTypeRegistry; encryptedSavedObjectsClient: EncryptedSavedObjectsClient; unsecuredSavedObjectsClient: SavedObjectsClientContract; inMemoryConnectors: InMemoryConnector[]; @@ -127,6 +129,7 @@ export interface ActionsClientContext { encryptedSavedObjectsClient: EncryptedSavedObjectsClient; unsecuredSavedObjectsClient: SavedObjectsClientContract; actionTypeRegistry: ActionTypeRegistry; + authTypeRegistry: AuthTypeRegistry; inMemoryConnectors: InMemoryConnector[]; actionExecutor: ActionExecutorContract; request: KibanaRequest; @@ -150,6 +153,7 @@ export class ActionsClient { constructor({ logger, actionTypeRegistry, + authTypeRegistry, kibanaIndices, scopedClusterClient, encryptedSavedObjectsClient, @@ -171,6 +175,7 @@ export class ActionsClient { this.context = { logger, actionTypeRegistry, + authTypeRegistry, encryptedSavedObjectsClient, unsecuredSavedObjectsClient, scopedClusterClient, diff --git a/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client_hooks.test.ts b/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client_hooks.test.ts index 710107f11a420..dcb09a523503b 100644 --- a/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client_hooks.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/actions_client/actions_client_hooks.test.ts @@ -33,6 +33,8 @@ import { connectorTokenClientMock } from '../lib/connector_token_client.mock'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { ConnectorRateLimiter } from '../lib/connector_rate_limiter'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import type { AuthTypeRegistry } from '../auth_types/auth_type_registry'; +import { authTypeRegistryMock } from '../auth_types/auth_type_registry.mock'; jest.mock('uuid', () => ({ v4: () => ConnectorSavedObject.id, @@ -61,6 +63,7 @@ let actionsClient: ActionsClient; let mockedLicenseState: jest.Mocked; let actionTypeRegistry: ActionTypeRegistry; let actionTypeRegistryParams: ActionTypeRegistryOpts; +let authTypeRegistry: AuthTypeRegistry; const executor: ExecutorType<{}, {}, {}, void> = async (options) => { return { status: 'ok', actionId: options.actionId }; }; @@ -135,9 +138,11 @@ beforeEach(() => { }; actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + authTypeRegistry = authTypeRegistryMock.create() as unknown as AuthTypeRegistry; actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_in_memory_connector.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_in_memory_connector.ts index 4cf56cf6277de..11a954a7393c3 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_in_memory_connector.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_in_memory_connector.ts @@ -27,6 +27,7 @@ export function connectorFromInMemoryConnector({ isSystemAction: inMemoryConnector.isSystemAction, isDeprecated: isConnectorDeprecated(inMemoryConnector), isConnectorTypeDeprecated: actionTypeRegistry.isDeprecated(inMemoryConnector.actionTypeId), + authMode: inMemoryConnector.authMode ?? 'shared', }; if (inMemoryConnector.exposeConfig) { diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_save_object.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_save_object.ts index 64b1b95bd4f4e..ada62abc3fac9 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_save_object.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/lib/connector_from_save_object.ts @@ -21,5 +21,6 @@ export function connectorFromSavedObject( isDeprecated, isSystemAction: false, isConnectorTypeDeprecated, + authMode: (savedObject.attributes.authMode ?? 'shared') as Connector['authMode'], }; } diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.test.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.test.ts new file mode 100644 index 0000000000000..4f50b6f34babf --- /dev/null +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.test.ts @@ -0,0 +1,1018 @@ +/* + * 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 { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; +import { actionsAuthorizationMock } from '../../../../authorization/actions_authorization.mock'; +import type { ActionsAuthorization } from '../../../../authorization/actions_authorization'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import type { Logger } from '@kbn/logging'; +import type { ActionTypeRegistry } from '../../../../action_type_registry'; +import type { AuthTypeRegistry } from '../../../../auth_types/auth_type_registry'; +import { create } from './create'; +import { getConnectorType } from '../../../../fixtures'; +import type { ActionsClientContext } from '../../../../actions_client'; +import { actionExecutorMock } from '../../../../lib/action_executor.mock'; +import { connectorTokenClientMock } from '../../../../lib/connector_token_client.mock'; +import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import { z } from '@kbn/zod'; +import { authTypeRegistryMock } from '../../../../auth_types/auth_type_registry.mock'; + +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); + return { + ...actual, + SavedObjectsUtils: { + generateId: () => 'mock-saved-object-id', + }, + }; +}); + +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); +const authorization = actionsAuthorizationMock.create(); +const request = httpServerMock.createKibanaRequest(); +const auditLogger = auditLoggerMock.create(); +const logger = loggingSystemMock.create().get() as jest.Mocked; +const preSaveHook = jest.fn(); +const postSaveHook = jest.fn(); +const actionExecutor = actionExecutorMock.create(); +const connectorTokenClient = connectorTokenClientMock.create(); +const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createClient(); +const bulkExecutionEnqueuer = jest.fn(); +const getEventLogClient = jest.fn(); +const getAxiosInstanceWithAuth = jest.fn(); + +const actionTypeRegistry: ActionTypeRegistry = { + get: jest.fn(), + isSystemActionType: jest.fn().mockReturnValue(false), + ensureActionTypeEnabled: jest.fn(), + isDeprecated: jest.fn().mockReturnValue(false), + getUtils: jest.fn().mockReturnValue({ + isHostnameAllowed: jest.fn().mockReturnValue(true), + isUriAllowed: jest.fn().mockReturnValue(true), + getMicrosoftGraphApiUrl: jest.fn(), + getProxySettings: jest.fn(), + }), +} as unknown as ActionTypeRegistry; + +const authTypeRegistry = authTypeRegistryMock.create() as unknown as AuthTypeRegistry; +const mockContext: ActionsClientContext = { + actionTypeRegistry, + authTypeRegistry, + authorization: authorization as unknown as ActionsAuthorization, + unsecuredSavedObjectsClient, + scopedClusterClient, + request, + auditLogger, + logger, + inMemoryConnectors: [], + kibanaIndices: ['.kibana'], + actionExecutor, + bulkExecutionEnqueuer, + connectorTokenClient, + getEventLogClient, + encryptedSavedObjectsClient, + isESOCanEncrypt: true, + getAxiosInstanceWithAuth, +}; + +describe('create()', () => { + beforeEach(() => { + jest.clearAllMocks(); + unsecuredSavedObjectsClient.create.mockReset(); + // Set up default action type with schemas that accept any properties + (actionTypeRegistry.get as jest.Mock).mockReturnValue( + getConnectorType({ + id: 'my-connector-type', + validate: { + config: { schema: z.any() }, + secrets: { schema: z.any() }, + params: { schema: z.object({}) }, + }, + }) + ); + (actionTypeRegistry.isDeprecated as jest.Mock).mockReturnValue(false); + (actionTypeRegistry.isSystemActionType as jest.Mock).mockReturnValue(false); + authorization.ensureAuthorized.mockResolvedValue(undefined); + }); + + describe('authorization', () => { + test('ensures user is authorised to create this type of action', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'create', + actionTypeId: 'my-connector-type', + }); + }); + + test('throws when user is not authorised to create this type of action', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to create a "my-connector-type" action`) + ); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to create a "my-connector-type" action]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'create', + actionTypeId: 'my-connector-type', + }); + }); + }); + + describe('auditLogger', () => { + test('logs audit event when creating a connector', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_create', + outcome: 'unknown', + }), + kibana: { saved_object: { id: 'mock-saved-object-id', type: 'action' } }, + }) + ); + }); + + test('logs audit event when not authorised to create a connector', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + authorization.ensureAuthorized.mockRejectedValue(new Error('Unauthorized')); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrow(); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_create', + }), + kibana: { saved_object: { id: 'mock-saved-object-id', type: 'action' } }, + error: { code: 'Error', message: 'Unauthorized' }, + }) + ); + }); + }); + + describe('system actions', () => { + test('throws an error when creating a system action', async () => { + (actionTypeRegistry.isSystemActionType as jest.Mock).mockReturnValue(true); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: '.cases', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrow('System action creation is forbidden. Action type: .cases.'); + }); + + test('throws an error when creating a connector with an ID that matches a system action', async () => { + const contextWithSystemAction = { + ...mockContext, + inMemoryConnectors: [ + { + id: 'system-connector-id', + actionTypeId: '.cases', + name: 'System Action', + isSystemAction: true, + config: {}, + secrets: {}, + isPreconfigured: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + }, + ], + }; + + await expect( + create({ + context: contextWithSystemAction, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + options: { id: 'system-connector-id' }, + }) + ).rejects.toThrow('System action creation is forbidden. Action type: my-connector-type.'); + }); + }); + + describe('preconfigured connectors', () => { + test('throws an error when creating a connector with an ID that matches a preconfigured connector', async () => { + const contextWithPreconfigured = { + ...mockContext, + inMemoryConnectors: [ + { + id: 'preconfigured-connector-id', + actionTypeId: 'my-connector-type', + name: 'Preconfigured Connector', + isPreconfigured: true, + isSystemAction: false, + config: {}, + secrets: {}, + isDeprecated: false, + isConnectorTypeDeprecated: false, + }, + ], + }; + + await expect( + create({ + context: contextWithPreconfigured, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + options: { id: 'preconfigured-connector-id' }, + }) + ).rejects.toThrow( + 'This preconfigured-connector-id already exists in a preconfigured action.' + ); + }); + }); + + describe('basic connector creation', () => { + test('creates an action with all given properties', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: { foo: 'bar' }, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + }, + }); + + expect(result).toEqual({ + id: '1', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + name: 'my name', + config: { foo: 'bar' }, + isPreconfigured: false, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + }); + + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'action', + { + actionTypeId: 'my-connector-type', + name: 'my name', + isMissingSecrets: false, + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + }, + { id: 'mock-saved-object-id' } + ); + }); + + test('creates an action with a custom ID when provided', async () => { + const savedObjectCreateResult = { + id: 'custom-id', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + options: { id: 'custom-id' }, + }); + + expect(result.id).toBe('custom-id'); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith('action', expect.anything(), { + id: 'custom-id', + }); + }); + + test('validates config and secrets', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: { validated: true }, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const actionType = getConnectorType({ + id: 'my-connector-type', + validate: { + config: { + schema: z.any().transform(() => ({ validated: true })), + }, + secrets: { + schema: z.any().transform(() => ({ validatedSecret: true })), + }, + }, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + + await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + }, + }); + + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'action', + expect.objectContaining({ + config: { validated: true }, + secrets: { validatedSecret: true }, + }), + expect.anything() + ); + }); + + test('ensures action type is enabled', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }); + + expect(actionTypeRegistry.ensureActionTypeEnabled).toHaveBeenCalledWith('my-connector-type'); + }); + }); + + describe('authMode', () => { + test('creates an action with authMode "shared"', async () => { + // Mock authTypeRegistry to return an auth type with authMode 'shared' + (authTypeRegistry.get as jest.Mock).mockReturnValue({ + id: 'basic', + }); + + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: { + authType: 'basic', + }, + authMode: 'shared' as const, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { authType: 'basic' }, + secrets: {}, + }, + }); + + expect(result).toEqual({ + id: '1', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + name: 'my name', + config: { + authType: 'basic', + }, + authMode: 'shared', + isPreconfigured: false, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + }); + + expect(authTypeRegistry.get).toHaveBeenCalledWith('basic'); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'action', + { + actionTypeId: 'my-connector-type', + name: 'my name', + isMissingSecrets: false, + config: { + authType: 'basic', + }, + secrets: {}, + authMode: 'shared', + }, + { id: 'mock-saved-object-id' } + ); + }); + + test('creates an action with authMode "per-user"', async () => { + // Mock authTypeRegistry to return an auth type with authMode 'per-user' + (authTypeRegistry.get as jest.Mock).mockReturnValue({ + id: 'oauth2', + authMode: 'per-user', + }); + + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + secrets: { authType: 'oauth_authorization_code' }, + authMode: 'per-user' as const, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: { authType: 'oauth_authorization_code' }, + }, + }); + + expect(result).toEqual({ + id: '1', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + name: 'my name', + config: {}, + isPreconfigured: false, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + authMode: 'per-user', + }); + + expect(authTypeRegistry.get).toHaveBeenCalledWith('oauth_authorization_code'); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'action', + { + actionTypeId: 'my-connector-type', + name: 'my name', + isMissingSecrets: false, + config: {}, + secrets: { authType: 'oauth_authorization_code' }, + authMode: 'per-user', + }, + { id: 'mock-saved-object-id' } + ); + }); + + test('creates an action without authMode when no authType is provided', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }); + + expect(result).toEqual({ + id: '1', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + name: 'my name', + config: {}, + isPreconfigured: false, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + }); + + expect(authTypeRegistry.get).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'action', + { + actionTypeId: 'my-connector-type', + name: 'my name', + isMissingSecrets: false, + config: {}, + secrets: {}, + }, + { id: 'mock-saved-object-id' } + ); + }); + }); + + describe('hooks', () => { + test('calls preSaveHook if defined', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const actionType = getConnectorType({ + id: 'my-connector-type', + preSaveHook, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + + await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + }, + }); + + expect(preSaveHook).toHaveBeenCalledWith({ + connectorId: 'mock-saved-object-id', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + logger, + request, + services: { scopedClusterClient }, + isUpdate: false, + }); + }); + + test('logs audit event and throws when preSaveHook fails', async () => { + const actionType = getConnectorType({ + id: 'my-connector-type', + preSaveHook, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + preSaveHook.mockRejectedValueOnce(new Error('preSaveHook failed')); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrow('preSaveHook failed'); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_create', + }), + error: expect.objectContaining({ + message: 'preSaveHook failed', + }), + }) + ); + }); + + test('calls postSaveHook if defined and connector created successfully', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const actionType = getConnectorType({ + id: 'my-connector-type', + postSaveHook, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + + await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + }, + }); + + expect(postSaveHook).toHaveBeenCalledWith({ + connectorId: 'mock-saved-object-id', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + logger, + request, + services: { scopedClusterClient }, + isUpdate: false, + wasSuccessful: true, + }); + }); + + test('calls postSaveHook with wasSuccessful false when connector creation fails', async () => { + unsecuredSavedObjectsClient.create.mockRejectedValueOnce(new Error('Save failed')); + + const actionType = getConnectorType({ + id: 'my-connector-type', + postSaveHook, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + }, + }) + ).rejects.toThrow('Save failed'); + + expect(postSaveHook).toHaveBeenCalledWith({ + connectorId: 'mock-saved-object-id', + config: { foo: 'bar' }, + secrets: { apiKey: 'secret' }, + logger, + request, + services: { scopedClusterClient }, + isUpdate: false, + wasSuccessful: false, + }); + }); + + test('logs error but does not throw when postSaveHook fails', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const actionType = getConnectorType({ + id: 'my-connector-type', + postSaveHook, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + postSaveHook.mockRejectedValueOnce(new Error('postSaveHook failed')); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }); + + expect(result.id).toBe('1'); + expect(logger.error).toHaveBeenCalledWith( + expect.stringContaining('postSaveHook create error'), + expect.objectContaining({ + tags: ['post-save-hook', 'mock-saved-object-id'], + }) + ); + }); + }); + + describe('error handling', () => { + test('throws when saved object client fails', async () => { + unsecuredSavedObjectsClient.create.mockRejectedValueOnce( + new Error('Failed to create saved object') + ); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrow('Failed to create saved object'); + }); + + test('throws when action type does not exist', async () => { + (actionTypeRegistry.get as jest.Mock).mockImplementation(() => { + throw new Error('Action type not found'); + }); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'non-existent-type', + config: {}, + secrets: {}, + }, + }) + ).rejects.toThrow('Action type not found'); + }); + + test('throws when config validation fails', async () => { + const actionType = getConnectorType({ + id: 'my-connector-type', + validate: { + config: { + schema: z.any().refine(() => { + throw new Error('Config validation failed'); + }), + }, + }, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: { invalid: 'config' }, + secrets: {}, + }, + }) + ).rejects.toThrow('Config validation failed'); + }); + + test('throws when secrets validation fails', async () => { + const actionType = getConnectorType({ + id: 'my-connector-type', + validate: { + secrets: { + schema: z.any().refine(() => { + throw new Error('Secrets validation failed'); + }), + }, + }, + }); + + (actionTypeRegistry.get as jest.Mock).mockReturnValue(actionType); + + await expect( + create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: { invalid: 'secret' }, + }, + }) + ).rejects.toThrow('Secrets validation failed'); + }); + }); + + describe('deprecated connectors', () => { + test('marks connector as deprecated when action type is deprecated', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: 'my-connector-type', + isMissingSecrets: false, + config: {}, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + (actionTypeRegistry.isDeprecated as jest.Mock).mockReturnValue(true); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: 'my-connector-type', + config: {}, + secrets: {}, + }, + }); + + expect(result.isConnectorTypeDeprecated).toBe(true); + }); + + test('marks connector as deprecated when attributes indicate deprecation', async () => { + const savedObjectCreateResult = { + id: '1', + type: 'action', + attributes: { + name: 'my name', + actionTypeId: '.servicenow', + isMissingSecrets: false, + config: { + usesTableApi: true, + }, + }, + references: [], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce(savedObjectCreateResult); + + const result = await create({ + context: mockContext, + action: { + name: 'my name', + actionTypeId: '.servicenow', + config: { + usesTableApi: true, + }, + secrets: {}, + }, + }); + + expect(result.isDeprecated).toBe(true); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.ts index 1baefe20e69fd..785b6765a995f 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/create/create.ts @@ -15,6 +15,7 @@ import { validateConfig, validateConnector, validateSecrets } from '../../../../ import { isConnectorDeprecated } from '../../lib'; import type { HookServices, ActionResult } from '../../../../types'; import { tryCatch } from '../../../../lib'; +import { inferAuthMode } from '../../../../lib/infer_auth_mode'; export async function create({ context, @@ -116,6 +117,12 @@ export async function create({ }) ); + const authMode = inferAuthMode({ + authTypeRegistry: context.authTypeRegistry, + secrets, + config, + }); + const result = await tryCatch( async () => await context.unsecuredSavedObjectsClient.create( @@ -126,6 +133,7 @@ export async function create({ isMissingSecrets: false, config: validatedActionTypeConfig as SavedObjectAttributes, secrets: validatedActionTypeSecrets as SavedObjectAttributes, + ...(authMode !== undefined ? { authMode } : {}), }, { id } ) @@ -168,5 +176,6 @@ export async function create({ isSystemAction: false, isDeprecated: isConnectorDeprecated(result.attributes), isConnectorTypeDeprecated: context.actionTypeRegistry.isDeprecated(actionTypeId), + ...(result.attributes.authMode !== undefined ? { authMode: result.attributes.authMode } : {}), }; } diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.test.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.test.ts new file mode 100644 index 0000000000000..74b7cf7d0ccca --- /dev/null +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.test.ts @@ -0,0 +1,570 @@ +/* + * 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 { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; +import { actionsAuthorizationMock } from '../../../../authorization/actions_authorization.mock'; +import type { ActionsAuthorization } from '../../../../authorization/actions_authorization'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import type { Logger } from '@kbn/logging'; +import type { ActionTypeRegistry } from '../../../../action_type_registry'; +import type { AuthTypeRegistry } from '../../../../auth_types/auth_type_registry'; +import { authTypeRegistryMock } from '../../../../auth_types/auth_type_registry.mock'; +import { get } from './get'; +import type { ActionsClientContext } from '../../../../actions_client'; +import { getConnectorSo } from '../../../../data/connector'; +import { connectorFromInMemoryConnector } from '../../lib/connector_from_in_memory_connector'; +import type { InMemoryConnector } from '../../../../types'; +import { actionExecutorMock } from '../../../../lib/action_executor.mock'; +import { connectorTokenClientMock } from '../../../../lib/connector_token_client.mock'; +import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; + +jest.mock('../../../../data/connector', () => ({ + getConnectorSo: jest.fn(), +})); + +jest.mock('../../lib/connector_from_in_memory_connector', () => ({ + connectorFromInMemoryConnector: jest.fn(), +})); + +const getConnectorSoMock = getConnectorSo as jest.Mock; +const connectorFromInMemoryConnectorMock = connectorFromInMemoryConnector as jest.Mock; + +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const scopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); +const authorization = actionsAuthorizationMock.create(); +const request = httpServerMock.createKibanaRequest(); +const auditLogger = auditLoggerMock.create(); +const logger = loggingSystemMock.create().get() as jest.Mocked; +const actionExecutor = actionExecutorMock.create(); +const connectorTokenClient = connectorTokenClientMock.create(); +const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createClient(); +const bulkExecutionEnqueuer = jest.fn(); +const getEventLogClient = jest.fn(); +const getAxiosInstanceWithAuth = jest.fn(); + +const actionTypeRegistry: ActionTypeRegistry = { + get: jest.fn(), + isSystemActionType: jest.fn().mockReturnValue(false), + ensureActionTypeEnabled: jest.fn(), + isDeprecated: jest.fn().mockReturnValue(false), + getUtils: jest.fn().mockReturnValue({ + isHostnameAllowed: jest.fn().mockReturnValue(true), + isUriAllowed: jest.fn().mockReturnValue(true), + getMicrosoftGraphApiUrl: jest.fn(), + getProxySettings: jest.fn(), + }), +} as unknown as ActionTypeRegistry; + +const authTypeRegistry = authTypeRegistryMock.create() as unknown as AuthTypeRegistry; + +const mockContext: ActionsClientContext = { + actionTypeRegistry, + authTypeRegistry, + authorization: authorization as unknown as ActionsAuthorization, + unsecuredSavedObjectsClient, + scopedClusterClient, + request, + auditLogger, + logger, + inMemoryConnectors: [], + kibanaIndices: ['.kibana'], + actionExecutor, + bulkExecutionEnqueuer, + connectorTokenClient, + getEventLogClient, + encryptedSavedObjectsClient, + isESOCanEncrypt: true, + getAxiosInstanceWithAuth, +}; + +describe('get()', () => { + beforeEach(() => { + jest.clearAllMocks(); + authorization.ensureAuthorized.mockResolvedValue(undefined); + (actionTypeRegistry.isDeprecated as jest.Mock).mockReturnValue(false); + }); + + describe('authorization', () => { + test('ensures user is authorised to get connector', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isMissingSecrets: false, + }, + references: [], + }); + + await get({ + context: mockContext, + id: '1', + }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'get', + }); + }); + + test('throws when user is not authorised to get connector', async () => { + authorization.ensureAuthorized.mockRejectedValue(new Error('Unauthorized to get connector')); + + await expect( + get({ + context: mockContext, + id: '1', + }) + ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to get connector]`); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'get', + }); + }); + }); + + describe('auditLogger', () => { + test('logs audit event when getting a connector from saved objects', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isMissingSecrets: false, + }, + references: [], + }); + + await get({ + context: mockContext, + id: '1', + }); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_get', + outcome: 'success', + }), + kibana: { saved_object: { id: '1', type: 'action' } }, + }) + ); + }); + + test('logs audit event when getting an in-memory connector', async () => { + const inMemoryConnector: InMemoryConnector = { + id: 'preconfigured-1', + actionTypeId: '.slack', + name: 'Preconfigured Slack', + isPreconfigured: true, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: {}, + secrets: {}, + }; + + connectorFromInMemoryConnectorMock.mockReturnValueOnce({ + id: 'preconfigured-1', + actionTypeId: '.slack', + name: 'Preconfigured Slack', + isPreconfigured: true, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: {}, + }); + + await get({ + context: { ...mockContext, inMemoryConnectors: [inMemoryConnector] }, + id: 'preconfigured-1', + }); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_get', + outcome: 'success', + }), + kibana: { saved_object: { id: 'preconfigured-1', type: 'action' } }, + }) + ); + }); + + test('logs audit event when not authorised to get a connector', async () => { + authorization.ensureAuthorized.mockRejectedValue(new Error('Unauthorized')); + + await expect( + get({ + context: mockContext, + id: '1', + }) + ).rejects.toThrow(); + + expect(auditLogger.log).toHaveBeenCalledWith( + expect.objectContaining({ + event: expect.objectContaining({ + action: 'connector_get', + }), + kibana: { saved_object: { id: '1', type: 'action' } }, + error: { code: 'Error', message: 'Unauthorized' }, + }) + ); + }); + }); + + describe('in-memory connectors', () => { + test('gets a preconfigured connector successfully', async () => { + const inMemoryConnector: InMemoryConnector = { + id: 'preconfigured-1', + actionTypeId: '.slack', + name: 'Preconfigured Slack', + isPreconfigured: true, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: { webhookUrl: 'https://hooks.slack.com/services/xxx' }, + secrets: {}, + }; + + const expectedConnector = { + id: 'preconfigured-1', + actionTypeId: '.slack', + name: 'Preconfigured Slack', + isPreconfigured: true, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: { webhookUrl: 'https://hooks.slack.com/services/xxx' }, + }; + + connectorFromInMemoryConnectorMock.mockReturnValueOnce(expectedConnector); + + const result = await get({ + context: { ...mockContext, inMemoryConnectors: [inMemoryConnector] }, + id: 'preconfigured-1', + }); + + expect(result).toEqual(expectedConnector); + expect(connectorFromInMemoryConnectorMock).toHaveBeenCalledWith({ + id: 'preconfigured-1', + inMemoryConnector, + actionTypeRegistry, + }); + expect(getConnectorSoMock).not.toHaveBeenCalled(); + }); + + test('gets a system action when throwIfSystemAction is false', async () => { + const inMemoryConnector: InMemoryConnector = { + id: 'system-1', + actionTypeId: '.cases', + name: 'System Cases Connector', + isPreconfigured: false, + isSystemAction: true, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: {}, + secrets: {}, + }; + + const expectedConnector = { + id: 'system-1', + actionTypeId: '.cases', + name: 'System Cases Connector', + isPreconfigured: false, + isSystemAction: true, + isDeprecated: false, + isConnectorTypeDeprecated: false, + }; + + connectorFromInMemoryConnectorMock.mockReturnValueOnce(expectedConnector); + + const result = await get({ + context: { ...mockContext, inMemoryConnectors: [inMemoryConnector] }, + id: 'system-1', + throwIfSystemAction: false, + }); + + expect(result).toEqual(expectedConnector); + expect(connectorFromInMemoryConnectorMock).toHaveBeenCalled(); + }); + + test('throws 404 for system action when throwIfSystemAction is true (default)', async () => { + const inMemoryConnector: InMemoryConnector = { + id: 'system-1', + actionTypeId: '.cases', + name: 'System Cases Connector', + isPreconfigured: false, + isSystemAction: true, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: {}, + secrets: {}, + }; + + await expect( + get({ + context: { ...mockContext, inMemoryConnectors: [inMemoryConnector] }, + id: 'system-1', + }) + ).rejects.toMatchInlineSnapshot(`[Error: Connector system-1 not found]`); + + expect(connectorFromInMemoryConnectorMock).not.toHaveBeenCalled(); + }); + + test('throws 404 for system action when throwIfSystemAction is explicitly true', async () => { + const inMemoryConnector: InMemoryConnector = { + id: 'system-1', + actionTypeId: '.cases', + name: 'System Cases Connector', + isPreconfigured: false, + isSystemAction: true, + isDeprecated: false, + isConnectorTypeDeprecated: false, + config: {}, + secrets: {}, + }; + + await expect( + get({ + context: { ...mockContext, inMemoryConnectors: [inMemoryConnector] }, + id: 'system-1', + throwIfSystemAction: true, + }) + ).rejects.toMatchInlineSnapshot(`[Error: Connector system-1 not found]`); + + expect(connectorFromInMemoryConnectorMock).not.toHaveBeenCalled(); + }); + }); + + describe('saved object connectors', () => { + test('gets a connector from saved objects successfully', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: { url: 'https://example.com' }, + isMissingSecrets: false, + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result).toEqual({ + id: '1', + actionTypeId: '.webhook', + name: 'Test Connector', + config: { url: 'https://example.com' }, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: false, + isDeprecated: false, + isConnectorTypeDeprecated: false, + authMode: 'shared', + }); + + expect(getConnectorSoMock).toHaveBeenCalledWith({ + unsecuredSavedObjectsClient, + id: '1', + }); + }); + + test('gets a connector with authMode "shared"', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isMissingSecrets: false, + authMode: 'shared', + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result.authMode).toBe('shared'); + }); + + test('gets a connector with authMode "per-user"', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: { + authType: 'oauth_authorization_code', + }, + isMissingSecrets: false, + authMode: 'per-user', + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result.authMode).toBe('per-user'); + }); + + test('defaults authMode to "shared" when not set', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.slack', + config: {}, + isMissingSecrets: false, + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result.authMode).toBe('shared'); + }); + + test('sets isMissingSecrets correctly', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isMissingSecrets: true, + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result.isMissingSecrets).toBe(true); + }); + + test('sets isConnectorTypeDeprecated correctly', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.pagerduty', + config: {}, + isMissingSecrets: false, + }, + references: [], + }); + + (actionTypeRegistry.isDeprecated as jest.Mock).mockReturnValueOnce(true); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result.isConnectorTypeDeprecated).toBe(true); + expect(actionTypeRegistry.isDeprecated).toHaveBeenCalledWith('.pagerduty'); + }); + + test('marks connector as deprecated when attributes indicate deprecation', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.servicenow', + config: { + apiUrl: 'https://instance.service-now.com', + usesTableApi: true, + }, + isMissingSecrets: false, + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result.isDeprecated).toBe(true); + }); + }); + + describe('schema validation', () => { + test('logs warning when connector fails validation but returns connector anyway', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isMissingSecrets: false, + }, + references: [], + }); + + const result = await get({ + context: mockContext, + id: '1', + }); + + expect(result).toBeDefined(); + expect(result.id).toBe('1'); + expect(result.name).toBe('Test Connector'); + }); + + test('does not throw when connector validation fails', async () => { + getConnectorSoMock.mockResolvedValueOnce({ + id: '1', + type: 'action', + attributes: { + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isMissingSecrets: false, + }, + references: [], + }); + + await expect( + get({ + context: mockContext, + id: '1', + }) + ).resolves.toBeDefined(); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.ts index 8015c4d414e17..4661496118e15 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get/get.ts @@ -88,6 +88,9 @@ export async function get({ isSystemAction: false, isDeprecated: isConnectorDeprecated(result.attributes), isConnectorTypeDeprecated: actionTypeRegistry.isDeprecated(result.attributes.actionTypeId), + authMode: result.attributes.authMode + ? (result.attributes.authMode as Connector['authMode']) + : 'shared', }; } diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts index 6ff08170ddc76..8347e11e1b6a6 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts @@ -28,6 +28,8 @@ import { getAllUnsecured } from './get_all'; import type { InferenceInferenceEndpointInfo } from '@elastic/elasticsearch/lib/api/types'; import { createMockInMemoryConnector } from '../../mocks'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import type { AuthTypeRegistry } from '../../../../auth_types/auth_type_registry'; +import { authTypeRegistryMock } from '../../../../auth_types/auth_type_registry.mock'; jest.mock('@kbn/core-saved-objects-utils-server', () => { const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); @@ -71,7 +73,8 @@ const isESOCanEncrypt = true; let actionsClient: ActionsClient; const actionTypeRegistry: ActionTypeRegistry = jest.fn() as unknown as ActionTypeRegistry; - +const authTypeRegistry: AuthTypeRegistry = + authTypeRegistryMock.create() as unknown as AuthTypeRegistry; describe('getAll()', () => { beforeEach(() => { jest.resetAllMocks(); @@ -79,6 +82,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -138,6 +142,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -283,6 +288,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -328,6 +334,7 @@ describe('getAll()', () => { isMissingSecrets: false, config: { foo: 'bar' }, referencedByCount: 6, + authMode: 'shared', }, { id: 'testPreconfigured', @@ -335,6 +342,7 @@ describe('getAll()', () => { name: 'test', isPreconfigured: true, referencedByCount: 2, + authMode: 'shared', }, ]); }); @@ -375,6 +383,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -415,6 +424,7 @@ describe('getAll()', () => { isSystemAction: true, name: 'System action: .cases', referencedByCount: 2, + authMode: 'shared', }, { id: '1', @@ -422,6 +432,7 @@ describe('getAll()', () => { isMissingSecrets: false, config: { foo: 'bar' }, referencedByCount: 6, + authMode: 'shared', }, { id: 'testPreconfigured', @@ -429,6 +440,7 @@ describe('getAll()', () => { name: 'test', isPreconfigured: true, referencedByCount: 2, + authMode: 'shared', }, ]); }); @@ -467,6 +479,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -503,6 +516,7 @@ describe('getAll()', () => { name: 'test', referencedByCount: 6, actionTypeId: undefined, + authMode: 'shared', }, { actionTypeId: '.slack', @@ -510,6 +524,7 @@ describe('getAll()', () => { isPreconfigured: true, name: 'test', referencedByCount: 2, + authMode: 'shared', }, ]); @@ -554,6 +569,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -599,6 +615,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -641,6 +658,7 @@ describe('getAll()', () => { isSystemAction: true, name: 'test2', referencedByCount: 2, + authMode: 'shared', }, ]); }); @@ -682,6 +700,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -718,6 +737,7 @@ describe('getAll()', () => { referencedByCount: 6, isConnectorTypeDeprecated: true, isMissingSecrets: false, + authMode: 'shared', }, { actionTypeId: '.slack', @@ -726,9 +746,269 @@ describe('getAll()', () => { name: 'test', referencedByCount: 2, isConnectorTypeDeprecated: true, + authMode: 'shared', + }, + ]); + }); + + test('returns connector with authMode "shared"', async () => { + const expectedResult = { + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'type', + attributes: { + name: 'test', + actionTypeId: '.test-connector-type', + isMissingSecrets: false, + config: { + foo: 'bar', + }, + }, + score: 1, + references: [], + }, + ], + }; + unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + '1': { doc_count: 6 }, + testWithAuthMode: { doc_count: 2 }, + }, + } + ); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + authTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + createMockInMemoryConnector({ + id: 'testWithAuthMode', + actionTypeId: '.webhook', + isPreconfigured: true, + name: 'test with authMode', + config: { + url: 'https://example.com', + }, + authMode: 'shared', + }), + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + encryptedSavedObjectsClient, + isESOCanEncrypt, + getAxiosInstanceWithAuth, + }); + + const result = await actionsClient.getAll(); + + expect(result).toContainConnectorsFindResult([ + { + id: '1', + name: 'test', + isMissingSecrets: false, + config: { foo: 'bar' }, + referencedByCount: 6, + authMode: 'shared', + }, + { + id: 'testWithAuthMode', + actionTypeId: '.webhook', + name: 'test with authMode', + isPreconfigured: true, + authMode: 'shared', + referencedByCount: 2, + }, + ]); + }); + + test('returns connector with authMode "per-user"', async () => { + const expectedResult = { + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'type', + attributes: { + name: 'test', + actionTypeId: '.test-connector-type', + isMissingSecrets: false, + config: { + foo: 'bar', + }, + }, + score: 1, + references: [], + }, + ], + }; + unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + '1': { doc_count: 6 }, + testWithPerUserAuth: { doc_count: 3 }, + }, + } + ); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + authTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + createMockInMemoryConnector({ + id: 'testWithPerUserAuth', + actionTypeId: '.webhook', + isPreconfigured: true, + name: 'test with per-user auth', + config: { + url: 'https://example.com', + }, + authMode: 'per-user', + }), + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + encryptedSavedObjectsClient, + isESOCanEncrypt, + getAxiosInstanceWithAuth, + }); + + const result = await actionsClient.getAll(); + + expect(result).toContainConnectorsFindResult([ + { + id: '1', + name: 'test', + isMissingSecrets: false, + config: { foo: 'bar' }, + referencedByCount: 6, + authMode: 'shared', + }, + { + id: 'testWithPerUserAuth', + actionTypeId: '.webhook', + name: 'test with per-user auth', + isPreconfigured: true, + authMode: 'per-user', + referencedByCount: 3, }, ]); }); + + test('returns connector without authMode when not set', async () => { + const expectedResult = { + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'type', + attributes: { + name: 'test', + actionTypeId: '.test-connector-type', + isMissingSecrets: false, + config: { + foo: 'bar', + }, + }, + score: 1, + references: [], + }, + ], + }; + unsecuredSavedObjectsClient.find.mockResolvedValueOnce(expectedResult); + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + '1': { doc_count: 6 }, + testWithoutAuthMode: { doc_count: 1 }, + }, + } + ); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + authTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + createMockInMemoryConnector({ + id: 'testWithoutAuthMode', + actionTypeId: '.slack', + isPreconfigured: true, + name: 'test without authMode', + config: { + url: 'https://slack.example.com', + }, + }), + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + encryptedSavedObjectsClient, + isESOCanEncrypt, + getAxiosInstanceWithAuth, + }); + + const result = await actionsClient.getAll(); + + expect(result).toContainConnectorsFindResult([ + { + id: '1', + name: 'test', + isMissingSecrets: false, + config: { foo: 'bar' }, + referencedByCount: 6, + authMode: 'shared', + }, + { + id: 'testWithoutAuthMode', + actionTypeId: '.slack', + name: 'test without authMode', + isPreconfigured: true, + referencedByCount: 1, + authMode: 'shared', + }, + ]); + + // Ensure authMode defaults to 'shared' even when not explicitly set + const connectorWithoutAuthMode = result.find((c) => c.id === 'testWithoutAuthMode'); + expect(connectorWithoutAuthMode).toBeDefined(); + expect(connectorWithoutAuthMode!.authMode).toBe('shared'); + }); }); describe('getAllSystemConnectors()', () => { @@ -746,6 +1026,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -820,6 +1101,7 @@ describe('getAll()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -863,6 +1145,7 @@ describe('getAll()', () => { name: 'Test system action', isSystemAction: true, referencedByCount: 2, + authMode: 'shared', }, ]); }); @@ -893,6 +1176,7 @@ describe('getAllUnsecured()', () => { foo: 'bar', }, secrets: 'this should not be returned', + authMode: 'shared', }, score: 1, references: [], @@ -950,6 +1234,7 @@ describe('getAllUnsecured()', () => { isMissingSecrets: false, config: { foo: 'bar' }, referencedByCount: 6, + authMode: 'shared', }, { id: 'testPreconfigured', @@ -958,6 +1243,7 @@ describe('getAllUnsecured()', () => { isPreconfigured: true, referencedByCount: 2, config: { foo: 'bar' }, + authMode: 'shared', }, ]); @@ -1060,6 +1346,7 @@ describe('getAllUnsecured()', () => { foo: 'bar', }, secrets: 'this should not be returned', + authMode: 'shared', }, score: 1, references: [], @@ -1115,6 +1402,7 @@ describe('getAllUnsecured()', () => { isMissingSecrets: false, config: { foo: 'bar' }, referencedByCount: 6, + authMode: 'shared', }, { id: 'testPreconfigured', @@ -1122,6 +1410,7 @@ describe('getAllUnsecured()', () => { name: 'test', isPreconfigured: true, referencedByCount: 2, + authMode: 'shared', }, ]); @@ -1223,6 +1512,7 @@ describe('getAllUnsecured()', () => { config: { foo: 'bar', }, + authMode: 'shared', }, score: 1, references: [], @@ -1269,6 +1559,7 @@ describe('getAllUnsecured()', () => { isMissingSecrets: false, name: 'test', referencedByCount: 6, + authMode: 'shared', }, { actionTypeId: '.slack', @@ -1276,6 +1567,7 @@ describe('getAllUnsecured()', () => { isPreconfigured: true, name: 'test', referencedByCount: 2, + authMode: 'shared', }, ]); diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts index 47cf17a6817d8..06876b2d09d87 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts @@ -130,6 +130,7 @@ async function getAllHelper({ isSystemAction: connector.isSystemAction, isConnectorTypeDeprecated: connectorTypeRegistry.isDeprecated(connector.actionTypeId), ...(connector.exposeConfig ? { config: connector.config } : {}), + authMode: connector.authMode ? connector.authMode : 'shared', }; }), ].sort((a, b) => a.name.localeCompare(b.name)); @@ -189,6 +190,7 @@ export async function getAllSystemConnectors({ isConnectorTypeDeprecated: context.actionTypeRegistry.isDeprecated( systemConnector.actionTypeId ), + authMode: systemConnector.authMode ? systemConnector.authMode : 'shared', })) .sort((a, b) => a.name.localeCompare(b.name)); diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_axios_instance/get_axios_instance.test.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_axios_instance/get_axios_instance.test.ts index f682e5c15a93b..e1bbe5639ae01 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_axios_instance/get_axios_instance.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_axios_instance/get_axios_instance.test.ts @@ -33,6 +33,8 @@ import { eventLogClientMock } from '@kbn/event-log-plugin/server/event_log_clien import { ConnectorRateLimiter } from '../../../../lib/connector_rate_limiter'; import { getConnectorType } from '../../../../fixtures'; import { createMockInMemoryConnector } from '../../mocks'; +import type { AuthTypeRegistry } from '../../../../auth_types/auth_type_registry'; +import { authTypeRegistryMock } from '../../../../auth_types/auth_type_registry.mock'; const defaultConnectorTypeId = '.connector-type-id'; const defaultConnectorId = 'connector-id'; @@ -63,7 +65,7 @@ const inMemoryMetrics = inMemoryMetricsMock.create(); let actionsClient: ActionsClient; let actionTypeRegistry: ActionTypeRegistry; - +let authTypeRegistry: AuthTypeRegistry; const actionTypeIdFromSavedObjectMock = (actionTypeId = defaultConnectorTypeId) => { return { attributes: { @@ -132,12 +134,14 @@ describe('getAxiosInstance()', () => { executor: undefined, }) ); + authTypeRegistry = authTypeRegistryMock.create() as unknown as AuthTypeRegistry; encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce( connectorSavedObject ); actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, @@ -193,6 +197,7 @@ describe('getAxiosInstance()', () => { actionsClient = new ActionsClient({ logger, actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient, scopedClusterClient, kibanaIndices, diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/list_types/list_types.test.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/list_types/list_types.test.ts index 44f489fcaefa2..b6f6e33313dfb 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/list_types/list_types.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/list_types/list_types.test.ts @@ -28,10 +28,13 @@ import { ActionsClient } from '../../../../actions_client/actions_client'; import { ConnectorRateLimiter } from '../../../../lib/connector_rate_limiter'; import { getConnectorType } from '../../../../fixtures'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import { authTypeRegistryMock } from '../../../../auth_types/auth_type_registry.mock'; +import type { AuthTypeRegistry } from '../../../../auth_types/auth_type_registry'; let mockedLicenseState: jest.Mocked; let actionTypeRegistryParams: ActionTypeRegistryOpts; let actionTypeRegistry: ActionTypeRegistry; +let authTypeRegistry: AuthTypeRegistry; describe('listTypes()', () => { let actionsClient: ActionsClient; @@ -56,11 +59,13 @@ describe('listTypes()', () => { inMemoryConnectors: [], }; actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + authTypeRegistry = authTypeRegistryMock.create() as unknown as AuthTypeRegistry; actionsClient = new ActionsClient({ logger: loggingSystemMock.create().get(), kibanaIndices: ['.kibana'], scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), actionTypeRegistry, + authTypeRegistry, unsecuredSavedObjectsClient: savedObjectsClientMock.create(), inMemoryConnectors: [], actionExecutor: actionExecutorMock.create(), diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/schemas/connector_schema.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/schemas/connector_schema.ts index 249f1f35a38e2..d9f85e33cd2ea 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/schemas/connector_schema.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/schemas/connector_schema.ts @@ -17,6 +17,7 @@ export const connectorSchema = schema.object({ isDeprecated: schema.boolean(), isSystemAction: schema.boolean(), isConnectorTypeDeprecated: schema.boolean({ defaultValue: false }), + authMode: schema.maybe(schema.oneOf([schema.literal('shared'), schema.literal('per-user')])), }); export const connectorWithExtraFindDataSchema = connectorSchema.extends({ diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/types/connector.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/types/connector.ts index 300b8c2b5efdc..07eb63ae6cef9 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/types/connector.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/types/connector.ts @@ -21,6 +21,7 @@ export interface Connector { isDeprecated: ConnectorSchemaType['isDeprecated']; isSystemAction: ConnectorSchemaType['isSystemAction']; isConnectorTypeDeprecated: ConnectorSchemaType['isConnectorTypeDeprecated']; + authMode?: ConnectorSchemaType['authMode']; } export interface ConnectorWithExtraFindData extends Connector { diff --git a/x-pack/platform/plugins/shared/actions/server/plugin.ts b/x-pack/platform/plugins/shared/actions/server/plugin.ts index 1e1dbb3cec02f..0d9d32fb7e931 100644 --- a/x-pack/platform/plugins/shared/actions/server/plugin.ts +++ b/x-pack/platform/plugins/shared/actions/server/plugin.ts @@ -528,6 +528,7 @@ export class ActionsPlugin logger, unsecuredSavedObjectsClient, actionTypeRegistry: actionTypeRegistry!, + authTypeRegistry: this.authTypeRegistry!, kibanaIndices: core.savedObjects.getAllIndices(), scopedClusterClient: core.elasticsearch.client.asScoped(request), inMemoryConnectors: this.inMemoryConnectors, @@ -820,6 +821,7 @@ export class ActionsPlugin ): IContextProvider => { const { actionTypeRegistry, + authTypeRegistry, isESOCanEncrypt, getInMemoryConnectors, actionExecutor, @@ -857,6 +859,7 @@ export class ActionsPlugin logger, unsecuredSavedObjectsClient, actionTypeRegistry: actionTypeRegistry!, + authTypeRegistry: authTypeRegistry!, kibanaIndices: savedObjects.getAllIndices(), scopedClusterClient: coreContext.elasticsearch.client, inMemoryConnectors, diff --git a/x-pack/platform/plugins/shared/actions/server/routes/connector/common_transforms/transform_connector_response/v1.ts b/x-pack/platform/plugins/shared/actions/server/routes/connector/common_transforms/transform_connector_response/v1.ts index eea33ad22c892..698cc693b8af3 100644 --- a/x-pack/platform/plugins/shared/actions/server/routes/connector/common_transforms/transform_connector_response/v1.ts +++ b/x-pack/platform/plugins/shared/actions/server/routes/connector/common_transforms/transform_connector_response/v1.ts @@ -15,6 +15,7 @@ export const transformConnectorResponse = ({ isDeprecated, isSystemAction, isConnectorTypeDeprecated, + authMode, ...res }: Connector): ConnectorResponseV1 => ({ ...res, @@ -24,4 +25,5 @@ export const transformConnectorResponse = ({ is_missing_secrets: isMissingSecrets, is_system_action: isSystemAction, is_connector_type_deprecated: isConnectorTypeDeprecated, + ...(authMode !== undefined ? { auth_mode: authMode } : {}), }); diff --git a/x-pack/platform/plugins/shared/actions/server/routes/connector/get/get.test.ts b/x-pack/platform/plugins/shared/actions/server/routes/connector/get/get.test.ts index ab7767090e288..52925a17094ad 100644 --- a/x-pack/platform/plugins/shared/actions/server/routes/connector/get/get.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/routes/connector/get/get.test.ts @@ -38,6 +38,7 @@ describe('getConnectorRoute', () => { actionTypeId: '2', name: 'action name', isMissingSecrets: false, + authMode: 'per-user', }); const actionsClient = actionsClientMock.create(); @@ -54,6 +55,7 @@ describe('getConnectorRoute', () => { expect(await handler(context, req, res)).toMatchInlineSnapshot(` Object { "body": Object { + "auth_mode": "per-user", "config": Object {}, "connector_type_id": "2", "id": "1", @@ -81,6 +83,7 @@ describe('getConnectorRoute', () => { is_missing_secrets: false, is_system_action: false, is_connector_type_deprecated: false, + auth_mode: 'per-user', }, }); }); diff --git a/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/get_all.test.ts b/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/get_all.test.ts index 5328cd76e2c4d..0cfa704c9a02b 100644 --- a/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/get_all.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/get_all.test.ts @@ -93,4 +93,141 @@ describe('getAllConnectorsRoute', () => { expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); }); + + it('returns connectors with authMode "shared"', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([ + { + id: '1', + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + isConnectorTypeDeprecated: false, + referencedByCount: 0, + authMode: 'shared', + }, + ]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + const result = await handler(context, req, res); + + expect(result).toMatchObject({ + body: [ + { + id: '1', + name: 'Test Connector', + connector_type_id: '.webhook', + is_preconfigured: false, + is_deprecated: false, + is_system_action: false, + is_connector_type_deprecated: false, + referenced_by_count: 0, + auth_mode: 'shared', + }, + ], + }); + + expect(actionsClient.getAll).toHaveBeenCalledTimes(1); + }); + + it('returns connectors with authMode "per-user"', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([ + { + id: '1', + name: 'Test Connector', + actionTypeId: '.webhook', + config: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + isConnectorTypeDeprecated: false, + referencedByCount: 0, + authMode: 'per-user', + }, + ]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + const result = await handler(context, req, res); + + expect(result).toMatchObject({ + body: [ + { + id: '1', + name: 'Test Connector', + connector_type_id: '.webhook', + is_preconfigured: false, + is_deprecated: false, + is_system_action: false, + is_connector_type_deprecated: false, + referenced_by_count: 0, + auth_mode: 'per-user', + }, + ], + }); + + expect(actionsClient.getAll).toHaveBeenCalledTimes(1); + }); + + it('returns connectors without authMode when not set', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([ + { + id: '1', + name: 'Test Connector', + actionTypeId: '.slack', + config: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, + isConnectorTypeDeprecated: false, + referencedByCount: 0, + }, + ]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + await handler(context, req, res); + + expect(res.ok).toHaveBeenCalledWith({ + body: expect.arrayContaining([ + expect.objectContaining({ + id: '1', + name: 'Test Connector', + connector_type_id: '.slack', + }), + ]), + }); + + const responseBody = (res.ok as jest.Mock).mock.calls[0][0].body; + expect(responseBody[0]).not.toHaveProperty('auth_mode'); + + expect(actionsClient.getAll).toHaveBeenCalledTimes(1); + }); }); diff --git a/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/transforms/transform_connectors_response/v1.ts b/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/transforms/transform_connectors_response/v1.ts index 027e50d08ce46..913287299373b 100644 --- a/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/transforms/transform_connectors_response/v1.ts +++ b/x-pack/platform/plugins/shared/actions/server/routes/connector/get_all/transforms/transform_connectors_response/v1.ts @@ -23,6 +23,7 @@ export const transformGetAllConnectorsResponse = ( isMissingSecrets, isSystemAction, isConnectorTypeDeprecated, + authMode, }) => ({ id, name, @@ -34,6 +35,7 @@ export const transformGetAllConnectorsResponse = ( is_missing_secrets: isMissingSecrets, is_system_action: isSystemAction, is_connector_type_deprecated: isConnectorTypeDeprecated, + ...(authMode !== undefined ? { auth_mode: authMode } : {}), }) ); }; diff --git a/x-pack/platform/plugins/shared/actions/server/routes/connector/update/transforms/transform_update_connector_response/v1.ts b/x-pack/platform/plugins/shared/actions/server/routes/connector/update/transforms/transform_update_connector_response/v1.ts index 223686f036194..b4e0fb9045dc2 100644 --- a/x-pack/platform/plugins/shared/actions/server/routes/connector/update/transforms/transform_update_connector_response/v1.ts +++ b/x-pack/platform/plugins/shared/actions/server/routes/connector/update/transforms/transform_update_connector_response/v1.ts @@ -15,6 +15,7 @@ export const transformUpdateConnectorResponse = ({ isDeprecated, isSystemAction, isConnectorTypeDeprecated, + authMode, ...res }: Connector): ConnectorResponseV1 => ({ ...res, @@ -24,4 +25,5 @@ export const transformUpdateConnectorResponse = ({ is_missing_secrets: isMissingSecrets, is_system_action: isSystemAction, is_connector_type_deprecated: isConnectorTypeDeprecated, + ...(authMode !== undefined ? { auth_mode: authMode } : {}), }); diff --git a/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.test.ts b/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.test.ts new file mode 100644 index 0000000000000..f5dbdf5604ad1 --- /dev/null +++ b/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SavedObjectModelTransformationContext, + SavedObjectsFullModelVersion, +} from '@kbn/core-saved-objects-server'; +import type { Logger } from '@kbn/core/server'; +import { connectorModelVersions } from './connector_model_versions'; + +describe('Connector Model Versions', () => { + describe('version 1', () => { + it('has correct structure', () => { + const version1 = connectorModelVersions['1'] as SavedObjectsFullModelVersion; + expect(version1).toBeDefined(); + expect(version1.changes).toEqual([]); + expect(version1.schemas).toBeDefined(); + expect(version1.schemas?.create).toBeDefined(); + }); + }); + + describe('version 2', () => { + const version2 = connectorModelVersions['2'] as SavedObjectsFullModelVersion; + const context: SavedObjectModelTransformationContext = { + log: { + get: () => ({ debug: jest.fn(), info: jest.fn(), warn: jest.fn() }), + } as unknown as Logger, + modelVersion: 2, + namespaceType: 'single', + }; + + it('has correct structure', () => { + expect(version2).toBeDefined(); + expect(version2.changes).toHaveLength(1); + expect(version2.changes[0].type).toBe('data_backfill'); + expect(version2.schemas).toBeDefined(); + expect(version2.schemas?.create).toBeDefined(); + expect(version2.schemas?.forwardCompatibility).toBeDefined(); + }); + + describe('backfillFn', () => { + const backfillChange = version2.changes.find((change) => change.type === 'data_backfill'); + const backfillFn = + backfillChange && backfillChange.type === 'data_backfill' + ? backfillChange.backfillFn + : undefined; + + it('exists', () => { + expect(backfillFn).toBeDefined(); + expect(typeof backfillFn).toBe('function'); + }); + + it('adds authMode "shared" correctly', () => { + const mockDocument = { + id: 'test-connector-id', + type: 'action', + attributes: { + actionTypeId: '.slack', + name: 'Test Connector', + isMissingSecrets: false, + config: { + authType: 'apiKey', + url: 'https://example.com', + }, + secrets: '{}', + }, + references: [], + migrationVersion: {}, + coreMigrationVersion: '8.0.0', + typeMigrationVersion: '8.0.0', + updated_at: '2024-01-01T00:00:00.000Z', + version: '1', + namespaces: ['default'], + }; + + const result = backfillFn!(mockDocument, context); + + expect(result).toEqual({ + ...mockDocument, + attributes: { + ...mockDocument.attributes, + authMode: 'shared', + }, + }); + }); + + it('does not overwrite existing authMode if already present', () => { + const mockDocument = { + id: 'test-connector-id', + type: 'action', + attributes: { + actionTypeId: '.webhook', + name: 'Test Webhook', + isMissingSecrets: false, + config: { + authType: 'bearer', + url: 'https://example.com', + }, + secrets: '{}', + authMode: 'per-user' as const, + }, + references: [], + migrationVersion: {}, + coreMigrationVersion: '8.0.0', + typeMigrationVersion: '8.0.0', + updated_at: '2024-01-01T00:00:00.000Z', + version: '1', + namespaces: ['default'], + }; + + const result = backfillFn!(mockDocument, context); + + expect(result).toEqual({ + ...mockDocument, + }); + }); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.ts b/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.ts index 79581f95f7b6f..c6e6ec557bba3 100644 --- a/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.ts +++ b/x-pack/platform/plugins/shared/actions/server/saved_objects/model_versions/connector_model_versions.ts @@ -6,7 +6,7 @@ */ import type { SavedObjectsModelVersionMap } from '@kbn/core-saved-objects-server'; -import { rawConnectorSchemaV1 } from '../schemas/raw_connector'; +import { rawConnectorSchemaV1, rawConnectorSchemaV2 } from '../schemas/raw_connector'; export const connectorModelVersions: SavedObjectsModelVersionMap = { '1': { @@ -15,4 +15,21 @@ export const connectorModelVersions: SavedObjectsModelVersionMap = { create: rawConnectorSchemaV1, }, }, + '2': { + changes: [ + { + type: 'data_backfill', + backfillFn: (doc) => { + if (!doc.attributes.authMode) { + return { ...doc, attributes: { ...doc.attributes, authMode: 'shared' } }; + } + return doc; + }, + }, + ], + schemas: { + create: rawConnectorSchemaV2, + forwardCompatibility: rawConnectorSchemaV2.extends({}, { unknowns: 'ignore' }), + }, + }, }; diff --git a/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/index.ts b/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/index.ts index f86d616c03393..b865332cb3001 100644 --- a/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/index.ts +++ b/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/index.ts @@ -6,3 +6,4 @@ */ export { rawConnectorSchema as rawConnectorSchemaV1 } from './v1'; +export { rawConnectorSchema as rawConnectorSchemaV2 } from './v2'; diff --git a/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/v2.test.ts b/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/v2.test.ts new file mode 100644 index 0000000000000..b064f5b151c62 --- /dev/null +++ b/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/v2.test.ts @@ -0,0 +1,46 @@ +/* + * 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 { rawConnectorSchema } from './v2'; + +const action = { + actionTypeId: '12345', + name: 'test-action-name', + isMissingSecrets: false, + config: { + foo: 'bar', + }, + secrets: JSON.stringify({ + pass: 'foo', + }), + isPreconfigured: false, + isSystemAction: false, +}; + +describe('Raw Connector Schema v2', () => { + describe('authMode attribute', () => { + test('validates action with authMode "shared"', () => { + const actionWithSharedAuth = { ...action, authMode: 'shared' as const }; + expect(rawConnectorSchema.validate(actionWithSharedAuth)).toEqual(actionWithSharedAuth); + }); + + test('validates action with authMode "per-user"', () => { + const actionWithPerUserAuth = { ...action, authMode: 'per-user' as const }; + expect(rawConnectorSchema.validate(actionWithPerUserAuth)).toEqual(actionWithPerUserAuth); + }); + + test('validates action without authMode (optional)', () => { + expect(rawConnectorSchema.validate(action)).toEqual(action); + }); + + test('rejects invalid authMode value', () => { + expect(() => rawConnectorSchema.validate({ ...action, authMode: 'invalid' })).toThrow( + /authMode/ + ); + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/v2.ts b/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/v2.ts new file mode 100644 index 0000000000000..069c5b6200127 --- /dev/null +++ b/x-pack/platform/plugins/shared/actions/server/saved_objects/schemas/raw_connector/v2.ts @@ -0,0 +1,13 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { rawConnectorSchema as rawConnectorSchemaV1 } from './v1'; + +export const rawConnectorSchema = rawConnectorSchemaV1.extends({ + authMode: schema.maybe(schema.oneOf([schema.literal('shared'), schema.literal('per-user')])), +}); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts index 56631dd2b9fcc..a3fe6aaa79bd8 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/cases_webhook.ts @@ -136,6 +136,7 @@ export default function casesWebhookTest({ getService }: FtrProviderContext) { is_missing_secrets: false, config: simulatorConfig, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -184,6 +185,7 @@ export default function casesWebhookTest({ getService }: FtrProviderContext) { is_missing_secrets: false, config: newConfig, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts index 45854723c0ccb..19e14ea2ac5a7 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/email.ts @@ -93,6 +93,7 @@ export default function emailTest({ getService }: FtrProviderContext) { oauthTokenUrl: null, tenantId: null, }, + auth_mode: 'shared', }); }); @@ -480,6 +481,7 @@ export default function emailTest({ getService }: FtrProviderContext) { oauthTokenUrl: null, tenantId: null, }, + auth_mode: 'shared', }); }); @@ -550,6 +552,7 @@ export default function emailTest({ getService }: FtrProviderContext) { oauthTokenUrl: null, tenantId: null, }, + auth_mode: 'shared', }); }); @@ -629,6 +632,7 @@ export default function emailTest({ getService }: FtrProviderContext) { clientId: '12345', tenantId: '1234567', }, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts index 1b9a9456d6647..52c8a4faa6508 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/es_index.ts @@ -72,6 +72,7 @@ export default function indexTest({ getService }: FtrProviderContext) { name: 'An index action', connector_type_id: '.index', config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null }, + auth_mode: 'shared', }); // create action with all config props @@ -125,6 +126,7 @@ export default function indexTest({ getService }: FtrProviderContext) { refresh: true, executionTimeField: 'test', }, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts index 7400488c3f947..5549d851ac405 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/jira.ts @@ -109,6 +109,7 @@ export default function jiraTest({ getService }: FtrProviderContext) { projectKey: mockJira.config.projectKey, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts index 75721ad0bd224..46ab56f98ec96 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/pagerduty.ts @@ -92,6 +92,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) { apiUrl: pagerdutySimulatorURL, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts index e119b65a7608e..57ac7a73ffa8f 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts @@ -112,6 +112,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { orgId: mockResilient.config.orgId, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts index a9966f8105a58..38de429605342 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/server_log.ts @@ -57,6 +57,7 @@ export default function serverLogTest({ getService }: FtrProviderContext) { is_missing_secrets: false, config: {}, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts index 3437af2c488ef..09d3b1cd6b56f 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itom.ts @@ -149,6 +149,7 @@ export default function serviceNowITOMTest({ getService }: FtrProviderContext) { userIdentifierValue: null, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -205,6 +206,7 @@ export default function serviceNowITOMTest({ getService }: FtrProviderContext) { userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts index 1a9e71e827b67..e05c0f99fcda2 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_itsm.ts @@ -161,6 +161,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { userIdentifierValue: null, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -219,6 +220,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts index c670a2596b67f..655a1b195f763 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/servicenow_sir.ts @@ -173,6 +173,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { userIdentifierValue: null, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -231,6 +232,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { userIdentifierValue: mockServiceNowOAuth.config.userIdentifierValue, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts index 4523625164608..bc3b66455efc9 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_api.ts @@ -54,6 +54,7 @@ export default function slackTest({ getService }: FtrProviderContext) { connector_type_id: '.slack_api', config: {}, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts index d20a3b221b6a0..2e7ae0a3c0b06 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/slack_webhook.ts @@ -86,6 +86,7 @@ export default function slackTest({ getService }: FtrProviderContext) { connector_type_id: '.slack', config: {}, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts index e19410041507e..5a9a7919a9e4d 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/swimlane.ts @@ -179,6 +179,7 @@ export default function swimlaneTest({ getService }: FtrProviderContext) { apiUrl: swimlaneSimulatorURL, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts index 0014244fafaa1..344dfa4b65644 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/webhook.ts @@ -159,6 +159,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { url: webhookSimulatorURL, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -203,7 +204,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { .get(`/api/actions/connector/${createdAction.id}`) .expect(200); - expect(fetchedAction).to.eql(expectedResult); + expect(fetchedAction).to.eql({ ...expectedResult, auth_mode: 'shared' }); }); } @@ -283,6 +284,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { }, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts index 49260f2a9ee7c..b14dbeb93a6b6 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get.ts @@ -68,6 +68,7 @@ export default function getConnectorTests({ getService }: FtrProviderContext) { is_connector_type_deprecated: false, is_missing_secrets: false, name: 'My Connector', + auth_mode: 'shared', config: { unencrypted: `This value shouldn't get encrypted`, }, @@ -155,6 +156,7 @@ export default function getConnectorTests({ getService }: FtrProviderContext) { is_system_action: false, is_deprecated: false, is_connector_type_deprecated: false, + auth_mode: 'shared', }); break; default: diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts index 785cb9b80fddf..3afba203d067f 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all.ts @@ -79,6 +79,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) }, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -89,6 +90,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -99,6 +101,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -109,6 +112,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -119,6 +123,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -129,6 +134,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -139,6 +145,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -149,6 +156,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -159,6 +167,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); break; @@ -248,6 +257,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) }, referenced_by_count: 1, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -258,6 +268,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -268,6 +279,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -278,6 +290,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -288,6 +301,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -298,6 +312,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -308,6 +323,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -318,6 +334,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -328,6 +345,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); break; @@ -389,6 +407,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -399,6 +418,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -409,6 +429,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -419,6 +440,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -429,6 +451,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -439,6 +462,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -449,6 +473,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -459,6 +484,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); break; diff --git a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts index 8db9b67e0235d..c8244ba1e9148 100644 --- a/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts +++ b/x-pack/platform/test/alerting_api_integration/security_and_spaces/group2/tests/actions/get_all_system.ts @@ -75,6 +75,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Cases', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: createdConnector.id, @@ -89,6 +90,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) }, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -99,6 +101,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'system-connector-.observability-ai-assistant', @@ -109,6 +112,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) referenced_by_count: 0, is_system_action: true, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -119,6 +123,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -129,6 +134,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -139,6 +145,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -149,6 +156,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -159,6 +167,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action', @@ -169,6 +178,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-allow-multiple', @@ -179,6 +189,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action allowing multiple instances', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-connector-adapter', @@ -189,6 +200,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action with a connector adapter set', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-kibana-privileges', @@ -199,6 +211,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action with kibana privileges', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -209,6 +222,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -219,6 +233,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.workflows', @@ -229,6 +244,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Workflows', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); break; @@ -314,6 +330,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Cases', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: createdConnector.id, @@ -328,6 +345,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) }, referenced_by_count: 1, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -338,6 +356,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.observability-ai-assistant', @@ -348,6 +367,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Observability AI Assistant', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -358,6 +378,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -368,6 +389,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -378,6 +400,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -388,6 +411,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -398,6 +422,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action', @@ -408,6 +433,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-allow-multiple', @@ -418,6 +444,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action allowing multiple instances', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-connector-adapter', @@ -428,6 +455,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action with a connector adapter set', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-kibana-privileges', @@ -438,6 +466,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action with kibana privileges', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { @@ -449,6 +478,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -459,6 +489,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.workflows', @@ -469,6 +500,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Workflows', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); break; @@ -530,6 +562,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Cases', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -540,6 +573,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'system-connector-.observability-ai-assistant', @@ -550,6 +584,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) referenced_by_count: 0, is_system_action: true, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -560,6 +595,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -570,6 +606,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -580,6 +617,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -590,6 +628,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -600,6 +639,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action', @@ -610,6 +650,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-allow-multiple', @@ -620,6 +661,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action allowing multiple instances', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-connector-adapter', @@ -630,6 +672,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action with a connector adapter set', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-kibana-privileges', @@ -640,6 +683,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test system action with kibana privileges', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -650,6 +694,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -660,6 +705,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.workflows', @@ -670,6 +716,7 @@ export default function getAllConnectorTests({ getService }: FtrProviderContext) name: 'Workflows', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); break; diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts index f46d87dd6790c..ba49372e56275 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/es_index.ts @@ -84,6 +84,7 @@ export default function indexTest({ getService }: FtrProviderContext) { name: 'An index connector', connector_type_id: '.index', config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null }, + auth_mode: 'shared', }); // create connector with all config props @@ -137,6 +138,7 @@ export default function indexTest({ getService }: FtrProviderContext) { refresh: true, executionTimeField: 'test', }, + auth_mode: 'shared', }); }); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/deprecated.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/deprecated.ts index fca2b35ed3499..cfac9a3e34cf0 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/deprecated.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/deprecated.ts @@ -68,6 +68,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) is_system_action: false, is_missing_secrets: false, is_connector_type_deprecated: true, + auth_mode: 'shared', }); }); }); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get.ts index 5fcaf1f157b2c..e380fb2463528 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -48,6 +48,7 @@ export default function getConnectorTests({ getService }: FtrProviderContext) { unencrypted: `This value shouldn't get encrypted`, }, is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -88,6 +89,7 @@ export default function getConnectorTests({ getService }: FtrProviderContext) { connector_type_id: '.slack', name: 'Slack#xyz', is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -112,6 +114,7 @@ export default function getConnectorTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', name: 'ServiceNow#xyz', is_connector_type_deprecated: false, + auth_mode: 'shared', }); await supertest @@ -126,6 +129,7 @@ export default function getConnectorTests({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', name: 'ServiceNow#xyz', is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); }); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts index 546e982c73e26..02cd70e4b1700 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -55,6 +55,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext is_system_action: false, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: createdConnector.id, @@ -69,6 +70,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext }, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -79,6 +81,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -89,6 +92,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -99,6 +103,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -109,6 +114,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -119,6 +125,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -129,6 +136,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -139,6 +147,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -149,6 +158,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); }); @@ -190,6 +200,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext is_system_action: false, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -200,6 +211,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -210,6 +222,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -220,6 +233,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -230,6 +244,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -240,6 +255,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -250,6 +266,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -260,6 +277,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -270,6 +288,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); }); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts index 11308ca2367cd..0cb5835cf0778 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_system.ts @@ -55,6 +55,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext is_system_action: false, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.cases', @@ -65,6 +66,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Cases', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: createdConnector.id, @@ -79,6 +81,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext }, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -89,6 +92,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'system-connector-.observability-ai-assistant', @@ -99,6 +103,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext referenced_by_count: 0, is_system_action: true, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -109,6 +114,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -119,6 +125,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -129,6 +136,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -139,6 +147,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -149,6 +158,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action', @@ -159,6 +169,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-allow-multiple', @@ -169,6 +180,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action allowing multiple instances', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-connector-adapter', @@ -179,6 +191,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action with a connector adapter set', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-kibana-privileges', @@ -189,6 +202,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action with kibana privileges', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { @@ -200,6 +214,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -210,6 +225,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.workflows', @@ -220,6 +236,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Workflows', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); }); @@ -261,6 +278,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext is_system_action: false, referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.cases', @@ -271,6 +289,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Cases', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.email', @@ -281,6 +300,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Notification Email Connector', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.observability-ai-assistant', @@ -291,6 +311,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext id: 'system-connector-.observability-ai-assistant', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -301,6 +322,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'preconfigured_es_index_action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -311,6 +333,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.servicenow', @@ -321,6 +344,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'ServiceNow#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-slack1', @@ -331,6 +355,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Slack#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'custom-system-abc-connector', @@ -341,6 +366,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'SystemABC', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action', @@ -351,6 +377,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-allow-multiple', @@ -361,6 +388,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action allowing multiple instances', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-connector-adapter', @@ -371,6 +399,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action with a connector adapter set', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: 'test.system-action-kibana-privileges', @@ -381,6 +410,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test system action with kibana privileges', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -391,6 +421,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Test:_Preconfigured_Index_Record', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { id: 'my-test-email', @@ -401,6 +432,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'TestEmail#xyz', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, { connector_type_id: '.workflows', @@ -411,6 +443,7 @@ export default function getAllConnectorsTests({ getService }: FtrProviderContext name: 'Workflows', referenced_by_count: 0, is_connector_type_deprecated: false, + auth_mode: 'shared', }, ]); }); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_unsecured_actions.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_unsecured_actions.ts index 714b9cdf804a3..fa558661977aa 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_unsecured_actions.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/get_all_unsecured_actions.ts @@ -24,6 +24,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'notification-email', @@ -34,6 +35,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'preconfigured-es-index-action', @@ -44,6 +46,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'my-deprecated-servicenow', @@ -54,6 +57,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'my-deprecated-servicenow-default', @@ -64,6 +68,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'my-slack1', @@ -74,6 +79,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'custom-system-abc-connector', @@ -84,6 +90,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'preconfigured.test.index-record', @@ -94,6 +101,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, { id: 'my-test-email', @@ -104,6 +112,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo isSystemAction: false, referencedByCount: 0, isConnectorTypeDeprecated: false, + authMode: 'shared', }, ]; @@ -180,6 +189,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo unencrypted: `This value shouldn't get encrypted`, }, referencedByCount: 0, + authMode: 'shared', }, ]); @@ -212,6 +222,7 @@ export default function createUnsecuredActionTests({ getService }: FtrProviderCo unencrypted: `This value shouldn't get encrypted`, }, referencedByCount: 0, + authMode: 'shared', }, ]); }); diff --git a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts index 8a6b9b287cd06..d2bba5b4c3c2a 100644 --- a/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts +++ b/x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts @@ -66,6 +66,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) is_missing_secrets: false, name: 'an action created before test.not-enabled was disabled', is_connector_type_deprecated: false, + auth_mode: 'shared', }); }); @@ -97,6 +98,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) is_system_action: false, name: 'an action created before test.not-enabled was disabled', is_connector_type_deprecated: false, + auth_mode: 'shared', }); });