[SIEM][Detection Engine] Reworks actionClient to work with new platform

## Summary

* Changes action client to use new platform from their changes
* Removes unit tests not needed
* Updates unit tests that need updating

### Checklist

Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR.

~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~

~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~

~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~

- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios

~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~

### For maintainers

~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~

- [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)
This commit is contained in:
Frank Hassanabad 2020-01-22 18:17:28 -07:00 committed by GitHub
parent 4348a34da9
commit da5710dac6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 291 additions and 488 deletions

View file

@ -153,11 +153,11 @@ export const siem = (kibana: any) => {
const { config, newPlatform, plugins, route } = server;
const { coreContext, env, setup } = newPlatform;
const initializerContext = { ...coreContext, env } as PluginInitializerContext;
const serverFacade = {
config,
plugins: {
alerting: plugins.alerting,
actions: newPlatform.start.plugins.actions,
elasticsearch: plugins.elasticsearch,
spaces: plugins.spaces,
savedObjects: server.savedObjects.SavedObjectsClient,

View file

@ -59,9 +59,12 @@ export const createMockServer = (config: Record<string, string> = defaultConfig)
};
server.decorate('request', 'getAlertsClient', () => alertsClient);
server.decorate('request', 'getBasePath', () => '/s/default');
server.decorate('request', 'getActionsClient', () => actionsClient);
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,
@ -79,11 +82,16 @@ export const createMockServerWithoutAlertClientDecoration = (
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();
serverWithoutAlertClient.decorate('request', 'getBasePath', () => '/s/default');
serverWithoutAlertClient.decorate('request', 'getActionsClient', () => actionsClient);
return {
serverWithoutAlertClient: serverWithoutAlertClient as ServerFacade & Hapi.Server,
@ -91,40 +99,6 @@ export const createMockServerWithoutAlertClientDecoration = (
};
};
export const createMockServerWithoutActionClientDecoration = (
config: Record<string, string> = defaultConfig
) => {
const serverWithoutActionClient = new Hapi.Server({
port: 0,
});
serverWithoutActionClient.config = () => createMockKibanaConfig(config);
const alertsClient = alertsClientMock.create();
serverWithoutActionClient.decorate('request', 'getBasePath', () => '/s/default');
serverWithoutActionClient.decorate('request', 'getAlertsClient', () => alertsClient);
return {
serverWithoutActionClient: serverWithoutActionClient as ServerFacade & Hapi.Server,
alertsClient,
};
};
export const createMockServerWithoutActionOrAlertClientDecoration = (
config: Record<string, string> = defaultConfig
) => {
const serverWithoutActionOrAlertClient = new Hapi.Server({
port: 0,
});
serverWithoutActionOrAlertClient.config = () => createMockKibanaConfig(config);
return {
serverWithoutActionOrAlertClient: serverWithoutActionOrAlertClient as ServerFacade &
Hapi.Server,
};
};
export const getMockIndexName = () =>
jest.fn().mockImplementation(() => ({
callWithRequest: jest.fn().mockImplementationOnce(() => 'index-name'),

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
getMockEmptyIndex,
getMockNonEmptyIndex,
} from '../__mocks__/_mock_server';
@ -67,30 +65,12 @@ describe('add_prepackaged_rules_route', () => {
expect(statusCode).toBe(200);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
createRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(addPrepackagedRulesRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
createRulesRoute(serverWithoutAlertClient);
const { statusCode } = await serverWithoutAlertClient.inject(addPrepackagedRulesRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
createRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(
addPrepackagedRulesRequest()
);
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -18,6 +18,7 @@ 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 => {
return {
@ -33,13 +34,13 @@ export const createAddPrepackedRulesRoute = (server: ServerFacade): Hapi.ServerR
},
async handler(request: RequestFacade, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: 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 || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
getMockEmptyIndex,
} from '../__mocks__/_mock_server';
import { createRulesRoute } from './create_rules_route';
@ -42,28 +40,12 @@ describe('create_rules_bulk', () => {
expect(statusCode).toBe(200);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
createRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(getReadBulkRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
createRulesRoute(serverWithoutAlertClient);
const { statusCode } = await serverWithoutAlertClient.inject(getReadBulkRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
createRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getReadBulkRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -21,6 +21,7 @@ import {
createBulkErrorObject,
} from '../utils';
import { createRulesBulkSchema } from '../schemas/create_rules_bulk_schema';
import { KibanaRequest } from '../../../../../../../../../src/core/server';
export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRoute => {
return {
@ -37,13 +38,13 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
},
async handler(request: BulkRulesRequest, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: 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 || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
getMockNonEmptyIndex,
getMockEmptyIndex,
} from '../__mocks__/_mock_server';
@ -58,28 +56,12 @@ describe('create_rules', () => {
expect(statusCode).toBe(200);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
createRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(getCreateRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
createRulesRoute(serverWithoutAlertClient);
const { statusCode } = await serverWithoutAlertClient.inject(getCreateRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
createRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getCreateRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -18,6 +18,7 @@ import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';
import { transformOrError } from './utils';
import { getIndexExists } from '../../index/get_index_exists';
import { callWithRequestFactory, getIndex, transformError } from '../utils';
import { KibanaRequest } from '../../../../../../../../../src/core/server';
export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute => {
return {
@ -62,13 +63,13 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
references,
} = request.payload;
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: 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 || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
} from '../__mocks__/_mock_server';
import { ServerInjectOptions } from 'hapi';
@ -97,28 +95,12 @@ describe('delete_rules', () => {
expect(parsed).toEqual(expected);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
deleteRulesBulkRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(getDeleteBulkRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
deleteRulesBulkRoute(serverWithoutAlertClient);
const { statusCode } = await serverWithoutAlertClient.inject(getDeleteBulkRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
deleteRulesBulkRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getDeleteBulkRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -15,64 +15,69 @@ import { transformOrBulkError, getIdBulkError } from './utils';
import { transformBulkError } from '../utils';
import { QueryBulkRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';
import { KibanaRequest } from '../../../../../../../../../src/core/server';
export const createDeleteRulesBulkRoute: Hapi.ServerRoute = {
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`,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
export const createDeleteRulesBulkRoute = (server: ServerFacade): 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`,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
},
payload: queryRulesBulkSchema,
},
payload: queryRulesBulkSchema,
},
},
async handler(request: QueryBulkRequest, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
const savedObjectsClient = isFunction(request.getSavedObjectsClient)
? request.getSavedObjectsClient()
: null;
if (!alertsClient || !actionsClient || !savedObjectsClient) {
return headers.response().code(404);
}
const rules = Promise.all(
request.payload.map(async payloadRule => {
const { id, rule_id: ruleId } = payloadRule;
const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)';
try {
const rule = await deleteRules({
actionsClient,
alertsClient,
id,
ruleId,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 6,
search: rule.id,
searchFields: ['alertId'],
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) {
return headers.response().code(404);
}
const rules = Promise.all(
request.payload.map(async payloadRule => {
const { id, rule_id: ruleId } = payloadRule;
const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)';
try {
const rule = await deleteRules({
actionsClient,
alertsClient,
id,
ruleId,
});
ruleStatuses.saved_objects.forEach(async obj =>
savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id)
);
return transformOrBulkError(idOrRuleIdOrUnknown, rule);
} else {
return getIdBulkError({ id, ruleId });
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 6,
search: rule.id,
searchFields: ['alertId'],
});
ruleStatuses.saved_objects.forEach(async obj =>
savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id)
);
return transformOrBulkError(idOrRuleIdOrUnknown, rule);
} else {
return getIdBulkError({ id, ruleId });
}
} catch (err) {
return transformBulkError(idOrRuleIdOrUnknown, err);
}
} catch (err) {
return transformBulkError(idOrRuleIdOrUnknown, err);
}
})
);
return rules;
},
})
);
return rules;
},
};
};
export const deleteRulesBulkRoute = (server: ServerFacade): void => {
server.route(createDeleteRulesBulkRoute);
server.route(createDeleteRulesBulkRoute(server));
};

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
} from '../__mocks__/_mock_server';
import { deleteRulesRoute } from './delete_rules_route';
@ -67,28 +65,12 @@ describe('delete_rules', () => {
expect(statusCode).toBe(404);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
deleteRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.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());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
deleteRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getDeleteRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -15,59 +15,64 @@ import { getIdError, transformOrError } 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: Hapi.ServerRoute = {
method: 'DELETE',
path: DETECTION_ENGINE_RULES_URL,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
export const createDeleteRulesRoute = (server: ServerFacade): Hapi.ServerRoute => {
return {
method: 'DELETE',
path: DETECTION_ENGINE_RULES_URL,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
},
query: queryRulesSchema,
},
query: queryRulesSchema,
},
},
async handler(request: QueryRequest, headers) {
const { id, rule_id: ruleId } = request.query;
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
const savedObjectsClient = isFunction(request.getSavedObjectsClient)
? request.getSavedObjectsClient()
: null;
if (!alertsClient || !actionsClient || !savedObjectsClient) {
return headers.response().code(404);
}
try {
const rule = await deleteRules({
actionsClient,
alertsClient,
id,
ruleId,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 6,
search: rule.id,
searchFields: ['alertId'],
});
ruleStatuses.saved_objects.forEach(async obj =>
savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id)
);
return transformOrError(rule, ruleStatuses.saved_objects[0]);
} else {
return getIdError({ id, ruleId });
async handler(request: QueryRequest, 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);
}
} catch (err) {
return transformError(err);
}
},
try {
const rule = await deleteRules({
actionsClient,
alertsClient,
id,
ruleId,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 6,
search: rule.id,
searchFields: ['alertId'],
});
ruleStatuses.saved_objects.forEach(async obj =>
savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id)
);
return transformOrError(rule, ruleStatuses.saved_objects[0]);
} else {
return getIdError({ id, ruleId });
}
} catch (err) {
return transformError(err);
}
},
};
};
export const deleteRulesRoute = (server: ServerFacade): void => {
server.route(createDeleteRulesRoute);
server.route(createDeleteRulesRoute(server));
};

View file

@ -31,11 +31,8 @@ export const createExportRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
},
async handler(request: ExportRulesRequest, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: null;
if (!alertsClient || !actionsClient) {
if (!alertsClient) {
return headers.response().code(404);
}

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
} from '../__mocks__/_mock_server';
import { findRulesRoute } from './find_rules_route';
@ -43,28 +41,12 @@ describe('find_rules', () => {
expect(statusCode).toBe(200);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
findRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(getFindRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
findRulesRoute(serverWithoutAlertClient);
const { statusCode } = await serverWithoutAlertClient.inject(getFindRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
findRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getFindRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -15,58 +15,61 @@ import { transformFindAlertsOrError } from './utils';
import { transformError } from '../utils';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';
export const createFindRulesRoute: Hapi.ServerRoute = {
method: 'GET',
path: `${DETECTION_ENGINE_RULES_URL}/_find`,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
export const createFindRulesRoute = (): Hapi.ServerRoute => {
return {
method: 'GET',
path: `${DETECTION_ENGINE_RULES_URL}/_find`,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
},
query: findRulesSchema,
},
query: findRulesSchema,
},
},
async handler(request: FindRulesRequest, headers) {
const { query } = request;
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
const savedObjectsClient = isFunction(request.getSavedObjectsClient)
? request.getSavedObjectsClient()
: null;
if (!alertsClient || !actionsClient || !savedObjectsClient) {
return headers.response().code(404);
}
async handler(request: FindRulesRequest, 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 rules = await findRules({
alertsClient,
perPage: query.per_page,
page: query.page,
sortField: query.sort_field,
sortOrder: query.sort_order,
filter: query.filter,
});
const ruleStatuses = await Promise.all(
rules.data.map(async rule => {
const results = await savedObjectsClient.find<IRuleSavedAttributesSavedObjectAttributes>({
type: ruleStatusSavedObjectType,
perPage: 1,
sortField: 'statusDate',
sortOrder: 'desc',
search: rule.id,
searchFields: ['alertId'],
});
return results;
})
);
return transformFindAlertsOrError(rules, ruleStatuses);
} catch (err) {
return transformError(err);
}
},
try {
const rules = await findRules({
alertsClient,
perPage: query.per_page,
page: query.page,
sortField: query.sort_field,
sortOrder: query.sort_order,
filter: query.filter,
});
const ruleStatuses = await Promise.all(
rules.data.map(async rule => {
const results = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 1,
sortField: 'statusDate',
sortOrder: 'desc',
search: rule.id,
searchFields: ['alertId'],
});
return results;
})
);
return transformFindAlertsOrError(rules, ruleStatuses);
} catch (err) {
return transformError(err);
}
},
};
};
export const findRulesRoute = (server: ServerFacade) => {
server.route(createFindRulesRoute);
server.route(createFindRulesRoute());
};

View file

@ -44,11 +44,10 @@ export const createFindRulesStatusRoute: Hapi.ServerRoute = {
async handler(request: FindRulesStatusesRequest, headers) {
const { query } = request;
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
const savedObjectsClient = isFunction(request.getSavedObjectsClient)
? request.getSavedObjectsClient()
: null;
if (!alertsClient || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
getMockNonEmptyIndex,
} from '../__mocks__/_mock_server';
import { createRulesRoute } from './create_rules_route';
@ -65,15 +63,6 @@ describe('get_prepackaged_rule_status_route', () => {
expect(statusCode).toBe(200);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
createRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(
getPrepackagedRulesStatusRequest()
);
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
createRulesRoute(serverWithoutAlertClient);
@ -82,17 +71,6 @@ describe('get_prepackaged_rule_status_route', () => {
);
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
createRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(
getPrepackagedRulesStatusRequest()
);
expect(statusCode).toBe(404);
});
});
describe('payload', () => {

View file

@ -29,11 +29,8 @@ export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => {
},
async handler(request: RequestFacade, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: null;
if (!alertsClient || !actionsClient) {
if (!alertsClient) {
return headers.response().code(404);
}

View file

@ -27,6 +27,7 @@ import { ImportRuleAlertRest } from '../../types';
import { transformOrImportError } from './utils';
import { updateRules } from '../../rules/update_rules';
import { importRulesQuerySchema, importRulesPayloadSchema } from '../schemas/import_rules_schema';
import { KibanaRequest } from '../../../../../../../../../src/core/server';
export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute => {
return {
@ -49,13 +50,13 @@ export const createImportRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
},
async handler(request: ImportRulesRequest, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: 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 || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}
const { filename } = request.payload.file.hapi;

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
} from '../__mocks__/_mock_server';
import { readRulesRoute } from './read_rules_route';
@ -44,28 +42,12 @@ describe('read_signals', () => {
expect(statusCode).toBe(200);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
readRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(getReadRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient is not available on the route', async () => {
const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration();
readRulesRoute(serverWithoutAlertClient);
const { statusCode } = await serverWithoutAlertClient.inject(getReadRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
readRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getReadRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -31,11 +31,10 @@ export const createReadRulesRoute: Hapi.ServerRoute = {
async handler(request: QueryRequest, headers) {
const { id, rule_id: ruleId } = request.query;
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
const savedObjectsClient = isFunction(request.getSavedObjectsClient)
? request.getSavedObjectsClient()
: null;
if (!alertsClient || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}
try {

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
} from '../__mocks__/_mock_server';
import { updateRulesRoute } from './update_rules_route';
@ -70,28 +68,12 @@ describe('update_rules_bulk', () => {
expect(parsed).toEqual(expected);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
updateRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.inject(getUpdateBulkRequest());
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(getUpdateBulkRequest());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
updateRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getUpdateBulkRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -17,6 +17,7 @@ import { transformBulkError } from '../utils';
import { updateRulesBulkSchema } from '../schemas/update_rules_bulk_schema';
import { updateRules } from '../../rules/update_rules';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';
import { KibanaRequest } from '../../../../../../../../../src/core/server';
export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRoute => {
return {
@ -33,13 +34,13 @@ export const createUpdateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
},
async handler(request: BulkUpdateRulesRequest, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient)
? request.getActionsClient()
: 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 || !actionsClient || !savedObjectsClient) {
if (!alertsClient || !savedObjectsClient) {
return headers.response().code(404);
}

View file

@ -6,9 +6,7 @@
import {
createMockServer,
createMockServerWithoutActionClientDecoration,
createMockServerWithoutAlertClientDecoration,
createMockServerWithoutActionOrAlertClientDecoration,
} from '../__mocks__/_mock_server';
import { updateRulesRoute } from './update_rules_route';
@ -55,28 +53,12 @@ describe('update_rules', () => {
expect(statusCode).toBe(404);
});
test('returns 404 if actionClient is not available on the route', async () => {
const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration();
updateRulesRoute(serverWithoutActionClient);
const { statusCode } = await serverWithoutActionClient.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());
expect(statusCode).toBe(404);
});
test('returns 404 if alertClient and actionClient are both not available on the route', async () => {
const {
serverWithoutActionOrAlertClient,
} = createMockServerWithoutActionOrAlertClientDecoration();
updateRulesRoute(serverWithoutActionOrAlertClient);
const { statusCode } = await serverWithoutActionOrAlertClient.inject(getUpdateRequest());
expect(statusCode).toBe(404);
});
});
describe('validation', () => {

View file

@ -14,81 +14,41 @@ import { ServerFacade } from '../../../../types';
import { getIdError, transformOrError } from './utils';
import { transformError } from '../utils';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';
import { KibanaRequest } from '../../../../../../../../../src/core/server';
export const createUpdateRulesRoute: Hapi.ServerRoute = {
method: 'PUT',
path: DETECTION_ENGINE_RULES_URL,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
export const createUpdateRulesRoute = (server: ServerFacade): Hapi.ServerRoute => {
return {
method: 'PUT',
path: DETECTION_ENGINE_RULES_URL,
options: {
tags: ['access:siem'],
validate: {
options: {
abortEarly: false,
},
payload: updateRulesSchema,
},
payload: updateRulesSchema,
},
},
async handler(request: UpdateRulesRequest, headers) {
const {
description,
enabled,
false_positives: falsePositives,
from,
query,
language,
output_index: outputIndex,
saved_id: savedId,
timeline_id: timelineId,
timeline_title: timelineTitle,
meta,
filters,
rule_id: ruleId,
id,
index,
interval,
max_signals: maxSignals,
risk_score: riskScore,
name,
severity,
tags,
to,
type,
threats,
references,
version,
} = request.payload;
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
const savedObjectsClient = isFunction(request.getSavedObjectsClient)
? request.getSavedObjectsClient()
: null;
if (!alertsClient || !actionsClient || !savedObjectsClient) {
return headers.response().code(404);
}
try {
const rule = await updateRules({
alertsClient,
actionsClient,
async handler(request: UpdateRulesRequest, headers) {
const {
description,
enabled,
falsePositives,
false_positives: falsePositives,
from,
query,
language,
outputIndex,
savedId,
savedObjectsClient,
timelineId,
timelineTitle,
output_index: outputIndex,
saved_id: savedId,
timeline_id: timelineId,
timeline_title: timelineTitle,
meta,
filters,
rule_id: ruleId,
id,
ruleId,
index,
interval,
maxSignals,
riskScore,
max_signals: maxSignals,
risk_score: riskScore,
name,
severity,
tags,
@ -97,28 +57,73 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = {
threats,
references,
version,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 1,
sortField: 'statusDate',
sortOrder: 'desc',
search: rule.id,
searchFields: ['alertId'],
});
return transformOrError(rule, ruleStatuses.saved_objects[0]);
} else {
return getIdError({ id, ruleId });
} = 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);
}
} catch (err) {
return transformError(err);
}
},
try {
const rule = await updateRules({
alertsClient,
actionsClient,
description,
enabled,
falsePositives,
from,
query,
language,
outputIndex,
savedId,
savedObjectsClient,
timelineId,
timelineTitle,
meta,
filters,
id,
ruleId,
index,
interval,
maxSignals,
riskScore,
name,
severity,
tags,
to,
type,
threats,
references,
version,
});
if (rule != null) {
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 1,
sortField: 'statusDate',
sortOrder: 'desc',
search: rule.id,
searchFields: ['alertId'],
});
return transformOrError(rule, ruleStatuses.saved_objects[0]);
} else {
return getIdError({ id, ruleId });
}
} catch (err) {
return transformError(err);
}
},
};
};
export const updateRulesRoute = (server: ServerFacade) => {
server.route(createUpdateRulesRoute);
server.route(createUpdateRulesRoute(server));
};

View file

@ -24,9 +24,7 @@ export const createReadTagsRoute: Hapi.ServerRoute = {
},
async handler(request: RequestFacade, headers) {
const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null;
const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null;
if (!alertsClient || !actionsClient) {
if (!alertsClient) {
return headers.response().code(404);
}

View file

@ -9,6 +9,8 @@ import { Legacy } from 'kibana';
export interface ServerFacade {
config: Legacy.Server['config'];
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'];