From c26a7d47b4d9485ce743f67a9de16bc6fbb7f816 Mon Sep 17 00:00:00 2001 From: Ashokaditya Date: Tue, 11 May 2021 16:33:26 +0200 Subject: [PATCH] WIP add tabs for endpoint details --- .../components/endpoint_details_tabs.tsx | 78 +++++++++++++++++ .../details/components/timeline_entry.tsx | 59 +++++++++++++ .../view/details/endpoint_activity_log.tsx | 24 ++++++ .../view/details/endpoints.stories.tsx | 46 ++++++++++ .../endpoint_hosts/view/details/index.tsx | 86 +++++++++++++++++-- .../pages/endpoint_hosts/view/translations.ts | 16 ++++ 6 files changed, 303 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/timeline_entry.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_activity_log.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx new file mode 100644 index 000000000000..68fec21e3001 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_details_tabs.tsx @@ -0,0 +1,78 @@ +/* + * 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, { useCallback, useMemo, useState } from 'react'; +import styled from 'styled-components'; +import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui'; + +export enum EndpointDetailsTabsTypes { + overview = 'overview', + activityLog = 'activity-log', +} + +export type EndpointDetailsTabsId = + | EndpointDetailsTabsTypes.overview + | EndpointDetailsTabsTypes.activityLog; + +interface EndpointDetailsTabs { + id: string; + name: string; + content: JSX.Element; +} + +const StyledEuiTabbedContent = styled(EuiTabbedContent)` + display: flex; + flex: 1; + flex-direction: column; + overflow: hidden; + + > [role='tabpanel'] { + padding: 12px 0; + display: flex; + flex: 1; + flex-direction: column; + overflow: hidden; + overflow-y: auto; + ::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; + } + ::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); + } + } +`; + +export const EndpointDetailsFlyoutTabs = ({ tabs }: { tabs: EndpointDetailsTabs[] }) => { + const [selectedTabId, setSelectedTabId] = useState( + EndpointDetailsTabsTypes.overview + ); + + const handleTabClick = useCallback( + (tab: EuiTabbedContentTab) => setSelectedTabId(tab.id as EndpointDetailsTabsId), + [setSelectedTabId] + ); + + const selectedTab = useMemo(() => tabs.find((tab) => tab.id === selectedTabId), [ + tabs, + selectedTabId, + ]); + + return ( + + ); +}; + +EndpointDetailsFlyoutTabs.displayName = 'EndpointDetailsFlyoutTabs'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/timeline_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/timeline_entry.tsx new file mode 100644 index 000000000000..05846b75606c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/timeline_entry.tsx @@ -0,0 +1,59 @@ +/* + * 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 from 'react'; +import styled from 'styled-components'; + +import { EuiComment, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; +import { EndpointAction } from '../../../../../../../common/endpoint/types'; + +const TimelineItem = styled(EuiComment)` + figure { + border: 0; + } + figcaption.euiCommentEvent__header { + background-color: transparent; + border: 0; + } +`; + +const CommentItem = styled.div` + max-width: 85%; + > div { + display: inline-flex; + } +`; + +export const TimelineEntry = ({ endpointAction }: { endpointAction: EndpointAction }) => { + const isIsolated = endpointAction?.data.command === 'isolate'; + const timelineIcon = isIsolated ? 'lock' : 'lockOpen'; + const event = `${isIsolated ? 'isolated' : 'unisolated'} the endpoint`; + const hasComment = !!endpointAction.data.comment; + return ( + + {endpointAction['@timestamp']} + + {hasComment ? ( + + + +

{endpointAction.data.comment}

+
+
+
+ ) : undefined} +
+ ); +}; + +TimelineEntry.displayName = 'TimelineEntry'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_activity_log.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_activity_log.tsx new file mode 100644 index 000000000000..ef8c29e336cb --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_activity_log.tsx @@ -0,0 +1,24 @@ +/* + * 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 from 'react'; + +import { EuiFieldSearch, EuiSpacer } from '@elastic/eui'; +import { TimelineEntry } from './components/timeline_entry'; +import { EndpointAction } from '../../../../../../common/endpoint/types'; + +export const EndpointActivityLog = ({ endpointActions }: { endpointActions: EndpointAction[] }) => ( + <> + + + {endpointActions.map((endpointAction) => ( + + ))} + +); + +EndpointActivityLog.displayName = 'EndpointActivityLog'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx new file mode 100644 index 000000000000..2d5f3fa479b0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx @@ -0,0 +1,46 @@ +/* + * 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, { ComponentType } from 'react'; + +import { EndpointDetailsFlyoutTabs } from './components/endpoint_details_tabs'; +import { EndpointActivityLog } from './endpoint_activity_log'; +import { EndpointDetailsFlyout } from '.'; +import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common'; + +import { dummyEndpointActions } from './'; + +export default { + title: 'Endpoints/Endpoint Details', + component: EndpointDetailsFlyout, + decorators: [ + (Story: ComponentType) => ( + + + + ), + ], +}; + +export const Tabs = () => ( + {'Endpoint Details'}, + }, + { + id: 'activity-log', + name: 'Activity Log', + content: ActivityLog(), + }, + ]} + /> +); + +export const ActivityLog = () => ; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx index e136b6357935..a014ecc2e9a6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx @@ -6,6 +6,8 @@ */ import React, { useCallback, useEffect, memo, useMemo } from 'react'; +import moment from 'moment'; + import { EuiFlyout, EuiFlyoutBody, @@ -16,6 +18,8 @@ import { EuiSpacer, EuiEmptyPrompt, EuiToolTip, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -41,15 +45,65 @@ import { policyResponseAppliedRevision, } from '../../store/selectors'; import { EndpointDetails } from './endpoint_details'; +import { EndpointActivityLog } from './endpoint_activity_log'; import { PolicyResponse } from './policy_response'; -import { HostMetadata } from '../../../../../../common/endpoint/types'; +import * as i18 from '../translations'; +import { EndpointAction, HostMetadata } from '../../../../../../common/endpoint/types'; import { FlyoutSubHeader, FlyoutSubHeaderProps } from './components/flyout_sub_header'; +import { + EndpointDetailsFlyoutTabs, + EndpointDetailsTabsTypes, +} from './components/endpoint_details_tabs'; import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { getEndpointListPath } from '../../../../common/routing'; import { SecurityPageName } from '../../../../../app/types'; import { useFormatUrl } from '../../../../../common/components/link_to'; import { PreferenceFormattedDateFromPrimitive } from '../../../../../common/components/formatted_date'; +export const dummyEndpointActions: EndpointAction[] = [ + { + action_id: '1', + '@timestamp': moment().subtract(2, 'hours').fromNow().toString(), + expiration: moment().add(1, 'day').fromNow().toString(), + type: 'INPUT_ACTION', + input_type: 'endpoint', + agents: ['x', 'y', 'z'], + user_id: 'ash', + data: { + command: 'isolate', + comment: 'Sem et tortor consequat id porta nibh venenatis cras sed.', + }, + }, + { + action_id: '2', + '@timestamp': moment().subtract(4, 'hours').fromNow().toString(), + expiration: moment().add(1, 'day').fromNow().toString(), + type: 'INPUT_ACTION', + input_type: 'endpoint', + agents: ['x', 'y', 'z'], + user_id: 'someone', + data: { + command: 'unisolate', + comment: 'Turpis egestas pretium aenean pharetra.', + }, + }, + { + action_id: '3', + '@timestamp': moment().subtract(1, 'day').fromNow().toString(), + expiration: moment().add(3, 'day').fromNow().toString(), + type: 'INPUT_ACTION', + input_type: 'endpoint', + agents: ['x', 'y', 'z'], + user_id: 'ash', + data: { + command: 'isolate', + comment: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ + sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + }, + }, +]; + export const EndpointDetailsFlyout = memo(() => { const history = useHistory(); const toasts = useToasts(); @@ -88,6 +142,7 @@ export const EndpointDetailsFlyout = memo(() => { style={{ zIndex: 4001 }} data-test-subj="endpointDetailsFlyout" size="m" + paddingSize="m" > {loading ? ( @@ -116,11 +171,30 @@ export const EndpointDetailsFlyout = memo(() => { {show === 'details' && ( <> - + + + + ), + }, + { + id: EndpointDetailsTabsTypes.activityLog, + name: i18.ACTIVITY_LOG, + content: , + }, + ]} + /> + + )} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts new file mode 100644 index 000000000000..7b5d0197eda1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts @@ -0,0 +1,16 @@ +/* + * 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 OVERVIEW = i18n.translate('xpack.securitySolution.endpointDetails.overview', { + defaultMessage: 'Overview', +}); + +export const ACTIVITY_LOG = i18n.translate('xpack.securitySolution.endpointDetails.activityLog', { + defaultMessage: 'Activity Log', +});