Skip to content

Commit

Permalink
[Security Solution] Remove sourcerer browser fields hover actions to …
Browse files Browse the repository at this point in the history
…help performance (elastic#131363)

* Batch setState calls to make sure all state updates are applied evenly

* Remove sourcerer hook from useHoverActions and pass needed fields as props

* Update snapshots, remove ReactDOM batching

* Make row renderers aggregatable where appropriate

* add pagination to details table

* Fix hover actions on host/network details

* Remove unneeded props

* fix table pagination tests

* update test

* Show top n for authentications and threat indicator match rules

* Fix anomaly score, entity, influence, and user id show top N

* Pass props on wrapper and not data provider

* Add missing row renderer draggables to use top N props

* Update snapshots

* Pr feedback

Co-authored-by: Michael Olorunnisola <michael.olorunnisola@elastic.co>
Co-authored-by: Robert Austin <robert.austin@elastic.co>
(cherry picked from commit f3c1ad7)

# Conflicts:
#	x-pack/plugins/security_solution/public/common/components/authentication/helpers.tsx
#	x-pack/plugins/security_solution/public/common/lib/cell_actions/expanded_cell_value_actions.tsx
  • Loading branch information
kqualters-elastic authored and oatkiller committed May 16, 2022
1 parent bd49aeb commit 02cc9a5
Show file tree
Hide file tree
Showing 90 changed files with 902 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
/*
* 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 { has } from 'lodash/fp';
import React from 'react';

import { DragEffects, DraggableWrapper } from '../drag_and_drop/draggable_wrapper';
import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { getEmptyTagValue } from '../empty_value';
import { FormattedRelativePreferenceDate } from '../formatted_date';
import { Columns, ItemsPerRow } from '../paginated_table';
import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider';
import { Provider } from '../../../timelines/components/timeline/data_providers/provider';
import { getRowItemDraggables } from '../tables/helpers';

import * as i18n from './translations';
import { HostDetailsLink, NetworkDetailsLink, UserDetailsLink } from '../links';
import { AuthenticationsEdges, MatrixHistogramType } from '../../../../common/search_strategy';
import { AuthTableColumns } from './types';
import {
MatrixHistogramConfigs,
MatrixHistogramMappingTypes,
MatrixHistogramOption,
} from '../matrix_histogram/types';
import { LensAttributes } from '../visualization_actions/types';
import { authenticationLensAttributes } from '../visualization_actions/lens_attributes/common/authentication';

export const getHostDetailsAuthenticationColumns = (usersEnabled: boolean): AuthTableColumns => [
getUserColumn(usersEnabled),
SUCCESS_COLUMN,
FAILURES_COLUMN,
LAST_SUCCESSFUL_TIME_COLUMN,
LAST_SUCCESSFUL_SOURCE_COLUMN,
LAST_FAILED_TIME_COLUMN,
LAST_FAILED_SOURCE_COLUMN,
];

export const getHostsPageAuthenticationColumns = (usersEnabled: boolean): AuthTableColumns => [
getUserColumn(usersEnabled),
SUCCESS_COLUMN,
FAILURES_COLUMN,
LAST_SUCCESSFUL_TIME_COLUMN,
LAST_SUCCESSFUL_SOURCE_COLUMN,
LAST_SUCCESSFUL_DESTINATION_COLUMN,
LAST_FAILED_TIME_COLUMN,
LAST_FAILED_SOURCE_COLUMN,
LAST_FAILED_DESTINATION_COLUMN,
];

export const getUsersPageAuthenticationColumns = (): AuthTableColumns =>
getHostsPageAuthenticationColumns(true);

export const getUserDetailsAuthenticationColumns = (): AuthTableColumns => [
HOST_COLUMN,
SUCCESS_COLUMN,
FAILURES_COLUMN,
LAST_SUCCESSFUL_TIME_COLUMN,
LAST_SUCCESSFUL_SOURCE_COLUMN,
LAST_FAILED_TIME_COLUMN,
LAST_FAILED_SOURCE_COLUMN,
];

export const rowItems: ItemsPerRow[] = [
{
text: i18n.ROWS_5,
numberOfRow: 5,
},
{
text: i18n.ROWS_10,
numberOfRow: 10,
},
];

const FAILURES_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.FAILURES,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) => {
const id = escapeDataProviderId(`authentications-table-${node._id}-failures-${node.failures}`);
return (
<DraggableWrapper
key={id}
dataProvider={{
and: [],
enabled: true,
id,
name: 'authentication_failure',
excluded: false,
kqlQuery: '',
queryMatch: {
field: 'event.type',
value: 'authentication_failure',
operator: IS_OPERATOR,
},
}}
isAggregatable={true}
fieldType={'keyword'}
render={(dataProvider, _, snapshot) =>
snapshot.isDragging ? (
<DragEffects>
<Provider dataProvider={dataProvider} />
</DragEffects>
) : (
node.failures
)
}
/>
);
},
width: '8%',
};
const LAST_SUCCESSFUL_TIME_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.LAST_SUCCESSFUL_TIME,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
has('lastSuccess.timestamp', node) && node.lastSuccess?.timestamp != null ? (
<FormattedRelativePreferenceDate value={node.lastSuccess?.timestamp} />
) : (
getEmptyTagValue()
),
};
const LAST_SUCCESSFUL_SOURCE_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.LAST_SUCCESSFUL_SOURCE,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
getRowItemDraggables({
rowItems: node.lastSuccess?.source?.ip || null,
isAggregatable: true,
fieldType: 'ip',
attrName: 'source.ip',
idPrefix: `authentications-table-${node._id}-lastSuccessSource`,
render: (item) => <NetworkDetailsLink ip={item} />,
}),
};
const LAST_SUCCESSFUL_DESTINATION_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.LAST_SUCCESSFUL_DESTINATION,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
getRowItemDraggables({
rowItems: node.lastSuccess?.host?.name ?? null,
isAggregatable: true,
fieldType: 'keyword',
attrName: 'host.name',
idPrefix: `authentications-table-${node._id}-lastSuccessfulDestination`,
render: (item) => <HostDetailsLink hostName={item} />,
}),
};
const LAST_FAILED_TIME_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.LAST_FAILED_TIME,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
has('lastFailure.timestamp', node) && node.lastFailure?.timestamp != null ? (
<FormattedRelativePreferenceDate value={node.lastFailure?.timestamp} />
) : (
getEmptyTagValue()
),
};
const LAST_FAILED_SOURCE_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.LAST_FAILED_SOURCE,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
getRowItemDraggables({
rowItems: node.lastFailure?.source?.ip || null,
isAggregatable: true,
fieldType: 'ip',
attrName: 'source.ip',
idPrefix: `authentications-table-${node._id}-lastFailureSource`,
render: (item) => <NetworkDetailsLink ip={item} />,
}),
};
const LAST_FAILED_DESTINATION_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.LAST_FAILED_DESTINATION,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
getRowItemDraggables({
rowItems: node.lastFailure?.host?.name || null,
attrName: 'host.name',
idPrefix: `authentications-table-${node._id}-lastFailureDestination`,
render: (item) => <HostDetailsLink hostName={item} />,
isAggregatable: true,
fieldType: 'ip',
}),
};

const getUserColumn = (
usersEnabled: boolean
): Columns<AuthenticationsEdges, AuthenticationsEdges> => ({
name: i18n.USER,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
getRowItemDraggables({
rowItems: node.stackedValue,
attrName: 'user.name',
isAggregatable: true,
fieldType: 'keyword',
idPrefix: `authentications-table-${node._id}-userName`,
render: (item) => (usersEnabled ? <UserDetailsLink userName={item} /> : <>{item}</>),
}),
});

const HOST_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.HOST,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) =>
getRowItemDraggables({
rowItems: node.stackedValue,
attrName: 'host.name',
isAggregatable: true,
fieldType: 'keyword',
idPrefix: `authentications-table-${node._id}-hostName`,
render: (item) => <HostDetailsLink hostName={item} />,
}),
};

const SUCCESS_COLUMN: Columns<AuthenticationsEdges, AuthenticationsEdges> = {
name: i18n.SUCCESSES,
truncateText: false,
mobileOptions: { show: true },
render: ({ node }) => {
const id = escapeDataProviderId(
`authentications-table-${node._id}-node-successes-${node.successes}`
);
return (
<DraggableWrapper
key={id}
dataProvider={{
and: [],
enabled: true,
id,
name: 'authentication_success',
excluded: false,
kqlQuery: '',
queryMatch: {
field: 'event.type',
value: 'authentication_success',
operator: IS_OPERATOR,
},
}}
isAggregatable={true}
fieldType="keyword"
render={(dataProvider, _, snapshot) =>
snapshot.isDragging ? (
<DragEffects>
<Provider dataProvider={dataProvider} />
</DragEffects>
) : (
node.successes
)
}
/>
);
},
width: '8%',
};

export const authenticationsStackByOptions: MatrixHistogramOption[] = [
{
text: 'event.outcome',
value: 'event.outcome',
},
];
const DEFAULT_STACK_BY = 'event.outcome';

enum AuthenticationsMatrixDataGroup {
authenticationsSuccess = 'success',
authenticationsFailure = 'failure',
}

export enum ChartColors {
authenticationsSuccess = '#54B399',
authenticationsFailure = '#E7664C',
}

export const authenticationsMatrixDataMappingFields: MatrixHistogramMappingTypes = {
[AuthenticationsMatrixDataGroup.authenticationsSuccess]: {
key: AuthenticationsMatrixDataGroup.authenticationsSuccess,
value: null,
color: ChartColors.authenticationsSuccess,
},
[AuthenticationsMatrixDataGroup.authenticationsFailure]: {
key: AuthenticationsMatrixDataGroup.authenticationsFailure,
value: null,
color: ChartColors.authenticationsFailure,
},
};

export const histogramConfigs: MatrixHistogramConfigs = {
defaultStackByOption:
authenticationsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ??
authenticationsStackByOptions[0],
errorMessage: i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA,
histogramType: MatrixHistogramType.authentications,
mapping: authenticationsMatrixDataMappingFields,
stackByOptions: authenticationsStackByOptions,
title: i18n.NAVIGATION_AUTHENTICATIONS_TITLE,
lensAttributes: authenticationLensAttributes as LensAttributes,
};
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ interface Props {
hideTopN?: boolean;
isDraggable?: boolean;
render: RenderFunctionProp;
isAggregatable?: boolean;
fieldType?: string;
timelineId?: string;
truncate?: boolean;
onFilterAdded?: () => void;
Expand Down Expand Up @@ -131,6 +133,8 @@ const DraggableOnWrapperComponent: React.FC<Props> = ({
hideTopN = false,
onFilterAdded,
render,
fieldType = '',
isAggregatable = false,
timelineId,
truncate,
}) => {
Expand All @@ -154,6 +158,8 @@ const DraggableOnWrapperComponent: React.FC<Props> = ({
hideTopN,
onFilterAdded,
render,
fieldType,
isAggregatable,
timelineId,
truncate,
});
Expand Down Expand Up @@ -313,6 +319,8 @@ const DraggableWrapperComponent: React.FC<Props> = ({
isDraggable = false,
onFilterAdded,
render,
isAggregatable = false,
fieldType = '',
timelineId,
truncate,
}) => {
Expand All @@ -327,6 +335,8 @@ const DraggableWrapperComponent: React.FC<Props> = ({
dataProvider,
hideTopN,
isDraggable,
isAggregatable,
fieldType,
onFilterAdded,
render,
timelineId,
Expand Down Expand Up @@ -372,6 +382,8 @@ const DraggableWrapperComponent: React.FC<Props> = ({
dataProvider={dataProvider}
hideTopN={hideTopN}
onFilterAdded={onFilterAdded}
fieldType={fieldType}
isAggregatable={isAggregatable}
render={render}
timelineId={timelineId}
truncate={truncate}
Expand Down
Loading

0 comments on commit 02cc9a5

Please sign in to comment.