review changes

This commit is contained in:
Ashokaditya 2021-05-19 17:13:52 +02:00
parent 21fe79e7c2
commit 2dd22ed92b
9 changed files with 140 additions and 100 deletions

View file

@ -250,7 +250,7 @@ export const showView: (state: EndpointState) => 'policy_response' | 'details' =
* Returns the selected endpoint's elastic agent Id
* used for fetching endpoint actions log
*/
export const selectedAgent = (state: Immutable<EndpointState>) => {
export const selectedAgent = (state: Immutable<EndpointState>): string => {
const hostList = state.hosts;
const { selected_endpoint: selectedEndpoint } = uiQueryParams(state);
return (

View file

@ -25,24 +25,19 @@ interface EndpointDetailsTabs {
}
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;
height: 100%;
padding-right: 12px;
overflow: hidden;
overflow-y: auto;
::-webkit-scrollbar {
-webkit-appearance: none;
width: 7px;
width: 4px;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
border-radius: 2px;
background-color: rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
}

View file

@ -5,41 +5,37 @@
* 2.0.
*/
import React from 'react';
import styled from 'styled-components';
import React, { memo } from 'react';
import moment from 'moment';
import { EuiComment, EuiText } from '@elastic/eui';
import { Immutable, EndpointAction } from '../../../../../../../common/endpoint/types';
const TimelineItem = styled(EuiComment)``;
export const TimelineEntry = ({
endpointAction,
}: {
endpointAction: Immutable<EndpointAction>;
}) => {
const isIsolated = endpointAction?.data.command === 'isolate';
const timelineIcon = isIsolated ? 'lock' : 'lockOpen';
const event = `${isIsolated ? 'isolated' : 'unisolated'} host`;
const hasComment = !!endpointAction.data.comment;
// do this better when we can distinguish between endpoint events vs user events
const commentType = endpointAction.user_id === 'sys' ? 'update' : 'regular';
return (
<TimelineItem
type={commentType}
username={endpointAction.user_id}
timestamp={endpointAction['@timestamp']}
event={event}
timelineIcon={timelineIcon}
data-test-subj="timelineEntry"
>
{hasComment ? (
<EuiText size="s">
<p>{endpointAction.data.comment}</p>
</EuiText>
) : undefined}
</TimelineItem>
);
};
export const TimelineEntry = memo(
({ endpointAction }: { endpointAction: Immutable<EndpointAction> }) => {
const isIsolated = endpointAction?.data.command === 'isolate';
const timelineIcon = isIsolated ? 'lock' : 'lockOpen';
const event = `${isIsolated ? 'isolated' : 'unisolated'} host`;
const hasComment = !!endpointAction.data.comment;
// do this better when we can distinguish between endpoint events vs user events
const commentType = endpointAction.user_id === 'sys' ? 'update' : 'regular';
return (
<EuiComment
type={commentType}
username={endpointAction.user_id}
timestamp={moment(endpointAction['@timestamp']).fromNow().toString()}
event={event}
timelineIcon={timelineIcon}
data-test-subj="timelineEntry"
>
{hasComment ? (
<EuiText size="s">
<p>{endpointAction.data.comment}</p>
</EuiText>
) : undefined}
</EuiComment>
);
}
);
TimelineEntry.displayName = 'TimelineEntry';

View file

@ -5,26 +5,35 @@
* 2.0.
*/
import React from 'react';
import React, { memo } from 'react';
import { EuiFieldSearch, EuiSpacer } from '@elastic/eui';
import { EuiFieldSearch, EuiEmptyPrompt, EuiSpacer } from '@elastic/eui';
import { TimelineEntry } from './components/timeline_entry';
import * as i18 from '../translations';
import { Immutable, EndpointAction } from '../../../../../../common/endpoint/types';
export const EndpointActivityLog = ({
endpointActions,
}: {
endpointActions: Immutable<EndpointAction[]>;
}) => {
return (
<>
<EuiFieldSearch compressed fullWidth placeholder="Search activity log" />
<EuiSpacer size="l" />
{endpointActions.map((endpointAction) => (
<TimelineEntry key={endpointAction['@timestamp']} endpointAction={endpointAction} />
))}
</>
);
};
export const EndpointActivityLog = memo(
({ endpointActions }: { endpointActions: Immutable<EndpointAction[]> }) => {
return (
<>
<EuiSpacer size="l" />
<EuiFieldSearch compressed fullWidth placeholder={i18.SEARCH_ACTIVITY_LOG} />
<EuiSpacer size="l" />
{!endpointActions.length ? (
<EuiEmptyPrompt
iconType="alert"
titleSize="s"
title={<h2>{'No activity logged for the endpoint'}</h2>}
body={<p>{'Perform some actions on the endpoint to see actions log here.'}</p>}
/>
) : (
endpointActions.map((endpointAction) => (
<TimelineEntry key={endpointAction.action_id} endpointAction={endpointAction} />
))
)}
</>
);
}
);
EndpointActivityLog.displayName = 'EndpointActivityLog';

View file

@ -258,6 +258,7 @@ export const EndpointDetails = memo(
return (
<>
<EuiSpacer size="l" />
<EuiDescriptionList
type="column"
listItems={detailsResultsUpper}

View file

@ -65,6 +65,27 @@ import { SecurityPageName } from '../../../../../app/types';
import { useFormatUrl } from '../../../../../common/components/link_to';
import { PreferenceFormattedDateFromPrimitive } from '../../../../../common/components/formatted_date';
const contentLoadingMarkup = (
<>
<EuiLoadingContent lines={3} /> <EuiSpacer size="l" /> <EuiLoadingContent lines={3} />
</>
);
const DetailsFlyoutBody = styled(EuiFlyoutBody)`
overflow-y: hidden;
flex: 1;
.euiFlyoutBody__overflow {
overflow: hidden;
mask-image: none;
}
.euiFlyoutBody__overflowContent {
height: 100%;
display: flex;
}
`;
export const EndpointDetailsFlyout = memo(() => {
const history = useHistory();
const toasts = useToasts();
@ -102,7 +123,7 @@ export const EndpointDetailsFlyout = memo(() => {
}, [history, queryParamsWithoutSelectedEndpoint]);
useEffect(() => {
if (hostDetailsError !== undefined || activityError !== undefined) {
if (hostDetailsError !== undefined) {
toasts.addDanger({
title: i18n.translate('xpack.securitySolution.endpoint.details.errorTitle', {
defaultMessage: 'Could not find host',
@ -112,6 +133,16 @@ export const EndpointDetailsFlyout = memo(() => {
}),
});
}
if (activityError !== undefined) {
toasts.addDanger({
title: i18n.translate('xpack.securitySolution.endpoint.activityLog.errorTitle', {
defaultMessage: 'Could not find activity log for host',
}),
text: i18n.translate('xpack.securitySolution.endpoint.activityLog.errorBody', {
defaultMessage: 'Please exit the flyout and select another host with actions.',
}),
});
}
}, [hostDetailsError, activityError, toasts]);
return (
@ -138,47 +169,48 @@ export const EndpointDetailsFlyout = memo(() => {
</EuiToolTip>
)}
</EuiFlyoutHeader>
{hostDetails === undefined || activityLog === undefined ? (
{show === 'details' && (
<>
<EuiFlyoutBody>
<EuiLoadingContent lines={3} /> <EuiSpacer size="l" /> <EuiLoadingContent lines={3} />
</EuiFlyoutBody>
</>
) : (
<>
{show === 'details' && (
<>
<EuiFlyoutBody data-test-subj="endpointDetailsFlyoutBody">
<EuiFlexGroup>
<EuiFlexItem>
<EndpointDetailsFlyoutTabs
tabs={[
{
id: EndpointDetailsTabsTypes.overview,
name: i18.OVERVIEW,
content: (
<EndpointDetails
details={hostDetails}
policyInfo={policyInfo}
hostStatus={hostStatus}
/>
),
},
{
id: EndpointDetailsTabsTypes.activityLog,
name: i18.ACTIVITY_LOG,
content: <EndpointActivityLog endpointActions={activityLog} />,
},
]}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutBody>
</>
)}
{show === 'policy_response' && <PolicyResponseFlyoutPanel hostMeta={hostDetails} />}
<DetailsFlyoutBody data-test-subj="endpointDetailsFlyoutBody">
<EuiFlexGroup>
<EuiFlexItem>
<EndpointDetailsFlyoutTabs
tabs={[
{
id: EndpointDetailsTabsTypes.overview,
name: i18.OVERVIEW,
content:
hostDetails === undefined ? (
contentLoadingMarkup
) : (
<EndpointDetails
details={hostDetails}
policyInfo={policyInfo}
hostStatus={hostStatus}
/>
),
},
{
id: EndpointDetailsTabsTypes.activityLog,
name: i18.ACTIVITY_LOG,
content:
activityLog === undefined ? (
contentLoadingMarkup
) : (
<EndpointActivityLog endpointActions={activityLog} />
),
},
]}
/>
</EuiFlexItem>
</EuiFlexGroup>
</DetailsFlyoutBody>
</>
)}
{show === 'policy_response' && !!hostDetails && (
<PolicyResponseFlyoutPanel hostMeta={hostDetails} />
)}
<EuiFlyoutFooter>
<EuiPagination
pageCount={pageCount}

View file

@ -14,3 +14,10 @@ export const OVERVIEW = i18n.translate('xpack.securitySolution.endpointDetails.o
export const ACTIVITY_LOG = i18n.translate('xpack.securitySolution.endpointDetails.activityLog', {
defaultMessage: 'Activity Log',
});
export const SEARCH_ACTIVITY_LOG = i18n.translate(
'xpack.securitySolution.endpointDetails.activityLog.search',
{
defaultMessage: 'Search activity log',
}
);

View file

@ -13,7 +13,7 @@ import { SecuritySolutionPluginRouter } from '../../../types';
import { EndpointAppContext } from '../../types';
/**
* Registers the Host-(un-)isolation routes
* Registers the endpoint activity-log route
*/
export function registerActionAuditLogRoutes(
router: SecuritySolutionPluginRouter,

View file

@ -47,7 +47,7 @@ export const actionsLogRequestHandler = (
return res.customError({
statusCode: 500,
body: {
message: `Error fetching Actions Log for: ${req.params.agent_id}`,
message: `Error fetching actions log for agent_id ${req.params.agent_id}`,
},
});
}