[SIEM] Import timeline schema update (#61622)

* allow users importing data if they are authorized

* rename props

* rename types

* hide import timeline btn if unauthorized

* unit test for TimelinesPageComponent

* update schemas

* update schema

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Angela Chuang 2020-03-30 15:56:16 +01:00 committed by GitHub
parent 1855d10d9d
commit c0c9d98538
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 117 additions and 31 deletions

View file

@ -0,0 +1,89 @@
/*
* 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 { TimelinesPageComponent } from './timelines_page';
import { useKibana } from '../../lib/kibana';
import { shallow, ShallowWrapper } from 'enzyme';
import React from 'react';
import ApolloClient from 'apollo-client';
jest.mock('../../lib/kibana', () => {
return {
useKibana: jest.fn(),
};
});
describe('TimelinesPageComponent', () => {
const mockAppollloClient = {} as ApolloClient<object>;
let wrapper: ShallowWrapper;
describe('If the user is authorised', () => {
beforeAll(() => {
((useKibana as unknown) as jest.Mock).mockReturnValue({
services: {
application: {
capabilities: {
siem: {
crud: true,
},
},
},
},
});
wrapper = shallow(<TimelinesPageComponent apolloClient={mockAppollloClient} />);
});
afterAll(() => {
((useKibana as unknown) as jest.Mock).mockReset();
});
test('should not show the import timeline modal by default', () => {
expect(
wrapper.find('[data-test-subj="stateful-open-timeline"]').prop('importDataModalToggle')
).toEqual(false);
});
test('should show the import timeline button', () => {
expect(wrapper.find('[data-test-subj="open-import-data-modal-btn"]').exists()).toEqual(true);
});
test('should show the import timeline modal after user clicking on the button', () => {
wrapper.find('[data-test-subj="open-import-data-modal-btn"]').simulate('click');
expect(
wrapper.find('[data-test-subj="stateful-open-timeline"]').prop('importDataModalToggle')
).toEqual(true);
});
});
describe('If the user is not authorised', () => {
beforeAll(() => {
((useKibana as unknown) as jest.Mock).mockReturnValue({
services: {
application: {
capabilities: {
siem: {
crud: false,
},
},
},
},
});
wrapper = shallow(<TimelinesPageComponent apolloClient={mockAppollloClient} />);
});
afterAll(() => {
((useKibana as unknown) as jest.Mock).mockReset();
});
test('should not show the import timeline modal by default', () => {
expect(
wrapper.find('[data-test-subj="stateful-open-timeline"]').prop('importDataModalToggle')
).toEqual(false);
});
test('should not show the import timeline button', () => {
expect(wrapper.find('[data-test-subj="open-import-data-modal-btn"]').exists()).toEqual(false);
});
});
});

View file

@ -28,7 +28,7 @@ type OwnProps = TimelinesProps;
export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10; export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10;
const TimelinesPageComponent: React.FC<OwnProps> = ({ apolloClient }) => { export const TimelinesPageComponent: React.FC<OwnProps> = ({ apolloClient }) => {
const [importDataModalToggle, setImportDataModalToggle] = useState<boolean>(false); const [importDataModalToggle, setImportDataModalToggle] = useState<boolean>(false);
const onImportTimelineBtnClick = useCallback(() => { const onImportTimelineBtnClick = useCallback(() => {
setImportDataModalToggle(true); setImportDataModalToggle(true);
@ -43,7 +43,11 @@ const TimelinesPageComponent: React.FC<OwnProps> = ({ apolloClient }) => {
<WrapperPage> <WrapperPage>
<HeaderPage border title={i18n.PAGE_TITLE}> <HeaderPage border title={i18n.PAGE_TITLE}>
{capabilitiesCanUserCRUD && ( {capabilitiesCanUserCRUD && (
<EuiButton iconType="indexOpen" onClick={onImportTimelineBtnClick}> <EuiButton
iconType="indexOpen"
onClick={onImportTimelineBtnClick}
data-test-subj="open-import-data-modal-btn"
>
{i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE} {i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE}
</EuiButton> </EuiButton>
)} )}
@ -57,6 +61,7 @@ const TimelinesPageComponent: React.FC<OwnProps> = ({ apolloClient }) => {
importDataModalToggle={importDataModalToggle && capabilitiesCanUserCRUD} importDataModalToggle={importDataModalToggle && capabilitiesCanUserCRUD}
setImportDataModalToggle={setImportDataModalToggle} setImportDataModalToggle={setImportDataModalToggle}
title={i18n.ALL_TIMELINES_PANEL_TITLE} title={i18n.ALL_TIMELINES_PANEL_TITLE}
data-test-subj="stateful-open-timeline"
/> />
</TimelinesContainer> </TimelinesContainer>
</WrapperPage> </WrapperPage>

View file

@ -6,14 +6,14 @@
import Joi from 'joi'; import Joi from 'joi';
const allowEmptyString = Joi.string().allow([null, '']); const allowEmptyString = Joi.string().allow([null, '']);
const columnHeaderType = Joi.string(); const columnHeaderType = allowEmptyString;
export const created = Joi.number().allow(null); export const created = Joi.number().allow(null);
export const createdBy = Joi.string(); export const createdBy = allowEmptyString;
export const description = allowEmptyString; export const description = allowEmptyString;
export const end = Joi.number(); export const end = Joi.number();
export const eventId = allowEmptyString; export const eventId = allowEmptyString;
export const eventType = Joi.string(); export const eventType = allowEmptyString;
export const filters = Joi.array() export const filters = Joi.array()
.items( .items(
@ -24,19 +24,11 @@ export const filters = Joi.array()
disabled: Joi.boolean().allow(null), disabled: Joi.boolean().allow(null),
field: allowEmptyString, field: allowEmptyString,
formattedValue: allowEmptyString, formattedValue: allowEmptyString,
index: { index: allowEmptyString,
type: 'keyword', key: allowEmptyString,
}, negate: Joi.boolean().allow(null),
key: {
type: 'keyword',
},
negate: {
type: 'boolean',
},
params: allowEmptyString, params: allowEmptyString,
type: { type: allowEmptyString,
type: 'keyword',
},
value: allowEmptyString, value: allowEmptyString,
}), }),
exists: allowEmptyString, exists: allowEmptyString,
@ -68,22 +60,22 @@ export const version = allowEmptyString;
export const columns = Joi.array().items( export const columns = Joi.array().items(
Joi.object({ Joi.object({
aggregatable: Joi.boolean().allow(null), aggregatable: Joi.boolean().allow(null),
category: Joi.string(), category: allowEmptyString,
columnHeaderType, columnHeaderType,
description, description,
example: allowEmptyString, example: allowEmptyString,
indexes: allowEmptyString, indexes: allowEmptyString,
id: Joi.string(), id: allowEmptyString,
name, name,
placeholder: allowEmptyString, placeholder: allowEmptyString,
searchable: Joi.boolean().allow(null), searchable: Joi.boolean().allow(null),
type: Joi.string(), type: allowEmptyString,
}).required() }).required()
); );
export const dataProviders = Joi.array() export const dataProviders = Joi.array()
.items( .items(
Joi.object({ Joi.object({
id: Joi.string(), id: allowEmptyString,
name: allowEmptyString, name: allowEmptyString,
enabled: Joi.boolean().allow(null), enabled: Joi.boolean().allow(null),
excluded: Joi.boolean().allow(null), excluded: Joi.boolean().allow(null),
@ -98,7 +90,7 @@ export const dataProviders = Joi.array()
and: Joi.array() and: Joi.array()
.items( .items(
Joi.object({ Joi.object({
id: Joi.string(), id: allowEmptyString,
name, name,
enabled: Joi.boolean().allow(null), enabled: Joi.boolean().allow(null),
excluded: Joi.boolean().allow(null), excluded: Joi.boolean().allow(null),
@ -122,9 +114,9 @@ export const dateRange = Joi.object({
}); });
export const favorite = Joi.array().items( export const favorite = Joi.array().items(
Joi.object({ Joi.object({
keySearch: Joi.string(), keySearch: allowEmptyString,
fullName: Joi.string(), fullName: allowEmptyString,
userName: Joi.string(), userName: allowEmptyString,
favoriteDate: Joi.number(), favoriteDate: Joi.number(),
}).allow(null) }).allow(null)
); );
@ -141,26 +133,26 @@ const noteItem = Joi.object({
}); });
export const eventNotes = Joi.array().items(noteItem); export const eventNotes = Joi.array().items(noteItem);
export const globalNotes = Joi.array().items(noteItem); export const globalNotes = Joi.array().items(noteItem);
export const kqlMode = Joi.string(); export const kqlMode = allowEmptyString;
export const kqlQuery = Joi.object({ export const kqlQuery = Joi.object({
filterQuery: Joi.object({ filterQuery: Joi.object({
kuery: Joi.object({ kuery: Joi.object({
kind: Joi.string(), kind: allowEmptyString,
expression: allowEmptyString, expression: allowEmptyString,
}), }),
serializedQuery: allowEmptyString, serializedQuery: allowEmptyString,
}), }),
}); });
export const pinnedEventIds = Joi.array() export const pinnedEventIds = Joi.array()
.items(Joi.string()) .items(allowEmptyString)
.allow(null); .allow(null);
export const sort = Joi.object({ export const sort = Joi.object({
columnId: Joi.string(), columnId: allowEmptyString,
sortDirection: Joi.string(), sortDirection: allowEmptyString,
}); });
/* eslint-disable @typescript-eslint/camelcase */ /* eslint-disable @typescript-eslint/camelcase */
export const ids = Joi.array().items(Joi.string()); export const ids = Joi.array().items(allowEmptyString);
export const exclude_export_details = Joi.boolean(); export const exclude_export_details = Joi.boolean();
export const file_name = allowEmptyString; export const file_name = allowEmptyString;