From 532a33b051d5e4067d81a8feb51214373a31c6b1 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Mon, 17 May 2021 11:16:50 -0400 Subject: [PATCH] [Security Solution][Endpoint] Refactor Host Isolation component used in Isolate use case (#100159) * EndpointHostIsolateForm component * Refactor Detections Host isolation flyout to use isolateform --- .../endpoint/host_isolation/index.ts | 9 ++ .../endpoint/host_isolation/isolate_form.tsx | 88 +++++++++++ .../host_isolation/isolate_success.tsx | 41 +++++ .../endpoint/host_isolation/translations.ts | 31 ++++ .../components/host_isolation/index.tsx | 140 +++++++----------- .../components/host_isolation/translations.ts | 17 --- 6 files changed, 219 insertions(+), 107 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/translations.ts diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts new file mode 100644 index 000000000000..de51df283251 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './isolate_success'; +export * from './isolate_form'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx new file mode 100644 index 000000000000..dd26f676c1fe --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_form.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { ChangeEventHandler, memo, ReactNode, useCallback } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTextArea, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CANCEL, COMMENT, COMMENT_PLACEHOLDER, CONFIRM } from './translations'; + +export interface EndpointIsolatedFormProps { + hostName: string; + onCancel: () => void; + onConfirm: () => void; + onChange: (changes: { comment: string }) => void; + comment?: string; + /** Any additional message to be appended to the default one */ + messageAppend?: ReactNode; + /** If true, then `Confirm` and `Cancel` buttons will be disabled, and `Confirm` button will loading loading style */ + isLoading?: boolean; +} + +export const EndpointIsolateForm = memo( + ({ hostName, onCancel, onConfirm, onChange, comment = '', messageAppend, isLoading = false }) => { + const handleCommentChange: ChangeEventHandler = useCallback( + (event) => { + onChange({ comment: event.target.value }); + }, + [onChange] + ); + + return ( + <> + +

+ {hostName} }} + />{' '} + {messageAppend} +

+
+ + + + +

{COMMENT}

+
+ + + + + + + + {CANCEL} + + + + + {CONFIRM} + + + + + ); + } +); + +EndpointIsolateForm.displayName = 'EndpointIsolateForm'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx new file mode 100644 index 000000000000..ee70a4526f5a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, ReactNode } from 'react'; +import { EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { GET_SUCCESS_MESSAGE } from './translations'; + +export interface EndpointIsolateSuccessProps { + hostName: string; + completeButtonLabel: string; + onComplete: () => void; + additionalInfo?: ReactNode; +} + +export const EndpointIsolateSuccess = memo( + ({ hostName, onComplete, completeButtonLabel, additionalInfo }) => { + return ( + <> + + {additionalInfo} + + + + + + +

{completeButtonLabel}

+
+
+
+
+ + ); + } +); + +EndpointIsolateSuccess.displayName = 'EndpointIsolateSuccess'; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/translations.ts b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/translations.ts new file mode 100644 index 000000000000..baeced2a7a69 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/translations.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CANCEL = i18n.translate('xpack.securitySolution.endpoint.hostIsolation.cancel', { + defaultMessage: 'Cancel', +}); + +export const CONFIRM = i18n.translate('xpack.securitySolution.endpoint.hostIsolation.confirm', { + defaultMessage: 'Confirm', +}); + +export const COMMENT = i18n.translate('xpack.securitySolution.endpoint.hostIsolation.comment', { + defaultMessage: 'Comment', +}); + +export const COMMENT_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.endpoint.hostIsolation.comment.placeholder', + { defaultMessage: 'You may leave an optional note here.' } +); + +export const GET_SUCCESS_MESSAGE = (hostName: string) => + i18n.translate('xpack.securitySolution.endpoint.hostIsolation.successfulMessage', { + defaultMessage: 'Host Isolation on {hostName} successfully submitted', + values: { hostName }, + }); diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx index 3897458e8459..e6fd3a8459f7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx @@ -7,32 +7,19 @@ import React, { useMemo, useState, useCallback } from 'react'; import { find } from 'lodash/fp'; -import { - EuiCallOut, - EuiTitle, - EuiText, - EuiTextArea, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiButton, - EuiButtonEmpty, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { EuiText, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { useHostIsolation } from '../../containers/detection_engine/alerts/use_host_isolation'; -import { - CANCEL, - CASES_ASSOCIATED_WITH_ALERT, - COMMENT, - COMMENT_PLACEHOLDER, - CONFIRM, - RETURN_TO_ALERT_DETAILS, -} from './translations'; +import { CASES_ASSOCIATED_WITH_ALERT, RETURN_TO_ALERT_DETAILS } from './translations'; import { Maybe } from '../../../../../observability/common/typings'; import { useCasesFromAlerts } from '../../containers/detection_engine/alerts/use_cases_from_alerts'; import { CaseDetailsLink } from '../../../common/components/links'; import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; +import { + EndpointIsolatedFormProps, + EndpointIsolateForm, + EndpointIsolateSuccess, +} from '../../../common/components/endpoint/host_isolation'; export const HostIsolationPanel = React.memo( ({ @@ -76,6 +63,11 @@ export const HostIsolationPanel = React.memo( const backToAlertDetails = useCallback(() => cancelCallback(), [cancelCallback]); + const handleIsolateFormChange: EndpointIsolatedFormProps['onChange'] = useCallback( + ({ comment: newComment }) => setComment(newComment), + [] + ); + const casesList = useMemo( () => caseIds.map((id, index) => { @@ -100,43 +92,29 @@ export const HostIsolationPanel = React.memo( return ( <> - - {caseCount > 0 && ( - <> - -

- -

-
- -
    {casesList}
-
- - )} -
- - - - -

{RETURN_TO_ALERT_DETAILS}

-
-
-
-
+ 0 && ( + <> + +

+ +

+
+ +
    {casesList}
+
+ + ) + } + /> ); }, [backToAlertDetails, hostName, caseCount, casesList]); @@ -145,13 +123,18 @@ export const HostIsolationPanel = React.memo( return ( <> - -

+ {hostName}, cases: ( {caseCount} @@ -161,42 +144,19 @@ export const HostIsolationPanel = React.memo( ), }} /> -

-
- - -

{COMMENT}

-
- ) => - setComment(event.target.value) } /> - - - - {CANCEL} - - - - {CONFIRM} - - - ); }, [ - alertRule, - backToAlertDetails, - comment, - confirmHostIsolation, hostName, + backToAlertDetails, + confirmHostIsolation, + handleIsolateFormChange, + comment, loading, caseCount, + alertRule, ]); return isIsolated ? hostIsolated : hostNotIsolated; diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/translations.ts b/x-pack/plugins/security_solution/public/detections/components/host_isolation/translations.ts index 8d6334f6c340..027a97cc3846 100644 --- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/translations.ts @@ -14,23 +14,6 @@ export const ISOLATE_HOST = i18n.translate( } ); -export const COMMENT = i18n.translate('xpack.securitySolution.endpoint.hostIsolation.comment', { - defaultMessage: 'Comment', -}); - -export const COMMENT_PLACEHOLDER = i18n.translate( - 'xpack.securitySolution.endpoint.hostIsolation.comment.placeholder', - { defaultMessage: 'You may leave an optional note here.' } -); - -export const CANCEL = i18n.translate('xpack.securitySolution.endpoint.hostIsolation.cancel', { - defaultMessage: 'Cancel', -}); - -export const CONFIRM = i18n.translate('xpack.securitySolution.endpoint.hostIsolation.confirm', { - defaultMessage: 'Confirm', -}); - export const CASES_ASSOCIATED_WITH_ALERT = (caseCount: number): string => i18n.translate( 'xpack.securitySolution.endpoint.hostIsolation.isolateHost.casesAssociatedWithAlert',