Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM] [Cases] External service selection per case #64775

Merged
merged 47 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
74534fa
wip
stephmilovic Apr 15, 2020
ab037ff
logs
stephmilovic Apr 15, 2020
f803e30
Merge branch 'master' into connector-selection-case
stephmilovic Apr 15, 2020
6cc292c
Merge branch 'master' into connector-selection-case
stephmilovic Apr 17, 2020
f34c722
add connector selector to case view
stephmilovic Apr 17, 2020
7fd93b2
connected to current selector
stephmilovic Apr 21, 2020
ba3976c
refactor configure cases
stephmilovic Apr 22, 2020
8823b2d
?
stephmilovic Apr 22, 2020
7352699
fixingish
stephmilovic Apr 23, 2020
85414df
fixed
stephmilovic Apr 23, 2020
9fcdc45
more cleanup
stephmilovic Apr 23, 2020
ea7390f
more cleanup
stephmilovic Apr 23, 2020
1ff3926
more cleanup
stephmilovic Apr 23, 2020
c6355d3
merged in configure refactor
stephmilovic Apr 23, 2020
c8b6ff8
reset configure_cases/index to match with PR
stephmilovic Apr 23, 2020
7747c54
add connector id to case data
stephmilovic Apr 23, 2020
8e092ec
more work for connectorId
stephmilovic Apr 23, 2020
f6ba967
fixed connectorId
stephmilovic Apr 24, 2020
b523948
fix unsaved changes bug
stephmilovic Apr 24, 2020
03aca05
Merge branch 'use-configure-refactor' into connector-selection-case
stephmilovic Apr 24, 2020
7f4fe94
button and dropdown now connected to case.connectorId
stephmilovic Apr 24, 2020
b0ed184
button/dropdown conenected
stephmilovic Apr 24, 2020
0870ec4
merge master
stephmilovic Apr 27, 2020
18e1503
cleanup
stephmilovic Apr 27, 2020
09c7cbf
user action enabled for connector_id
stephmilovic Apr 27, 2020
6099f46
this commit is late
stephmilovic Apr 28, 2020
366b3ad
by golly it works
stephmilovic Apr 28, 2020
9c30d90
tf no ver
stephmilovic Apr 28, 2020
7607a8e
big master merge
stephmilovic Apr 28, 2020
fc0183e
decision time
stephmilovic Apr 29, 2020
4a03422
tt
stephmilovic Apr 29, 2020
6b58e7d
happy place
stephmilovic Apr 29, 2020
99fa0fb
fix tests
stephmilovic Apr 30, 2020
bc8f155
fix push status
stephmilovic Apr 30, 2020
a650476
fix
stephmilovic Apr 30, 2020
fa4a3cf
unit testing done
stephmilovic Apr 30, 2020
576fc86
Merge branch 'master' into connector-selection-case
stephmilovic Apr 30, 2020
b4eba97
add to case columns
stephmilovic Apr 30, 2020
17e9f13
merge in master
stephmilovic Apr 30, 2020
29230bb
test fix
stephmilovic Apr 30, 2020
92b1d00
PR changes
stephmilovic May 4, 2020
3a954a1
PR changes 2
stephmilovic May 4, 2020
5d2d2bc
translation changes
stephmilovic May 4, 2020
bf31cf3
Merge branch 'master' into connector-selection-case
stephmilovic May 4, 2020
e224b59
Merge branch 'master' into connector-selection-case
stephmilovic May 4, 2020
e175cb6
fix i18n whoops
stephmilovic May 4, 2020
b6a1641
fix jest
stephmilovic May 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/plugins/case/common/api/cases/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { ActionTypeExecutorResult } from '../../../../actions/server/types';
const StatusRt = rt.union([rt.literal('open'), rt.literal('closed')]);

const CaseBasicRt = rt.type({
connector_id: rt.string,
description: rt.string,
status: StatusRt,
tags: rt.array(rt.string),
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/case/common/api/cases/user_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { UserRT } from '../user';
const UserActionFieldRt = rt.array(
rt.union([
rt.literal('comment'),
rt.literal('connector_id'),
Copy link
Member

@cnasikas cnasikas May 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit -> I think is better to describe the action as connector.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we are really updating the connector_id field

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd like to leave it. its updating the field connector_id, there is no field connector

Copy link
Member

@cnasikas cnasikas May 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! I am not against it. For the sake of argument this is a verb that describes an action, not a field. So the user creates a comment, updates the description, updates the connector etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol, maybe I did not do that correctly but the goal here was to be a field ;)

rt.literal('description'),
rt.literal('pushed'),
rt.literal('tags'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const mockCases: Array<SavedObject<CaseAttributes>> = [
attributes: {
closed_at: null,
closed_by: null,
connector_id: 'none',
created_at: '2019-11-25T21:54:48.952Z',
created_by: {
full_name: 'elastic',
Expand Down Expand Up @@ -46,6 +47,7 @@ export const mockCases: Array<SavedObject<CaseAttributes>> = [
attributes: {
closed_at: null,
closed_by: null,
connector_id: 'none',
created_at: '2019-11-25T22:32:00.900Z',
created_by: {
full_name: 'elastic',
Expand Down Expand Up @@ -74,6 +76,7 @@ export const mockCases: Array<SavedObject<CaseAttributes>> = [
attributes: {
closed_at: null,
closed_by: null,
connector_id: '123',
created_at: '2019-11-25T22:32:17.947Z',
created_by: {
full_name: 'elastic',
Expand Down Expand Up @@ -106,6 +109,7 @@ export const mockCases: Array<SavedObject<CaseAttributes>> = [
email: '[email protected]',
username: 'elastic',
},
connector_id: '123',
created_at: '2019-11-25T22:32:17.947Z',
created_by: {
full_name: 'elastic',
Expand All @@ -130,6 +134,35 @@ export const mockCases: Array<SavedObject<CaseAttributes>> = [
},
];

export const mockCaseNoConnectorId: SavedObject<Partial<CaseAttributes>> = {
type: 'cases',
id: 'mock-no-connector_id',
attributes: {
closed_at: null,
closed_by: null,
created_at: '2019-11-25T21:54:48.952Z',
created_by: {
full_name: 'elastic',
email: '[email protected]',
username: 'elastic',
},
description: 'This is a brand new case of a bad meanie defacing data',
external_service: null,
title: 'Super Bad Security Issue',
status: 'open',
tags: ['defacement'],
updated_at: '2019-11-25T21:54:48.952Z',
updated_by: {
full_name: 'elastic',
email: '[email protected]',
username: 'elastic',
},
},
references: [],
updated_at: '2019-11-25T21:54:48.952Z',
version: 'WzAsMV0=',
};

export const mockCasesErrorTriggerData = [
{
id: 'valid-id',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import { RouteDeps } from '../../types';
import { escapeHatch, wrapError, flattenCaseSavedObject } from '../../utils';
import { CASE_COMMENTS_URL } from '../../../../../common/constants';

export function initPatchCommentApi({ caseService, router, userActionService }: RouteDeps) {
export function initPatchCommentApi({
caseConfigureService,
caseService,
router,
userActionService,
}: RouteDeps) {
router.patch(
{
path: CASE_COMMENTS_URL,
Expand Down Expand Up @@ -64,7 +69,7 @@ export function initPatchCommentApi({ caseService, router, userActionService }:

const { username, full_name, email } = await caseService.getUser({ request, response });
const updatedDate = new Date().toISOString();
const [updatedComment, updatedCase] = await Promise.all([
const [updatedComment, updatedCase, myCaseConfigure] = await Promise.all([
caseService.patchComment({
client,
commentId: query.id,
Expand All @@ -84,6 +89,7 @@ export function initPatchCommentApi({ caseService, router, userActionService }:
},
version: myCase.version,
}),
caseConfigureService.find({ client }),
]);

const totalCommentsFindByCases = await caseService.getAllCaseComments({
Expand All @@ -95,6 +101,10 @@ export function initPatchCommentApi({ caseService, router, userActionService }:
perPage: 1,
},
});
const caseConfigureConnectorId =
myCaseConfigure.saved_objects.length > 0
? myCaseConfigure.saved_objects[0].attributes.connector_id
: 'none';

const [comments] = await Promise.all([
caseService.getAllCaseComments({
Expand Down Expand Up @@ -125,16 +135,17 @@ export function initPatchCommentApi({ caseService, router, userActionService }:

return response.ok({
body: CaseResponseRt.encode(
flattenCaseSavedObject(
{
flattenCaseSavedObject({
savedObject: {
...myCase,
...updatedCase,
attributes: { ...myCase.attributes, ...updatedCase.attributes },
version: updatedCase.version ?? myCase.version,
references: myCase.references,
},
comments.saved_objects
)
comments: comments.saved_objects,
caseConfigureConnectorId,
})
),
});
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ import { escapeHatch, transformNewComment, wrapError, flattenCaseSavedObject } f
import { RouteDeps } from '../../types';
import { CASE_COMMENTS_URL } from '../../../../../common/constants';

export function initPostCommentApi({ caseService, router, userActionService }: RouteDeps) {
export function initPostCommentApi({
caseConfigureService,
caseService,
router,
userActionService,
}: RouteDeps) {
router.post(
{
path: CASE_COMMENTS_URL,
Expand Down Expand Up @@ -45,7 +50,7 @@ export function initPostCommentApi({ caseService, router, userActionService }: R
const { username, full_name, email } = await caseService.getUser({ request, response });
const createdDate = new Date().toISOString();

const [newComment, updatedCase] = await Promise.all([
const [newComment, updatedCase, myCaseConfigure] = await Promise.all([
caseService.postNewComment({
client,
attributes: transformNewComment({
Expand All @@ -72,8 +77,13 @@ export function initPostCommentApi({ caseService, router, userActionService }: R
},
version: myCase.version,
}),
caseConfigureService.find({ client }),
]);

const caseConfigureConnectorId =
myCaseConfigure.saved_objects.length > 0
? myCaseConfigure.saved_objects[0].attributes.connector_id
: 'none';
const totalCommentsFindByCases = await caseService.getAllCaseComments({
client,
caseId,
Expand Down Expand Up @@ -112,16 +122,17 @@ export function initPostCommentApi({ caseService, router, userActionService }: R

return response.ok({
body: CaseResponseRt.encode(
flattenCaseSavedObject(
{
flattenCaseSavedObject({
savedObject: {
...myCase,
...updatedCase,
attributes: { ...myCase.attributes, ...updatedCase.attributes },
version: updatedCase.version ?? myCase.version,
references: myCase.references,
},
comments.saved_objects
)
comments: comments.saved_objects,
caseConfigureConnectorId,
})
),
});
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { RouteDeps } from '../../types';
import { wrapError } from '../../utils';
import { CASE_CONFIGURE_URL } from '../../../../../common/constants';

export function initGetCaseConfigure({ caseConfigureService, caseService, router }: RouteDeps) {
export function initGetCaseConfigure({ caseConfigureService, router }: RouteDeps) {
router.get(
{
path: CASE_CONFIGURE_URL,
Expand Down
36 changes: 35 additions & 1 deletion x-pack/plugins/case/server/routes/api/cases/find_cases.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import {
} from '../__fixtures__';
import { initFindCasesApi } from './find_cases';
import { CASES_URL } from '../../../../common/constants';
import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects';

describe('GET all cases', () => {
describe('FIND all cases', () => {
let routeHandler: RequestHandler<any, any, any>;
beforeAll(async () => {
routeHandler = await createRoute(initFindCasesApi, 'get');
Expand All @@ -37,4 +38,37 @@ describe('GET all cases', () => {
expect(response.status).toEqual(200);
expect(response.payload.cases).toHaveLength(4);
});
it(`adds 'none' connector id to cases without when 3rd party unconfigured`, async () => {
const request = httpServerMock.createKibanaRequest({
path: `${CASES_URL}/_find`,
method: 'get',
});

const theContext = createRouteContext(
createMockSavedObjectsRepository({
caseSavedObject: [mockCaseNoConnectorId],
})
);

const response = await routeHandler(theContext, request, kibanaResponseFactory);
expect(response.status).toEqual(200);
expect(response.payload.cases[0].connector_id).toEqual('none');
});
it(`adds default connector id to cases without when 3rd party configured`, async () => {
Copy link
Contributor

@XavierM XavierM May 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit -> we can get one more test to male make sure we get the connector_id if there is one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I will apply to all the tests that applied if you agree

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the test should female sure we get the connector_id because the future is female

const request = httpServerMock.createKibanaRequest({
path: `${CASES_URL}/_find`,
method: 'get',
});

const theContext = createRouteContext(
createMockSavedObjectsRepository({
caseSavedObject: [mockCaseNoConnectorId],
caseConfigureSavedObject: mockCaseConfigure,
})
);

const response = await routeHandler(theContext, request, kibanaResponseFactory);
expect(response.status).toEqual(200);
expect(response.payload.cases[0].connector_id).toEqual('123');
});
});
11 changes: 7 additions & 4 deletions x-pack/plugins/case/server/routes/api/cases/find_cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const buildFilter = (
: `${CASE_SAVED_OBJECT}.attributes.${field}: ${filters}`
: '';

export function initFindCasesApi({ caseService, router }: RouteDeps) {
export function initFindCasesApi({ caseService, caseConfigureService, router }: RouteDeps) {
router.get(
{
path: `${CASES_URL}/_find`,
Expand Down Expand Up @@ -94,12 +94,12 @@ export function initFindCasesApi({ caseService, router }: RouteDeps) {
filter: getStatusFilter('closed', myFilters),
},
};
const [cases, openCases, closesCases] = await Promise.all([
const [cases, openCases, closesCases, myCaseConfigure] = await Promise.all([
caseService.findCases(args),
caseService.findCases(argsOpenCases),
caseService.findCases(argsClosedCases),
caseConfigureService.find({ client }),
]);

const totalCommentsFindByCases = await Promise.all(
cases.saved_objects.map(c =>
caseService.getAllCaseComments({
Expand Down Expand Up @@ -135,7 +135,10 @@ export function initFindCasesApi({ caseService, router }: RouteDeps) {
cases,
openCases.total ?? 0,
closesCases.total ?? 0,
totalCommentsByCases
totalCommentsByCases,
myCaseConfigure.saved_objects.length > 0
? myCaseConfigure.saved_objects[0].attributes.connector_id
: 'none'
)
),
});
Expand Down
59 changes: 52 additions & 7 deletions x-pack/plugins/case/server/routes/api/cases/get_case.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { flattenCaseSavedObject } from '../utils';
import { initGetCaseApi } from './get_case';
import { CASE_DETAILS_URL } from '../../../../common/constants';
import { mockCaseConfigure, mockCaseNoConnectorId } from '../__fixtures__/mock_saved_objects';

describe('GET case', () => {
let routeHandler: RequestHandler<any, any, any>;
Expand All @@ -44,14 +45,11 @@ describe('GET case', () => {
);

const response = await routeHandler(theContext, request, kibanaResponseFactory);

const savedObject = (mockCases.find(s => s.id === 'mock-id-1') as unknown) as SavedObject<
CaseAttributes
>;
expect(response.status).toEqual(200);
expect(response.payload).toEqual(
flattenCaseSavedObject(
(mockCases.find(s => s.id === 'mock-id-1') as unknown) as SavedObject<CaseAttributes>,
[]
)
);
expect(response.payload).toEqual(flattenCaseSavedObject({ savedObject }));
expect(response.payload.comments).toEqual([]);
});
it(`returns an error when thrown from getCase`, async () => {
Expand Down Expand Up @@ -123,4 +121,51 @@ describe('GET case', () => {

expect(response.status).toEqual(400);
});
it(`case w/o connector_id - returns the case with connector id when 3rd party unconfigured`, async () => {
const request = httpServerMock.createKibanaRequest({
path: CASE_DETAILS_URL,
method: 'get',
params: {
case_id: 'mock-no-connector_id',
},
query: {
includeComments: false,
},
});

const theContext = createRouteContext(
createMockSavedObjectsRepository({
caseSavedObject: [mockCaseNoConnectorId],
})
);

const response = await routeHandler(theContext, request, kibanaResponseFactory);

expect(response.status).toEqual(200);
expect(response.payload.connector_id).toEqual('none');
});
it(`case w/o connector_id - returns the case with connector id when 3rd party configured`, async () => {
const request = httpServerMock.createKibanaRequest({
path: CASE_DETAILS_URL,
method: 'get',
params: {
case_id: 'mock-no-connector_id',
},
query: {
includeComments: false,
},
});

const theContext = createRouteContext(
createMockSavedObjectsRepository({
caseSavedObject: [mockCaseNoConnectorId],
caseConfigureSavedObject: mockCaseConfigure,
})
);

const response = await routeHandler(theContext, request, kibanaResponseFactory);

expect(response.status).toEqual(200);
expect(response.payload.connector_id).toEqual('123');
});
});
Loading