mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[SIEM] Final Shimming/Prep for Server NP Migration (#56814)
* Route all our server setup through the plugin Next we'll be trimming down ServerFacade to the final few dependencies, and using our plugin dependencies for the rest. * Clean up server plugin exports For now, let's try to simplify our typings by exporting our plugin's dependencies from the plugin itself, since we know it already knows about them. * Move DE Routes to to conventional location I'm throwing the alerting registration in the plugin for now, too. * Loosen up our RequestFacade Now that we've audited our use of request objects, we can switch to the more friendly LegacyRequest that all our utilities are expecting. LegacyRequest doesn't have plugins, either, so our only remaining errant usage is retrieving clients from it, which we call out explicitly. * Remove uses of 'src' alias This just threw an error on startup, so I'm fixing all of them to be safe. * Fix types of our GraphQL requests These come through as new KibanaRequests and not as the LegacyRequest that I had incorrectly typed them as. I'm only caring about the `body` property right now, since that's all we really deal with, and in testing it was all that was populated in our actual requests, too. * Initialize our routes with SetupServices We're using the legacy version for now, but ServerFacade will be gone by the end of the migration. * Swap legacy spaces plugin for NP This changes the signature of getIndex, which is used in quite a few places. We'll see how bad that looks in the next commit. * Remove unneeded typing We're already ignoring another portion of the platform shim due to it being incompatibly typed, so we might as well remove this. * WIP: Converting our DE routes to use consolidated services This contains our legacy stuff for now. Eventually, it will only be our NP services. This breaks a few tests due to the way createMockServer works, but I'll clean that up momentarily. * Fix DE routing tests following refactor The createMockServer helper does a few things differently: * returns mocked LegacyServices that can be passed to our route factories * does not return a server object as we don't need it, except for: * returns an inject function that we can use to execute a request We're casting our services because we're only mocking a subset of what LegacySetupServices entails. Mainly, this allows me to continue moving things off of ServerFacade without significant refactoring of tests. * Fix incompatible request types Unfortunately, LegacyRequest does not allow a request's `query` values to be anything other than a string or string[]. However, in practice they can (and are) objects, booleans, etc. Our request types (e.g. QueryRequest) are correct, but because service functions are LegacyRequest's implementation of `query`, we need to cast them lest a type error occur. For now, the easiest solution is to do this in the request handler: intersecting with our RequestFacade (LegacyRequest) allows us to both a) pluck our query params off the request and b) pass the request to NP services. * Move our use of encryptedSavedObjects to NP We're just retrieving a boolean from it right now, which is also guarded against the plugin being unavailable. If this usage becomes more widespread, we'll make this available at a higher level, probably in redux. * Use NP elasticsearch client * Simplifies our generic type to accept two arguments: params and the return value * Options is fixed and we were never specifying anything meaningful there * Updates all DE cluster calls to use callWithRequestFactory * Update DE mocks with NP elasticsearch * createMockServer now returns the callCluster mock, so that you can easily mock a client response without needing to know the details of how we define that function * Remove savedObjects dependency from our legacy dependencies This was added during a refactor, but we were never actually using this code. We always retrieve the client from the request via getSavedObjectsClient. I think that the NP client has a slightly different interface, so we're going to create a helper to retrieve it from the request, same as we do with the elastic client. In the future, both of these will be available on the request context, and we can remove the helpers entirely. * WIP: Convert services to stateful object In trying to migrate over the savedObjectsClient, I realized that it is not available during setup in the same way that the ES client is. Since our routes need pieces from both setup and start phases, I've added a Services class to accumulate/transform these services and expose scoped clients when given a legacy request. In New Platform, these clients will be available upon the request context and we should be able to remove getScopedServicesFactory for our routes. A subset of Services' functionality may still be useful, we'll see. * WIP: Converting routes to use Services factory I decided that config shouldn't live in here, as this is only client-related stuff. Probably going to rename this ClientsService. Things are still very much broken. * WIP: Qualifying our Service to ClientsService This gets us client-related services (ES, SavedObjects, Alerts), but it is independent of any configuration, which is gonna be another service. * Fix types on getIndex function This is a weird helper, I'm not really sure where it should go. * Our ClientsService is a clients ... service Return clients, as this is closer to what we'll get in the request context. * Clean up our server types * Declare legacy types at top-level file * Don't re-export from the plugin solely for convenience, that's a slippery slope straight to circular dependencies * Remove RequestFacade as it was a facade for LegacyRequest * Rename ServerFacade/LegacySetupServices to just LegacyServices * Refactor mocks for new architecture * Separates config, server, and client mocks, as they're now independent in our system, route-wise. * gets one test working, the rest will follow. * Simplify our routing mocks * Adds mock for our new clients service * Greatly simplifies both server and mock configs * Renames factory method of client service * Loosen graphQL endpoint validations These work fine in production, but it's graphQL so we don't really need the additional validation of these endpoints, and we weren't leveraging these types anywhere in Typescript land. Additionally, these restrictive validations prevent the initial introspection calls done by graphiQL to get schema information, and without schemae graphiql wasn't very helpful. This is a dev-only problem, but that's the audience of graphiql. * Remove unused graphql endpoint This was only registered in dev mode; I thought that it was needed by graphiql. However, after digging further I realized that graphiQL also only makes POST calls to our real graphQL endpoint, so this route is unnecessary. * Reduce our dependence on PluginInitializerContext After a little more introspection I realized our FrameworkAdapter doesn't need the kibana version. It was only used in order to make a dev request via (graphiql), but even that can be performed with a simpler xsrf header. This meant that we really only wanted to know whether we're in production or not, so instead we pass that simple boolean to the constructor. * Fix FrameworkAdapter type We no longer need this property. * Update detections route tests Uses the new routes interfaces, and our corresponding new mocks. * Remove unnecessary null checks Our savedObjectsClient is always going to be there. * Remove unused type YAGNI * Remove unused savedObjects client Turns out we were only destructuring this client for the null check. * Handle case where spaces is disabled We already null-coalesce properly in the clients service, but this property access was missed. * Return default signals index if spaces are disabled * Remove unnecessary casting of our alerts client mock I think that this was the result of us importing the wrong AlertsClient type, or perhaps the types were out of sync. Regardless, they work now. * Return the 'default' space even when spaces are disabled This will allow users with spaces disabled to enable spaces without losing data. The tradeoff is that they may be surprised when signals don't exist within their configured xpack.siem.signalsIndex. * Account for spaces being disabled in ClientsService * Updates types to reflect that spaces may be unavailable * Adds a test for getSpaceId's behavior when spaces are disabled * Fix false positives in query signals routes tests * Refactors mock expectations so that they're actually evaluated; they can't go within a mockImplementation call as it's evaluated in the wrong scope. * Fixes duplicated test to use the proper assertions * style: Prefer null coalescing over ternary
This commit is contained in:
parent
b22045433e
commit
8513498e2d
93 changed files with 1408 additions and 1392 deletions
|
@ -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
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -29,7 +29,6 @@ describe('alerts elasticsearch_adapter', () => {
|
|||
return mockAlertsHistogramDataResponse;
|
||||
});
|
||||
const mockFramework: FrameworkAdapter = {
|
||||
version: 'mock',
|
||||
callWithRequest: mockCallWithRequest,
|
||||
registerGraphQLEndpoint: jest.fn(),
|
||||
getIndexPatternsService: jest.fn(),
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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<unknown> => {
|
||||
return callWithRequest('transport.request', {
|
||||
|
|
|
@ -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<IndicesDeleteParams, CallClusterOptions, boolean>,
|
||||
callWithRequest: CallWithRequest<IndicesDeleteParams, boolean>,
|
||||
index: string
|
||||
): Promise<boolean> => {
|
||||
return callWithRequest('indices.delete', {
|
||||
|
|
|
@ -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<unknown> => {
|
||||
return callWithRequest('transport.request', {
|
||||
|
|
|
@ -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<IndicesDeleteTemplateParams, CallClusterOptions, unknown>,
|
||||
callWithRequest: CallWithRequest<IndicesDeleteTemplateParams, unknown>,
|
||||
name: string
|
||||
): Promise<unknown> => {
|
||||
return callWithRequest('indices.deleteTemplate', {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<boolean> => {
|
||||
try {
|
||||
|
|
|
@ -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<IndicesExistsTemplateParams, CallClusterOptions, boolean>,
|
||||
callWithRequest: CallWithRequest<IndicesExistsTemplateParams, boolean>,
|
||||
template: string
|
||||
): Promise<boolean> => {
|
||||
return callWithRequest('indices.existsTemplate', {
|
||||
|
|
|
@ -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<IndicesGetSettingsParams, CallClusterOptions, unknown>,
|
||||
callWithRequest: CallWithRequest<IndicesGetSettingsParams, unknown>,
|
||||
index: string
|
||||
): Promise<unknown> => {
|
||||
return callWithRequest('indices.get', {
|
||||
|
|
|
@ -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<unknown> => {
|
||||
|
|
|
@ -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<IndicesPutTemplateParams, CallClusterOptions, unknown>,
|
||||
callWithRequest: CallWithRequest<IndicesPutTemplateParams, unknown>,
|
||||
name: string,
|
||||
body: unknown
|
||||
): Promise<unknown> => {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { CallWithRequest } from '../types';
|
||||
|
||||
export const readPrivileges = async (
|
||||
callWithRequest: CallWithRequest<unknown, unknown, unknown>,
|
||||
callWithRequest: CallWithRequest<{}, unknown>,
|
||||
index: string
|
||||
): Promise<unknown> => {
|
||||
return callWithRequest('transport.request', {
|
||||
|
|
|
@ -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<string, string>): 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<string, string> = 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<string, string> = 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 } })),
|
||||
}));
|
|
@ -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<ActionsClient>,
|
||||
alertsClient: alertsClientMock.create(),
|
||||
clusterClient: elasticsearchServiceMock.createScopedClusterClient(),
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
spacesClient: { getSpaceId: jest.fn() },
|
||||
});
|
||||
|
||||
const createGetScoped = () =>
|
||||
jest.fn(() => Promise.resolve(createClients()) as ReturnType<GetScopedClients>);
|
||||
|
||||
const createClientsServiceMock = () => {
|
||||
return {
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
createGetScoped,
|
||||
};
|
||||
};
|
||||
|
||||
export const clientsServiceMock = {
|
||||
create: createClientsServiceMock,
|
||||
createGetScoped,
|
||||
createClients,
|
||||
};
|
|
@ -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(),
|
||||
});
|
|
@ -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<IRuleSavedAttrib
|
|||
total: 0,
|
||||
saved_objects: [],
|
||||
});
|
||||
|
||||
export const getIndexName = () => 'index-name';
|
||||
export const getEmptyIndex = (): { _shards: Partial<ShardsResponse> } => ({
|
||||
_shards: { total: 0 },
|
||||
});
|
||||
export const getNonEmptyIndex = (): { _shards: Partial<ShardsResponse> } => ({
|
||||
_shards: { total: 1 },
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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`,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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`,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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<number>('savedObjects.maxImportExportSize');
|
||||
const exportSizeLimit = config().get<number>('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));
|
||||
};
|
||||
|
|
|
@ -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]`,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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 = <T extends Record<string, any>>(obj: T): Partial<T> |
|
|||
}, {});
|
||||
};
|
||||
|
||||
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<RuleStatusResponse>({}));
|
||||
return statuses;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const findRulesStatusesRoute = (server: ServerFacade): void => {
|
||||
server.route(createFindRulesStatusRoute);
|
||||
export const findRulesStatusesRoute = (
|
||||
route: LegacyServices['route'],
|
||||
getClients: GetScopedClients
|
||||
): void => {
|
||||
route(createFindRulesStatusRoute(getClients));
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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<number>('savedObjects.maxImportExportSize');
|
||||
const objectLimit = config().get<number>('savedObjects.maxImportExportSize');
|
||||
const readStream = createRulesStreamFromNdJson(request.payload.file, objectLimit);
|
||||
const parsedObjects = await createPromiseFromStreams<PromiseFromStreams[]>([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));
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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 { ServerInjectOptions } from 'hapi';
|
||||
import { omit } from 'lodash/fp';
|
||||
|
||||
import { updateRulesRoute } from './update_rules_route';
|
||||
import { ServerInjectOptions } from 'hapi';
|
||||
|
||||
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',
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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 { ServerInjectOptions } from 'hapi';
|
||||
import { omit } from 'lodash/fp';
|
||||
|
||||
import { updateRulesRoute } from './update_rules_route';
|
||||
import { ServerInjectOptions } from 'hapi';
|
||||
|
||||
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',
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
beforeEach(() => {
|
||||
mockConfig = () => ({
|
||||
get: jest.fn(() => 'mockSignalsIndex'),
|
||||
has: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it('appends the space id to the configured index', () => {
|
||||
const getSpaceId = jest.fn(() => 'myspace');
|
||||
const index = getIndex(getSpaceId, mockConfig);
|
||||
|
||||
expect(index).toEqual('mockSignalsIndex-myspace');
|
||||
});
|
||||
|
||||
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 };
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<RequestFacade, 'query'>,
|
||||
server: ServerFacade
|
||||
): string => {
|
||||
const spaceId = server.plugins.spaces?.getSpaceId?.(request) ?? 'default';
|
||||
const signalsIndex = server.config().get(`xpack.${APP_ID}.${SIGNALS_INDEX_KEY}`);
|
||||
export const getIndex = (getSpaceId: () => string, config: LegacyServices['config']): string => {
|
||||
const signalsIndex = config().get<string>(`xpack.${APP_ID}.${SIGNALS_INDEX_KEY}`);
|
||||
const spaceId = getSpaceId();
|
||||
|
||||
return `${signalsIndex}-${spaceId}`;
|
||||
};
|
||||
|
||||
export const callWithRequestFactory = (
|
||||
request: RequestFacade | Omit<RequestFacade, 'query'>,
|
||||
server: ServerFacade
|
||||
) => {
|
||||
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
|
||||
return <T, U>(endpoint: string, params: T, options?: U) => {
|
||||
return callWithRequest(request, endpoint, params, options);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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' }],
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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<RuleAlertParamsRest> & {
|
||||
|
@ -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<RequestFacade, 'query'> {
|
||||
export interface ImportRulesRequest extends Omit<LegacyRequest, 'query'> {
|
||||
query: { overwrite: boolean };
|
||||
payload: { file: HapiReadableStream };
|
||||
}
|
||||
|
||||
export interface ExportRulesRequest extends Omit<RequestFacade, 'query'> {
|
||||
export interface ExportRulesRequest extends Omit<LegacyRequest, 'query'> {
|
||||
payload: { objects: Array<{ rule_id: string }> | null | undefined };
|
||||
query: {
|
||||
file_name: string;
|
||||
|
@ -125,11 +125,11 @@ export interface ExportRulesRequest extends Omit<RequestFacade, 'query'> {
|
|||
};
|
||||
}
|
||||
|
||||
export type QueryRequest = Omit<RequestFacade, 'query'> & {
|
||||
export type QueryRequest = Omit<LegacyRequest, 'query'> & {
|
||||
query: { id: string | undefined; rule_id: string | undefined };
|
||||
};
|
||||
|
||||
export interface QueryBulkRequest extends RequestFacade {
|
||||
export interface QueryBulkRequest extends LegacyRequest {
|
||||
payload: Array<QueryRequest['query']>;
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ export interface FindRuleParams {
|
|||
sortOrder?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
export interface FindRulesRequest extends Omit<RequestFacade, 'query'> {
|
||||
export interface FindRulesRequest extends Omit<LegacyRequest, 'query'> {
|
||||
query: {
|
||||
per_page: number;
|
||||
page: number;
|
||||
|
@ -155,7 +155,7 @@ export interface FindRulesRequest extends Omit<RequestFacade, 'query'> {
|
|||
};
|
||||
}
|
||||
|
||||
export interface FindRulesStatusesRequest extends Omit<RequestFacade, 'query'> {
|
||||
export interface FindRulesStatusesRequest extends Omit<LegacyRequest, 'query'> {
|
||||
query: {
|
||||
ids: string[];
|
||||
};
|
||||
|
|
|
@ -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<SignalsStatusParams, 'signalIds'> & {
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<T, U, V> = (endpoint: string, params: T, options?: U) => Promise<V>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type CallWithRequest<T extends Record<string, any>, V> = (
|
||||
endpoint: string,
|
||||
params: T,
|
||||
options?: CallAPIOptions
|
||||
) => Promise<V>;
|
||||
|
|
|
@ -519,7 +519,6 @@ describe('events elasticsearch_adapter', () => {
|
|||
return mockResponseMap;
|
||||
});
|
||||
const mockFramework: FrameworkAdapter = {
|
||||
version: 'mock',
|
||||
callWithRequest: mockCallWithRequest,
|
||||
registerGraphQLEndpoint: jest.fn(),
|
||||
getIndexPatternsService: jest.fn(),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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<InternalRequest extends WrappableRequest>(
|
||||
req: InternalRequest,
|
||||
export function wrapRequest(
|
||||
request: KibanaRequest,
|
||||
context: RequestHandlerContext,
|
||||
user: AuthenticatedUser | null
|
||||
): FrameworkRequest<InternalRequest> {
|
||||
const { auth, params, payload, query } = req;
|
||||
|
||||
): FrameworkRequest {
|
||||
return {
|
||||
[internalFrameworkRequest]: req,
|
||||
auth,
|
||||
[internalFrameworkRequest]: request,
|
||||
body: request.body,
|
||||
context,
|
||||
params,
|
||||
payload,
|
||||
query,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<Hit = {}, Aggregation = undefined>(
|
||||
req: FrameworkRequest,
|
||||
|
@ -46,24 +43,12 @@ export interface FrameworkAdapter {
|
|||
getIndexPatternsService(req: FrameworkRequest): FrameworkIndexPatternsService;
|
||||
}
|
||||
|
||||
export interface FrameworkRequest<InternalRequest extends WrappableRequest = RequestFacade> {
|
||||
[internalFrameworkRequest]: InternalRequest;
|
||||
export interface FrameworkRequest extends Pick<KibanaRequest, 'body'> {
|
||||
[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 = any, Params = any, Query = any> {
|
||||
payload: Payload;
|
||||
params: Params;
|
||||
query: Query;
|
||||
auth: RequestAuth;
|
||||
}
|
||||
|
||||
export interface DatabaseResponse {
|
||||
took: number;
|
||||
timeout: boolean;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 type SiemPluginSecurity = Pick<SecurityPluginSetup, 'authc'>;
|
||||
export { CoreSetup, CoreStart };
|
||||
|
||||
export interface PluginsSetup {
|
||||
features: FeaturesSetupContract;
|
||||
security: SiemPluginSecurity;
|
||||
export interface SetupPlugins {
|
||||
encryptedSavedObjects: EncryptedSavedObjectsSetup;
|
||||
features: FeaturesSetup;
|
||||
security: SecuritySetup;
|
||||
spaces?: SpacesSetup;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
78
x-pack/legacy/plugins/siem/server/routes/index.ts
Normal file
78
x-pack/legacy/plugins/siem/server/routes/index.ts
Normal file
|
@ -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);
|
||||
};
|
32
x-pack/legacy/plugins/siem/server/services/clients.test.ts
Normal file
32
x-pack/legacy/plugins/siem/server/services/clients.test.ts
Normal file
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
65
x-pack/legacy/plugins/siem/server/services/clients.ts
Normal file
65
x-pack/legacy/plugins/siem/server/services/clients.ts
Normal file
|
@ -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<Clients & LegacyClients>;
|
||||
|
||||
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',
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
7
x-pack/legacy/plugins/siem/server/services/index.ts
Normal file
7
x-pack/legacy/plugins/siem/server/services/index.ts
Normal file
|
@ -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';
|
|
@ -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'];
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue