Skip to content

Commit

Permalink
Merge branch 'master' into make-actions-exportable
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Oct 19, 2021
2 parents b3f36b6 + d0bc10f commit 701c96e
Show file tree
Hide file tree
Showing 17 changed files with 954 additions and 208 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
export const ENDPOINT_ACTIONS_DS = '.logs-endpoint.actions';
export const ENDPOINT_ACTIONS_INDEX = `${ENDPOINT_ACTIONS_DS}-default`;
export const ENDPOINT_ACTION_RESPONSES_DS = '.logs-endpoint.action.responses';
export const ENDPOINT_ACTION_RESPONSES_INDEX = `${ENDPOINT_ACTIONS_DS}-default`;
export const ENDPOINT_ACTION_RESPONSES_INDEX = `${ENDPOINT_ACTION_RESPONSES_DS}-default`;

export const eventsIndexPattern = 'logs-endpoint.events.*';
export const alertsIndexPattern = 'logs-endpoint.alerts-*';
Expand Down Expand Up @@ -60,3 +60,5 @@ export const UNISOLATE_HOST_ROUTE = `${BASE_ENDPOINT_ROUTE}/unisolate`;
/** Endpoint Actions Log Routes */
export const ENDPOINT_ACTION_LOG_ROUTE = `/api/endpoint/action_log/{agent_id}`;
export const ACTION_STATUS_ROUTE = `/api/endpoint/action_status`;

export const failedFleetActionErrorCode = '424';
33 changes: 30 additions & 3 deletions x-pack/plugins/security_solution/common/endpoint/types/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import { ActionStatusRequestSchema, HostIsolationRequestSchema } from '../schema

export type ISOLATION_ACTIONS = 'isolate' | 'unisolate';

export const ActivityLogItemTypes = {
ACTION: 'action' as const,
RESPONSE: 'response' as const,
FLEET_ACTION: 'fleetAction' as const,
FLEET_RESPONSE: 'fleetResponse' as const,
};

interface EcsError {
code?: string;
id?: string;
Expand Down Expand Up @@ -87,8 +94,24 @@ export interface EndpointActionResponse {
action_data: EndpointActionData;
}

export interface EndpointActivityLogAction {
type: typeof ActivityLogItemTypes.ACTION;
item: {
id: string;
data: LogsEndpointAction;
};
}

export interface EndpointActivityLogActionResponse {
type: typeof ActivityLogItemTypes.RESPONSE;
item: {
id: string;
data: LogsEndpointActionResponse;
};
}

export interface ActivityLogAction {
type: 'action';
type: typeof ActivityLogItemTypes.FLEET_ACTION;
item: {
// document _id
id: string;
Expand All @@ -97,15 +120,19 @@ export interface ActivityLogAction {
};
}
export interface ActivityLogActionResponse {
type: 'response';
type: typeof ActivityLogItemTypes.FLEET_RESPONSE;
item: {
// document id
id: string;
// document _source
data: EndpointActionResponse;
};
}
export type ActivityLogEntry = ActivityLogAction | ActivityLogActionResponse;
export type ActivityLogEntry =
| ActivityLogAction
| ActivityLogActionResponse
| EndpointActivityLogAction
| EndpointActivityLogActionResponse;
export interface ActivityLog {
page: number;
pageSize: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ interface Range {

const DatePickerWrapper = styled.div`
width: ${(props) => props.theme.eui.fractions.single.percentage};
max-width: 350px;
`;
const StickyFlexItem = styled(EuiFlexItem)`
background: ${(props) => `${props.theme.eui.euiHeaderBackgroundColor}`};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,54 @@ import React, { memo, useMemo } from 'react';
import styled from 'styled-components';

import { EuiComment, EuiText, EuiAvatarProps, EuiCommentProps, IconType } from '@elastic/eui';
import { Immutable, ActivityLogEntry } from '../../../../../../../common/endpoint/types';
import {
Immutable,
ActivityLogEntry,
ActivityLogItemTypes,
} from '../../../../../../../common/endpoint/types';
import { FormattedRelativePreferenceDate } from '../../../../../../common/components/formatted_date';
import { LogEntryTimelineIcon } from './log_entry_timeline_icon';
import { useEuiTheme } from '../../../../../../common/lib/theme/use_eui_theme';

import * as i18 from '../../translations';

const useLogEntryUIProps = (
logEntry: Immutable<ActivityLogEntry>
logEntry: Immutable<ActivityLogEntry>,
theme: ReturnType<typeof useEuiTheme>
): {
actionEventTitle: string;
avatarColor: EuiAvatarProps['color'];
avatarIconColor: EuiAvatarProps['iconColor'];
avatarSize: EuiAvatarProps['size'];
commentText: string;
commentType: EuiCommentProps['type'];
displayComment: boolean;
displayResponseEvent: boolean;
failedActionEventTitle: string;
iconType: IconType;
isResponseEvent: boolean;
isSuccessful: boolean;
isCompleted: boolean;
responseEventTitle: string;
username: string | React.ReactNode;
} => {
return useMemo(() => {
let iconType: IconType = 'dot';
let commentType: EuiCommentProps['type'] = 'update';
let commentText: string = '';
let avatarColor: EuiAvatarProps['color'] = theme.euiColorLightestShade;
let avatarIconColor: EuiAvatarProps['iconColor'];
let avatarSize: EuiAvatarProps['size'] = 's';
let failedActionEventTitle: string = '';
let isIsolateAction: boolean = false;
let isResponseEvent: boolean = false;
let isSuccessful: boolean = false;
let isCompleted: boolean = false;
let displayComment: boolean = false;
let displayResponseEvent: boolean = true;
let username: EuiCommentProps['username'] = '';

if (logEntry.type === 'action') {
if (logEntry.type === ActivityLogItemTypes.FLEET_ACTION) {
avatarSize = 'm';
commentType = 'regular';
commentText = logEntry.item.data.data.comment?.trim() ?? '';
Expand All @@ -59,13 +73,51 @@ const useLogEntryUIProps = (
displayComment = true;
}
}
} else if (logEntry.type === 'response') {
}
if (logEntry.type === ActivityLogItemTypes.ACTION) {
avatarSize = 'm';
commentType = 'regular';
commentText = logEntry.item.data.EndpointActions.data.comment?.trim() ?? '';
displayResponseEvent = false;
iconType = 'lockOpen';
username = logEntry.item.data.user.id;
avatarIconColor = theme.euiColorVis9_behindText;
failedActionEventTitle = i18.ACTIVITY_LOG.LogEntry.action.failedEndpointReleaseAction;
if (logEntry.item.data.EndpointActions.data) {
const data = logEntry.item.data.EndpointActions.data;
if (data.command === 'isolate') {
iconType = 'lock';
failedActionEventTitle = i18.ACTIVITY_LOG.LogEntry.action.failedEndpointIsolateAction;
}
if (commentText) {
displayComment = true;
}
}
} else if (logEntry.type === ActivityLogItemTypes.FLEET_RESPONSE) {
isResponseEvent = true;
if (logEntry.item.data.action_data.command === 'isolate') {
isIsolateAction = true;
}
if (!!logEntry.item.data.completed_at && !logEntry.item.data.error) {
isSuccessful = true;
} else {
avatarColor = theme.euiColorVis9_behindText;
}
} else if (logEntry.type === ActivityLogItemTypes.RESPONSE) {
iconType = 'check';
isResponseEvent = true;
if (logEntry.item.data.EndpointActions.data.command === 'isolate') {
isIsolateAction = true;
}
if (logEntry.item.data.EndpointActions.completed_at) {
isCompleted = true;
if (!logEntry.item.data.error) {
isSuccessful = true;
avatarColor = theme.euiColorVis0_behindText;
} else {
isSuccessful = false;
avatarColor = theme.euiColorVis9_behindText;
}
}
}

Expand All @@ -75,13 +127,23 @@ const useLogEntryUIProps = (

const getResponseEventTitle = () => {
if (isIsolateAction) {
if (isSuccessful) {
if (isCompleted) {
if (isSuccessful) {
return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndSuccessful;
}
return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndUnsuccessful;
} else if (isSuccessful) {
return i18.ACTIVITY_LOG.LogEntry.response.isolationSuccessful;
} else {
return i18.ACTIVITY_LOG.LogEntry.response.isolationFailed;
}
} else {
if (isSuccessful) {
if (isCompleted) {
if (isSuccessful) {
return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndSuccessful;
}
return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndUnsuccessful;
} else if (isSuccessful) {
return i18.ACTIVITY_LOG.LogEntry.response.unisolationSuccessful;
} else {
return i18.ACTIVITY_LOG.LogEntry.response.unisolationFailed;
Expand All @@ -91,18 +153,22 @@ const useLogEntryUIProps = (

return {
actionEventTitle,
avatarColor,
avatarIconColor,
avatarSize,
commentText,
commentType,
displayComment,
displayResponseEvent,
failedActionEventTitle,
iconType,
isResponseEvent,
isSuccessful,
isCompleted,
responseEventTitle: getResponseEventTitle(),
username,
};
}, [logEntry]);
}, [logEntry, theme]);
};

const StyledEuiComment = styled(EuiComment)`
Expand All @@ -126,28 +192,41 @@ const StyledEuiComment = styled(EuiComment)`
`;

export const LogEntry = memo(({ logEntry }: { logEntry: Immutable<ActivityLogEntry> }) => {
const theme = useEuiTheme();
const {
actionEventTitle,
avatarColor,
avatarIconColor,
avatarSize,
commentText,
commentType,
displayComment,
displayResponseEvent,
failedActionEventTitle,
iconType,
isResponseEvent,
isSuccessful,
responseEventTitle,
username,
} = useLogEntryUIProps(logEntry);
} = useLogEntryUIProps(logEntry, theme);

return (
<StyledEuiComment
type={(commentType ?? 'regular') as EuiCommentProps['type']}
username={username}
timestamp={<FormattedRelativePreferenceDate value={logEntry.item.data['@timestamp']} />}
event={<b>{displayResponseEvent ? responseEventTitle : actionEventTitle}</b>}
event={
<b>
{displayResponseEvent
? responseEventTitle
: failedActionEventTitle
? failedActionEventTitle
: actionEventTitle}
</b>
}
timelineIcon={
<LogEntryTimelineIcon {...{ avatarSize, iconType, isResponseEvent, isSuccessful }} />
<LogEntryTimelineIcon
{...{ avatarSize, iconType, isResponseEvent, avatarColor, avatarIconColor }}
/>
}
data-test-subj="timelineEntry"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,27 @@

import React, { memo } from 'react';
import { EuiAvatar, EuiAvatarProps } from '@elastic/eui';
import { useEuiTheme } from '../../../../../../common/lib/theme/use_eui_theme';

export const LogEntryTimelineIcon = memo(
({
avatarColor,
avatarIconColor,
avatarSize,
isResponseEvent,
isSuccessful,
iconType,
isResponseEvent,
}: {
avatarColor: EuiAvatarProps['color'];
avatarIconColor?: EuiAvatarProps['iconColor'];
avatarSize: EuiAvatarProps['size'];
isResponseEvent: boolean;
isSuccessful: boolean;
iconType: EuiAvatarProps['iconType'];
isResponseEvent: boolean;
}) => {
const euiTheme = useEuiTheme();

return (
<EuiAvatar
name="Timeline Icon"
size={avatarSize ?? 's'}
color={
isResponseEvent && !isSuccessful
? euiTheme.euiColorVis9_behindText
: euiTheme.euiColorLightestShade
}
iconColor="default"
color={avatarColor}
iconColor={avatarIconColor ?? 'default'}
iconType={iconType ?? 'dot'}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
import React, { ComponentType } from 'react';
import moment from 'moment';

import { ActivityLog, Immutable } from '../../../../../../common/endpoint/types';
import {
ActivityLog,
Immutable,
ActivityLogItemTypes,
} from '../../../../../../common/endpoint/types';
import { EndpointDetailsFlyoutTabs } from './components/endpoint_details_tabs';
import { EndpointActivityLog } from './endpoint_activity_log';
import { EndpointDetailsFlyout } from '.';
Expand All @@ -26,7 +30,7 @@ export const dummyEndpointActivityLog = (
endDate: moment().toString(),
data: [
{
type: 'action',
type: ActivityLogItemTypes.FLEET_ACTION,
item: {
id: '',
data: {
Expand All @@ -44,7 +48,7 @@ export const dummyEndpointActivityLog = (
},
},
{
type: 'action',
type: ActivityLogItemTypes.FLEET_ACTION,
item: {
id: '',
data: {
Expand All @@ -63,7 +67,7 @@ export const dummyEndpointActivityLog = (
},
},
{
type: 'action',
type: ActivityLogItemTypes.FLEET_ACTION,
item: {
id: '',
data: {
Expand All @@ -82,7 +86,7 @@ export const dummyEndpointActivityLog = (
},
},
{
type: 'action',
type: ActivityLogItemTypes.FLEET_ACTION,
item: {
id: '',
data: {
Expand Down
Loading

0 comments on commit 701c96e

Please sign in to comment.