mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
137539 add missing network details revamp (#137541)
* update event tab to show both alerts and events with toggle. (#136540) * add test for SignasByCategory * modify external_alerts_filter to be more efficient * Update usage across explore views to only use EventsQueryTabBody * remove unused files and code related to external alerts and move old alerts files to events_tab folder * test fixes, and more removal of old usage * update failing snapshots * last bit of cleanup * Fix type error * fix type and translations issue Co-authored-by: Kristof-Pierre Cummings <kristofpierre.cummings@elastic.co> * update network details to match hosts, and users details pages * add events table to network pages * Fix minor bugs with network routeing and allow old route to reach new view * Fix failing tests * fix types and transltions * minor fixes before code review * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * update failing snapshot * re-add /ip/ and have :/flowTarget appear before :/tabName * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * remove unneeded import and update type * fix navigation issue * use constant for administration page, and add fallback route * Update links and redirect behaviour * fix dependency array Co-authored-by: Kristof-Pierre Cummings <kristofpierre.cummings@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2f7603de18
commit
241406a8df
27 changed files with 505 additions and 229 deletions
|
@ -107,7 +107,7 @@ export enum SecurityPageName {
|
|||
network = 'network',
|
||||
networkAnomalies = 'network-anomalies',
|
||||
networkDns = 'network-dns',
|
||||
networkExternalAlerts = 'network-external_alerts',
|
||||
networkEvents = 'network-events',
|
||||
networkHttp = 'network-http',
|
||||
networkTls = 'network-tls',
|
||||
noPage = '',
|
||||
|
@ -126,7 +126,6 @@ export enum SecurityPageName {
|
|||
usersAnomalies = 'users-anomalies',
|
||||
usersAuthentications = 'users-authentications',
|
||||
usersEvents = 'users-events',
|
||||
usersExternalAlerts = 'users-external_alerts',
|
||||
usersRisk = 'users-risk',
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlNetworkSingleIpNullKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'app/security/network/ip/127.0.0.1/source?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))'
|
||||
'app/security/network/ip/127.0.0.1/source/flows?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -105,7 +105,7 @@ describe('ml conditional links', () => {
|
|||
visitWithoutDateRange(mlNetworkSingleIpKqlQuery);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/app/security/network/ip/127.0.0.1/source?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))'
|
||||
'/app/security/network/ip/127.0.0.1/source/flows?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import type { FlowTargetSourceDest } from '../../../../common/search_strategy/security_solution/network';
|
||||
import { FlowTarget } from '../../../../common/search_strategy/security_solution/network';
|
||||
import { NetworkDetailsRouteType } from '../../../network/pages/details/types';
|
||||
|
||||
import { appendSearch } from './helpers';
|
||||
|
||||
|
@ -15,5 +16,6 @@ export const getNetworkUrl = (search?: string) => `${appendSearch(search)}`;
|
|||
export const getNetworkDetailsUrl = (
|
||||
detailName: string,
|
||||
flowTarget?: FlowTarget | FlowTargetSourceDest,
|
||||
search?: string
|
||||
) => `/ip/${detailName}/${flowTarget || FlowTarget.source}${appendSearch(search)}`;
|
||||
search?: string,
|
||||
tabName = NetworkDetailsRouteType.flows
|
||||
) => `/ip/${detailName}/${flowTarget || FlowTarget.source}/${tabName}${appendSearch(search)}`;
|
||||
|
|
|
@ -75,17 +75,17 @@ describe('Custom Links', () => {
|
|||
test('can handle array of ips', () => {
|
||||
const wrapper = mount(<NetworkDetailsLink ip={[ipv4, ipv4a]} />);
|
||||
expect(wrapper.find('EuiLink').first().prop('href')).toEqual(
|
||||
`/ip/${encodeURIComponent(ipv4)}/source`
|
||||
`/ip/${encodeURIComponent(ipv4)}/source/flows`
|
||||
);
|
||||
expect(wrapper.text()).toEqual(`${ipv4}${ipv4a}`);
|
||||
expect(wrapper.find('EuiLink').last().prop('href')).toEqual(
|
||||
`/ip/${encodeURIComponent(ipv4a)}/source`
|
||||
`/ip/${encodeURIComponent(ipv4a)}/source/flows`
|
||||
);
|
||||
});
|
||||
test('should render valid link to IP Details with ipv4 as the display text', () => {
|
||||
const wrapper = mount(<NetworkDetailsLink ip={ipv4} />);
|
||||
expect(wrapper.find('EuiLink').prop('href')).toEqual(
|
||||
`/ip/${encodeURIComponent(ipv4)}/source`
|
||||
`/ip/${encodeURIComponent(ipv4)}/source/flows`
|
||||
);
|
||||
expect(wrapper.text()).toEqual(ipv4);
|
||||
});
|
||||
|
@ -93,7 +93,7 @@ describe('Custom Links', () => {
|
|||
test('should render valid link to IP Details with child text as the display text', () => {
|
||||
const wrapper = mount(<NetworkDetailsLink ip={ipv4}>{hostName}</NetworkDetailsLink>);
|
||||
expect(wrapper.find('EuiLink').prop('href')).toEqual(
|
||||
`/ip/${encodeURIComponent(ipv4)}/source`
|
||||
`/ip/${encodeURIComponent(ipv4)}/source/flows`
|
||||
);
|
||||
expect(wrapper.text()).toEqual(hostName);
|
||||
});
|
||||
|
@ -101,7 +101,7 @@ describe('Custom Links', () => {
|
|||
test('should render valid link to IP Details with ipv6 as the display text', () => {
|
||||
const wrapper = mount(<NetworkDetailsLink ip={ipv6} />);
|
||||
expect(wrapper.find('EuiLink').prop('href')).toEqual(
|
||||
`/ip/${encodeURIComponent(ipv6Encoded)}/source`
|
||||
`/ip/${encodeURIComponent(ipv6Encoded)}/source/flows`
|
||||
);
|
||||
expect(wrapper.text()).toEqual(ipv6);
|
||||
});
|
||||
|
|
|
@ -213,32 +213,24 @@ const NetworkDetailsLinkComponent: React.FC<{
|
|||
onClick?: (e: SyntheticEvent) => void | undefined;
|
||||
title?: string;
|
||||
}> = ({ Component, children, ip, flowTarget = FlowTarget.source, isButton, onClick, title }) => {
|
||||
const { formatUrl, search } = useFormatUrl(SecurityPageName.network);
|
||||
const { navigateToApp } = useKibana().services.application;
|
||||
const goToNetworkDetails = useCallback(
|
||||
(ev, cIp: string) => {
|
||||
ev.preventDefault();
|
||||
navigateToApp(APP_UI_ID, {
|
||||
deepLinkId: SecurityPageName.network,
|
||||
path: getNetworkDetailsUrl(encodeURIComponent(encodeIpv6(cIp)), flowTarget, search),
|
||||
});
|
||||
},
|
||||
[flowTarget, navigateToApp, search]
|
||||
);
|
||||
const getHref = useCallback(
|
||||
(cIp: string) => formatUrl(getNetworkDetailsUrl(encodeURIComponent(encodeIpv6(cIp)))),
|
||||
[formatUrl]
|
||||
);
|
||||
const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps();
|
||||
|
||||
const getLink = useCallback(
|
||||
(cIp: string, i: number) =>
|
||||
isButton ? (
|
||||
(cIp: string, i: number) => {
|
||||
const { onClick: onClickNavigation, href } = getSecuritySolutionLinkProps({
|
||||
deepLinkId: SecurityPageName.network,
|
||||
path: getNetworkDetailsUrl(encodeURIComponent(encodeIpv6(cIp)), flowTarget),
|
||||
});
|
||||
|
||||
const onLinkClick = onClick ?? ((e: SyntheticEvent) => onClickNavigation(e as MouseEvent));
|
||||
|
||||
return isButton ? (
|
||||
<GenericLinkButton
|
||||
Component={Component}
|
||||
key={`${cIp}-${i}`}
|
||||
dataTestSubj="data-grid-network-details"
|
||||
onClick={onClick ?? ((e: SyntheticEvent) => goToNetworkDetails(e, cIp))}
|
||||
href={getHref(cIp)}
|
||||
onClick={onLinkClick}
|
||||
href={href}
|
||||
title={title ?? cIp}
|
||||
>
|
||||
{children}
|
||||
|
@ -246,14 +238,15 @@ const NetworkDetailsLinkComponent: React.FC<{
|
|||
) : (
|
||||
<LinkAnchor
|
||||
key={`${cIp}-${i}`}
|
||||
onClick={onClick ?? ((e: SyntheticEvent) => goToNetworkDetails(e, cIp))}
|
||||
href={getHref(cIp)}
|
||||
onClick={onLinkClick}
|
||||
href={href}
|
||||
data-test-subj="network-details"
|
||||
>
|
||||
{children ? children : cIp}
|
||||
</LinkAnchor>
|
||||
),
|
||||
[Component, children, getHref, goToNetworkDetails, isButton, onClick, title]
|
||||
);
|
||||
},
|
||||
[children, Component, flowTarget, getSecuritySolutionLinkProps, onClick, isButton, title]
|
||||
);
|
||||
return isArray(ip) ? <>{ip.map(getLink)}</> : getLink(ip, 0);
|
||||
};
|
||||
|
|
|
@ -262,7 +262,7 @@ describe('Matrix Histogram Component', () => {
|
|||
{
|
||||
detailName: undefined,
|
||||
pageName: 'network',
|
||||
tabName: 'external-alerts',
|
||||
tabName: 'events',
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
networkBreadcrumb,
|
||||
{
|
||||
text: ipv4,
|
||||
href: `securitySolutionUI/network/ip/${ipv4}/source`,
|
||||
href: `securitySolutionUI/network/ip/${ipv4}/source/flows`,
|
||||
},
|
||||
{ text: 'Flows', href: '' },
|
||||
]);
|
||||
|
@ -274,7 +274,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
networkBreadcrumb,
|
||||
{
|
||||
text: ipv6,
|
||||
href: `securitySolutionUI/network/ip/${ipv6Encoded}/source`,
|
||||
href: `securitySolutionUI/network/ip/${ipv6Encoded}/source/flows`,
|
||||
},
|
||||
{ text: 'Flows', href: '' },
|
||||
]);
|
||||
|
@ -574,7 +574,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
networkBreadcrumb,
|
||||
{
|
||||
text: ipv4,
|
||||
href: `securitySolutionUI/network/ip/${ipv4}/source`,
|
||||
href: `securitySolutionUI/network/ip/${ipv4}/source/flows`,
|
||||
},
|
||||
{ text: 'Flows', href: '' },
|
||||
]);
|
||||
|
@ -592,7 +592,7 @@ describe('Navigation Breadcrumbs', () => {
|
|||
networkBreadcrumb,
|
||||
{
|
||||
text: ipv6,
|
||||
href: `securitySolutionUI/network/ip/${ipv6Encoded}/source`,
|
||||
href: `securitySolutionUI/network/ip/${ipv6Encoded}/source/flows`,
|
||||
},
|
||||
{ text: 'Flows', href: '' },
|
||||
]);
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { useEffect, useState, useCallback } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-components';
|
||||
import { SecurityPageName } from '../../../../../common/constants';
|
||||
import type { PrimaryNavigationProps } from './types';
|
||||
import { usePrimaryNavigationItems } from './use_navigation_items';
|
||||
import { useIsGroupedNavigationEnabled } from '../helpers';
|
||||
|
@ -25,10 +26,14 @@ export const usePrimaryNavigation = ({
|
|||
tabName,
|
||||
}: PrimaryNavigationProps): KibanaPageTemplateProps['solutionNav'] => {
|
||||
const isGroupedNavigationEnabled = useIsGroupedNavigationEnabled();
|
||||
const mapLocationToTab = useCallback(
|
||||
(): string => ((tabName && navTabs[tabName]) || navTabs[pageName])?.id ?? '',
|
||||
[pageName, tabName, navTabs]
|
||||
);
|
||||
const mapLocationToTab = useCallback((): string => {
|
||||
if (pageName === SecurityPageName.administration) {
|
||||
// revist with ticket #137625. consider using tab Ids instead of tab Name for matching
|
||||
return ((tabName && navTabs[tabName]) || navTabs[pageName])?.id ?? '';
|
||||
} else {
|
||||
return (navTabs[pageName] || (tabName && navTabs[tabName]))?.id ?? '';
|
||||
}
|
||||
}, [pageName, tabName, navTabs]);
|
||||
|
||||
const [selectedTabId, setSelectedTabId] = useState(mapLocationToTab());
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ export const useLensAttributes = ({
|
|||
return hostNameExistsFilter;
|
||||
}
|
||||
|
||||
if (pageName === SecurityPageName.network && tabName === NetworkRouteType.alerts) {
|
||||
if (pageName === SecurityPageName.network && tabName === NetworkRouteType.events) {
|
||||
return filterNetworkExternalAlertData;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,41 @@ export const hostNameExistsFilter: Filter[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export const getNetworkDetailsPageFilter = (ipAddress?: string): Filter[] =>
|
||||
ipAddress
|
||||
? [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'source.ip',
|
||||
params: {
|
||||
query: ipAddress,
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match_phrase: {
|
||||
'source.ip': ipAddress,
|
||||
},
|
||||
},
|
||||
{
|
||||
match_phrase: {
|
||||
'destination.ip': ipAddress,
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
export const filterNetworkExternalAlertData: Filter[] = [
|
||||
{
|
||||
query: {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 { NETWORK_PATH } from '../../../common/constants';
|
||||
import { FlowTargetSourceDest } from '../../../common/search_strategy';
|
||||
import { NetworkDetailsRouteType } from './details/types';
|
||||
import { NetworkRouteType } from './navigation/types';
|
||||
|
||||
const NETWORK_TABS = [
|
||||
NetworkRouteType.flows,
|
||||
NetworkRouteType.dns,
|
||||
NetworkRouteType.http,
|
||||
NetworkRouteType.tls,
|
||||
NetworkRouteType.events,
|
||||
];
|
||||
|
||||
const NETWORK_WITHOUT_ANOMALIES_TAB_PARAM = NETWORK_TABS.join('|');
|
||||
const NETWORK_WITH_ANOMALIES_TAB_PARAM = [...NETWORK_TABS, NetworkRouteType.anomalies].join('|');
|
||||
|
||||
export const NETWORK_PATH_WITH_ANOMALIES = `${NETWORK_PATH}/:tabName(${NETWORK_WITH_ANOMALIES_TAB_PARAM})`;
|
||||
export const NETWORK_PATH_WITHOUT_ANOMALIES = `${NETWORK_PATH}/:tabName(${NETWORK_WITHOUT_ANOMALIES_TAB_PARAM})`;
|
||||
|
||||
const DETAIL_TABS_PARAM = [
|
||||
NetworkDetailsRouteType.flows,
|
||||
NetworkDetailsRouteType.http,
|
||||
NetworkDetailsRouteType.tls,
|
||||
NetworkDetailsRouteType.anomalies,
|
||||
NetworkDetailsRouteType.events,
|
||||
NetworkDetailsRouteType.users,
|
||||
].join('|');
|
||||
|
||||
export const FLOW_TARGET_PARAM = [
|
||||
FlowTargetSourceDest.source,
|
||||
FlowTargetSourceDest.destination,
|
||||
].join('|');
|
||||
|
||||
export const NETWORK_DETAILS_PAGE_PATH = `${NETWORK_PATH}/ip/:detailName`;
|
||||
|
||||
export const NETWORK_DETAILS_TAB_PATH = `${NETWORK_DETAILS_PAGE_PATH}/:flowTarget(${FLOW_TARGET_PARAM})/:tabName(${DETAIL_TABS_PARAM})`;
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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, { useMemo } from 'react';
|
||||
import { Switch } from 'react-router-dom';
|
||||
|
||||
import { EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import type { DataViewBase, Filter } from '@kbn/es-query';
|
||||
import { Route } from '@kbn/kibana-react-plugin/public';
|
||||
import { TimelineId } from '@kbn/timelines-plugin/common';
|
||||
import { getNetworkDetailsPageFilter } from '../../../common/components/visualization_actions/utils';
|
||||
import { AnomaliesNetworkTable } from '../../../common/components/ml/tables/anomalies_network_table';
|
||||
import { FlowTargetSourceDest } from '../../../../common/search_strategy/security_solution/network';
|
||||
import { EventsQueryTabBody } from '../../../common/components/events_tab/events_query_tab_body';
|
||||
import type { GlobalTimeArgs } from '../../../common/containers/use_global_time';
|
||||
|
||||
import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body';
|
||||
|
||||
import { NETWORK_DETAILS_PAGE_PATH } from '../constants';
|
||||
|
||||
import {
|
||||
CountriesQueryTabBody,
|
||||
HttpQueryTabBody,
|
||||
IPsQueryTabBody,
|
||||
TlsQueryTabBody,
|
||||
UsersQueryTabBody,
|
||||
} from '../navigation';
|
||||
import { ConditionalFlexGroup } from '../navigation/conditional_flex_group';
|
||||
import { networkModel } from '../../store';
|
||||
import { NetworkDetailsRouteType } from './types';
|
||||
|
||||
interface NetworkDetailTabsProps {
|
||||
ip: string;
|
||||
endDate: string;
|
||||
startDate: string;
|
||||
filterQuery: string | undefined;
|
||||
indexNames: string[];
|
||||
skip: boolean;
|
||||
setQuery: GlobalTimeArgs['setQuery'];
|
||||
narrowDateRange: (score: unknown, interval: unknown) => void;
|
||||
indexPattern: DataViewBase;
|
||||
flowTarget: FlowTargetSourceDest;
|
||||
}
|
||||
|
||||
export const NetworkDetailsTabs = React.memo<NetworkDetailTabsProps>(
|
||||
({ narrowDateRange, indexPattern, flowTarget, ...rest }) => {
|
||||
const type = networkModel.NetworkType.details;
|
||||
|
||||
const commonProps = { ...rest, type };
|
||||
const flowTabProps = { ...commonProps, indexPattern };
|
||||
const commonPropsWithFlowTarget = { ...commonProps, flowTarget };
|
||||
|
||||
const networkDetailsPageFilters: Filter[] = useMemo(
|
||||
() => getNetworkDetailsPageFilter(rest.ip),
|
||||
[rest.ip]
|
||||
);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget/:tabName(${NetworkDetailsRouteType.flows})`}
|
||||
>
|
||||
<>
|
||||
<ConditionalFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<IPsQueryTabBody {...flowTabProps} flowTarget={FlowTargetSourceDest.source} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<IPsQueryTabBody {...flowTabProps} flowTarget={FlowTargetSourceDest.destination} />
|
||||
</EuiFlexItem>
|
||||
</ConditionalFlexGroup>
|
||||
<EuiSpacer />
|
||||
<ConditionalFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<CountriesQueryTabBody {...flowTabProps} flowTarget={FlowTargetSourceDest.source} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<CountriesQueryTabBody
|
||||
{...flowTabProps}
|
||||
flowTarget={FlowTargetSourceDest.destination}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</ConditionalFlexGroup>
|
||||
</>
|
||||
</Route>
|
||||
<Route
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget/:tabName(${NetworkDetailsRouteType.users})`}
|
||||
>
|
||||
<UsersQueryTabBody {...commonPropsWithFlowTarget} />
|
||||
</Route>
|
||||
<Route
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget/:tabName(${NetworkDetailsRouteType.http})`}
|
||||
>
|
||||
<HttpQueryTabBody {...commonProps} />
|
||||
</Route>
|
||||
<Route
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget/:tabName(${NetworkDetailsRouteType.tls})`}
|
||||
>
|
||||
<TlsQueryTabBody {...commonPropsWithFlowTarget} />
|
||||
</Route>
|
||||
<Route
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget/:tabName(${NetworkDetailsRouteType.anomalies})`}
|
||||
>
|
||||
<AnomaliesQueryTabBody
|
||||
{...commonPropsWithFlowTarget}
|
||||
hideHistogramIfEmpty={true}
|
||||
AnomaliesTableComponent={AnomaliesNetworkTable}
|
||||
narrowDateRange={narrowDateRange}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget/:tabName(${NetworkDetailsRouteType.events})`}
|
||||
>
|
||||
<EventsQueryTabBody
|
||||
pageFilters={networkDetailsPageFilters}
|
||||
timelineId={TimelineId.networkPageEvents}
|
||||
{...commonProps}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
NetworkDetailsTabs.displayName = 'UsersDetailsTabs';
|
|
@ -64,7 +64,6 @@ jest.mock('react-router-dom', () => {
|
|||
jest.mock('../../containers/details', () => ({
|
||||
useNetworkDetails: jest.fn().mockReturnValue([true, { networkDetails: {} }]),
|
||||
}));
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
jest.mock('../../../common/containers/sourcerer');
|
||||
jest.mock('../../../common/containers/use_global_time', () => ({
|
||||
useGlobalTime: jest.fn().mockReturnValue({
|
||||
|
@ -75,6 +74,27 @@ jest.mock('../../../common/containers/use_global_time', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../common/lib/kibana', () => {
|
||||
const original = jest.requireActual('../../../common/lib/kibana');
|
||||
return {
|
||||
...original,
|
||||
useNavigation: () => ({
|
||||
getAppUrl: jest.fn,
|
||||
}),
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
...original.useKibana().services,
|
||||
timelines: {
|
||||
getUseDraggableKeyboardWrapper: () => () => ({
|
||||
onBlur: jest.fn,
|
||||
onKeyDown: jest.fn,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
// Test will fail because we will to need to mock some core services to make the test work
|
||||
// For now let's forget about SiemSearchBar and QueryBar
|
||||
jest.mock('../../../common/components/search_bar', () => ({
|
||||
|
|
|
@ -5,21 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiHorizontalRule, EuiSpacer, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { getEsQueryConfig } from '@kbn/data-plugin/common';
|
||||
import {
|
||||
CountriesQueryTabBody,
|
||||
HttpQueryTabBody,
|
||||
IPsQueryTabBody,
|
||||
TlsQueryTabBody,
|
||||
UsersQueryTabBody,
|
||||
} from '../navigation';
|
||||
|
||||
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
|
||||
import { FlowTargetSourceDest, LastEventIndexKey } from '../../../../common/search_strategy';
|
||||
import { LastEventIndexKey } from '../../../../common/search_strategy';
|
||||
import type { FlowTargetSourceDest } from '../../../../common/search_strategy';
|
||||
import { useGlobalTime } from '../../../common/containers/use_global_time';
|
||||
import { FiltersGlobal } from '../../../common/components/filters_global';
|
||||
import { HeaderPage } from '../../../common/components/header_page';
|
||||
|
@ -27,7 +22,6 @@ import { LastEventTime } from '../../../common/components/last_event_time';
|
|||
import { useAnomaliesTableData } from '../../../common/components/ml/anomaly/use_anomalies_table_data';
|
||||
import { networkToCriteria } from '../../../common/components/ml/criteria/network_to_criteria';
|
||||
import { scoreIntervalToDateTime } from '../../../common/components/ml/score/score_interval_to_datetime';
|
||||
import { AnomaliesNetworkTable } from '../../../common/components/ml/tables/anomalies_network_table';
|
||||
import { manageQuery } from '../../../common/components/page/manage_query';
|
||||
import { FlowTargetSelectConnected } from '../../components/flow_target_select_connected';
|
||||
import { IpOverview } from '../../components/details';
|
||||
|
@ -37,23 +31,29 @@ import { useNetworkDetails, ID } from '../../containers/details';
|
|||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { decodeIpv6 } from '../../../common/lib/helpers';
|
||||
import { convertToBuildEsQuery } from '../../../common/lib/keury';
|
||||
import { ConditionalFlexGroup } from '../navigation/conditional_flex_group';
|
||||
import { inputsSelectors } from '../../../common/store';
|
||||
import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions';
|
||||
import { setNetworkDetailsTablesActivePageToZero } from '../../store/actions';
|
||||
import { SpyRoute } from '../../../common/utils/route/spy_routes';
|
||||
import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body';
|
||||
import { networkModel } from '../../store';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
import { useSourcererDataView } from '../../../common/containers/sourcerer';
|
||||
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
|
||||
import { LandingPageComponent } from '../../../common/components/landing_page';
|
||||
import { SecuritySolutionTabNavigation } from '../../../common/components/navigation';
|
||||
import { getNetworkDetailsPageFilter } from '../../../common/components/visualization_actions/utils';
|
||||
import { hasMlUserPermissions } from '../../../../common/machine_learning/has_ml_user_permissions';
|
||||
import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities';
|
||||
import { navTabsNetworkDetails } from './nav_tabs';
|
||||
import { NetworkDetailsTabs } from './details_tabs';
|
||||
|
||||
export { getTrailingBreadcrumbs } from './utils';
|
||||
|
||||
const NetworkDetailsManage = manageQuery(IpOverview);
|
||||
|
||||
const NetworkDetailsComponent: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
const capabilities = useMlCapabilities();
|
||||
const { to, from, setQuery, isInitializing } = useGlobalTime();
|
||||
const { detailName, flowTarget } = useParams<{
|
||||
detailName: string;
|
||||
|
@ -92,11 +92,17 @@ const NetworkDetailsComponent: React.FC = () => {
|
|||
|
||||
const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView();
|
||||
const ip = decodeIpv6(detailName);
|
||||
|
||||
const queryFilters = useMemo(
|
||||
() => [...getNetworkDetailsPageFilter(ip), ...filters],
|
||||
[filters, ip]
|
||||
);
|
||||
|
||||
const [filterQuery, kqlError] = convertToBuildEsQuery({
|
||||
config: getEsQueryConfig(uiSettings),
|
||||
indexPattern,
|
||||
queries: [query],
|
||||
filters,
|
||||
filters: queryFilters,
|
||||
});
|
||||
|
||||
useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to });
|
||||
|
@ -150,7 +156,6 @@ const NetworkDetailsComponent: React.FC = () => {
|
|||
>
|
||||
<FlowTargetSelectConnected flowTarget={flowTarget} />
|
||||
</HeaderPage>
|
||||
|
||||
<NetworkDetailsManage
|
||||
id={id}
|
||||
inspect={inspect}
|
||||
|
@ -169,131 +174,23 @@ const NetworkDetailsComponent: React.FC = () => {
|
|||
narrowDateRange={narrowDateRange}
|
||||
indexPatterns={selectedPatterns}
|
||||
/>
|
||||
|
||||
<EuiHorizontalRule />
|
||||
|
||||
<ConditionalFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<IPsQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={FlowTargetSourceDest.source}
|
||||
indexNames={selectedPatterns}
|
||||
indexPattern={indexPattern}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<IPsQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={FlowTargetSourceDest.destination}
|
||||
indexNames={selectedPatterns}
|
||||
indexPattern={indexPattern}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</ConditionalFlexGroup>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<ConditionalFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<CountriesQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={FlowTargetSourceDest.source}
|
||||
indexNames={selectedPatterns}
|
||||
indexPattern={indexPattern}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<CountriesQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={FlowTargetSourceDest.destination}
|
||||
indexNames={selectedPatterns}
|
||||
indexPattern={indexPattern}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</ConditionalFlexGroup>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<UsersQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={flowTarget}
|
||||
indexNames={selectedPatterns}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
<SecuritySolutionTabNavigation
|
||||
navTabs={navTabsNetworkDetails(ip, hasMlUserPermissions(capabilities), flowTarget)}
|
||||
/>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<HttpQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
indexNames={selectedPatterns}
|
||||
<NetworkDetailsTabs
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
endDate={to}
|
||||
startDate={from}
|
||||
type={type}
|
||||
/>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<TlsQueryTabBody
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={flowTarget}
|
||||
indexNames={selectedPatterns}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
/>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<AnomaliesQueryTabBody
|
||||
AnomaliesTableComponent={AnomaliesNetworkTable}
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
flowTarget={flowTarget}
|
||||
hideHistogramIfEmpty={true}
|
||||
indexNames={selectedPatterns}
|
||||
ip={ip}
|
||||
setQuery={setQuery}
|
||||
narrowDateRange={narrowDateRange}
|
||||
setQuery={setQuery}
|
||||
skip={shouldSkip}
|
||||
startDate={from}
|
||||
type={type}
|
||||
indexPattern={indexPattern}
|
||||
flowTarget={flowTarget}
|
||||
/>
|
||||
</SecuritySolutionPageWrapper>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 { FlowTargetSourceDest } from '../../../../common/search_strategy';
|
||||
import { navTabsNetworkDetails } from './nav_tabs';
|
||||
import { NetworkDetailsRouteType } from './types';
|
||||
|
||||
describe('navTabsNetworkDetails', () => {
|
||||
test('it should return all tabs if user has ML capabilities', () => {
|
||||
const tabs = navTabsNetworkDetails('<ip-address>', true, FlowTargetSourceDest.source);
|
||||
expect(tabs).toEqual(mockTabs);
|
||||
});
|
||||
|
||||
test('it should not display anomalies tab if user has no ml permission', () => {
|
||||
const tabs = navTabsNetworkDetails('<ip-address>', false, FlowTargetSourceDest.source);
|
||||
|
||||
expect(tabs).not.toHaveProperty(NetworkDetailsRouteType.anomalies);
|
||||
});
|
||||
});
|
||||
|
||||
const mockTabs = {
|
||||
flows: {
|
||||
id: 'flows',
|
||||
name: 'Flows',
|
||||
href: '/network/ip/<ip-address>/source/flows',
|
||||
disabled: false,
|
||||
},
|
||||
users: {
|
||||
id: 'users',
|
||||
name: 'Users',
|
||||
href: '/network/ip/<ip-address>/source/users',
|
||||
disabled: false,
|
||||
},
|
||||
http: {
|
||||
id: 'http',
|
||||
name: 'HTTP',
|
||||
href: '/network/ip/<ip-address>/source/http',
|
||||
disabled: false,
|
||||
},
|
||||
tls: {
|
||||
id: 'tls',
|
||||
name: 'TLS',
|
||||
href: '/network/ip/<ip-address>/source/tls',
|
||||
disabled: false,
|
||||
},
|
||||
anomalies: {
|
||||
id: 'anomalies',
|
||||
name: 'Anomalies',
|
||||
href: '/network/ip/<ip-address>/source/anomalies',
|
||||
disabled: false,
|
||||
},
|
||||
events: {
|
||||
id: 'events',
|
||||
name: 'Events',
|
||||
href: '/network/ip/<ip-address>/source/events',
|
||||
disabled: false,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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 type { FlowTargetSourceDest } from '../../../../common/search_strategy';
|
||||
import { NETWORK_PATH } from '../../../../common/constants';
|
||||
import * as i18n from '../translations';
|
||||
import type { NetworkDetailsNavTabs } from './types';
|
||||
import { NetworkDetailsRouteType } from './types';
|
||||
|
||||
const getTabsOnNetworkDetailsUrl = (
|
||||
ipAddress: string,
|
||||
tabName: NetworkDetailsRouteType,
|
||||
flowTarget: FlowTargetSourceDest
|
||||
) => `${NETWORK_PATH}/ip/${ipAddress}/${flowTarget}/${tabName}`;
|
||||
|
||||
export const navTabsNetworkDetails = (
|
||||
ipAddress: string,
|
||||
hasMlUserPermissions: boolean,
|
||||
flowTarget: FlowTargetSourceDest
|
||||
): NetworkDetailsNavTabs => {
|
||||
const networkDetailsNavTabs: NetworkDetailsNavTabs = {
|
||||
[NetworkDetailsRouteType.flows]: {
|
||||
id: NetworkDetailsRouteType.flows,
|
||||
name: i18n.NAVIGATION_FLOWS_TITLE,
|
||||
href: getTabsOnNetworkDetailsUrl(ipAddress, NetworkDetailsRouteType.flows, flowTarget),
|
||||
disabled: false,
|
||||
},
|
||||
[NetworkDetailsRouteType.users]: {
|
||||
id: NetworkDetailsRouteType.users,
|
||||
name: i18n.NAVIGATION_USERS_TITLE,
|
||||
href: getTabsOnNetworkDetailsUrl(ipAddress, NetworkDetailsRouteType.users, flowTarget),
|
||||
disabled: false,
|
||||
},
|
||||
[NetworkDetailsRouteType.http]: {
|
||||
id: NetworkDetailsRouteType.http,
|
||||
name: i18n.NAVIGATION_HTTP_TITLE,
|
||||
href: getTabsOnNetworkDetailsUrl(ipAddress, NetworkDetailsRouteType.http, flowTarget),
|
||||
disabled: false,
|
||||
},
|
||||
[NetworkDetailsRouteType.tls]: {
|
||||
id: NetworkDetailsRouteType.tls,
|
||||
name: i18n.NAVIGATION_TLS_TITLE,
|
||||
href: getTabsOnNetworkDetailsUrl(ipAddress, NetworkDetailsRouteType.tls, flowTarget),
|
||||
disabled: false,
|
||||
},
|
||||
[NetworkDetailsRouteType.anomalies]: {
|
||||
id: NetworkDetailsRouteType.anomalies,
|
||||
name: i18n.NAVIGATION_ANOMALIES_TITLE,
|
||||
href: getTabsOnNetworkDetailsUrl(ipAddress, NetworkDetailsRouteType.anomalies, flowTarget),
|
||||
disabled: false,
|
||||
},
|
||||
[NetworkDetailsRouteType.events]: {
|
||||
id: NetworkDetailsRouteType.events,
|
||||
name: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
href: getTabsOnNetworkDetailsUrl(ipAddress, NetworkDetailsRouteType.events, flowTarget),
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
if (!hasMlUserPermissions) {
|
||||
delete networkDetailsNavTabs.anomalies;
|
||||
}
|
||||
|
||||
return networkDetailsNavTabs;
|
||||
};
|
|
@ -5,9 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Optional } from '@kbn/utility-types';
|
||||
|
||||
import type { ESTermQuery } from '../../../../common/typed_json';
|
||||
import { NetworkType } from '../../store/model';
|
||||
import type { NavTab } from '../../../common/components/navigation/types';
|
||||
import type { GlobalTimeArgs } from '../../../common/containers/use_global_time';
|
||||
import { NetworkType } from '../../store/model';
|
||||
|
||||
export const type = NetworkType.details;
|
||||
|
||||
|
@ -21,3 +24,17 @@ export interface OwnProps {
|
|||
skip: boolean;
|
||||
setQuery: GlobalTimeArgs['setQuery'];
|
||||
}
|
||||
|
||||
export enum NetworkDetailsRouteType {
|
||||
anomalies = 'anomalies',
|
||||
flows = 'flows',
|
||||
tls = 'tls',
|
||||
http = 'http',
|
||||
events = 'events',
|
||||
users = 'users',
|
||||
}
|
||||
|
||||
export type NetworkDetailsNavTabs = Optional<
|
||||
Record<`${NetworkDetailsRouteType}`, NavTab>,
|
||||
'anomalies'
|
||||
>;
|
||||
|
|
|
@ -12,19 +12,21 @@ import { decodeIpv6 } from '../../../common/lib/helpers';
|
|||
import { getNetworkDetailsUrl } from '../../../common/components/link_to/redirect_to_network';
|
||||
import { networkModel } from '../../store';
|
||||
import * as i18n from '../translations';
|
||||
import { NetworkRouteType } from '../navigation/types';
|
||||
import { NetworkDetailsRouteType } from './types';
|
||||
import type { NetworkRouteSpyState } from '../../../common/utils/route/types';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
import type { GetSecuritySolutionUrl } from '../../../common/components/link_to';
|
||||
import { NetworkRouteType } from '../navigation/types';
|
||||
|
||||
export const type = networkModel.NetworkType.details;
|
||||
const TabNameMappedToI18nKey: Record<NetworkRouteType, string> = {
|
||||
[NetworkRouteType.alerts]: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
[NetworkRouteType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE,
|
||||
[NetworkRouteType.flows]: i18n.NAVIGATION_FLOWS_TITLE,
|
||||
const TabNameMappedToI18nKey: Record<NetworkDetailsRouteType | NetworkRouteType, string> = {
|
||||
[NetworkDetailsRouteType.events]: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
[NetworkDetailsRouteType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE,
|
||||
[NetworkDetailsRouteType.flows]: i18n.NAVIGATION_FLOWS_TITLE,
|
||||
[NetworkDetailsRouteType.users]: i18n.NAVIGATION_USERS_TITLE,
|
||||
[NetworkDetailsRouteType.http]: i18n.NAVIGATION_HTTP_TITLE,
|
||||
[NetworkDetailsRouteType.tls]: i18n.NAVIGATION_TLS_TITLE,
|
||||
[NetworkRouteType.dns]: i18n.NAVIGATION_DNS_TITLE,
|
||||
[NetworkRouteType.http]: i18n.NAVIGATION_HTTP_TITLE,
|
||||
[NetworkRouteType.tls]: i18n.NAVIGATION_TLS_TITLE,
|
||||
};
|
||||
|
||||
export const getTrailingBreadcrumbs = (
|
||||
|
|
|
@ -17,10 +17,18 @@ import { Network } from './network';
|
|||
import { getNetworkRoutePath } from './navigation';
|
||||
import { NetworkRouteType } from './navigation/types';
|
||||
import { MlNetworkConditionalContainer } from '../../common/components/ml/conditional_links/ml_network_conditional_container';
|
||||
import { FlowTarget } from '../../../common/search_strategy';
|
||||
import { NETWORK_PATH } from '../../../common/constants';
|
||||
import { FlowTargetSourceDest } from '../../../common/search_strategy';
|
||||
import {
|
||||
FLOW_TARGET_PARAM,
|
||||
NETWORK_DETAILS_PAGE_PATH,
|
||||
NETWORK_DETAILS_TAB_PATH,
|
||||
} from './constants';
|
||||
|
||||
const ipDetailsPageBasePath = `${NETWORK_PATH}/ip/:detailName`;
|
||||
const getPathWithFlowType = (detailName: string, flowTarget?: FlowTargetSourceDest) =>
|
||||
`${NETWORK_PATH}/ip/${detailName}/${flowTarget || FlowTargetSourceDest.source}/${
|
||||
NetworkRouteType.flows
|
||||
}`;
|
||||
|
||||
const NetworkContainerComponent = () => {
|
||||
const capabilities = useMlCapabilities();
|
||||
|
@ -53,25 +61,32 @@ const NetworkContainerComponent = () => {
|
|||
hasMlUserPermissions={userHasMlUserPermissions}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${ipDetailsPageBasePath}/:flowTarget`}>
|
||||
<Route path={NETWORK_DETAILS_TAB_PATH}>
|
||||
<NetworkDetails />
|
||||
</Route>
|
||||
<Route
|
||||
path={ipDetailsPageBasePath}
|
||||
path={`${NETWORK_DETAILS_PAGE_PATH}/:flowTarget(${FLOW_TARGET_PARAM})?`}
|
||||
render={({
|
||||
location: { search = '' },
|
||||
match: {
|
||||
params: { detailName },
|
||||
params: { detailName, flowTarget },
|
||||
},
|
||||
location: { search = '' },
|
||||
}) => (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: `${NETWORK_PATH}/ip/${detailName}/${FlowTarget.source}`,
|
||||
pathname: getPathWithFlowType(detailName, flowTarget),
|
||||
search,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route>
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: NETWORK_PATH,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -45,10 +45,10 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab =>
|
|||
href: getTabsOnNetworkUrl(NetworkRouteType.anomalies),
|
||||
disabled: false,
|
||||
},
|
||||
[NetworkRouteType.alerts]: {
|
||||
id: NetworkRouteType.alerts,
|
||||
[NetworkRouteType.events]: {
|
||||
id: NetworkRouteType.events,
|
||||
name: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
href: getTabsOnNetworkUrl(NetworkRouteType.alerts),
|
||||
href: getTabsOnNetworkUrl(NetworkRouteType.events),
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -155,7 +155,7 @@ export const NetworkRoutes = React.memo<NetworkRoutesProps>(
|
|||
AnomaliesTableComponent={AnomaliesNetworkTable}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${NETWORK_PATH}/:tabName(${NetworkRouteType.alerts})`}>
|
||||
<Route path={`${NETWORK_PATH}/:tabName(${NetworkRouteType.events})`}>
|
||||
<EventsQueryTabBody
|
||||
pageFilters={filterNetworkExternalAlertData}
|
||||
timelineId={TimelineId.networkPageEvents}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { DataViewBase } from '@kbn/es-query';
|
||||
import type { Optional } from 'utility-types';
|
||||
|
||||
import type { NarrowDateRange } from '../../../common/components/ml/types';
|
||||
import type { ESTermQuery } from '../../../../common/typed_json';
|
||||
|
||||
|
@ -62,21 +64,10 @@ export enum NetworkRouteType {
|
|||
anomalies = 'anomalies',
|
||||
tls = 'tls',
|
||||
http = 'http',
|
||||
alerts = 'events', // changed officially to events in #136427
|
||||
events = 'events',
|
||||
}
|
||||
|
||||
export type KeyNetworkNavTabWithoutMlPermission = NetworkRouteType.dns &
|
||||
NetworkRouteType.flows &
|
||||
NetworkRouteType.http &
|
||||
NetworkRouteType.tls &
|
||||
NetworkRouteType.alerts;
|
||||
|
||||
type KeyNetworkNavTabWithMlPermission = KeyNetworkNavTabWithoutMlPermission &
|
||||
NetworkRouteType.anomalies;
|
||||
|
||||
type KeyNetworkNavTab = KeyNetworkNavTabWithoutMlPermission | KeyNetworkNavTabWithMlPermission;
|
||||
|
||||
export type NetworkNavTab = Record<KeyNetworkNavTab, NavTab>;
|
||||
export type NetworkNavTab = Optional<Record<`${NetworkRouteType}`, NavTab>, 'anomalies'>;
|
||||
|
||||
export type GetNetworkRoutePath = (
|
||||
capabilitiesFetched: boolean,
|
||||
|
|
|
@ -5,25 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { NETWORK_PATH } from '../../../../common/constants';
|
||||
import { NETWORK_PATH_WITH_ANOMALIES, NETWORK_PATH_WITHOUT_ANOMALIES } from '../constants';
|
||||
import type { GetNetworkRoutePath } from './types';
|
||||
import { NetworkRouteType } from './types';
|
||||
|
||||
export const getNetworkRoutePath: GetNetworkRoutePath = (
|
||||
capabilitiesFetched,
|
||||
hasMlUserPermission
|
||||
) => {
|
||||
if (capabilitiesFetched && !hasMlUserPermission) {
|
||||
return `${NETWORK_PATH}/:tabName(${NetworkRouteType.flows}|${NetworkRouteType.dns}|${NetworkRouteType.http}|${NetworkRouteType.tls}|${NetworkRouteType.alerts})`;
|
||||
}
|
||||
|
||||
return (
|
||||
`${NETWORK_PATH}/:tabName(` +
|
||||
`${NetworkRouteType.flows}|` +
|
||||
`${NetworkRouteType.dns}|` +
|
||||
`${NetworkRouteType.anomalies}|` +
|
||||
`${NetworkRouteType.http}|` +
|
||||
`${NetworkRouteType.tls}|` +
|
||||
`${NetworkRouteType.alerts})`
|
||||
);
|
||||
};
|
||||
) =>
|
||||
capabilitiesFetched && !hasMlUserPermission
|
||||
? NETWORK_PATH_WITHOUT_ANOMALIES
|
||||
: NETWORK_PATH_WITH_ANOMALIES;
|
||||
|
|
|
@ -87,7 +87,7 @@ const NetworkComponent = React.memo<NetworkComponentProps>(
|
|||
const canUseMaps = kibana.services.application.capabilities.maps.show;
|
||||
|
||||
const tabsFilters = useMemo(() => {
|
||||
if (tabName === NetworkRouteType.alerts) {
|
||||
if (tabName === NetworkRouteType.events) {
|
||||
return filters.length > 0
|
||||
? [...filters, ...filterNetworkExternalAlertData]
|
||||
: filterNetworkExternalAlertData;
|
||||
|
|
|
@ -25,6 +25,13 @@ export const NAVIGATION_DNS_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const NAVIGATION_USERS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.network.navigation.usersTitle',
|
||||
{
|
||||
defaultMessage: 'Users',
|
||||
}
|
||||
);
|
||||
|
||||
export const NAVIGATION_TLS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.network.navigation.tlsTitle',
|
||||
{
|
||||
|
|
|
@ -1123,7 +1123,7 @@ tr:hover .c3:focus::before {
|
|||
<a
|
||||
class="euiLink emotion-euiLink-primary"
|
||||
data-test-subj="network-details"
|
||||
href="/ip/192.168.1.2/source"
|
||||
href="/ip/192.168.1.2/source/flows"
|
||||
rel="noreferrer"
|
||||
>
|
||||
192.168.1.2
|
||||
|
@ -2002,7 +2002,7 @@ tr:hover .c3:focus::before {
|
|||
<a
|
||||
class="euiLink emotion-euiLink-primary"
|
||||
data-test-subj="network-details"
|
||||
href="/ip/10.1.2.3/source"
|
||||
href="/ip/10.1.2.3/source/flows"
|
||||
rel="noreferrer"
|
||||
>
|
||||
10.1.2.3
|
||||
|
|
|
@ -1261,7 +1261,7 @@ tr:hover .c5:focus::before {
|
|||
<a
|
||||
class="euiLink emotion-euiLink-primary"
|
||||
data-test-subj="network-details"
|
||||
href="/ip/192.168.1.2/source"
|
||||
href="/ip/192.168.1.2/source/flows"
|
||||
rel="noreferrer"
|
||||
>
|
||||
192.168.1.2
|
||||
|
@ -2305,7 +2305,7 @@ tr:hover .c5:focus::before {
|
|||
<a
|
||||
class="euiLink emotion-euiLink-primary"
|
||||
data-test-subj="network-details"
|
||||
href="/ip/10.1.2.3/source"
|
||||
href="/ip/10.1.2.3/source/flows"
|
||||
rel="noreferrer"
|
||||
>
|
||||
10.1.2.3
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue