diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts index 0a3e447ac64a1..c786dad61c09d 100644 --- a/x-pack/legacy/plugins/siem/index.ts +++ b/x-pack/legacy/plugins/siem/index.ts @@ -5,12 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { get } from 'lodash/fp'; import { resolve } from 'path'; import { Server } from 'hapi'; import { Root } from 'joi'; -import { PluginInitializerContext } from '../../../../src/core/server'; import { plugin } from './server'; import { savedObjectMappings } from './server/saved_objects'; @@ -32,7 +30,6 @@ import { SIGNALS_INDEX_KEY, } from './common/constants'; import { defaultIndexPattern } from './default_index_pattern'; -import { initServerWithKibana } from './server/kibana.index'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -151,27 +148,20 @@ export const siem = (kibana: any) => { mappings: savedObjectMappings, }, init(server: Server) { - const { config, newPlatform, plugins, route } = server; - const { coreContext, env, setup } = newPlatform; - const initializerContext = { ...coreContext, env } as PluginInitializerContext; - const serverFacade = { - config, - usingEphemeralEncryptionKey: - get('usingEphemeralEncryptionKey', newPlatform.setup.plugins.encryptedSavedObjects) ?? - false, - plugins: { - alerting: plugins.alerting, - actions: newPlatform.start.plugins.actions, - elasticsearch: plugins.elasticsearch, - spaces: plugins.spaces, - savedObjects: server.savedObjects.SavedObjectsClient, - }, - route: route.bind(server), + const { coreContext, env, setup, start } = server.newPlatform; + const initializerContext = { ...coreContext, env }; + const __legacy = { + config: server.config, + alerting: server.plugins.alerting, + route: server.route.bind(server), }; - // @ts-ignore-next-line: setup.plugins is too loosely typed - plugin(initializerContext).setup(setup.core, setup.plugins); - initServerWithKibana(initializerContext, serverFacade); + // @ts-ignore-next-line: NewPlatform shim is too loosely typed + const pluginInstance = plugin(initializerContext); + // @ts-ignore-next-line: NewPlatform shim is too loosely typed + pluginInstance.setup(setup.core, setup.plugins, __legacy); + // @ts-ignore-next-line: NewPlatform shim is too loosely typed + pluginInstance.start(start.core, start.plugins); }, config(Joi: Root) { // See x-pack/plugins/siem/server/config.ts if you're adding another diff --git a/x-pack/legacy/plugins/siem/server/index.ts b/x-pack/legacy/plugins/siem/server/index.ts index 882475390ae98..8513f871cb6c1 100644 --- a/x-pack/legacy/plugins/siem/server/index.ts +++ b/x-pack/legacy/plugins/siem/server/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'src/core/server'; +import { PluginInitializerContext } from '../../../../../src/core/server'; import { Plugin } from './plugin'; export const plugin = (context: PluginInitializerContext) => { diff --git a/x-pack/legacy/plugins/siem/server/kibana.index.ts b/x-pack/legacy/plugins/siem/server/kibana.index.ts deleted file mode 100644 index bab7936005c04..0000000000000 --- a/x-pack/legacy/plugins/siem/server/kibana.index.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PluginInitializerContext } from 'src/core/server'; - -import { signalRulesAlertType } from './lib/detection_engine/signals/signal_rule_alert_type'; -import { createRulesRoute } from './lib/detection_engine/routes/rules/create_rules_route'; -import { createIndexRoute } from './lib/detection_engine/routes/index/create_index_route'; -import { readIndexRoute } from './lib/detection_engine/routes/index/read_index_route'; -import { readRulesRoute } from './lib/detection_engine/routes/rules/read_rules_route'; -import { findRulesRoute } from './lib/detection_engine/routes/rules/find_rules_route'; -import { deleteRulesRoute } from './lib/detection_engine/routes/rules/delete_rules_route'; -import { patchRulesRoute } from './lib/detection_engine/routes/rules/patch_rules_route'; -import { setSignalsStatusRoute } from './lib/detection_engine/routes/signals/open_close_signals_route'; -import { querySignalsRoute } from './lib/detection_engine/routes/signals/query_signals_route'; -import { ServerFacade } from './types'; -import { deleteIndexRoute } from './lib/detection_engine/routes/index/delete_index_route'; -import { isAlertExecutor } from './lib/detection_engine/signals/types'; -import { readTagsRoute } from './lib/detection_engine/routes/tags/read_tags_route'; -import { readPrivilegesRoute } from './lib/detection_engine/routes/privileges/read_privileges_route'; -import { addPrepackedRulesRoute } from './lib/detection_engine/routes/rules/add_prepackaged_rules_route'; -import { createRulesBulkRoute } from './lib/detection_engine/routes/rules/create_rules_bulk_route'; -import { patchRulesBulkRoute } from './lib/detection_engine/routes/rules/patch_rules_bulk_route'; -import { deleteRulesBulkRoute } from './lib/detection_engine/routes/rules/delete_rules_bulk_route'; -import { importRulesRoute } from './lib/detection_engine/routes/rules/import_rules_route'; -import { exportRulesRoute } from './lib/detection_engine/routes/rules/export_rules_route'; -import { findRulesStatusesRoute } from './lib/detection_engine/routes/rules/find_rules_status_route'; -import { getPrepackagedRulesStatusRoute } from './lib/detection_engine/routes/rules/get_prepackaged_rules_status_route'; -import { updateRulesRoute } from './lib/detection_engine/routes/rules/update_rules_route'; -import { updateRulesBulkRoute } from './lib/detection_engine/routes/rules/update_rules_bulk_route'; - -const APP_ID = 'siem'; - -export const initServerWithKibana = (context: PluginInitializerContext, __legacy: ServerFacade) => { - const logger = context.logger.get('plugins', APP_ID); - const version = context.env.packageInfo.version; - - if (__legacy.plugins.alerting != null) { - const type = signalRulesAlertType({ logger, version }); - if (isAlertExecutor(type)) { - __legacy.plugins.alerting.setup.registerType(type); - } - } - - // Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules - // All REST rule creation, deletion, updating, etc... - createRulesRoute(__legacy); - readRulesRoute(__legacy); - updateRulesRoute(__legacy); - deleteRulesRoute(__legacy); - findRulesRoute(__legacy); - patchRulesRoute(__legacy); - - addPrepackedRulesRoute(__legacy); - getPrepackagedRulesStatusRoute(__legacy); - createRulesBulkRoute(__legacy); - updateRulesBulkRoute(__legacy); - deleteRulesBulkRoute(__legacy); - patchRulesBulkRoute(__legacy); - - importRulesRoute(__legacy); - exportRulesRoute(__legacy); - - findRulesStatusesRoute(__legacy); - - // Detection Engine Signals routes that have the REST endpoints of /api/detection_engine/signals - // POST /api/detection_engine/signals/status - // Example usage can be found in siem/server/lib/detection_engine/scripts/signals - setSignalsStatusRoute(__legacy); - querySignalsRoute(__legacy); - - // Detection Engine index routes that have the REST endpoints of /api/detection_engine/index - // All REST index creation, policy management for spaces - createIndexRoute(__legacy); - readIndexRoute(__legacy); - deleteIndexRoute(__legacy); - - // Detection Engine tags routes that have the REST endpoints of /api/detection_engine/tags - readTagsRoute(__legacy); - - // Privileges API to get the generic user privileges - readPrivilegesRoute(__legacy); -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts index 3aefb6c0e1e5f..210c97892e25c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/alerts/elasticseatch_adapter.test.ts @@ -29,7 +29,6 @@ describe('alerts elasticsearch_adapter', () => { return mockAlertsHistogramDataResponse; }); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts b/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts index 30fdf7520a3ed..0ab6f1a8df779 100644 --- a/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts +++ b/x-pack/legacy/plugins/siem/server/lib/compose/kibana.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, PluginInitializerContext } from '../../../../../../../src/core/server'; -import { PluginsSetup } from '../../plugin'; +import { CoreSetup, SetupPlugins } from '../../plugin'; import { Anomalies } from '../anomalies'; import { ElasticsearchAnomaliesAdapter } from '../anomalies/elasticsearch_adapter'; @@ -37,10 +36,10 @@ import { Alerts, ElasticsearchAlertsAdapter } from '../alerts'; export function compose( core: CoreSetup, - plugins: PluginsSetup, - env: PluginInitializerContext['env'] + plugins: SetupPlugins, + isProductionMode: boolean ): AppBackendLibs { - const framework = new KibanaBackendFrameworkAdapter(core, plugins, env); + const framework = new KibanaBackendFrameworkAdapter(core, plugins, isProductionMode); const sources = new Sources(new ConfigurationSourcesAdapter()); const sourceStatus = new SourceStatus(new ElasticsearchSourceStatusAdapter(framework)); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/create_bootstrap_index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/create_bootstrap_index.ts index dff6e7136bff2..253bccad2e9f8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/create_bootstrap_index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/create_bootstrap_index.ts @@ -4,18 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CallClusterOptions } from 'src/legacy/core_plugins/elasticsearch'; import { CallWithRequest } from '../types'; // See the reference(s) below on explanations about why -000001 was chosen and // why the is_write_index is true as well as the bootstrapping step which is needed. // Ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/applying-policy-to-template.html export const createBootstrapIndex = async ( - callWithRequest: CallWithRequest< - { path: string; method: 'PUT'; body: unknown }, - CallClusterOptions, - boolean - >, + callWithRequest: CallWithRequest<{ path: string; method: 'PUT'; body: unknown }, boolean>, index: string ): Promise => { return callWithRequest('transport.request', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_all_index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_all_index.ts index b1d8f994615ae..d165bf69f1da1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_all_index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_all_index.ts @@ -5,11 +5,10 @@ */ import { IndicesDeleteParams } from 'elasticsearch'; -import { CallClusterOptions } from 'src/legacy/core_plugins/elasticsearch'; import { CallWithRequest } from '../types'; export const deleteAllIndex = async ( - callWithRequest: CallWithRequest, + callWithRequest: CallWithRequest, index: string ): Promise => { return callWithRequest('indices.delete', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_policy.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_policy.ts index aa31c427ec84f..00213e271c7e8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_policy.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_policy.ts @@ -7,7 +7,7 @@ import { CallWithRequest } from '../types'; export const deletePolicy = async ( - callWithRequest: CallWithRequest<{ path: string; method: 'DELETE' }, {}, unknown>, + callWithRequest: CallWithRequest<{ path: string; method: 'DELETE' }, unknown>, policy: string ): Promise => { return callWithRequest('transport.request', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_template.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_template.ts index 63c32d13ccb8d..3402c25fb1ab1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_template.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/delete_template.ts @@ -5,11 +5,10 @@ */ import { IndicesDeleteTemplateParams } from 'elasticsearch'; -import { CallClusterOptions } from 'src/legacy/core_plugins/elasticsearch'; import { CallWithRequest } from '../types'; export const deleteTemplate = async ( - callWithRequest: CallWithRequest, + callWithRequest: CallWithRequest, name: string ): Promise => { return callWithRequest('indices.deleteTemplate', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_index_exists.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_index_exists.ts index 705f542b50124..d81f23a283451 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_index_exists.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_index_exists.ts @@ -9,7 +9,6 @@ import { CallWithRequest } from '../types'; export const getIndexExists = async ( callWithRequest: CallWithRequest< { index: string; size: number; terminate_after: number; allow_no_indices: boolean }, - {}, { _shards: { total: number } } >, index: string diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_policy_exists.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_policy_exists.ts index d5ab1a10180c0..8a54ceac8ab78 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_policy_exists.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_policy_exists.ts @@ -7,7 +7,7 @@ import { CallWithRequest } from '../types'; export const getPolicyExists = async ( - callWithRequest: CallWithRequest<{ path: string; method: 'GET' }, {}, unknown>, + callWithRequest: CallWithRequest<{ path: string; method: 'GET' }, unknown>, policy: string ): Promise => { try { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_template_exists.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_template_exists.ts index fac402155619e..fd5eec8db4140 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_template_exists.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/get_template_exists.ts @@ -5,11 +5,10 @@ */ import { IndicesExistsTemplateParams } from 'elasticsearch'; -import { CallClusterOptions } from 'src/legacy/core_plugins/elasticsearch'; import { CallWithRequest } from '../types'; export const getTemplateExists = async ( - callWithRequest: CallWithRequest, + callWithRequest: CallWithRequest, template: string ): Promise => { return callWithRequest('indices.existsTemplate', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/read_index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/read_index.ts index 0abe2b992b780..ca987f85c446c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/read_index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/read_index.ts @@ -5,11 +5,10 @@ */ import { IndicesGetSettingsParams } from 'elasticsearch'; -import { CallClusterOptions } from 'src/legacy/core_plugins/elasticsearch'; import { CallWithRequest } from '../types'; export const readIndex = async ( - callWithRequest: CallWithRequest, + callWithRequest: CallWithRequest, index: string ): Promise => { return callWithRequest('indices.get', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_policy.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_policy.ts index fae28bab749ca..90d5bf9a9871b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_policy.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_policy.ts @@ -7,7 +7,7 @@ import { CallWithRequest } from '../types'; export const setPolicy = async ( - callWithRequest: CallWithRequest<{ path: string; method: 'PUT'; body: unknown }, {}, unknown>, + callWithRequest: CallWithRequest<{ path: string; method: 'PUT'; body: unknown }, unknown>, policy: string, body: unknown ): Promise => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_template.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_template.ts index dc9ad5dda9f7d..0894f930feffb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_template.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/index/set_template.ts @@ -5,11 +5,10 @@ */ import { IndicesPutTemplateParams } from 'elasticsearch'; -import { CallClusterOptions } from 'src/legacy/core_plugins/elasticsearch'; import { CallWithRequest } from '../types'; export const setTemplate = async ( - callWithRequest: CallWithRequest, + callWithRequest: CallWithRequest, name: string, body: unknown ): Promise => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/privileges/read_privileges.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/privileges/read_privileges.ts index a93be40738e57..01819eb4703fb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/privileges/read_privileges.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/privileges/read_privileges.ts @@ -7,7 +7,7 @@ import { CallWithRequest } from '../types'; export const readPrivileges = async ( - callWithRequest: CallWithRequest, + callWithRequest: CallWithRequest<{}, unknown>, index: string ): Promise => { return callWithRequest('transport.request', { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts deleted file mode 100644 index 5b85012fd9f08..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Hapi from 'hapi'; -import { KibanaConfig } from 'src/legacy/server/kbn_server'; -import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch'; -import { savedObjectsClientMock } from '../../../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../../alerting/server/alerts_client.mock'; -import { actionsClientMock } from '../../../../../../../../plugins/actions/server/mocks'; -import { APP_ID, SIGNALS_INDEX_KEY } from '../../../../../common/constants'; -import { ServerFacade } from '../../../../types'; - -const defaultConfig = { - 'kibana.index': '.kibana', - [`xpack.${APP_ID}.${SIGNALS_INDEX_KEY}`]: '.siem-signals', -}; - -const isKibanaConfig = (config: unknown): config is KibanaConfig => - Object.getOwnPropertyDescriptor(config, 'get') != null && - Object.getOwnPropertyDescriptor(config, 'has') != null; - -const assertNever = (): never => { - throw new Error('Unexpected object'); -}; - -const createMockKibanaConfig = (config: Record): KibanaConfig => { - const returnConfig = { - get(key: string) { - return config[key]; - }, - has(key: string) { - return config[key] != null; - }, - }; - if (isKibanaConfig(returnConfig)) { - return returnConfig; - } else { - return assertNever(); - } -}; - -export const createMockServer = (config: Record = defaultConfig) => { - const server = new Hapi.Server({ - port: 0, - }); - - server.config = () => createMockKibanaConfig(config); - - const actionsClient = actionsClientMock.create(); - const alertsClient = alertsClientMock.create(); - const savedObjectsClient = savedObjectsClientMock.create(); - const elasticsearch = { - getCluster: jest.fn().mockImplementation(() => ({ - callWithRequest: jest.fn(), - })), - }; - server.decorate('request', 'getAlertsClient', () => alertsClient); - server.plugins.elasticsearch = (elasticsearch as unknown) as ElasticsearchPlugin; - server.plugins.spaces = { getSpaceId: () => 'default' }; - server.plugins.actions = { - getActionsClientWithRequest: () => actionsClient, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; // The types have really bad conflicts at the moment so I have to use any - server.decorate('request', 'getSavedObjectsClient', () => savedObjectsClient); - return { - server: server as ServerFacade & Hapi.Server, - alertsClient, - actionsClient, - elasticsearch, - savedObjectsClient, - }; -}; - -export const createMockServerWithoutAlertClientDecoration = ( - config: Record = defaultConfig -) => { - const serverWithoutAlertClient = new Hapi.Server({ - port: 0, - }); - - const savedObjectsClient = savedObjectsClientMock.create(); - serverWithoutAlertClient.config = () => createMockKibanaConfig(config); - serverWithoutAlertClient.decorate('request', 'getSavedObjectsClient', () => savedObjectsClient); - serverWithoutAlertClient.plugins.actions = { - getActionsClientWithRequest: () => actionsClient, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; // The types have really bad conflicts at the moment so I have to use any - - const actionsClient = actionsClientMock.create(); - - return { - serverWithoutAlertClient: serverWithoutAlertClient as ServerFacade & Hapi.Server, - actionsClient, - }; -}; - -export const getMockIndexName = () => - jest.fn().mockImplementation(() => ({ - callWithRequest: jest.fn().mockImplementationOnce(() => 'index-name'), - })); - -export const getMockEmptyIndex = () => - jest.fn().mockImplementation(() => ({ - callWithRequest: jest.fn().mockImplementation(() => ({ _shards: { total: 0 } })), - })); - -export const getMockNonEmptyIndex = () => - jest.fn().mockImplementation(() => ({ - callWithRequest: jest.fn().mockImplementation(() => ({ _shards: { total: 1 } })), - })); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts new file mode 100644 index 0000000000000..f89e938b8a636 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/clients_service_mock.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + elasticsearchServiceMock, + savedObjectsClientMock, +} from '../../../../../../../../../src/core/server/mocks'; +import { alertsClientMock } from '../../../../../../alerting/server/alerts_client.mock'; +import { ActionsClient } from '../../../../../../../../plugins/actions/server'; +import { actionsClientMock } from '../../../../../../../../plugins/actions/server/mocks'; +import { GetScopedClients } from '../../../../services'; + +const createClients = () => ({ + actionsClient: actionsClientMock.create() as jest.Mocked, + alertsClient: alertsClientMock.create(), + clusterClient: elasticsearchServiceMock.createScopedClusterClient(), + savedObjectsClient: savedObjectsClientMock.create(), + spacesClient: { getSpaceId: jest.fn() }, +}); + +const createGetScoped = () => + jest.fn(() => Promise.resolve(createClients()) as ReturnType); + +const createClientsServiceMock = () => { + return { + setup: jest.fn(), + start: jest.fn(), + createGetScoped, + }; +}; + +export const clientsServiceMock = { + create: createClientsServiceMock, + createGetScoped, + createClients, +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/index.ts new file mode 100644 index 0000000000000..250b006814294 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Hapi from 'hapi'; + +export { clientsServiceMock } from './clients_service_mock'; + +export const createMockServer = () => { + const server = new Hapi.Server({ port: 0 }); + + return { + route: server.route.bind(server), + inject: server.inject.bind(server), + }; +}; + +export const createMockConfig = () => () => ({ + get: jest.fn(), + has: jest.fn(), +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index b008ead8df948..f380b82c1e05f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -17,6 +17,7 @@ import { INTERNAL_IMMUTABLE_KEY, DETECTION_ENGINE_PREPACKAGED_URL, } from '../../../../../common/constants'; +import { ShardsResponse } from '../../../types'; import { RuleAlertType, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { RuleAlertParamsRest, PrepackagedRules } from '../../types'; @@ -413,3 +414,11 @@ export const getFindResultStatus = (): SavedObjectsFindResponse 'index-name'; +export const getEmptyIndex = (): { _shards: Partial } => ({ + _shards: { total: 0 }, +}); +export const getNonEmptyIndex = (): { _shards: Partial } => ({ + _shards: { total: 1 }, +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/create_index_route.ts index e0d48836013ec..2502009a2e6a2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/create_index_route.ts @@ -7,9 +7,9 @@ import Hapi from 'hapi'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; -import signalsPolicy from './signals_policy.json'; -import { ServerFacade, RequestFacade } from '../../../../types'; -import { transformError, getIndex, callWithRequestFactory } from '../utils'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; +import { transformError, getIndex } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; import { getPolicyExists } from '../../index/get_policy_exists'; import { setPolicy } from '../../index/set_policy'; @@ -17,8 +17,12 @@ import { setTemplate } from '../../index/set_template'; import { getSignalsTemplate } from './get_signals_template'; import { getTemplateExists } from '../../index/get_template_exists'; import { createBootstrapIndex } from '../../index/create_bootstrap_index'; +import signalsPolicy from './signals_policy.json'; -export const createCreateIndexRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createCreateIndexRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: DETECTION_ENGINE_INDEX_URL, @@ -30,11 +34,13 @@ export const createCreateIndexRoute = (server: ServerFacade): Hapi.ServerRoute = }, }, }, - async handler(request: RequestFacade, headers) { + async handler(request: LegacyRequest, headers) { try { - const index = getIndex(request, server); - const callWithRequest = callWithRequestFactory(request, server); - const indexExists = await getIndexExists(callWithRequest, index); + const { clusterClient, spacesClient } = await getClients(request); + const callCluster = clusterClient.callAsCurrentUser; + + const index = getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists(callCluster, index); if (indexExists) { return headers .response({ @@ -43,16 +49,16 @@ export const createCreateIndexRoute = (server: ServerFacade): Hapi.ServerRoute = }) .code(409); } else { - const policyExists = await getPolicyExists(callWithRequest, index); + const policyExists = await getPolicyExists(callCluster, index); if (!policyExists) { - await setPolicy(callWithRequest, index, signalsPolicy); + await setPolicy(callCluster, index, signalsPolicy); } - const templateExists = await getTemplateExists(callWithRequest, index); + const templateExists = await getTemplateExists(callCluster, index); if (!templateExists) { const template = getSignalsTemplate(index); - await setTemplate(callWithRequest, index, template); + await setTemplate(callCluster, index, template); } - await createBootstrapIndex(callWithRequest, index); + await createBootstrapIndex(callCluster, index); return { acknowledged: true }; } } catch (err) { @@ -68,6 +74,10 @@ export const createCreateIndexRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const createIndexRoute = (server: ServerFacade) => { - server.route(createCreateIndexRoute(server)); +export const createIndexRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +) => { + route(createCreateIndexRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts index c1edc824b81eb..ae61afb6f8d06 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/delete_index_route.ts @@ -7,8 +7,9 @@ import Hapi from 'hapi'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; -import { ServerFacade, RequestFacade } from '../../../../types'; -import { transformError, getIndex, callWithRequestFactory } from '../utils'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; +import { transformError, getIndex } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; import { getPolicyExists } from '../../index/get_policy_exists'; import { deletePolicy } from '../../index/delete_policy'; @@ -26,7 +27,10 @@ import { deleteTemplate } from '../../index/delete_template'; * * And ensuring they're all gone */ -export const createDeleteIndexRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createDeleteIndexRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'DELETE', path: DETECTION_ENGINE_INDEX_URL, @@ -38,11 +42,13 @@ export const createDeleteIndexRoute = (server: ServerFacade): Hapi.ServerRoute = }, }, }, - async handler(request: RequestFacade, headers) { + async handler(request: LegacyRequest, headers) { try { - const index = getIndex(request, server); - const callWithRequest = callWithRequestFactory(request, server); - const indexExists = await getIndexExists(callWithRequest, index); + const { clusterClient, spacesClient } = await getClients(request); + const callCluster = clusterClient.callAsCurrentUser; + + const index = getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists(callCluster, index); if (!indexExists) { return headers .response({ @@ -51,14 +57,14 @@ export const createDeleteIndexRoute = (server: ServerFacade): Hapi.ServerRoute = }) .code(404); } else { - await deleteAllIndex(callWithRequest, `${index}-*`); - const policyExists = await getPolicyExists(callWithRequest, index); + await deleteAllIndex(callCluster, `${index}-*`); + const policyExists = await getPolicyExists(callCluster, index); if (policyExists) { - await deletePolicy(callWithRequest, index); + await deletePolicy(callCluster, index); } - const templateExists = await getTemplateExists(callWithRequest, index); + const templateExists = await getTemplateExists(callCluster, index); if (templateExists) { - await deleteTemplate(callWithRequest, index); + await deleteTemplate(callCluster, index); } return { acknowledged: true }; } @@ -75,6 +81,10 @@ export const createDeleteIndexRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const deleteIndexRoute = (server: ServerFacade) => { - server.route(createDeleteIndexRoute(server)); +export const deleteIndexRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +) => { + route(createDeleteIndexRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts index 1a5018d446747..41be42f7c0fe1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/read_index_route.ts @@ -7,11 +7,15 @@ import Hapi from 'hapi'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; -import { ServerFacade, RequestFacade } from '../../../../types'; -import { transformError, getIndex, callWithRequestFactory } from '../utils'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; +import { transformError, getIndex } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; -export const createReadIndexRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createReadIndexRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'GET', path: DETECTION_ENGINE_INDEX_URL, @@ -23,11 +27,14 @@ export const createReadIndexRoute = (server: ServerFacade): Hapi.ServerRoute => }, }, }, - async handler(request: RequestFacade, headers) { + async handler(request: LegacyRequest, headers) { try { - const index = getIndex(request, server); - const callWithRequest = callWithRequestFactory(request, server); - const indexExists = await getIndexExists(callWithRequest, index); + const { clusterClient, spacesClient } = await getClients(request); + const callCluster = clusterClient.callAsCurrentUser; + + const index = getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists(callCluster, index); + if (indexExists) { // head request is used for if you want to get if the index exists // or not and it will return a content-length: 0 along with either a 200 or 404 @@ -62,6 +69,10 @@ export const createReadIndexRoute = (server: ServerFacade): Hapi.ServerRoute => }; }; -export const readIndexRoute = (server: ServerFacade) => { - server.route(createReadIndexRoute(server)); +export const readIndexRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +) => { + route(createReadIndexRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index 1ea681afb7949..308ee95a77e20 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -4,35 +4,38 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../__mocks__/_mock_server'; -import { getPrivilegeRequest, getMockPrivileges } from '../__mocks__/request_responses'; import { readPrivilegesRoute } from './read_privileges_route'; -import * as myUtils from '../utils'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; +import { getPrivilegeRequest, getMockPrivileges } from '../__mocks__/request_responses'; describe('read_privileges', () => { - let { server, elasticsearch } = createMockServer(); + let { route, inject } = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { - jest.spyOn(myUtils, 'getIndex').mockReturnValue('fakeindex'); - ({ server, elasticsearch } = createMockServer()); - elasticsearch.getCluster = jest.fn(() => ({ - callWithRequest: jest.fn(() => getMockPrivileges()), - })); - readPrivilegesRoute(server); - }); - - afterEach(() => { jest.resetAllMocks(); + ({ route, inject } = createMockServer()); + + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getMockPrivileges()); + + readPrivilegesRoute(route, config, false, getClients); }); describe('normal status codes', () => { test('returns 200 when doing a normal request', async () => { - const { statusCode } = await server.inject(getPrivilegeRequest()); + const { statusCode } = await inject(getPrivilegeRequest()); expect(statusCode).toBe(200); }); test('returns the payload when doing a normal request', async () => { - const { payload } = await server.inject(getPrivilegeRequest()); + const { payload } = await inject(getPrivilegeRequest()); expect(JSON.parse(payload)).toEqual(getMockPrivileges()); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 45ecb7dc97288..e9b9bffbaf054 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -6,13 +6,19 @@ import Hapi from 'hapi'; import { merge } from 'lodash/fp'; + import { DETECTION_ENGINE_PRIVILEGES_URL } from '../../../../../common/constants'; +import { LegacyServices } from '../../../../types'; import { RulesRequest } from '../../rules/types'; -import { ServerFacade } from '../../../../types'; -import { callWithRequestFactory, transformError, getIndex } from '../utils'; +import { GetScopedClients } from '../../../../services'; +import { transformError, getIndex } from '../utils'; import { readPrivileges } from '../../privileges/read_privileges'; -export const createReadPrivilegesRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createReadPrivilegesRulesRoute = ( + config: LegacyServices['config'], + usingEphemeralEncryptionKey: boolean, + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'GET', path: DETECTION_ENGINE_PRIVILEGES_URL, @@ -26,10 +32,10 @@ export const createReadPrivilegesRulesRoute = (server: ServerFacade): Hapi.Serve }, async handler(request: RulesRequest, headers) { try { - const callWithRequest = callWithRequestFactory(request, server); - const index = getIndex(request, server); - const permissions = await readPrivileges(callWithRequest, index); - const usingEphemeralEncryptionKey = server.usingEphemeralEncryptionKey; + const { clusterClient, spacesClient } = await getClients(request); + + const index = getIndex(spacesClient.getSpaceId, config); + const permissions = await readPrivileges(clusterClient.callAsCurrentUser, index); return merge(permissions, { is_authenticated: request?.auth?.isAuthenticated ?? false, has_encryption_key: !usingEphemeralEncryptionKey, @@ -47,6 +53,11 @@ export const createReadPrivilegesRulesRoute = (server: ServerFacade): Hapi.Serve }; }; -export const readPrivilegesRoute = (server: ServerFacade): void => { - server.route(createReadPrivilegesRulesRoute(server)); +export const readPrivilegesRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + usingEphemeralEncryptionKey: boolean, + getClients: GetScopedClients +) => { + route(createReadPrivilegesRulesRoute(config, usingEphemeralEncryptionKey, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index ec86de84ff3c7..e018ed4cc22ff 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -4,12 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, - getMockEmptyIndex, - getMockNonEmptyIndex, -} from '../__mocks__/_mock_server'; +import { omit } from 'lodash/fp'; + import { createRulesRoute } from './create_rules_route'; import { getFindResult, @@ -17,7 +13,10 @@ import { createActionResult, addPrepackagedRulesRequest, getFindResultWithSingleHit, + getEmptyIndex, + getNonEmptyIndex, } from '../__mocks__/request_responses'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; jest.mock('../../rules/get_prepackaged_rules', () => { return { @@ -48,45 +47,56 @@ import { addPrepackedRulesRoute } from './add_prepackaged_rules_route'; import { PrepackagedRules } from '../../types'; describe('add_prepackaged_rules_route', () => { - let { server, alertsClient, actionsClient, elasticsearch } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient, elasticsearch } = createMockServer()); - elasticsearch.getCluster = getMockNonEmptyIndex(); - addPrepackedRulesRoute(server); + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getNonEmptyIndex()); + + addPrepackedRulesRoute(server.route, config, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when creating a with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { statusCode } = await server.inject(addPrepackagedRulesRequest()); expect(statusCode).toBe(200); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - createRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(addPrepackagedRulesRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { inject, route } = createMockServer(); + createRulesRoute(route, config, getClients); + const { statusCode } = await inject(addPrepackagedRulesRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('it returns a 400 if the index does not exist', async () => { - elasticsearch.getCluster = getMockEmptyIndex(); - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(addPrepackagedRulesRequest()); expect(JSON.parse(payload)).toEqual({ - message: - 'Pre-packaged rules cannot be installed until the space index is created: .siem-signals-default', + message: expect.stringContaining( + 'Pre-packaged rules cannot be installed until the space index is created' + ), status_code: 400, }); }); @@ -94,10 +104,10 @@ describe('add_prepackaged_rules_route', () => { describe('payload', () => { test('1 rule is installed and 0 are updated when find results are empty', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(addPrepackagedRulesRequest()); expect(JSON.parse(payload)).toEqual({ rules_installed: 1, @@ -106,10 +116,10 @@ describe('add_prepackaged_rules_route', () => { }); test('1 rule is updated and 0 are installed when we return a single find and the versions are different', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(addPrepackagedRulesRequest()); expect(JSON.parse(payload)).toEqual({ rules_installed: 0, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index e796f21d9c03a..c4d0489486ef8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -5,21 +5,23 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; -import { ServerFacade, RequestFacade } from '../../../../types'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { getIndexExists } from '../../index/get_index_exists'; -import { callWithRequestFactory, getIndex, transformError } from '../utils'; +import { getIndex, transformError } from '../utils'; import { getPrepackagedRules } from '../../rules/get_prepackaged_rules'; import { installPrepackagedRules } from '../../rules/install_prepacked_rules'; import { updatePrepackagedRules } from '../../rules/update_prepacked_rules'; import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; -export const createAddPrepackedRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createAddPrepackedRulesRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'PUT', path: DETECTION_ENGINE_PREPACKAGED_URL, @@ -31,29 +33,32 @@ export const createAddPrepackedRulesRoute = (server: ServerFacade): Hapi.ServerR }, }, }, - async handler(request: RequestFacade, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } - + async handler(request: LegacyRequest, headers) { try { - const callWithRequest = callWithRequestFactory(request, server); + const { + actionsClient, + alertsClient, + clusterClient, + savedObjectsClient, + spacesClient, + } = await getClients(request); + + if (!actionsClient || !alertsClient) { + return headers.response().code(404); + } + const rulesFromFileSystem = getPrepackagedRules(); const prepackagedRules = await getExistingPrepackagedRules({ alertsClient }); const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackagedRules); const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackagedRules); - const spaceIndex = getIndex(request, server); + const spaceIndex = getIndex(spacesClient.getSpaceId, config); if (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0) { - const spaceIndexExists = await getIndexExists(callWithRequest, spaceIndex); + const spaceIndexExists = await getIndexExists( + clusterClient.callAsCurrentUser, + spaceIndex + ); if (!spaceIndexExists) { return headers .response({ @@ -90,6 +95,10 @@ export const createAddPrepackedRulesRoute = (server: ServerFacade): Hapi.ServerR }; }; -export const addPrepackedRulesRoute = (server: ServerFacade): void => { - server.route(createAddPrepackedRulesRoute(server)); +export const addPrepackedRulesRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +): void => { + route(createAddPrepackedRulesRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index f1169442484c6..664d27a7572ad 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -4,59 +4,66 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, - getMockEmptyIndex, -} from '../__mocks__/_mock_server'; -import { createRulesRoute } from './create_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; + import { getFindResult, getResult, createActionResult, typicalPayload, getReadBulkRequest, + getEmptyIndex, } from '../__mocks__/request_responses'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { createRulesBulkRoute } from './create_rules_bulk_route'; import { BulkError } from '../utils'; import { OutputRuleAlertRest } from '../../types'; describe('create_rules_bulk', () => { - let { server, alertsClient, actionsClient, elasticsearch } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient, elasticsearch } = createMockServer()); - createRulesBulkRoute(server); + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + getClients.mockResolvedValue(clients); + + createRulesBulkRoute(server.route, config, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when creating a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getReadBulkRequest()); expect(statusCode).toBe(200); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - createRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getReadBulkRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { inject, route } = createMockServer(); + createRulesBulkRoute(route, config, getClients); + const { statusCode } = await inject(getReadBulkRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('it gets a 409 if the index does not exist', async () => { - elasticsearch.getCluster = getMockEmptyIndex(); - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(getReadBulkRequest()); expect(JSON.parse(payload)).toEqual([ { @@ -71,10 +78,10 @@ describe('create_rules_bulk', () => { }); test('returns 200 if rule_id is not given as the id is auto generated from the alert framework', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); // missing rule_id should return 200 as it will be auto generated if not given const { rule_id, ...noRuleId } = typicalPayload(); const request: ServerInjectOptions = { @@ -87,10 +94,10 @@ describe('create_rules_bulk', () => { }); test('returns 200 if type is query', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', @@ -107,10 +114,10 @@ describe('create_rules_bulk', () => { }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', @@ -128,10 +135,10 @@ describe('create_rules_bulk', () => { }); test('returns 409 if duplicate rule_ids found in request payload', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'POST', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, @@ -143,10 +150,10 @@ describe('create_rules_bulk', () => { }); test('returns one error object in response when duplicate rule_ids found in request payload', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'POST', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index e7145d2a6f055..51b7b132fc794 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -5,25 +5,24 @@ */ import Hapi from 'hapi'; -import { isFunction, countBy } from 'lodash/fp'; +import { countBy } from 'lodash/fp'; import uuid from 'uuid'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { GetScopedClients } from '../../../../services'; +import { LegacyServices } from '../../../../types'; import { createRules } from '../../rules/create_rules'; import { BulkRulesRequest } from '../../rules/types'; -import { ServerFacade } from '../../../../types'; import { readRules } from '../../rules/read_rules'; import { transformOrBulkError, getDuplicates } from './utils'; import { getIndexExists } from '../../index/get_index_exists'; -import { - callWithRequestFactory, - getIndex, - transformBulkError, - createBulkErrorObject, -} from '../utils'; +import { getIndex, transformBulkError, createBulkErrorObject } from '../utils'; import { createRulesBulkSchema } from '../schemas/create_rules_bulk_schema'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; -export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createCreateRulesBulkRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, @@ -37,14 +36,11 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou }, }, async handler(request: BulkRulesRequest, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) + const { actionsClient, alertsClient, clusterClient, spacesClient } = await getClients( + request ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { + + if (!actionsClient || !alertsClient) { return headers.response().code(404); } @@ -85,9 +81,8 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou } = payloadRule; const ruleIdOrUuid = ruleId ?? uuid.v4(); try { - const finalIndex = outputIndex != null ? outputIndex : getIndex(request, server); - const callWithRequest = callWithRequestFactory(request, server); - const indexExists = await getIndexExists(callWithRequest, finalIndex); + const finalIndex = outputIndex ?? getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, finalIndex); if (!indexExists) { return createBulkErrorObject({ ruleId: ruleIdOrUuid, @@ -155,6 +150,10 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou }; }; -export const createRulesBulkRoute = (server: ServerFacade): void => { - server.route(createCreateRulesBulkRoute(server)); +export const createRulesBulkRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +): void => { + route(createCreateRulesBulkRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index e51634c0d2c07..4f28771db8ed7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, - getMockNonEmptyIndex, - getMockEmptyIndex, -} from '../__mocks__/_mock_server'; -import { createRulesRoute } from './create_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; + +import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { createRulesRoute } from './create_rules_route'; import { getFindResult, @@ -20,57 +17,58 @@ import { getCreateRequest, typicalPayload, getFindResultStatus, + getNonEmptyIndex, + getEmptyIndex, } from '../__mocks__/request_responses'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; describe('create_rules', () => { - let { - server, - alertsClient, - actionsClient, - elasticsearch, - savedObjectsClient, - } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ - server, - alertsClient, - actionsClient, - elasticsearch, - savedObjectsClient, - } = createMockServer()); - elasticsearch.getCluster = getMockNonEmptyIndex(); - createRulesRoute(server); + + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getNonEmptyIndex()); + + createRulesRoute(server.route, config, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when creating a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getCreateRequest()); expect(statusCode).toBe(200); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - createRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getCreateRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + createRulesRoute(route, config, getClients); + const { statusCode } = await inject(getCreateRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('it returns a 400 if the index does not exist', async () => { - elasticsearch.getCluster = getMockEmptyIndex(); - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getEmptyIndex()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(getCreateRequest()); expect(JSON.parse(payload)).toEqual({ message: 'To create a rule, the index must exist first. Index .siem-signals does not exist', @@ -79,11 +77,11 @@ describe('create_rules', () => { }); test('returns 200 if rule_id is not given as the id is auto generated from the alert framework', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); // missing rule_id should return 200 as it will be auto generated if not given const { rule_id, ...noRuleId } = typicalPayload(); const request: ServerInjectOptions = { @@ -96,11 +94,11 @@ describe('create_rules', () => { }); test('returns 200 if type is query', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', @@ -115,11 +113,11 @@ describe('create_rules', () => { }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index de874f66d0444..19e772165628d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -5,21 +5,24 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import uuid from 'uuid'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { GetScopedClients } from '../../../../services'; +import { LegacyServices } from '../../../../types'; import { createRules } from '../../rules/create_rules'; import { RulesRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { createRulesSchema } from '../schemas/create_rules_schema'; -import { ServerFacade } from '../../../../types'; import { readRules } from '../../rules/read_rules'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; import { transform } from './utils'; import { getIndexExists } from '../../index/get_index_exists'; -import { callWithRequestFactory, getIndex, transformError } from '../utils'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; +import { getIndex, transformError } from '../utils'; -export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createCreateRulesRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: DETECTION_ENGINE_RULES_URL, @@ -59,21 +62,21 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = type, references, } = request.payload; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } - try { - const finalIndex = outputIndex != null ? outputIndex : getIndex(request, server); - const callWithRequest = callWithRequestFactory(request, server); - const indexExists = await getIndexExists(callWithRequest, finalIndex); + const { + alertsClient, + actionsClient, + clusterClient, + savedObjectsClient, + spacesClient, + } = await getClients(request); + + if (!actionsClient || !alertsClient) { + return headers.response().code(404); + } + + const finalIndex = outputIndex ?? getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists(clusterClient.callAsCurrentUser, finalIndex); if (!indexExists) { return headers .response({ @@ -157,6 +160,10 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const createRulesRoute = (server: ServerFacade): void => { - server.route(createCreateRulesRoute(server)); +export const createRulesRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +): void => { + route(createCreateRulesRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index e66fc765c08bf..855bf7f634c26 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; +import { omit } from 'lodash/fp'; import { ServerInjectOptions } from 'hapi'; import { @@ -20,70 +17,75 @@ import { getDeleteAsPostBulkRequestById, getFindResultStatus, } from '../__mocks__/request_responses'; +import { createMockServer, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { deleteRulesBulkRoute } from './delete_rules_bulk_route'; import { BulkError } from '../utils'; describe('delete_rules', () => { - let { server, alertsClient, savedObjectsClient } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { - ({ server, alertsClient, savedObjectsClient } = createMockServer()); - deleteRulesBulkRoute(server); - }); - - afterEach(() => { jest.resetAllMocks(); + + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + deleteRulesBulkRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteBulkRequest()); expect(statusCode).toBe(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId using POST', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteAsPostBulkRequest()); expect(statusCode).toBe(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteBulkRequestById()); expect(statusCode).toBe(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id using POST', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteAsPostBulkRequestById()); expect(statusCode).toBe(200); }); test('returns 200 because the error is in the payload when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteBulkRequest()); expect(statusCode).toBe(200); }); test('returns 404 in the payload when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); - savedObjectsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue({}); const { payload } = await server.inject(getDeleteBulkRequest()); const parsed: BulkError[] = JSON.parse(payload); const expected: BulkError[] = [ @@ -96,18 +98,19 @@ describe('delete_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - deleteRulesBulkRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getDeleteBulkRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + deleteRulesBulkRoute(route, getClients); + const { statusCode } = await inject(getDeleteBulkRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if given a non-existent id in the payload', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const request: ServerInjectOptions = { method: 'DELETE', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index b3f8eafa24115..6438318cb43db 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -5,19 +5,18 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; -import { deleteRules } from '../../rules/delete_rules'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { queryRulesBulkSchema } from '../schemas/query_rules_bulk_schema'; import { transformOrBulkError, getIdBulkError } from './utils'; import { transformBulkError } from '../utils'; import { QueryBulkRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; +import { deleteRules } from '../../rules/delete_rules'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; -export const createDeleteRulesBulkRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createDeleteRulesBulkRoute = (getClients: GetScopedClients): Hapi.ServerRoute => { return { method: ['POST', 'DELETE'], // allow both POST and DELETE in case their client does not support bodies in DELETE path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, @@ -31,14 +30,9 @@ export const createDeleteRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou }, }, async handler(request: QueryBulkRequest, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { + const { actionsClient, alertsClient, savedObjectsClient } = await getClients(request); + + if (!actionsClient || !alertsClient) { return headers.response().code(404); } const rules = await Promise.all( @@ -78,6 +72,9 @@ export const createDeleteRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou }; }; -export const deleteRulesBulkRoute = (server: ServerFacade): void => { - server.route(createDeleteRulesBulkRoute(server)); +export const deleteRulesBulkRoute = ( + route: LegacyServices['route'], + getClients: GetScopedClients +): void => { + route(createDeleteRulesBulkRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index 0aa60d3bbd922..a0a6f61223279 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -4,13 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; - -import { deleteRulesRoute } from './delete_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; +import { deleteRulesRoute } from './delete_rules_route'; import { getFindResult, @@ -20,64 +16,70 @@ import { getDeleteRequestById, getFindResultStatus, } from '../__mocks__/request_responses'; +import { createMockServer, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; describe('delete_rules', () => { - let { server, alertsClient, savedObjectsClient } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { - ({ server, alertsClient, savedObjectsClient } = createMockServer()); - deleteRulesRoute(server); - }); - - afterEach(() => { jest.resetAllMocks(); + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + + deleteRulesRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); - savedObjectsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteRequest()); expect(statusCode).toBe(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); - savedObjectsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteRequestById()); expect(statusCode).toBe(200); }); test('returns 404 when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); - savedObjectsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.savedObjectsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - deleteRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getDeleteRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + deleteRulesRoute(route, getClients); + const { statusCode } = await inject(getDeleteRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if given a non-existent id', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); const request: ServerInjectOptions = { method: 'DELETE', url: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts index e4d3787c90072..340782523b724 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -5,19 +5,18 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { deleteRules } from '../../rules/delete_rules'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { queryRulesSchema } from '../schemas/query_rules_schema'; import { getIdError, transform } from './utils'; import { transformError } from '../utils'; import { QueryRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; -export const createDeleteRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createDeleteRulesRoute = (getClients: GetScopedClients): Hapi.ServerRoute => { return { method: 'DELETE', path: DETECTION_ENGINE_RULES_URL, @@ -30,20 +29,16 @@ export const createDeleteRulesRoute = (server: ServerFacade): Hapi.ServerRoute = query: queryRulesSchema, }, }, - async handler(request: QueryRequest, headers) { + async handler(request: QueryRequest & LegacyRequest, headers) { const { id, rule_id: ruleId } = request.query; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } try { + const { actionsClient, alertsClient, savedObjectsClient } = await getClients(request); + + if (!actionsClient || !alertsClient) { + return headers.response().code(404); + } + const rule = await deleteRules({ actionsClient, alertsClient, @@ -95,6 +90,9 @@ export const createDeleteRulesRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const deleteRulesRoute = (server: ServerFacade): void => { - server.route(createDeleteRulesRoute(server)); +export const deleteRulesRoute = ( + route: LegacyServices['route'], + getClients: GetScopedClients +): void => { + route(createDeleteRulesRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts index 5da5ffcd58bf1..1966b06701803 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -5,17 +5,21 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { ExportRulesRequest } from '../../rules/types'; -import { ServerFacade } from '../../../../types'; import { getNonPackagedRulesCount } from '../../rules/get_existing_prepackaged_rules'; import { exportRulesSchema, exportRulesQuerySchema } from '../schemas/export_rules_schema'; import { getExportByObjectIds } from '../../rules/get_export_by_object_ids'; import { getExportAll } from '../../rules/get_export_all'; import { transformError } from '../utils'; -export const createExportRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createExportRulesRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: `${DETECTION_ENGINE_RULES_URL}/_export`, @@ -29,15 +33,15 @@ export const createExportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = query: exportRulesQuerySchema, }, }, - async handler(request: ExportRulesRequest, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; + async handler(request: ExportRulesRequest & LegacyRequest, headers) { + const { alertsClient } = await getClients(request); if (!alertsClient) { return headers.response().code(404); } try { - const exportSizeLimit = server.config().get('savedObjects.maxImportExportSize'); + const exportSizeLimit = config().get('savedObjects.maxImportExportSize'); if (request.payload?.objects != null && request.payload.objects.length > exportSizeLimit) { return headers .response({ @@ -82,6 +86,10 @@ export const createExportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const exportRulesRoute = (server: ServerFacade): void => { - server.route(createExportRulesRoute(server)); +export const exportRulesRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +): void => { + route(createExportRulesRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 62c9f44da1e33..5b75f17164acf 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; +import { omit } from 'lodash/fp'; + +import { createMockServer } from '../__mocks__'; +import { clientsServiceMock } from '../__mocks__/clients_service_mock'; import { findRulesRoute } from './find_rules_route'; import { ServerInjectOptions } from 'hapi'; @@ -16,43 +16,49 @@ import { getFindResult, getResult, getFindRequest } from '../__mocks__/request_r import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; describe('find_rules', () => { - let { server, alertsClient, actionsClient } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { - ({ server, alertsClient, actionsClient } = createMockServer()); - findRulesRoute(server); - }); - - afterEach(() => { jest.resetAllMocks(); + + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + + findRulesRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when finding a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.find.mockResolvedValue({ + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.find.mockResolvedValue({ page: 1, perPage: 1, total: 0, data: [], }); - alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getFindRequest()); expect(statusCode).toBe(200); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - findRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getFindRequest()); + const { route, inject } = createMockServer(); + getClients.mockResolvedValue(omit('alertsClient', clients)); + findRulesRoute(route, getClients); + const { statusCode } = await inject(getFindRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if a bad query parameter is given', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'GET', url: `${DETECTION_ENGINE_RULES_URL}/_find?invalid_value=500`, @@ -62,8 +68,8 @@ describe('find_rules', () => { }); test('returns 200 if the set of optional query parameters are given', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'GET', url: `${DETECTION_ENGINE_RULES_URL}/_find?page=2&per_page=20&sort_field=timestamp&fields=["field-1","field-2","field-3]`, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts index b15c1db7222cf..4297e4aebfd58 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -5,17 +5,17 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { findRules } from '../../rules/find_rules'; import { FindRulesRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { findRulesSchema } from '../schemas/find_rules_schema'; -import { ServerFacade } from '../../../../types'; import { transformFindAlerts } from './utils'; import { transformError } from '../utils'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -export const createFindRulesRoute = (): Hapi.ServerRoute => { +export const createFindRulesRoute = (getClients: GetScopedClients): Hapi.ServerRoute => { return { method: 'GET', path: `${DETECTION_ENGINE_RULES_URL}/_find`, @@ -28,17 +28,14 @@ export const createFindRulesRoute = (): Hapi.ServerRoute => { query: findRulesSchema, }, }, - async handler(request: FindRulesRequest, headers) { + async handler(request: FindRulesRequest & LegacyRequest, headers) { const { query } = request; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } - try { + const { alertsClient, savedObjectsClient } = await getClients(request); + if (!alertsClient) { + return headers.response().code(404); + } + const rules = await findRules({ alertsClient, perPage: query.per_page, @@ -86,6 +83,6 @@ export const createFindRulesRoute = (): Hapi.ServerRoute => { }; }; -export const findRulesRoute = (server: ServerFacade) => { - server.route(createFindRulesRoute()); +export const findRulesRoute = (route: LegacyServices['route'], getClients: GetScopedClients) => { + route(createFindRulesRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts index 8b3113a044b5a..fe8742ff0b60c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_status_route.ts @@ -5,10 +5,11 @@ */ import Hapi from 'hapi'; -import { isFunction, snakeCase } from 'lodash/fp'; +import { snakeCase } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { findRulesStatusesSchema } from '../schemas/find_rules_statuses_schema'; import { FindRulesStatusesRequest, @@ -29,7 +30,7 @@ const convertToSnakeCase = >(obj: T): Partial | }, {}); }; -export const createFindRulesStatusRoute: Hapi.ServerRoute = { +export const createFindRulesStatusRoute = (getClients: GetScopedClients): Hapi.ServerRoute => ({ method: 'GET', path: `${DETECTION_ENGINE_RULES_URL}/_find_statuses`, options: { @@ -41,19 +42,17 @@ export const createFindRulesStatusRoute: Hapi.ServerRoute = { query: findRulesStatusesSchema, }, }, - async handler(request: FindRulesStatusesRequest, headers) { + async handler(request: FindRulesStatusesRequest & LegacyRequest, headers) { const { query } = request; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { + const { alertsClient, savedObjectsClient } = await getClients(request); + + if (!alertsClient) { return headers.response().code(404); } // build return object with ids as keys and errors as values. /* looks like this - { + { "someAlertId": [{"myerrorobject": "some error value"}, etc..], "anotherAlertId": ... } @@ -86,8 +85,11 @@ export const createFindRulesStatusRoute: Hapi.ServerRoute = { }, Promise.resolve({})); return statuses; }, -}; +}); -export const findRulesStatusesRoute = (server: ServerFacade): void => { - server.route(createFindRulesStatusRoute); +export const findRulesStatusesRoute = ( + route: LegacyServices['route'], + getClients: GetScopedClients +): void => { + route(createFindRulesStatusRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts index de7f0fe26cc74..8f27910a7e5e2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts @@ -4,19 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, - getMockNonEmptyIndex, -} from '../__mocks__/_mock_server'; -import { createRulesRoute } from './create_rules_route'; +import { omit } from 'lodash/fp'; + +import { getPrepackagedRulesStatusRoute } from './get_prepackaged_rules_status_route'; + import { getFindResult, getResult, createActionResult, getFindResultWithSingleHit, getPrepackagedRulesStatusRequest, + getNonEmptyIndex, } from '../__mocks__/request_responses'; +import { createMockServer, clientsServiceMock } from '../__mocks__'; jest.mock('../../rules/get_prepackaged_rules', () => { return { @@ -41,44 +41,49 @@ jest.mock('../../rules/get_prepackaged_rules', () => { }; }); -import { getPrepackagedRulesStatusRoute } from './get_prepackaged_rules_status_route'; - describe('get_prepackaged_rule_status_route', () => { - let { server, alertsClient, actionsClient, elasticsearch } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient, elasticsearch } = createMockServer()); - elasticsearch.getCluster = getMockNonEmptyIndex(); - getPrepackagedRulesStatusRoute(server); + + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getNonEmptyIndex()); + + getPrepackagedRulesStatusRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when creating a with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getPrepackagedRulesStatusRequest()); expect(statusCode).toBe(200); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - createRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject( - getPrepackagedRulesStatusRequest() - ); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + getPrepackagedRulesStatusRoute(route, getClients); + const { statusCode } = await inject(getPrepackagedRulesStatusRequest()); expect(statusCode).toBe(404); }); }); describe('payload', () => { test('0 rules installed, 0 custom rules, 1 rules not installed, and 1 rule not updated', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(getPrepackagedRulesStatusRequest()); expect(JSON.parse(payload)).toEqual({ rules_custom_installed: 0, @@ -89,10 +94,10 @@ describe('get_prepackaged_rule_status_route', () => { }); test('1 rule installed, 1 custom rules, 0 rules not installed, and 1 rule to not updated', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.create.mockResolvedValue(createActionResult()); + clients.alertsClient.create.mockResolvedValue(getResult()); const { payload } = await server.inject(getPrepackagedRulesStatusRequest()); expect(JSON.parse(payload)).toEqual({ rules_custom_installed: 1, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index c999292ba7674..bee57d6b38127 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -5,10 +5,10 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; -import { ServerFacade, RequestFacade } from '../../../../types'; +import { LegacyServices, LegacyRequest } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { transformError } from '../utils'; import { getPrepackagedRules } from '../../rules/get_prepackaged_rules'; import { getRulesToInstall } from '../../rules/get_rules_to_install'; @@ -16,7 +16,9 @@ import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { findRules } from '../../rules/find_rules'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => { +export const createGetPrepackagedRulesStatusRoute = ( + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'GET', path: `${DETECTION_ENGINE_PREPACKAGED_URL}/_status`, @@ -28,8 +30,8 @@ export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => { }, }, }, - async handler(request: RequestFacade, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; + async handler(request: LegacyRequest, headers) { + const { alertsClient } = await getClients(request); if (!alertsClient) { return headers.response().code(404); @@ -67,6 +69,9 @@ export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => { }; }; -export const getPrepackagedRulesStatusRoute = (server: ServerFacade): void => { - server.route(createGetPrepackagedRulesStatusRoute()); +export const getPrepackagedRulesStatusRoute = ( + route: LegacyServices['route'], + getClients: GetScopedClients +): void => { + route(createGetPrepackagedRulesStatusRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index 5e87c99d815ef..a9188cc2adc12 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -5,40 +5,39 @@ */ import Hapi from 'hapi'; -import { chunk, isEmpty, isFunction } from 'lodash/fp'; +import { chunk, isEmpty } from 'lodash/fp'; import { extname } from 'path'; import uuid from 'uuid'; + import { createPromiseFromStreams } from '../../../../../../../../../src/legacy/utils/streams'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { LegacyServices, LegacyRequest } from '../../../../types'; import { createRules } from '../../rules/create_rules'; import { ImportRulesRequest } from '../../rules/types'; -import { ServerFacade } from '../../../../types'; import { readRules } from '../../rules/read_rules'; import { getIndexExists } from '../../index/get_index_exists'; -import { - callWithRequestFactory, - getIndex, - createBulkErrorObject, - ImportRuleResponse, -} from '../utils'; +import { getIndex, createBulkErrorObject, ImportRuleResponse } from '../utils'; import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson'; import { ImportRuleAlertRest } from '../../types'; import { patchRules } from '../../rules/patch_rules'; import { importRulesQuerySchema, importRulesPayloadSchema } from '../schemas/import_rules_schema'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; +import { GetScopedClients } from '../../../../services'; type PromiseFromStreams = ImportRuleAlertRest | Error; const CHUNK_PARSED_OBJECT_SIZE = 10; -export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createImportRulesRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: `${DETECTION_ENGINE_RULES_URL}/_import`, options: { tags: ['access:siem'], payload: { - maxBytes: server.config().get('savedObjects.maxImportPayloadBytes'), + maxBytes: config().get('savedObjects.maxImportPayloadBytes'), output: 'stream', allow: 'multipart/form-data', }, @@ -50,17 +49,19 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = payload: importRulesPayloadSchema, }, }, - async handler(request: ImportRulesRequest, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { + async handler(request: ImportRulesRequest & LegacyRequest, headers) { + const { + actionsClient, + alertsClient, + clusterClient, + spacesClient, + savedObjectsClient, + } = await getClients(request); + + if (!actionsClient || !alertsClient) { return headers.response().code(404); } + const { filename } = request.payload.file.hapi; const fileExtension = extname(filename).toLowerCase(); if (fileExtension !== '.ndjson') { @@ -72,7 +73,7 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = .code(400); } - const objectLimit = server.config().get('savedObjects.maxImportExportSize'); + const objectLimit = config().get('savedObjects.maxImportExportSize'); const readStream = createRulesStreamFromNdJson(request.payload.file, objectLimit); const parsedObjects = await createPromiseFromStreams([readStream]); const uniqueParsedObjects = Array.from( @@ -146,9 +147,11 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = version, } = parsedRule; try { - const finalIndex = getIndex(request, server); - const callWithRequest = callWithRequestFactory(request, server); - const indexExists = await getIndexExists(callWithRequest, finalIndex); + const finalIndex = getIndex(spacesClient.getSpaceId, config); + const indexExists = await getIndexExists( + clusterClient.callAsCurrentUser, + finalIndex + ); if (!indexExists) { resolve( createBulkErrorObject({ @@ -261,6 +264,10 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const importRulesRoute = (server: ServerFacade): void => { - server.route(createImportRulesRoute(server)); +export const importRulesRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +): void => { + route(createImportRulesRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts index aa0dd04786a2e..02af4135b534f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk.test.ts @@ -4,13 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; - -import { patchRulesRoute } from './patch_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { patchRulesRoute } from './patch_rules_route'; +import { omit } from 'lodash/fp'; import { getFindResult, @@ -20,43 +16,51 @@ import { getFindResultWithSingleHit, getPatchBulkRequest, } from '../__mocks__/request_responses'; +import { createMockServer, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { patchRulesBulkRoute } from './patch_rules_bulk_route'; import { BulkError } from '../utils'; describe('patch_rules_bulk', () => { - let { server, alertsClient, actionsClient } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient } = createMockServer()); - patchRulesBulkRoute(server); + + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + patchRulesBulkRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when updating a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getPatchBulkRequest()); expect(statusCode).toBe(200); }); test('returns 200 as a response when updating a single rule that does not exist', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getPatchBulkRequest()); expect(statusCode).toBe(200); }); test('returns 404 within the payload when updating a single rule that does not exist', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { payload } = await server.inject(getPatchBulkRequest()); const parsed: BulkError[] = JSON.parse(payload); const expected: BulkError[] = [ @@ -69,17 +73,18 @@ describe('patch_rules_bulk', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - patchRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getPatchBulkRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + patchRulesRoute(route, getClients); + const { statusCode } = await inject(getPatchBulkRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if id is not given in either the body or the url', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); const { rule_id, ...noId } = typicalPayload(); const request: ServerInjectOptions = { method: 'PATCH', @@ -91,9 +96,9 @@ describe('patch_rules_bulk', () => { }); test('returns errors as 200 to just indicate ok something happened', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PATCH', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -104,9 +109,9 @@ describe('patch_rules_bulk', () => { }); test('returns 404 in the payload if the record does not exist yet', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PATCH', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -124,10 +129,10 @@ describe('patch_rules_bulk', () => { }); test('returns 200 if type is query', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PATCH', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -138,10 +143,10 @@ describe('patch_rules_bulk', () => { }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'PATCH', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 00184b6c16b7e..d3f92e9e05bcc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -5,21 +5,21 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { BulkPatchRulesRequest, IRuleSavedAttributesSavedObjectAttributes, } from '../../rules/types'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { transformOrBulkError, getIdBulkError } from './utils'; import { transformBulkError } from '../utils'; import { patchRulesBulkSchema } from '../schemas/patch_rules_bulk_schema'; import { patchRules } from '../../rules/patch_rules'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; -export const createPatchRulesBulkRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createPatchRulesBulkRoute = (getClients: GetScopedClients): Hapi.ServerRoute => { return { method: 'PATCH', path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -33,14 +33,9 @@ export const createPatchRulesBulkRoute = (server: ServerFacade): Hapi.ServerRout }, }, async handler(request: BulkPatchRulesRequest, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { + const { actionsClient, alertsClient, savedObjectsClient } = await getClients(request); + + if (!actionsClient || !alertsClient) { return headers.response().code(404); } @@ -132,6 +127,9 @@ export const createPatchRulesBulkRoute = (server: ServerFacade): Hapi.ServerRout }; }; -export const patchRulesBulkRoute = (server: ServerFacade): void => { - server.route(createPatchRulesBulkRoute(server)); +export const patchRulesBulkRoute = ( + route: LegacyServices['route'], + getClients: GetScopedClients +): void => { + route(createPatchRulesBulkRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index d315d45046e2d..cc84b08fdef11 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -4,13 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; - -import { patchRulesRoute } from './patch_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; +import { patchRulesRoute } from './patch_rules_route'; import { getFindResult, @@ -21,51 +17,59 @@ import { typicalPayload, getFindResultWithSingleHit, } from '../__mocks__/request_responses'; +import { createMockServer, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; describe('patch_rules', () => { - let { server, alertsClient, actionsClient, savedObjectsClient } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient, savedObjectsClient } = createMockServer()); - patchRulesRoute(server); + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + patchRulesRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when updating a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getPatchRequest()); expect(statusCode).toBe(200); }); test('returns 404 when updating a single rule that does not exist', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getPatchRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - patchRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getPatchRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + patchRulesRoute(route, getClients); + const { statusCode } = await inject(getPatchRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if id is not given in either the body or the url', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { rule_id, ...noId } = typicalPayload(); const request: ServerInjectOptions = { method: 'PATCH', @@ -79,10 +83,10 @@ describe('patch_rules', () => { }); test('returns 404 if the record does not exist yet', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const request: ServerInjectOptions = { method: 'PATCH', url: DETECTION_ENGINE_RULES_URL, @@ -93,11 +97,11 @@ describe('patch_rules', () => { }); test('returns 200 if type is query', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const request: ServerInjectOptions = { method: 'PATCH', url: DETECTION_ENGINE_RULES_URL, @@ -108,11 +112,11 @@ describe('patch_rules', () => { }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'PATCH', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts index e27ae81362f27..761d22b084237 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -5,18 +5,18 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { patchRules } from '../../rules/patch_rules'; import { PatchRulesRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { patchRulesSchema } from '../schemas/patch_rules_schema'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { getIdError, transform } from './utils'; import { transformError } from '../utils'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; -export const createPatchRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createPatchRulesRoute = (getClients: GetScopedClients): Hapi.ServerRoute => { return { method: 'PATCH', path: DETECTION_ENGINE_RULES_URL, @@ -59,21 +59,16 @@ export const createPatchRulesRoute = (server: ServerFacade): Hapi.ServerRoute => version, } = request.payload; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } - try { + const { alertsClient, actionsClient, savedObjectsClient } = await getClients(request); + + if (!actionsClient || !alertsClient) { + return headers.response().code(404); + } + const rule = await patchRules({ - alertsClient, actionsClient, + alertsClient, description, enabled, falsePositives, @@ -146,6 +141,6 @@ export const createPatchRulesRoute = (server: ServerFacade): Hapi.ServerRoute => }; }; -export const patchRulesRoute = (server: ServerFacade) => { - server.route(createPatchRulesRoute(server)); +export const patchRulesRoute = (route: LegacyServices['route'], getClients: GetScopedClients) => { + route(createPatchRulesRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index 000cd29af8ba9..7c4653af97f21 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; - -import { readRulesRoute } from './read_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { readRulesRoute } from './read_rules_route'; import { getFindResult, getResult, @@ -19,43 +16,48 @@ import { getFindResultWithSingleHit, getFindResultStatus, } from '../__mocks__/request_responses'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { createMockServer, clientsServiceMock } from '../__mocks__'; describe('read_signals', () => { - let { server, alertsClient, savedObjectsClient } = createMockServer(); + let server = createMockServer(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { - ({ server, alertsClient, savedObjectsClient } = createMockServer()); - readRulesRoute(server); - }); - - afterEach(() => { jest.resetAllMocks(); + + server = createMockServer(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + readRulesRoute(server.route, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when reading a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getReadRequest()); expect(statusCode).toBe(200); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - readRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getReadRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + readRulesRoute(route, getClients); + const { statusCode } = await inject(getReadRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if given a non-existent id', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - alertsClient.delete.mockResolvedValue({}); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.delete.mockResolvedValue({}); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const request: ServerInjectOptions = { method: 'GET', url: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts index e82ad92704695..0180b208d1bb7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -5,18 +5,18 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getIdError, transform } from './utils'; import { transformError } from '../utils'; import { readRules } from '../../rules/read_rules'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices, LegacyRequest } from '../../../../types'; import { queryRulesSchema } from '../schemas/query_rules_schema'; import { QueryRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +import { GetScopedClients } from '../../../../services'; -export const createReadRulesRoute: Hapi.ServerRoute = { +export const createReadRulesRoute = (getClients: GetScopedClients): Hapi.ServerRoute => ({ method: 'GET', path: DETECTION_ENGINE_RULES_URL, options: { @@ -28,16 +28,15 @@ export const createReadRulesRoute: Hapi.ServerRoute = { query: queryRulesSchema, }, }, - async handler(request: QueryRequest, headers) { + async handler(request: QueryRequest & LegacyRequest, headers) { const { id, rule_id: ruleId } = request.query; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } + try { + const { alertsClient, savedObjectsClient } = await getClients(request); + if (!alertsClient) { + return headers.response().code(404); + } + const rule = await readRules({ alertsClient, id, @@ -84,8 +83,8 @@ export const createReadRulesRoute: Hapi.ServerRoute = { .code(error.statusCode); } }, -}; +}); -export const readRulesRoute = (server: ServerFacade) => { - server.route(createReadRulesRoute); +export const readRulesRoute = (route: LegacyServices['route'], getClients: GetScopedClients) => { + route(createReadRulesRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts index 81b6444f38603..9ff7ebc37aab1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts @@ -4,14 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; - -import { updateRulesRoute } from './update_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; +import { updateRulesRoute } from './update_rules_route'; import { getFindResult, getResult, @@ -20,43 +16,52 @@ import { getFindResultWithSingleHit, getUpdateBulkRequest, } from '../__mocks__/request_responses'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { updateRulesBulkRoute } from './update_rules_bulk_route'; import { BulkError } from '../utils'; describe('update_rules_bulk', () => { - let { server, alertsClient, actionsClient } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient } = createMockServer()); - updateRulesBulkRoute(server); + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + updateRulesBulkRoute(server.route, config, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when updating a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getUpdateBulkRequest()); expect(statusCode).toBe(200); }); test('returns 200 as a response when updating a single rule that does not exist', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getUpdateBulkRequest()); expect(statusCode).toBe(200); }); test('returns 404 within the payload when updating a single rule that does not exist', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { payload } = await server.inject(getUpdateBulkRequest()); const parsed: BulkError[] = JSON.parse(payload); const expected: BulkError[] = [ @@ -69,17 +74,18 @@ describe('update_rules_bulk', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - updateRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getUpdateBulkRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + updateRulesRoute(route, config, getClients); + const { statusCode } = await inject(getUpdateBulkRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if id is not given in either the body or the url', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); const { rule_id, ...noId } = typicalPayload(); const request: ServerInjectOptions = { method: 'PUT', @@ -91,9 +97,9 @@ describe('update_rules_bulk', () => { }); test('returns errors as 200 to just indicate ok something happened', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PUT', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -104,9 +110,9 @@ describe('update_rules_bulk', () => { }); test('returns 404 in the payload if the record does not exist yet', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PUT', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -124,10 +130,10 @@ describe('update_rules_bulk', () => { }); test('returns 200 if type is query', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PUT', url: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -138,10 +144,10 @@ describe('update_rules_bulk', () => { }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'PUT', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 671497f9f65db..98ed01474c3dc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -5,21 +5,24 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; + import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { BulkUpdateRulesRequest, IRuleSavedAttributesSavedObjectAttributes, } from '../../rules/types'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { transformOrBulkError, getIdBulkError } from './utils'; import { transformBulkError, getIndex } from '../utils'; import { updateRulesBulkSchema } from '../schemas/update_rules_bulk_schema'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; import { updateRules } from '../../rules/update_rules'; -export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createUpdateRulesBulkRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'PUT', path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, @@ -33,14 +36,11 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou }, }, async handler(request: BulkUpdateRulesRequest, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) + const { actionsClient, alertsClient, savedObjectsClient, spacesClient } = await getClients( + request ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { + + if (!actionsClient || !alertsClient) { return headers.response().code(404); } @@ -74,7 +74,7 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou references, version, } = payloadRule; - const finalIndex = outputIndex != null ? outputIndex : getIndex(request, server); + const finalIndex = outputIndex ?? getIndex(spacesClient.getSpaceId, config); const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)'; try { const rule = await updateRules({ @@ -134,6 +134,10 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou }; }; -export const updateRulesBulkRoute = (server: ServerFacade): void => { - server.route(createUpdateRulesBulkRoute(server)); +export const updateRulesBulkRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +): void => { + route(createUpdateRulesBulkRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index c4f10d7a20327..7cadfa94467a7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -4,14 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - createMockServer, - createMockServerWithoutAlertClientDecoration, -} from '../__mocks__/_mock_server'; - -import { updateRulesRoute } from './update_rules_route'; import { ServerInjectOptions } from 'hapi'; +import { omit } from 'lodash/fp'; +import { updateRulesRoute } from './update_rules_route'; import { getFindResult, getFindResultStatus, @@ -21,51 +17,62 @@ import { typicalPayload, getFindResultWithSingleHit, } from '../__mocks__/request_responses'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; describe('update_rules', () => { - let { server, alertsClient, actionsClient, savedObjectsClient } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); - ({ server, alertsClient, actionsClient, savedObjectsClient } = createMockServer()); - updateRulesRoute(server); + + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + updateRulesRoute(server.route, config, getClients); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when updating a single rule with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getUpdateRequest()); expect(statusCode).toBe(200); }); test('returns 404 when updating a single rule that does not exist', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { statusCode } = await server.inject(getUpdateRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { - const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - updateRulesRoute(serverWithoutAlertClient); - const { statusCode } = await serverWithoutAlertClient.inject(getUpdateRequest()); + getClients.mockResolvedValue(omit('alertsClient', clients)); + const { route, inject } = createMockServer(); + updateRulesRoute(route, config, getClients); + const { statusCode } = await inject(getUpdateRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { test('returns 400 if id is not given in either the body or the url', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { rule_id, ...noId } = typicalPayload(); const request: ServerInjectOptions = { method: 'PUT', @@ -79,10 +86,10 @@ describe('update_rules', () => { }); test('returns 404 if the record does not exist yet', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const request: ServerInjectOptions = { method: 'PUT', url: DETECTION_ENGINE_RULES_URL, @@ -93,11 +100,11 @@ describe('update_rules', () => { }); test('returns 200 if type is query', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const request: ServerInjectOptions = { method: 'PUT', url: DETECTION_ENGINE_RULES_URL, @@ -108,11 +115,11 @@ describe('update_rules', () => { }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(getResult()); - savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); + clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.alertsClient.get.mockResolvedValue(getResult()); + clients.actionsClient.update.mockResolvedValue(updateActionResult()); + clients.alertsClient.update.mockResolvedValue(getResult()); + clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'PUT', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts index a01627d2094b7..80fdfc1df8e0e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -5,18 +5,20 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { UpdateRulesRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { updateRulesSchema } from '../schemas/update_rules_schema'; -import { ServerFacade } from '../../../../types'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { getIdError, transform } from './utils'; import { transformError, getIndex } from '../utils'; import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; -import { KibanaRequest } from '../../../../../../../../../src/core/server'; import { updateRules } from '../../rules/update_rules'; -export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute => { +export const createUpdateRulesRoute = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'PUT', path: DETECTION_ENGINE_RULES_URL, @@ -59,19 +61,16 @@ export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = version, } = request.payload; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; - const actionsClient = await server.plugins.actions.getActionsClientWithRequest( - KibanaRequest.from((request as unknown) as Hapi.Request) - ); - const savedObjectsClient = isFunction(request.getSavedObjectsClient) - ? request.getSavedObjectsClient() - : null; - if (!alertsClient || !savedObjectsClient) { - return headers.response().code(404); - } - try { - const finalIndex = outputIndex != null ? outputIndex : getIndex(request, server); + const { alertsClient, actionsClient, savedObjectsClient, spacesClient } = await getClients( + request + ); + + if (!actionsClient || !alertsClient) { + return headers.response().code(404); + } + + const finalIndex = outputIndex ?? getIndex(spacesClient.getSpaceId, config); const rule = await updateRules({ alertsClient, actionsClient, @@ -148,6 +147,10 @@ export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = }; }; -export const updateRulesRoute = (server: ServerFacade) => { - server.route(createUpdateRulesRoute(server)); +export const updateRulesRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +) => { + route(createUpdateRulesRoute(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 35e1e5933af64..3e7ed4de6d8c6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../__mocks__/_mock_server'; +import { ServerInjectOptions } from 'hapi'; +import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../../common/constants'; import { setSignalsStatusRoute } from './open_close_signals_route'; import * as myUtils from '../utils'; -import { ServerInjectOptions } from 'hapi'; import { getSetSignalStatusByIdsRequest, @@ -16,19 +16,27 @@ import { typicalSetStatusSignalByQueryPayload, setStatusSignalMissingIdsAndQueryPayload, } from '../__mocks__/request_responses'; -import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../../common/constants'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; describe('set signal status', () => { - let { server, elasticsearch } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); jest.spyOn(myUtils, 'getIndex').mockReturnValue('fakeindex'); - ({ server, elasticsearch } = createMockServer()); - elasticsearch.getCluster = jest.fn(() => ({ - callWithRequest: jest.fn(() => true), - })); - setSignalsStatusRoute(server); + + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(true); + + setSignalsStatusRoute(server.route, config, getClients); }); describe('status on signal', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index 4755869c3d908..b2b938625180e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -6,12 +6,16 @@ import Hapi from 'hapi'; import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../../common/constants'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { SignalsStatusRequest } from '../../signals/types'; import { setSignalsStatusSchema } from '../schemas/set_signal_status_schema'; -import { ServerFacade } from '../../../../types'; import { transformError, getIndex } from '../utils'; -export const setSignalsStatusRouteDef = (server: ServerFacade): Hapi.ServerRoute => { +export const setSignalsStatusRouteDef = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: DETECTION_ENGINE_SIGNALS_STATUS_URL, @@ -26,8 +30,9 @@ export const setSignalsStatusRouteDef = (server: ServerFacade): Hapi.ServerRoute }, async handler(request: SignalsStatusRequest) { const { signal_ids: signalIds, query, status } = request.payload; - const index = getIndex(request, server); - const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); + const { clusterClient, spacesClient } = await getClients(request); + const index = getIndex(spacesClient.getSpaceId, config); + let queryObject; if (signalIds) { queryObject = { ids: { values: signalIds } }; @@ -40,7 +45,7 @@ export const setSignalsStatusRouteDef = (server: ServerFacade): Hapi.ServerRoute }; } try { - return callWithRequest(request, 'updateByQuery', { + return clusterClient.callAsCurrentUser('updateByQuery', { index, body: { script: { @@ -58,6 +63,10 @@ export const setSignalsStatusRouteDef = (server: ServerFacade): Hapi.ServerRoute }; }; -export const setSignalsStatusRoute = (server: ServerFacade) => { - server.route(setSignalsStatusRouteDef(server)); +export const setSignalsStatusRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +) => { + route(setSignalsStatusRouteDef(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts index 5b86d0a4b36c0..9439adfcec3cb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts @@ -4,77 +4,78 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../__mocks__/_mock_server'; -import { querySignalsRoute } from './query_signals_route'; -import * as myUtils from '../utils'; import { ServerInjectOptions } from 'hapi'; +import { querySignalsRoute } from './query_signals_route'; +import * as myUtils from '../utils'; import { getSignalsQueryRequest, getSignalsAggsQueryRequest, typicalSignalsQuery, typicalSignalsQueryAggs, } from '../__mocks__/request_responses'; +import { createMockServer, createMockConfig, clientsServiceMock } from '../__mocks__'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../common/constants'; describe('query for signal', () => { - let { server, elasticsearch } = createMockServer(); + let server = createMockServer(); + let config = createMockConfig(); + let getClients = clientsServiceMock.createGetScoped(); + let clients = clientsServiceMock.createClients(); beforeEach(() => { jest.resetAllMocks(); jest.spyOn(myUtils, 'getIndex').mockReturnValue('fakeindex'); - ({ server, elasticsearch } = createMockServer()); - elasticsearch.getCluster = jest.fn(() => ({ - callWithRequest: jest.fn(() => true), - })); - querySignalsRoute(server); + + server = createMockServer(); + config = createMockConfig(); + getClients = clientsServiceMock.createGetScoped(); + clients = clientsServiceMock.createClients(); + + getClients.mockResolvedValue(clients); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(true); + + querySignalsRoute(server.route, config, getClients); }); describe('query and agg on signals index', () => { test('returns 200 when using single query', async () => { - elasticsearch.getCluster = jest.fn(() => ({ - callWithRequest: jest.fn( - (_req, _type: string, queryBody: { index: string; body: object }) => { - expect(queryBody.body).toMatchObject({ ...typicalSignalsQueryAggs() }); - return true; - } - ), - })); - const { statusCode } = await server.inject(getSignalsAggsQueryRequest()); + const { statusCode } = await server.inject(getSignalsQueryRequest()); + expect(statusCode).toBe(200); + expect(clients.clusterClient.callAsCurrentUser).toHaveBeenCalledWith( + 'search', + expect.objectContaining({ body: typicalSignalsQuery() }) + ); expect(myUtils.getIndex).toHaveReturnedWith('fakeindex'); }); test('returns 200 when using single agg', async () => { - elasticsearch.getCluster = jest.fn(() => ({ - callWithRequest: jest.fn( - (_req, _type: string, queryBody: { index: string; body: object }) => { - expect(queryBody.body).toMatchObject({ ...typicalSignalsQueryAggs() }); - return true; - } - ), - })); const { statusCode } = await server.inject(getSignalsAggsQueryRequest()); + expect(statusCode).toBe(200); + expect(clients.clusterClient.callAsCurrentUser).toHaveBeenCalledWith( + 'search', + expect.objectContaining({ body: typicalSignalsQueryAggs() }) + ); expect(myUtils.getIndex).toHaveReturnedWith('fakeindex'); }); test('returns 200 when using aggs and query together', async () => { - const allTogether = getSignalsQueryRequest(); - allTogether.payload = { ...typicalSignalsQueryAggs(), ...typicalSignalsQuery() }; - elasticsearch.getCluster = jest.fn(() => ({ - callWithRequest: jest.fn( - (_req, _type: string, queryBody: { index: string; body: object }) => { - expect(queryBody.body).toMatchObject({ - ...typicalSignalsQueryAggs(), - ...typicalSignalsQuery(), - }); - return true; - } - ), - })); - const { statusCode } = await server.inject(allTogether); + const request = getSignalsQueryRequest(); + request.payload = { ...typicalSignalsQueryAggs(), ...typicalSignalsQuery() }; + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(200); + expect(clients.clusterClient.callAsCurrentUser).toHaveBeenCalledWith( + 'search', + expect.objectContaining({ + body: { + ...typicalSignalsQuery(), + ...typicalSignalsQueryAggs(), + }, + }) + ); expect(myUtils.getIndex).toHaveReturnedWith('fakeindex'); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts index 6d1896b1a8171..a3602ffbded41 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -6,12 +6,16 @@ import Hapi from 'hapi'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../common/constants'; +import { LegacyServices } from '../../../../types'; +import { GetScopedClients } from '../../../../services'; import { SignalsQueryRequest } from '../../signals/types'; import { querySignalsSchema } from '../schemas/query_signals_index_schema'; -import { ServerFacade } from '../../../../types'; import { transformError, getIndex } from '../utils'; -export const querySignalsRouteDef = (server: ServerFacade): Hapi.ServerRoute => { +export const querySignalsRouteDef = ( + config: LegacyServices['config'], + getClients: GetScopedClients +): Hapi.ServerRoute => { return { method: 'POST', path: DETECTION_ENGINE_QUERY_SIGNALS_URL, @@ -26,11 +30,12 @@ export const querySignalsRouteDef = (server: ServerFacade): Hapi.ServerRoute => }, async handler(request: SignalsQueryRequest) { const { query, aggs, _source, track_total_hits, size } = request.payload; - const index = getIndex(request, server); - const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); + const { clusterClient, spacesClient } = await getClients(request); + + const index = getIndex(spacesClient.getSpaceId, config); try { - return callWithRequest(request, 'search', { + return clusterClient.callAsCurrentUser('search', { index, body: { query, aggs, _source, track_total_hits, size }, }); @@ -42,6 +47,10 @@ export const querySignalsRouteDef = (server: ServerFacade): Hapi.ServerRoute => }; }; -export const querySignalsRoute = (server: ServerFacade) => { - server.route(querySignalsRouteDef(server)); +export const querySignalsRoute = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + getClients: GetScopedClients +) => { + route(querySignalsRouteDef(config, getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts index f6d297b0cbf43..b17be21d15a19 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/tags/read_tags_route.ts @@ -5,13 +5,14 @@ */ import Hapi from 'hapi'; -import { isFunction } from 'lodash/fp'; + import { DETECTION_ENGINE_TAGS_URL } from '../../../../../common/constants'; -import { ServerFacade, RequestFacade } from '../../../../types'; +import { LegacyServices, LegacyRequest } from '../../../../types'; import { transformError } from '../utils'; import { readTags } from '../../tags/read_tags'; +import { GetScopedClients } from '../../../../services'; -export const createReadTagsRoute: Hapi.ServerRoute = { +export const createReadTagsRoute = (getClients: GetScopedClients): Hapi.ServerRoute => ({ method: 'GET', path: DETECTION_ENGINE_TAGS_URL, options: { @@ -22,8 +23,9 @@ export const createReadTagsRoute: Hapi.ServerRoute = { }, }, }, - async handler(request: RequestFacade, headers) { - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; + async handler(request: LegacyRequest, headers) { + const { alertsClient } = await getClients(request); + if (!alertsClient) { return headers.response().code(404); } @@ -43,8 +45,8 @@ export const createReadTagsRoute: Hapi.ServerRoute = { .code(error.statusCode); } }, -}; +}); -export const readTagsRoute = (server: ServerFacade) => { - server.route(createReadTagsRoute); +export const readTagsRoute = (route: LegacyServices['route'], getClients: GetScopedClients) => { + route(createReadTagsRoute(getClients)); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts index 2699f687c5106..957ddd4ee6caa 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts @@ -16,6 +16,7 @@ import { createImportErrorObject, transformImportError, } from './utils'; +import { createMockConfig } from './__mocks__'; describe('utils', () => { describe('transformError', () => { @@ -295,34 +296,20 @@ describe('utils', () => { }); describe('getIndex', () => { - it('appends the space ID to the configured index if spaces are enabled', () => { - const mockGet = jest.fn(); - const mockGetSpaceId = jest.fn(); - const config = jest.fn(() => ({ get: mockGet, has: jest.fn() })); - const server = { plugins: { spaces: { getSpaceId: mockGetSpaceId } }, config }; + let mockConfig = createMockConfig(); - mockGet.mockReturnValue('mockSignalsIndex'); - mockGetSpaceId.mockReturnValue('myspace'); - // @ts-ignore-next-line TODO these dependencies are simplified on - // https://github.com/elastic/kibana/pull/56814. We're currently mocking - // out what we need. - const index = getIndex(null, server); - - expect(index).toEqual('mockSignalsIndex-myspace'); + beforeEach(() => { + mockConfig = () => ({ + get: jest.fn(() => 'mockSignalsIndex'), + has: jest.fn(), + }); }); - it('appends the default space ID to the configured index if spaces are disabled', () => { - const mockGet = jest.fn(); - const config = jest.fn(() => ({ get: mockGet, has: jest.fn() })); - const server = { plugins: {}, config }; + it('appends the space id to the configured index', () => { + const getSpaceId = jest.fn(() => 'myspace'); + const index = getIndex(getSpaceId, mockConfig); - mockGet.mockReturnValue('mockSignalsIndex'); - // @ts-ignore-next-line TODO these dependencies are simplified on - // https://github.com/elastic/kibana/pull/56814. We're currently mocking - // out what we need. - const index = getIndex(null, server); - - expect(index).toEqual('mockSignalsIndex-default'); + expect(index).toEqual('mockSignalsIndex-myspace'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts index 20871e5309c30..4a586e21c9e7f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -6,7 +6,7 @@ import Boom from 'boom'; import { APP_ID, SIGNALS_INDEX_KEY } from '../../../../common/constants'; -import { ServerFacade, RequestFacade } from '../../../types'; +import { LegacyServices } from '../../../types'; export interface OutputError { message: string; @@ -174,21 +174,9 @@ export const transformBulkError = ( } }; -export const getIndex = ( - request: RequestFacade | Omit, - server: ServerFacade -): string => { - const spaceId = server.plugins.spaces?.getSpaceId?.(request) ?? 'default'; - const signalsIndex = server.config().get(`xpack.${APP_ID}.${SIGNALS_INDEX_KEY}`); - return `${signalsIndex}-${spaceId}`; -}; +export const getIndex = (getSpaceId: () => string, config: LegacyServices['config']): string => { + const signalsIndex = config().get(`xpack.${APP_ID}.${SIGNALS_INDEX_KEY}`); + const spaceId = getSpaceId(); -export const callWithRequestFactory = ( - request: RequestFacade | Omit, - server: ServerFacade -) => { - const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); - return (endpoint: string, params: T, options?: U) => { - return callWithRequest(request, endpoint, params, options); - }; + return `${signalsIndex}-${spaceId}`; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts index 8d00ddb18be6b..25bac383ecf72 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts @@ -5,7 +5,6 @@ */ import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; -import { AlertsClient } from '../../../../../alerting'; import { getResult, getFindResultWithSingleHit, @@ -28,10 +27,7 @@ describe('get_existing_prepackaged_rules', () => { test('should return a single item in a single page', async () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getExistingPrepackagedRules({ - alertsClient: unsafeCast, - }); + const rules = await getExistingPrepackagedRules({ alertsClient }); expect(rules).toEqual([getResult()]); }); @@ -70,10 +66,7 @@ describe('get_existing_prepackaged_rules', () => { }) ); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getExistingPrepackagedRules({ - alertsClient: unsafeCast, - }); + const rules = await getExistingPrepackagedRules({ alertsClient }); expect(rules).toEqual([result1, result2, result3]); }); }); @@ -82,10 +75,7 @@ describe('get_existing_prepackaged_rules', () => { test('should return a single item in a single page', async () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getNonPackagedRules({ - alertsClient: unsafeCast, - }); + const rules = await getNonPackagedRules({ alertsClient }); expect(rules).toEqual([getResult()]); }); @@ -113,10 +103,7 @@ describe('get_existing_prepackaged_rules', () => { getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 }) ); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getNonPackagedRules({ - alertsClient: unsafeCast, - }); + const rules = await getNonPackagedRules({ alertsClient }); expect(rules).toEqual([result1, result2]); }); @@ -152,10 +139,7 @@ describe('get_existing_prepackaged_rules', () => { }) ); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getNonPackagedRules({ - alertsClient: unsafeCast, - }); + const rules = await getNonPackagedRules({ alertsClient }); expect(rules).toEqual([result1, result2, result3]); }); }); @@ -164,11 +148,7 @@ describe('get_existing_prepackaged_rules', () => { test('should return a single item in a single page', async () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getRules({ - alertsClient: unsafeCast, - filter: '', - }); + const rules = await getRules({ alertsClient, filter: '' }); expect(rules).toEqual([getResult()]); }); @@ -196,11 +176,7 @@ describe('get_existing_prepackaged_rules', () => { getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 }) ); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getRules({ - alertsClient: unsafeCast, - filter: '', - }); + const rules = await getRules({ alertsClient, filter: '' }); expect(rules).toEqual([result1, result2]); }); }); @@ -209,11 +185,7 @@ describe('get_existing_prepackaged_rules', () => { test('it returns a count', async () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getRulesCount({ - alertsClient: unsafeCast, - filter: '', - }); + const rules = await getRulesCount({ alertsClient, filter: '' }); expect(rules).toEqual(1); }); }); @@ -222,10 +194,7 @@ describe('get_existing_prepackaged_rules', () => { test('it returns a count', async () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const rules = await getNonPackagedRulesCount({ - alertsClient: unsafeCast, - }); + const rules = await getNonPackagedRulesCount({ alertsClient }); expect(rules).toEqual(1); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index ff48b9f5f7c33..35d3489dad6fc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -10,7 +10,6 @@ import { getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; -import { AlertsClient } from '../../../../../alerting'; import { getExportAll } from './get_export_all'; describe('getExportAll', () => { @@ -19,8 +18,7 @@ describe('getExportAll', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const exports = await getExportAll(unsafeCast); + const exports = await getExportAll(alertsClient); expect(exports).toEqual({ rulesNdjson: '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"saved_id":"some-id","timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"framework":"MITRE ATT&CK","tactic":{"id":"TA0040","name":"impact","reference":"https://attack.mitre.org/tactics/TA0040/"},"technique":[{"id":"T1499","name":"endpoint denial of service","reference":"https://attack.mitre.org/techniques/T1499/"}]}],"version":1}\n', @@ -39,8 +37,7 @@ describe('getExportAll', () => { alertsClient.find.mockResolvedValue(findResult); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const exports = await getExportAll(unsafeCast); + const exports = await getExportAll(alertsClient); expect(exports).toEqual({ rulesNdjson: '', exportDetails: '{"exported_count":0,"missing_rules":[],"missing_rules_count":0}\n', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 236d04acc782b..4b6ea527a2027 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -11,7 +11,6 @@ import { getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; -import { AlertsClient } from '../../../../../alerting'; describe('get_export_by_object_ids', () => { describe('getExportByObjectIds', () => { @@ -20,9 +19,8 @@ describe('get_export_by_object_ids', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const objects = [{ rule_id: 'rule-1' }]; - const exports = await getExportByObjectIds(unsafeCast, objects); + const exports = await getExportByObjectIds(alertsClient, objects); expect(exports).toEqual({ rulesNdjson: '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"saved_id":"some-id","timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"framework":"MITRE ATT&CK","tactic":{"id":"TA0040","name":"impact","reference":"https://attack.mitre.org/tactics/TA0040/"},"technique":[{"id":"T1499","name":"endpoint denial of service","reference":"https://attack.mitre.org/techniques/T1499/"}]}],"version":1}\n', @@ -45,9 +43,8 @@ describe('get_export_by_object_ids', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(findResult); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const objects = [{ rule_id: 'rule-1' }]; - const exports = await getExportByObjectIds(unsafeCast, objects); + const exports = await getExportByObjectIds(alertsClient, objects); expect(exports).toEqual({ rulesNdjson: '', exportDetails: @@ -62,9 +59,8 @@ describe('get_export_by_object_ids', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(unsafeCast, objects); + const exports = await getRulesFromObjects(alertsClient, objects); const expected: RulesErrors = { exportedCount: 1, missingRules: [], @@ -138,9 +134,8 @@ describe('get_export_by_object_ids', () => { alertsClient.get.mockResolvedValue(result); alertsClient.find.mockResolvedValue(findResult); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(unsafeCast, objects); + const exports = await getRulesFromObjects(alertsClient, objects); const expected: RulesErrors = { exportedCount: 0, missingRules: [{ rule_id: 'rule-1' }], @@ -162,9 +157,8 @@ describe('get_export_by_object_ids', () => { alertsClient.get.mockRejectedValue({ output: { statusCode: 404 } }); alertsClient.find.mockResolvedValue(findResult); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(unsafeCast, objects); + const exports = await getRulesFromObjects(alertsClient, objects); const expected: RulesErrors = { exportedCount: 0, missingRules: [{ rule_id: 'rule-1' }], diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts index 6ba0aa95bdd7b..c637860c5646a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/read_rules.test.ts @@ -6,7 +6,6 @@ import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; import { readRules } from './read_rules'; -import { AlertsClient } from '../../../../../alerting'; import { getResult, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; describe('read_rules', () => { @@ -15,9 +14,8 @@ describe('read_rules', () => { const alertsClient = alertsClientMock.create(); alertsClient.get.mockResolvedValue(getResult()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const rule = await readRules({ - alertsClient: unsafeCast, + alertsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, }); @@ -28,9 +26,8 @@ describe('read_rules', () => { const alertsClient = alertsClientMock.create(); alertsClient.get.mockResolvedValue(getResult()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const rule = await readRules({ - alertsClient: unsafeCast, + alertsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: null, }); @@ -42,9 +39,8 @@ describe('read_rules', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const rule = await readRules({ - alertsClient: unsafeCast, + alertsClient, id: undefined, ruleId: 'rule-1', }); @@ -56,9 +52,8 @@ describe('read_rules', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const rule = await readRules({ - alertsClient: unsafeCast, + alertsClient, id: null, ruleId: 'rule-1', }); @@ -70,9 +65,8 @@ describe('read_rules', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const rule = await readRules({ - alertsClient: unsafeCast, + alertsClient, id: null, ruleId: null, }); @@ -84,9 +78,8 @@ describe('read_rules', () => { alertsClient.get.mockResolvedValue(getResult()); alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; const rule = await readRules({ - alertsClient: unsafeCast, + alertsClient, id: undefined, ruleId: undefined, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts index 8c44d82f46b53..8579447a74c69 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -14,10 +14,10 @@ import { SavedObjectsClientContract, } from 'kibana/server'; import { SIGNALS_ID } from '../../../../common/constants'; +import { LegacyRequest } from '../../../types'; import { AlertsClient } from '../../../../../alerting/server'; import { ActionsClient } from '../../../../../../../plugins/actions/server'; import { RuleAlertParams, RuleTypeParams, RuleAlertParamsRest } from '../types'; -import { RequestFacade } from '../../../types'; import { Alert } from '../../../../../alerting/server/types'; export type PatchRuleAlertParamsRest = Partial & { @@ -39,19 +39,19 @@ export interface FindParamsRest { filter: string; } -export interface PatchRulesRequest extends RequestFacade { +export interface PatchRulesRequest extends LegacyRequest { payload: PatchRuleAlertParamsRest; } -export interface BulkPatchRulesRequest extends RequestFacade { +export interface BulkPatchRulesRequest extends LegacyRequest { payload: PatchRuleAlertParamsRest[]; } -export interface UpdateRulesRequest extends RequestFacade { +export interface UpdateRulesRequest extends LegacyRequest { payload: UpdateRuleAlertParamsRest; } -export interface BulkUpdateRulesRequest extends RequestFacade { +export interface BulkUpdateRulesRequest extends LegacyRequest { payload: UpdateRuleAlertParamsRest[]; } @@ -99,11 +99,11 @@ export interface IRuleStatusFindType { export type RuleStatusString = 'succeeded' | 'failed' | 'going to run' | 'executing'; -export interface RulesRequest extends RequestFacade { +export interface RulesRequest extends LegacyRequest { payload: RuleAlertParamsRest; } -export interface BulkRulesRequest extends RequestFacade { +export interface BulkRulesRequest extends LegacyRequest { payload: RuleAlertParamsRest[]; } @@ -112,12 +112,12 @@ export interface HapiReadableStream extends Readable { filename: string; }; } -export interface ImportRulesRequest extends Omit { +export interface ImportRulesRequest extends Omit { query: { overwrite: boolean }; payload: { file: HapiReadableStream }; } -export interface ExportRulesRequest extends Omit { +export interface ExportRulesRequest extends Omit { payload: { objects: Array<{ rule_id: string }> | null | undefined }; query: { file_name: string; @@ -125,11 +125,11 @@ export interface ExportRulesRequest extends Omit { }; } -export type QueryRequest = Omit & { +export type QueryRequest = Omit & { query: { id: string | undefined; rule_id: string | undefined }; }; -export interface QueryBulkRequest extends RequestFacade { +export interface QueryBulkRequest extends LegacyRequest { payload: Array; } @@ -143,7 +143,7 @@ export interface FindRuleParams { sortOrder?: 'asc' | 'desc'; } -export interface FindRulesRequest extends Omit { +export interface FindRulesRequest extends Omit { query: { per_page: number; page: number; @@ -155,7 +155,7 @@ export interface FindRulesRequest extends Omit { }; } -export interface FindRulesStatusesRequest extends Omit { +export interface FindRulesStatusesRequest extends Omit { query: { ids: string[]; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts index 9b7b2b8f1fff9..e9159ab87a0c0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts @@ -6,7 +6,7 @@ import { RuleAlertParams, OutputRuleAlertRest } from '../types'; import { SearchResponse } from '../../types'; -import { RequestFacade } from '../../../types'; +import { LegacyRequest } from '../../../types'; import { AlertType, State, AlertExecutorOptions } from '../../../../../alerting/server/types'; export interface SignalsParams { @@ -35,11 +35,11 @@ export type SignalsStatusRestParams = Omit & { export type SignalsQueryRestParams = SignalQueryParams; -export interface SignalsStatusRequest extends RequestFacade { +export interface SignalsStatusRequest extends LegacyRequest { payload: SignalsStatusRestParams; } -export interface SignalsQueryRequest extends RequestFacade { +export interface SignalsQueryRequest extends LegacyRequest { payload: SignalsQueryRestParams; } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts index 87739bf785012..940011924de79 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/tags/read_tags.test.ts @@ -5,7 +5,6 @@ */ import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; -import { AlertsClient } from '../../../../../alerting'; import { getResult, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; @@ -30,10 +29,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readRawTags({ - alertsClient: unsafeCast, - }); + const tags = await readRawTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -51,10 +47,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readRawTags({ - alertsClient: unsafeCast, - }); + const tags = await readRawTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -72,10 +65,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readRawTags({ - alertsClient: unsafeCast, - }); + const tags = await readRawTags({ alertsClient }); expect(tags).toEqual([]); }); @@ -88,10 +78,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readRawTags({ - alertsClient: unsafeCast, - }); + const tags = await readRawTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2']); }); @@ -104,10 +91,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readRawTags({ - alertsClient: unsafeCast, - }); + const tags = await readRawTags({ alertsClient }); expect(tags).toEqual([]); }); }); @@ -127,10 +111,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -148,10 +129,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -169,10 +147,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual([]); }); @@ -185,10 +160,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2']); }); @@ -201,10 +173,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual([]); }); @@ -221,10 +190,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual(['tag 1']); }); @@ -257,10 +223,7 @@ describe('read_tags', () => { const alertsClient = alertsClientMock.create(); alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; - const tags = await readTags({ - alertsClient: unsafeCast, - }); + const tags = await readTags({ alertsClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4', 'tag 5']); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts index e15053db75777..08cb2e7bc19ee 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { CallAPIOptions } from '../../../../../../../src/core/server'; import { Filter } from '../../../../../../../src/plugins/data/server'; import { IRuleStatusAttributes } from './rules/types'; @@ -117,4 +118,9 @@ export type PrepackagedRules = Omit< | 'created_at' > & { rule_id: string; immutable: boolean }; -export type CallWithRequest = (endpoint: string, params: T, options?: U) => Promise; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type CallWithRequest, V> = ( + endpoint: string, + params: T, + options?: CallAPIOptions +) => Promise; diff --git a/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.test.ts index b1f0c3c4a3a18..42dc13d84fd98 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/elasticsearch_adapter.test.ts @@ -519,7 +519,6 @@ describe('events elasticsearch_adapter', () => { return mockResponseMap; }); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/events/mock.ts b/x-pack/legacy/plugins/siem/server/lib/events/mock.ts index 195c0cd674af5..3eb841cbad411 100644 --- a/x-pack/legacy/plugins/siem/server/lib/events/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/events/mock.ts @@ -189,8 +189,7 @@ export const mockOptions: RequestDetailsOptions = { }; export const mockRequest = { - params: {}, - payload: { + body: { operationName: 'GetNetworkTopNFlowQuery', variables: { indexName: 'auditbeat-8.0.0-2019.03.29-000003', diff --git a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts index 39f75e6ea36c3..4cce0b0999257 100644 --- a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts @@ -9,35 +9,27 @@ import { GraphQLSchema } from 'graphql'; import { runHttpQuery } from 'apollo-server-core'; import { schema as configSchema } from '@kbn/config-schema'; import { - CoreSetup, IRouter, KibanaResponseFactory, RequestHandlerContext, - PluginInitializerContext, KibanaRequest, } from '../../../../../../../src/core/server'; import { IndexPatternsFetcher } from '../../../../../../../src/plugins/data/server'; import { AuthenticatedUser } from '../../../../../../plugins/security/common/model'; -import { RequestFacade } from '../../types'; +import { CoreSetup, SetupPlugins } from '../../plugin'; import { FrameworkAdapter, FrameworkIndexPatternsService, FrameworkRequest, internalFrameworkRequest, - WrappableRequest, } from './types'; -import { SiemPluginSecurity, PluginsSetup } from '../../plugin'; export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { - public version: string; - private isProductionMode: boolean; private router: IRouter; - private security: SiemPluginSecurity; + private security: SetupPlugins['security']; - constructor(core: CoreSetup, plugins: PluginsSetup, env: PluginInitializerContext['env']) { - this.version = env.packageInfo.version; - this.isProductionMode = env.mode.prod; + constructor(core: CoreSetup, plugins: SetupPlugins, private isProductionMode: boolean) { this.router = core.http.createRouter(); this.security = plugins.security; } @@ -68,13 +60,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { this.router.post( { path: routePath, - validate: { - body: configSchema.object({ - operationName: configSchema.string(), - query: configSchema.string(), - variables: configSchema.object({}, { allowUnknowns: true }), - }), - }, + validate: { body: configSchema.object({}, { allowUnknowns: true }) }, options: { tags: ['access:siem'], }, @@ -84,7 +70,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { const user = await this.getCurrentUserInfo(request); const gqlResponse = await runHttpQuery([request], { method: 'POST', - options: (req: RequestFacade) => ({ + options: (req: KibanaRequest) => ({ context: { req: wrapRequest(req, context, user) }, schema, }), @@ -104,39 +90,6 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { ); if (!this.isProductionMode) { - this.router.get( - { - path: routePath, - validate: { query: configSchema.object({}, { allowUnknowns: true }) }, - options: { - tags: ['access:siem'], - }, - }, - async (context, request, response) => { - try { - const user = await this.getCurrentUserInfo(request); - const { query } = request; - const gqlResponse = await runHttpQuery([request], { - method: 'GET', - options: (req: RequestFacade) => ({ - context: { req: wrapRequest(req, context, user) }, - schema, - }), - query, - }); - - return response.ok({ - body: gqlResponse, - headers: { - 'content-type': 'application/json', - }, - }); - } catch (error) { - return this.handleError(error, response); - } - } - ); - this.router.get( { path: `${routePath}/graphiql`, @@ -150,7 +103,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { request.query, { endpointURL: routePath, - passHeader: `'kbn-version': '${this.version}'`, + passHeader: "'kbn-xsrf': 'graphiql'", }, request ); @@ -208,20 +161,15 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { } } -export function wrapRequest( - req: InternalRequest, +export function wrapRequest( + request: KibanaRequest, context: RequestHandlerContext, user: AuthenticatedUser | null -): FrameworkRequest { - const { auth, params, payload, query } = req; - +): FrameworkRequest { return { - [internalFrameworkRequest]: req, - auth, + [internalFrameworkRequest]: request, + body: request.body, context, - params, - payload, - query, user, }; } diff --git a/x-pack/legacy/plugins/siem/server/lib/framework/types.ts b/x-pack/legacy/plugins/siem/server/lib/framework/types.ts index 67861ce0dcf28..9fc78e6fb84fe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/framework/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/framework/types.ts @@ -6,9 +6,8 @@ import { IndicesGetMappingParams } from 'elasticsearch'; import { GraphQLSchema } from 'graphql'; -import { RequestAuth } from 'hapi'; -import { RequestHandlerContext } from '../../../../../../../src/core/server'; +import { RequestHandlerContext, KibanaRequest } from '../../../../../../../src/core/server'; import { AuthenticatedUser } from '../../../../../../plugins/security/common/model'; import { ESQuery } from '../../../common/typed_json'; import { @@ -19,14 +18,12 @@ import { TimerangeInput, Maybe, } from '../../graphql/types'; -import { RequestFacade } from '../../types'; export * from '../../utils/typed_resolvers'; export const internalFrameworkRequest = Symbol('internalFrameworkRequest'); export interface FrameworkAdapter { - version: string; registerGraphQLEndpoint(routePath: string, schema: GraphQLSchema): void; callWithRequest( req: FrameworkRequest, @@ -46,24 +43,12 @@ export interface FrameworkAdapter { getIndexPatternsService(req: FrameworkRequest): FrameworkIndexPatternsService; } -export interface FrameworkRequest { - [internalFrameworkRequest]: InternalRequest; +export interface FrameworkRequest extends Pick { + [internalFrameworkRequest]: KibanaRequest; context: RequestHandlerContext; - payload: InternalRequest['payload']; - params: InternalRequest['params']; - query: InternalRequest['query']; - auth: InternalRequest['auth']; user: AuthenticatedUser | null; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export interface WrappableRequest { - payload: Payload; - params: Params; - query: Query; - auth: RequestAuth; -} - export interface DatabaseResponse { took: number; timeout: boolean; diff --git a/x-pack/legacy/plugins/siem/server/lib/hosts/elasticsearch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/hosts/elasticsearch_adapter.test.ts index 0d698f1e19213..20510e1089f96 100644 --- a/x-pack/legacy/plugins/siem/server/lib/hosts/elasticsearch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/hosts/elasticsearch_adapter.test.ts @@ -159,7 +159,6 @@ describe('hosts elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockGetHostsResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -180,7 +179,6 @@ describe('hosts elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockGetHostOverviewResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -201,7 +199,6 @@ describe('hosts elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockGetHostLastFirstSeenResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/hosts/mock.ts b/x-pack/legacy/plugins/siem/server/lib/hosts/mock.ts index 66b73742cc45e..6b72c4a5a2843 100644 --- a/x-pack/legacy/plugins/siem/server/lib/hosts/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/hosts/mock.ts @@ -49,8 +49,7 @@ export const mockGetHostsOptions: HostsRequestOptions = { }; export const mockGetHostsRequest = { - params: {}, - payload: { + body: { operationName: 'GetHostsTableQuery', variables: { sourceId: 'default', @@ -67,7 +66,6 @@ export const mockGetHostsRequest = { query: 'query GetHostsTableQuery($sourceId: ID!, $timerange: TimerangeInput!, $pagination: PaginationInput!, $sort: HostsSortField!, $filterQuery: String) {\n source(id: $sourceId) {\n id\n Hosts(timerange: $timerange, pagination: $pagination, sort: $sort, filterQuery: $filterQuery) {\n totalCount\n edges {\n node {\n _id\n host {\n id\n name\n os {\n name\n version\n __typename\n }\n __typename\n }\n __typename\n }\n cursor {\n value\n __typename\n }\n __typename\n }\n pageInfo {\n endCursor {\n value\n __typename\n }\n hasNextPage\n __typename\n }\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; export const mockGetHostsResponse = { @@ -327,14 +325,12 @@ export const mockGetHostOverviewOptions: HostOverviewRequestOptions = { }; export const mockGetHostOverviewRequest = { - params: {}, - payload: { + body: { operationName: 'GetHostOverviewQuery', variables: { sourceId: 'default', hostName: 'siem-es' }, query: 'query GetHostOverviewQuery($sourceId: ID!, $hostName: String!, $timerange: TimerangeInput!) {\n source(id: $sourceId) {\n id\n HostOverview(hostName: $hostName, timerange: $timerange) {\n _id\n host {\n architecture\n id\n ip\n mac\n name\n os {\n family\n name\n platform\n version\n __typename\n }\n type\n __typename\n }\n cloud {\n instance {\n id\n __typename\n }\n machine {\n type\n __typename\n }\n provider\n region\n __typename\n }\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; export const mockGetHostOverviewResponse = { @@ -520,14 +516,12 @@ export const mockGetHostLastFirstSeenOptions: HostLastFirstSeenRequestOptions = }; export const mockGetHostLastFirstSeenRequest = { - params: {}, - payload: { + body: { operationName: 'GetHostLastFirstSeenQuery', variables: { sourceId: 'default', hostName: 'siem-es' }, query: 'query GetHostLastFirstSeenQuery($sourceId: ID!, $hostName: String!) {\n source(id: $sourceId) {\n id\n HostLastFirstSeen(hostName: $hostName) {\n firstSeen\n lastSeen\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; export const mockGetHostLastFirstSeenResponse = { diff --git a/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/elasticsearch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/elasticsearch_adapter.test.ts index 4a179073852b0..059d15220b619 100644 --- a/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/elasticsearch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/elasticsearch_adapter.test.ts @@ -53,7 +53,6 @@ describe('getKpiHosts', () => { let data: KpiHostsData; const mockCallWithRequest = jest.fn(); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -167,7 +166,6 @@ describe('getKpiHostDetails', () => { let data: KpiHostDetailsData; const mockCallWithRequest = jest.fn(); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/mock.ts b/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/mock.ts index a1962067f9bec..b82a540900bd0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/kpi_hosts/mock.ts @@ -43,8 +43,7 @@ export const mockKpiHostDetailsOptions: RequestBasicOptions = { }; export const mockKpiHostsRequest = { - params: {}, - payload: { + body: { operationName: 'GetKpiHostsQuery', variables: { sourceId: 'default', @@ -54,12 +53,10 @@ export const mockKpiHostsRequest = { query: 'fragment KpiHostChartFields on KpiHostHistogramData {\n x\n y\n __typename\n}\n\nquery GetKpiHostsQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String, $defaultIndex: [String!]!) {\n source(id: $sourceId) {\n id\n KpiHosts(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex) {\n hosts\n hostsHistogram {\n ...KpiHostChartFields\n __typename\n }\n authSuccess\n authSuccessHistogram {\n ...KpiHostChartFields\n __typename\n }\n authFailure\n authFailureHistogram {\n ...KpiHostChartFields\n __typename\n }\n uniqueSourceIps\n uniqueSourceIpsHistogram {\n ...KpiHostChartFields\n __typename\n }\n uniqueDestinationIps\n uniqueDestinationIpsHistogram {\n ...KpiHostChartFields\n __typename\n }\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; export const mockKpiHostDetailsRequest = { - params: {}, - payload: { + body: { operationName: 'GetKpiHostDetailsQuery', variables: { sourceId: 'default', @@ -69,7 +66,6 @@ export const mockKpiHostDetailsRequest = { query: 'fragment KpiHostDetailsChartFields on KpiHostHistogramData {\n x\n y\n __typename\n}\n\nquery GetKpiHostDetailsQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String, $defaultIndex: [String!]!, $hostName: String!) {\n source(id: $sourceId) {\n id\n KpiHostDetails(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex, hostName: $hostName) {\n authSuccess\n authSuccessHistogram {\n ...KpiHostDetailsChartFields\n __typename\n }\n authFailure\n authFailureHistogram {\n ...KpiHostDetailsChartFields\n __typename\n }\n uniqueSourceIps\n uniqueSourceIpsHistogram {\n ...KpiHostDetailsChartFields\n __typename\n }\n uniqueDestinationIps\n uniqueDestinationIpsHistogram {\n ...KpiHostDetailsChartFields\n __typename\n }\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; const mockUniqueIpsResponse = { diff --git a/x-pack/legacy/plugins/siem/server/lib/kpi_network/elastic_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/kpi_network/elastic_adapter.test.ts index 11d007f591fac..58ee7c9aa1cf8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/kpi_network/elastic_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/kpi_network/elastic_adapter.test.ts @@ -48,7 +48,6 @@ describe('Network Kpi elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/kpi_network/mock.ts b/x-pack/legacy/plugins/siem/server/lib/kpi_network/mock.ts index 5b0601b88c779..7d86769de09f1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/kpi_network/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/kpi_network/mock.ts @@ -24,8 +24,7 @@ export const mockOptions: RequestBasicOptions = { }; export const mockRequest = { - params: {}, - payload: { + body: { operationName: 'GetKpiNetworkQuery', variables: { sourceId: 'default', @@ -35,7 +34,6 @@ export const mockRequest = { query: 'fragment KpiNetworkChartFields on KpiNetworkHistogramData {\n x\n y\n __typename\n}\n\nquery GetKpiNetworkQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String, $defaultIndex: [String!]!) {\n source(id: $sourceId) {\n id\n KpiNetwork(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex) {\n networkEvents\n uniqueFlowId\n uniqueSourcePrivateIps\n uniqueSourcePrivateIpsHistogram {\n ...KpiNetworkChartFields\n __typename\n }\n uniqueDestinationPrivateIps\n uniqueDestinationPrivateIpsHistogram {\n ...KpiNetworkChartFields\n __typename\n }\n dnsQueries\n tlsHandshakes\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; export const mockResponse = { diff --git a/x-pack/legacy/plugins/siem/server/lib/network/elastic_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/network/elastic_adapter.test.ts index eeea4bec2fb25..eab461ee07ca7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/elastic_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/elastic_adapter.test.ts @@ -35,7 +35,6 @@ describe('Network Top N flow elasticsearch_adapter with FlowTarget=source', () = const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, getIndexPatternsService: jest.fn(), registerGraphQLEndpoint: jest.fn(), @@ -61,7 +60,6 @@ describe('Network Top N flow elasticsearch_adapter with FlowTarget=source', () = const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockNoDataResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -101,7 +99,6 @@ describe('Network Top N flow elasticsearch_adapter with FlowTarget=source', () = ].buckets[0].location.top_geo.hits.hits = []; mockCallWithRequest.mockResolvedValue(mockNoGeoDataResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, getIndexPatternsService: jest.fn(), registerGraphQLEndpoint: jest.fn(), @@ -132,7 +129,6 @@ describe('Network Top N flow elasticsearch_adapter with FlowTarget=source', () = const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockNoPaginationResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -155,7 +151,6 @@ describe('Network Top N flow elasticsearch_adapter with FlowTarget=source', () = const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockResponseIp); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, getIndexPatternsService: jest.fn(), registerGraphQLEndpoint: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/network/mock.ts b/x-pack/legacy/plugins/siem/server/lib/network/mock.ts index 21b00bf188d20..7ea692f27ef04 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/mock.ts @@ -59,8 +59,7 @@ export const mockOptions: NetworkTopNFlowRequestOptions = { }; export const mockRequest = { - params: {}, - payload: { + body: { operationName: 'GetNetworkTopNFlowQuery', variables: { filterQuery: '', @@ -1507,10 +1506,10 @@ export const mockOptionsIp: NetworkTopNFlowRequestOptions = { export const mockRequestIp = { ...mockRequest, - payload: { - ...mockRequest.payload, + body: { + ...mockRequest.body, variables: { - ...mockRequest.payload.variables, + ...mockRequest.body.variables, ip: '1.1.1.1', }, }, diff --git a/x-pack/legacy/plugins/siem/server/lib/overview/elastic_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/overview/elastic_adapter.test.ts index 29035f4539be8..f421704dffe12 100644 --- a/x-pack/legacy/plugins/siem/server/lib/overview/elastic_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/overview/elastic_adapter.test.ts @@ -36,7 +36,6 @@ describe('Siem Overview elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockResponseNetwork); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -70,7 +69,6 @@ describe('Siem Overview elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockNoDataResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -108,7 +106,6 @@ describe('Siem Overview elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockResponseHost); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), @@ -148,7 +145,6 @@ describe('Siem Overview elasticsearch_adapter', () => { const mockCallWithRequest = jest.fn(); mockCallWithRequest.mockResolvedValue(mockNoDataResponse); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/overview/mock.ts b/x-pack/legacy/plugins/siem/server/lib/overview/mock.ts index 6196f45029313..410b4d90b1e78 100644 --- a/x-pack/legacy/plugins/siem/server/lib/overview/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/overview/mock.ts @@ -24,8 +24,7 @@ export const mockOptionsNetwork: RequestBasicOptions = { }; export const mockRequestNetwork = { - params: {}, - payload: { + body: { operationName: 'GetOverviewNetworkQuery', variables: { sourceId: 'default', @@ -35,7 +34,6 @@ export const mockRequestNetwork = { query: 'query GetOverviewNetworkQuery(\n $sourceId: ID!\n $timerange: TimerangeInput!\n $filterQuery: String\n ) {\n source(id: $sourceId) {\n id\n OverviewNetwork(timerange: $timerange, filterQuery: $filterQuery) {\n packetbeatFlow\n packetbeatDNS\n filebeatSuricata\n filebeatZeek\n auditbeatSocket\n }\n }\n }', }, - query: {}, }; export const mockResponseNetwork = { @@ -97,8 +95,7 @@ export const mockOptionsHost: RequestBasicOptions = { }; export const mockRequestHost = { - params: {}, - payload: { + body: { operationName: 'GetOverviewHostQuery', variables: { sourceId: 'default', @@ -108,7 +105,6 @@ export const mockRequestHost = { query: 'query GetOverviewHostQuery(\n $sourceId: ID!\n $timerange: TimerangeInput!\n $filterQuery: String\n ) {\n source(id: $sourceId) {\n id\n OverviewHost(timerange: $timerange, filterQuery: $filterQuery) {\n auditbeatAuditd\n auditbeatFIM\n auditbeatLogin\n auditbeatPackage\n auditbeatProcess\n auditbeatUser\n }\n }\n }', }, - query: {}, }; export const mockResponseHost = { diff --git a/x-pack/legacy/plugins/siem/server/lib/tls/elasticsearch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/tls/elasticsearch_adapter.test.ts index 32a5c72215dda..428685cbaddb8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/tls/elasticsearch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/tls/elasticsearch_adapter.test.ts @@ -22,7 +22,6 @@ describe('elasticsearch_adapter', () => { let data: TlsData; const mockCallWithRequest = jest.fn(); const mockFramework: FrameworkAdapter = { - version: 'mock', callWithRequest: mockCallWithRequest, registerGraphQLEndpoint: jest.fn(), getIndexPatternsService: jest.fn(), diff --git a/x-pack/legacy/plugins/siem/server/lib/tls/mock.ts b/x-pack/legacy/plugins/siem/server/lib/tls/mock.ts index a81862b6e7e90..4b27d541ec992 100644 --- a/x-pack/legacy/plugins/siem/server/lib/tls/mock.ts +++ b/x-pack/legacy/plugins/siem/server/lib/tls/mock.ts @@ -212,8 +212,7 @@ export const expectedTlsEdges = [ ]; export const mockRequest = { - params: {}, - payload: { + body: { operationName: 'GetTlsQuery', variables: { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], @@ -229,7 +228,6 @@ export const mockRequest = { query: 'query GetTlsQuery($sourceId: ID!, $filterQuery: String, $flowTarget: FlowTarget!, $ip: String!, $pagination: PaginationInputPaginated!, $sort: TlsSortField!, $timerange: TimerangeInput!, $defaultIndex: [String!]!, $inspect: Boolean!) {\n source(id: $sourceId) {\n id\n Tls(filterQuery: $filterQuery, flowTarget: $flowTarget, ip: $ip, pagination: $pagination, sort: $sort, timerange: $timerange, defaultIndex: $defaultIndex) {\n totalCount\n edges {\n node {\n _id\n alternativeNames\n commonNames\n ja3\n issuerNames\n notAfter\n __typename\n }\n cursor {\n value\n __typename\n }\n __typename\n }\n pageInfo {\n activePage\n fakeTotalCount\n showMorePagesIndicator\n __typename\n }\n inspect @include(if: $inspect) {\n dsl\n response\n __typename\n }\n __typename\n }\n __typename\n }\n}\n', }, - query: {}, }; export const mockResponse = { diff --git a/x-pack/legacy/plugins/siem/server/lib/types.ts b/x-pack/legacy/plugins/siem/server/lib/types.ts index 9034ab4e6af83..34a50cf962412 100644 --- a/x-pack/legacy/plugins/siem/server/lib/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/types.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AuthenticatedUser } from '../../../../../plugins/security/public'; +import { RequestHandlerContext } from '../../../../../../src/core/server'; export { ConfigType as Configuration } from '../../../../../plugins/siem/server'; + import { Anomalies } from './anomalies'; import { Authentications } from './authentications'; import { Events } from './events'; @@ -54,6 +57,8 @@ export interface AppBackendLibs extends AppDomainLibs { export interface SiemContext { req: FrameworkRequest; + context: RequestHandlerContext; + user: AuthenticatedUser | null; } export interface TotalValue { diff --git a/x-pack/legacy/plugins/siem/server/plugin.ts b/x-pack/legacy/plugins/siem/server/plugin.ts index 94314367be59c..e15248e5200ee 100644 --- a/x-pack/legacy/plugins/siem/server/plugin.ts +++ b/x-pack/legacy/plugins/siem/server/plugin.ts @@ -5,39 +5,71 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreSetup, PluginInitializerContext, Logger } from 'src/core/server'; -import { SecurityPluginSetup } from '../../../../plugins/security/server'; -import { PluginSetupContract as FeaturesSetupContract } from '../../../../plugins/features/server'; + +import { + CoreSetup, + CoreStart, + PluginInitializerContext, + Logger, +} from '../../../../../src/core/server'; +import { SecurityPluginSetup as SecuritySetup } from '../../../../plugins/security/server'; +import { PluginSetupContract as FeaturesSetup } from '../../../../plugins/features/server'; +import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../../../plugins/encrypted_saved_objects/server'; +import { SpacesPluginSetup as SpacesSetup } from '../../../../plugins/spaces/server'; +import { PluginStartContract as ActionsStart } from '../../../../plugins/actions/server'; +import { LegacyServices } from './types'; import { initServer } from './init_server'; import { compose } from './lib/compose/kibana'; +import { initRoutes, LegacyInitRoutes } from './routes'; +import { isAlertExecutor } from './lib/detection_engine/signals/types'; +import { signalRulesAlertType } from './lib/detection_engine/signals/signal_rule_alert_type'; import { noteSavedObjectType, pinnedEventSavedObjectType, timelineSavedObjectType, ruleStatusSavedObjectType, } from './saved_objects'; +import { ClientsService } from './services'; + +export { CoreSetup, CoreStart }; -export type SiemPluginSecurity = Pick; +export interface SetupPlugins { + encryptedSavedObjects: EncryptedSavedObjectsSetup; + features: FeaturesSetup; + security: SecuritySetup; + spaces?: SpacesSetup; +} -export interface PluginsSetup { - features: FeaturesSetupContract; - security: SiemPluginSecurity; +export interface StartPlugins { + actions: ActionsStart; } export class Plugin { readonly name = 'siem'; private readonly logger: Logger; private context: PluginInitializerContext; + private clients: ClientsService; + private legacyInitRoutes?: LegacyInitRoutes; constructor(context: PluginInitializerContext) { this.context = context; this.logger = context.logger.get('plugins', this.name); + this.clients = new ClientsService(); this.logger.debug('Shim plugin initialized'); } - public setup(core: CoreSetup, plugins: PluginsSetup) { + public setup(core: CoreSetup, plugins: SetupPlugins, __legacy: LegacyServices) { this.logger.debug('Shim plugin setup'); + + this.clients.setup(core.elasticsearch.dataClient, plugins.spaces?.spacesService); + + this.legacyInitRoutes = initRoutes( + __legacy.route, + __legacy.config, + plugins.encryptedSavedObjects?.usingEphemeralEncryptionKey ?? false + ); + plugins.features.registerFeature({ id: this.name, name: i18n.translate('xpack.siem.featureRegistry.linkSiemTitle', { @@ -98,7 +130,23 @@ export class Plugin { }, }); - const libs = compose(core, plugins, this.context.env); + if (__legacy.alerting != null) { + const type = signalRulesAlertType({ + logger: this.logger, + version: this.context.env.packageInfo.version, + }); + if (isAlertExecutor(type)) { + __legacy.alerting.setup.registerType(type); + } + } + + const libs = compose(core, plugins, this.context.env.mode.prod); initServer(libs); } + + public start(core: CoreStart, plugins: StartPlugins) { + this.clients.start(core.savedObjects, plugins.actions); + + this.legacyInitRoutes!(this.clients.createGetScoped()); + } } diff --git a/x-pack/legacy/plugins/siem/server/routes/index.ts b/x-pack/legacy/plugins/siem/server/routes/index.ts new file mode 100644 index 0000000000000..82fc4d8c11722 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/routes/index.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { LegacyServices } from '../types'; +import { GetScopedClients } from '../services'; + +import { createRulesRoute } from '../lib/detection_engine/routes/rules/create_rules_route'; +import { createIndexRoute } from '../lib/detection_engine/routes/index/create_index_route'; +import { readIndexRoute } from '../lib/detection_engine/routes/index/read_index_route'; +import { readRulesRoute } from '../lib/detection_engine/routes/rules/read_rules_route'; +import { findRulesRoute } from '../lib/detection_engine/routes/rules/find_rules_route'; +import { deleteRulesRoute } from '../lib/detection_engine/routes/rules/delete_rules_route'; +import { updateRulesRoute } from '../lib/detection_engine/routes/rules/update_rules_route'; +import { patchRulesRoute } from '../lib/detection_engine/routes/rules/patch_rules_route'; +import { setSignalsStatusRoute } from '../lib/detection_engine/routes/signals/open_close_signals_route'; +import { querySignalsRoute } from '../lib/detection_engine/routes/signals/query_signals_route'; +import { deleteIndexRoute } from '../lib/detection_engine/routes/index/delete_index_route'; +import { readTagsRoute } from '../lib/detection_engine/routes/tags/read_tags_route'; +import { readPrivilegesRoute } from '../lib/detection_engine/routes/privileges/read_privileges_route'; +import { addPrepackedRulesRoute } from '../lib/detection_engine/routes/rules/add_prepackaged_rules_route'; +import { createRulesBulkRoute } from '../lib/detection_engine/routes/rules/create_rules_bulk_route'; +import { updateRulesBulkRoute } from '../lib/detection_engine/routes/rules/update_rules_bulk_route'; +import { patchRulesBulkRoute } from '../lib/detection_engine/routes/rules/patch_rules_bulk_route'; +import { deleteRulesBulkRoute } from '../lib/detection_engine/routes/rules/delete_rules_bulk_route'; +import { importRulesRoute } from '../lib/detection_engine/routes/rules/import_rules_route'; +import { exportRulesRoute } from '../lib/detection_engine/routes/rules/export_rules_route'; +import { findRulesStatusesRoute } from '../lib/detection_engine/routes/rules/find_rules_status_route'; +import { getPrepackagedRulesStatusRoute } from '../lib/detection_engine/routes/rules/get_prepackaged_rules_status_route'; + +export type LegacyInitRoutes = (getClients: GetScopedClients) => void; + +export const initRoutes = ( + route: LegacyServices['route'], + config: LegacyServices['config'], + usingEphemeralEncryptionKey: boolean +) => (getClients: GetScopedClients): void => { + // Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules + // All REST rule creation, deletion, updating, etc...... + createRulesRoute(route, config, getClients); + readRulesRoute(route, getClients); + updateRulesRoute(route, config, getClients); + patchRulesRoute(route, getClients); + deleteRulesRoute(route, getClients); + findRulesRoute(route, getClients); + + addPrepackedRulesRoute(route, config, getClients); + getPrepackagedRulesStatusRoute(route, getClients); + createRulesBulkRoute(route, config, getClients); + updateRulesBulkRoute(route, config, getClients); + patchRulesBulkRoute(route, getClients); + deleteRulesBulkRoute(route, getClients); + + importRulesRoute(route, config, getClients); + exportRulesRoute(route, config, getClients); + + findRulesStatusesRoute(route, getClients); + + // Detection Engine Signals routes that have the REST endpoints of /api/detection_engine/signals + // POST /api/detection_engine/signals/status + // Example usage can be found in siem/server/lib/detection_engine/scripts/signals + setSignalsStatusRoute(route, config, getClients); + querySignalsRoute(route, config, getClients); + + // Detection Engine index routes that have the REST endpoints of /api/detection_engine/index + // All REST index creation, policy management for spaces + createIndexRoute(route, config, getClients); + readIndexRoute(route, config, getClients); + deleteIndexRoute(route, config, getClients); + + // Detection Engine tags routes that have the REST endpoints of /api/detection_engine/tags + readTagsRoute(route, getClients); + + // Privileges API to get the generic user privileges + readPrivilegesRoute(route, config, usingEphemeralEncryptionKey, getClients); +}; diff --git a/x-pack/legacy/plugins/siem/server/services/clients.test.ts b/x-pack/legacy/plugins/siem/server/services/clients.test.ts new file mode 100644 index 0000000000000..7f63a8f5e949c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/services/clients.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { coreMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { actionsMock } from '../../../../../plugins/actions/server/mocks'; + +import { ClientsService } from './clients'; + +describe('ClientsService', () => { + describe('spacesClient', () => { + describe('#getSpaceId', () => { + it('returns the default spaceId if spaces are disabled', async () => { + const clients = new ClientsService(); + + const actions = actionsMock.createStart(); + const { elasticsearch } = coreMock.createSetup(); + const { savedObjects } = coreMock.createStart(); + const request = httpServerMock.createRawRequest(); + const spacesService = undefined; + + clients.setup(elasticsearch.dataClient, spacesService); + clients.start(savedObjects, actions); + + const { spacesClient } = await clients.createGetScoped()(request); + expect(spacesClient.getSpaceId()).toEqual('default'); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/services/clients.ts b/x-pack/legacy/plugins/siem/server/services/clients.ts new file mode 100644 index 0000000000000..ca50eda4e7a6c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/services/clients.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + IClusterClient, + IScopedClusterClient, + KibanaRequest, + LegacyRequest, + SavedObjectsClientContract, +} from '../../../../../../src/core/server'; +import { ActionsClient } from '../../../../../plugins/actions/server'; +import { AlertsClient } from '../../../../../legacy/plugins/alerting/server'; +import { SpacesServiceSetup } from '../../../../../plugins/spaces/server'; +import { CoreStart, StartPlugins } from '../plugin'; + +export interface Clients { + actionsClient?: ActionsClient; + clusterClient: IScopedClusterClient; + spacesClient: { getSpaceId: () => string }; + savedObjectsClient: SavedObjectsClientContract; +} +interface LegacyClients { + alertsClient?: AlertsClient; +} +export type GetScopedClients = (request: LegacyRequest) => Promise; + +export class ClientsService { + private actions?: StartPlugins['actions']; + private clusterClient?: IClusterClient; + private savedObjects?: CoreStart['savedObjects']; + private spacesService?: SpacesServiceSetup; + + public setup(clusterClient: IClusterClient, spacesService?: SpacesServiceSetup) { + this.clusterClient = clusterClient; + this.spacesService = spacesService; + } + + public start(savedObjects: CoreStart['savedObjects'], actions: StartPlugins['actions']) { + this.savedObjects = savedObjects; + this.actions = actions; + } + + public createGetScoped(): GetScopedClients { + if (!this.clusterClient || !this.savedObjects) { + throw new Error('Services not initialized'); + } + + return async (request: LegacyRequest) => { + const kibanaRequest = KibanaRequest.from(request); + + return { + alertsClient: request.getAlertsClient?.(), + actionsClient: await this.actions?.getActionsClientWithRequest?.(kibanaRequest), + clusterClient: this.clusterClient!.asScoped(kibanaRequest), + savedObjectsClient: this.savedObjects!.getScopedClient(kibanaRequest), + spacesClient: { + getSpaceId: () => this.spacesService?.getSpaceId?.(kibanaRequest) ?? 'default', + }, + }; + }; + } +} diff --git a/x-pack/legacy/plugins/siem/server/services/index.ts b/x-pack/legacy/plugins/siem/server/services/index.ts new file mode 100644 index 0000000000000..f4deea2c2a3fd --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/services/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ClientsService, GetScopedClients } from './clients'; diff --git a/x-pack/legacy/plugins/siem/server/types.ts b/x-pack/legacy/plugins/siem/server/types.ts index 7c07e63404eaa..e7831bb5d0451 100644 --- a/x-pack/legacy/plugins/siem/server/types.ts +++ b/x-pack/legacy/plugins/siem/server/types.ts @@ -6,28 +6,10 @@ import { Legacy } from 'kibana'; -export interface ServerFacade { +export { LegacyRequest } from '../../../../../src/core/server'; + +export interface LegacyServices { + alerting?: Legacy.Server['plugins']['alerting']; config: Legacy.Server['config']; - usingEphemeralEncryptionKey: boolean; - plugins: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - actions: any; // We have to do this at the moment because the types are not compatible - alerting?: Legacy.Server['plugins']['alerting']; - elasticsearch: Legacy.Server['plugins']['elasticsearch']; - spaces: Legacy.Server['plugins']['spaces']; - savedObjects: Legacy.Server['savedObjects']['SavedObjectsClient']; - }; route: Legacy.Server['route']; } - -export interface RequestFacade { - auth: Legacy.Request['auth']; - getAlertsClient?: Legacy.Request['getAlertsClient']; - getActionsClient?: Legacy.Request['getActionsClient']; - getSavedObjectsClient?: Legacy.Request['getSavedObjectsClient']; - headers: Legacy.Request['headers']; - method: Legacy.Request['method']; - params: Legacy.Request['params']; - payload: unknown; - query: Legacy.Request['query']; -} diff --git a/x-pack/plugins/siem/server/config.ts b/x-pack/plugins/siem/server/config.ts index 456646cc825f3..224043c0c6fe5 100644 --- a/x-pack/plugins/siem/server/config.ts +++ b/x-pack/plugins/siem/server/config.ts @@ -6,7 +6,7 @@ import { Observable } from 'rxjs'; import { schema, TypeOf } from '@kbn/config-schema'; -import { PluginInitializerContext } from 'src/core/server'; +import { PluginInitializerContext } from '../../../../src/core/server'; import { SIGNALS_INDEX_KEY, DEFAULT_SIGNALS_INDEX, diff --git a/x-pack/plugins/siem/server/index.ts b/x-pack/plugins/siem/server/index.ts index c675be691b47e..83e2f900a3b90 100644 --- a/x-pack/plugins/siem/server/index.ts +++ b/x-pack/plugins/siem/server/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'src/core/server'; +import { PluginInitializerContext } from '../../../../src/core/server'; import { Plugin } from './plugin'; import { configSchema, ConfigType } from './config'; diff --git a/x-pack/plugins/siem/server/plugin.ts b/x-pack/plugins/siem/server/plugin.ts index 866f4d7575e2f..ccc6aef1452b2 100644 --- a/x-pack/plugins/siem/server/plugin.ts +++ b/x-pack/plugins/siem/server/plugin.ts @@ -6,7 +6,7 @@ import { Observable } from 'rxjs'; -import { CoreSetup, PluginInitializerContext, Logger } from 'src/core/server'; +import { CoreSetup, PluginInitializerContext, Logger } from '../../../../src/core/server'; import { createConfig$, ConfigType } from './config'; export class Plugin {