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][CASE][skip-ci] Connectors flyout #58632

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7a47cf5
Refactor structure
cnasikas Feb 28, 2020
4e940c1
Init ServiceNow class
cnasikas Mar 1, 2020
3d098d3
Add constants
cnasikas Mar 1, 2020
58f6fd2
Add configuration scheme
cnasikas Mar 2, 2020
82fae42
Refactor configuration schema
cnasikas Mar 4, 2020
8ef660a
Refactor parameters schema
cnasikas Mar 4, 2020
4e710ad
Create new types
cnasikas Mar 4, 2020
91a85a6
Add supported source fields
cnasikas Mar 4, 2020
5af92be
Create helpers
cnasikas Mar 4, 2020
9585d6b
Create ServiceNow lib
cnasikas Mar 4, 2020
709e1e5
Push incident
cnasikas Mar 4, 2020
818f2fe
Declare private methods
cnasikas Mar 4, 2020
b537fc6
Create UpdateIncident type
cnasikas Mar 4, 2020
2f805bb
Create updateIncident method
cnasikas Mar 4, 2020
cacef33
Create executor actions
cnasikas Mar 4, 2020
389ae3d
Refactor response
cnasikas Mar 4, 2020
bf87ebe
Test helpers
cnasikas Mar 5, 2020
6e75fac
Remove unnecessary validation
cnasikas Mar 5, 2020
66ce060
Fix validation errors
cnasikas Mar 5, 2020
33f3d6e
Throw error for unsupported actions
cnasikas Mar 5, 2020
700d82c
Create mock incident
cnasikas Mar 5, 2020
b5d95f7
Test executor
cnasikas Mar 5, 2020
6998d8f
Test ServiceNow lib
cnasikas Mar 5, 2020
e392650
Convert to camelCase
cnasikas Mar 6, 2020
9ff7184
Remove caller_id
cnasikas Mar 6, 2020
d7afa1b
Refactor helpers
cnasikas Mar 6, 2020
d044981
Refactor schema
cnasikas Mar 6, 2020
9c9b25e
Remove executorAction
cnasikas Mar 6, 2020
4ad2fdd
Test action handlers
cnasikas Mar 6, 2020
bfbbc3f
Refactor tests
cnasikas Mar 6, 2020
9535eaa
Create and update comments
cnasikas Mar 6, 2020
d8eebee
Create servicenow connector
cnasikas Feb 26, 2020
c76dc04
Register servicenow connector
cnasikas Feb 26, 2020
ac7afa3
Add ServiceNow logo
cnasikas Feb 27, 2020
512698e
Create connnectors mapping
cnasikas Feb 27, 2020
3c1ab93
Create validators in utils
cnasikas Feb 27, 2020
e29a9a4
Use validators in connectors
cnasikas Feb 27, 2020
baa8481
Validate URL
cnasikas Feb 27, 2020
379e693
Use connectors from config
cnasikas Feb 27, 2020
83523db
Enable triggers_aciton_ui plugin
cnasikas Mar 6, 2020
7e42a5d
Show flyout
cnasikas Mar 6, 2020
6449515
Add closures options
cnasikas Mar 6, 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
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/siem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const siem = (kibana: any) => {
id: APP_ID,
configPrefix: 'xpack.siem',
publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch', 'alerting', 'actions'],
require: ['kibana', 'elasticsearch', 'alerting', 'actions', 'triggers_actions_ui'],
uiExports: {
app: {
description: i18n.translate('xpack.siem.securityDescription', {
Expand Down
17 changes: 17 additions & 0 deletions x-pack/legacy/plugins/siem/public/lib/connectors/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 { Connector } from './types';
import serviceNowLogo from './logos/servicenow.svg';

const connectors = new Map<string, Connector>();

connectors.set('.servicenow', {
actionTypeId: '.servicenow',
logo: serviceNowLogo,
});

export { connectors };
7 changes: 7 additions & 0 deletions x-pack/legacy/plugins/siem/public/lib/connectors/index.ts
Original file line number Diff line number Diff line change
@@ -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 { getActionType as serviceNowActionType } from './servicenow';
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
238 changes: 238 additions & 0 deletions x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
/*
* 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 React from 'react';
import {
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFieldPassword,
EuiRadioGroup,
} from '@elastic/eui';
import {
ActionConnectorFieldsProps,
ActionTypeModel,
ValidationResult,
ActionParamsProps,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../../plugins/triggers_actions_ui/public/types';

import * as i18n from './translations';

import { ServiceNowActionConnector } from './types';
import { isUrlInvalid } from './validators';

import { connectors } from './config';

const serviceNowDefinition = connectors.get('.servicenow')!;

interface ServiceNowActionParams {
message: string;
}

export function getActionType(): ActionTypeModel {
return {
id: serviceNowDefinition.actionTypeId,
iconClass: serviceNowDefinition.logo,
selectMessage: i18n.SERVICENOW_DESC,
actionTypeTitle: i18n.SERVICENOW_TITLE,
validateConnector: (action: ServiceNowActionConnector): ValidationResult => {
const validationResult = { errors: {} };
const errors = {
apiUrl: [] as string[],
username: [] as string[],
password: [] as string[],
};

if (!action.config.apiUrl) {
errors.apiUrl.push(i18n.SERVICENOW_API_URL_REQUIRED);
}

if (isUrlInvalid(action.config.apiUrl)) {
errors.apiUrl.push(i18n.SERVICENOW_API_URL_INVALID);
}

if (!action.secrets.username) {
errors.username.push(i18n.SERVICENOW_USERNAME_REQUIRED);
}

if (!action.secrets.password) {
errors.password.push(i18n.SERVICENOW_PASSWORD_REQUIRED);
}

validationResult.errors = errors;
return validationResult;
},
validateParams: (actionParams: ServiceNowActionParams): ValidationResult => {
const validationResult = { errors: {} };
const errors = {
message: [] as string[],
};
validationResult.errors = errors;
return validationResult;
},
actionConnectorFields: ServiceNowConnectorFields,
actionParamsFields: ServiceNowParamsFields,
};
}

const ServiceNowConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps<
ServiceNowActionConnector
>> = ({ action, editActionConfig, editActionSecrets, errors }) => {
const { apiUrl, casesConfiguration } = action.config;
const { username, password } = action.secrets;

const closure = casesConfiguration?.closure ?? 'manual';

const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl !== undefined;
const isUsernameInvalid: boolean = errors.username.length > 0 && username !== undefined;
const isPasswordInvalid: boolean = errors.password.length > 0 && password !== undefined;

if (!casesConfiguration) {
editActionConfig('casesConfiguration', {
closure: 'manual',
mapping: [
{
source: 'title',
target: 'description',
onEditAndUpdate: 'nothing',
},
{
source: 'description',
target: 'short_description',
onEditAndUpdate: 'nothing',
},
{
source: 'comments',
target: 'work_notes',
onEditAndUpdate: 'nothing',
},
],
});
}

const radios = [
{
id: 'manual',
label: 'Manually close SIEM cases',
},
{
id: 'newIncident',
label: 'Automatically close SIEM cases when pushing new incident to third-party',
},
{
id: 'closedIncident',
label: 'Automatically close SIEM cases when incident is closed in third-party',
},
];

return (
<>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow
id="apiUrl"
fullWidth
error={errors.apiUrl}
isInvalid={isApiUrlInvalid}
label={i18n.SERVICENOW_API_URL_LABEL}
>
<EuiFieldText
fullWidth
isInvalid={isApiUrlInvalid}
name="apiUrl"
value={apiUrl}
data-test-subj="apiUrlFromInput"
placeholder="https://<instance>.service-now.com"
onChange={e => {
editActionConfig('apiUrl', e.target.value);
}}
onBlur={() => {
if (!apiUrl) {
editActionConfig('apiUrl', '');
}
}}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow
id="username"
fullWidth
error={errors.username}
isInvalid={isUsernameInvalid}
label={i18n.SERVICENOW_USERNAME_LABEL}
>
<EuiFieldText
fullWidth
isInvalid={isUsernameInvalid}
name="username"
value={username}
data-test-subj="usernameFromInput"
onChange={e => {
editActionSecrets('username', e.target.value);
}}
onBlur={() => {
if (!username) {
editActionSecrets('username', '');
}
}}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow
id="password"
fullWidth
error={errors.password}
isInvalid={isPasswordInvalid}
label={i18n.SERVICENOW_PASSWORD_LABEL}
>
<EuiFieldPassword
fullWidth
isInvalid={isPasswordInvalid}
name="password"
value={password}
data-test-subj="passwordFromInput"
onChange={e => {
editActionSecrets('password', e.target.value);
}}
onBlur={() => {
if (!password) {
editActionSecrets('password', '');
}
}}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow id="closure" fullWidth label={'Case closure options'}>
<EuiRadioGroup
options={radios}
idSelected={closure}
onChange={(val: string) => {
editActionConfig('casesConfiguration', { ...casesConfiguration, closure: val });
}}
name="closure"
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
};

const ServiceNowParamsFields: React.FunctionComponent<ActionParamsProps<
ServiceNowActionParams
>> = ({ actionParams, editAction, index, errors }) => {
return null;
};
70 changes: 70 additions & 0 deletions x-pack/legacy/plugins/siem/public/lib/connectors/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const SERVICENOW_DESC = i18n.translate(
'xpack.siem.case.connectors.servicenow.selectMessageText',
{
defaultMessage: 'Push or update SIEM case data to a new incident in ServiceNow',
}
);

export const SERVICENOW_TITLE = i18n.translate(
'xpack.siem.case.connectors.servicenow.actionTypeTitle',
{
defaultMessage: 'ServiceNow',
}
);

export const SERVICENOW_API_URL_LABEL = i18n.translate(
'xpack.siem.case.connectors.servicenow.apiUrlTextFieldLabel',
{
defaultMessage: 'URL',
}
);

export const SERVICENOW_API_URL_REQUIRED = i18n.translate(
'xpack.siem.case.connectors.servicenow.requiredApiUrlTextField',
{
defaultMessage: 'URL is required',
}
);

export const SERVICENOW_API_URL_INVALID = i18n.translate(
'xpack.siem.case.connectors.servicenow.invalidApiUrlTextField',
{
defaultMessage: 'URL is invalid',
}
);

export const SERVICENOW_USERNAME_LABEL = i18n.translate(
'xpack.siem.case.connectors.servicenow.usernameTextFieldLabel',
{
defaultMessage: 'Username',
}
);

export const SERVICENOW_USERNAME_REQUIRED = i18n.translate(
'xpack.siem.case.connectors.servicenow.requiredUsernameTextField',
{
defaultMessage: 'Username is required',
}
);

export const SERVICENOW_PASSWORD_LABEL = i18n.translate(
'xpack.siem.case.connectors.servicenow.passwordTextFieldLabel',
{
defaultMessage: 'Password',
}
);

export const SERVICENOW_PASSWORD_REQUIRED = i18n.translate(
'xpack.siem.case.connectors.servicenow.requiredPasswordTextField',
{
defaultMessage: 'Password is required',
}
);
27 changes: 27 additions & 0 deletions x-pack/legacy/plugins/siem/public/lib/connectors/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.
*/

interface ServiceNowConfig {
apiUrl: string;
casesConfiguration: {
closure: string;
};
}

interface ServiceNowSecrets {
username: string;
password: string;
}

export interface ServiceNowActionConnector {
config: ServiceNowConfig;
secrets: ServiceNowSecrets;
}

export interface Connector {
actionTypeId: string;
logo: string;
}
Original file line number Diff line number Diff line change
@@ -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 { isUrlInvalid } from '../../utils/validators';
Loading