Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] Add 3rd level breadcrumb to admin page #71275

Merged
merged 11 commits into from
Jul 14, 2020
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export enum SecurityPageName {
network = 'network',
timelines = 'timelines',
case = 'case',
management = 'management',
administration = 'administration',
}

export const APP_OVERVIEW_PATH = `${APP_PATH}/overview`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CASES,
DETECTIONS,
HOSTS,
MANAGEMENT,
ADMINISTRATION,
NETWORK,
OVERVIEW,
TIMELINES,
Expand Down Expand Up @@ -73,7 +73,7 @@ describe('top-level navigation common to all pages in the Security app', () => {
});

it('navigates to the Administration page', () => {
navigateFromHeaderTo(MANAGEMENT);
navigateFromHeaderTo(ADMINISTRATION);
cy.url().should('include', ADMINISTRATION_URL);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const HOSTS = '[data-test-subj="navigation-hosts"]';

export const KQL_INPUT = '[data-test-subj="queryInput"]';

export const MANAGEMENT = '[data-test-subj="navigation-management"]';
export const ADMINISTRATION = '[data-test-subj="navigation-administration"]';

export const NETWORK = '[data-test-subj="navigation-network"]';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export const navTabs: SiemNavTab = {
disabled: false,
urlKey: 'case',
},
[SecurityPageName.management]: {
id: SecurityPageName.management,
[SecurityPageName.administration]: {
id: SecurityPageName.administration,
name: i18n.ADMINISTRATION,
href: APP_MANAGEMENT_PATH,
disabled: false,
urlKey: SecurityPageName.management,
urlKey: SecurityPageName.administration,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../../network/p
import { getBreadcrumbs as getCaseDetailsBreadcrumbs } from '../../../../cases/pages/utils';
import { getBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../../detections/pages/detection_engine/rules/utils';
import { getBreadcrumbs as getTimelinesBreadcrumbs } from '../../../../timelines/pages';
import { getBreadcrumbs as getAdminBreadcrumbs } from '../../../../management/pages';
import { SecurityPageName } from '../../../../app/types';
import {
RouteSpyState,
HostRouteSpyState,
NetworkRouteSpyState,
TimelineRouteSpyState,
AdministrationRouteSpyState,
} from '../../../utils/route/types';
import { getAppOverviewUrl } from '../../link_to';

Expand Down Expand Up @@ -61,6 +63,10 @@ const isCaseRoutes = (spyState: RouteSpyState): spyState is RouteSpyState =>
const isAlertsRoutes = (spyState: RouteSpyState) =>
spyState != null && spyState.pageName === SecurityPageName.detections;

const isAdminRoutes = (spyState: RouteSpyState): spyState is AdministrationRouteSpyState =>
spyState != null && spyState.pageName === SecurityPageName.administration;

// eslint-disable-next-line complexity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what the ESLint error was here.

export const getBreadcrumbsForRoute = (
object: RouteSpyState & TabNavigationProps,
getUrlForApp: GetUrlForApp
Expand Down Expand Up @@ -159,6 +165,27 @@ export const getBreadcrumbsForRoute = (
),
];
}

if (isAdminRoutes(spyState) && object.navTabs) {
const tempNav: SearchNavTab = { urlKey: 'administration', isDetailPage: false };
let urlStateKeys = [getOr(tempNav, spyState.pageName, object.navTabs)];
if (spyState.tabName != null) {
urlStateKeys = [...urlStateKeys, getOr(tempNav, spyState.tabName, object.navTabs)];
}

return [
...siemRootBreadcrumb,
...getAdminBreadcrumbs(
spyState,
urlStateKeys.reduce(
(acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)],
[]
),
getUrlForApp
),
];
}

if (
spyState != null &&
object.navTabs &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ describe('SIEM Navigation', () => {
name: 'Cases',
urlKey: 'case',
},
management: {
administration: {
disabled: false,
href: '/app/security/administration',
id: 'management',
id: 'administration',
name: 'Administration',
urlKey: 'management',
urlKey: 'administration',
},
hosts: {
disabled: false,
Expand Down Expand Up @@ -218,12 +218,12 @@ describe('SIEM Navigation', () => {
name: 'Hosts',
urlKey: 'host',
},
management: {
administration: {
disabled: false,
href: '/app/security/administration',
id: 'management',
id: 'administration',
name: 'Administration',
urlKey: 'management',
urlKey: 'administration',
},
network: {
disabled: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type SiemNavTabKey =
| SecurityPageName.detections
| SecurityPageName.timelines
| SecurityPageName.case
| SecurityPageName.management;
| SecurityPageName.administration;

export type SiemNavTab = Record<SiemNavTabKey, NavTab>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ export type UrlStateType =
| 'network'
| 'overview'
| 'timeline'
| 'management';
| 'administration';
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ export const getUrlType = (pageName: string): UrlStateType => {
return 'timeline';
} else if (pageName === SecurityPageName.case) {
return 'case';
} else if (pageName === SecurityPageName.administration) {
return 'administration';
}
return 'overview';
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const URL_STATE_KEYS: Record<UrlStateType, KeyUrlState[]> = {
CONSTANTS.timerange,
CONSTANTS.timeline,
],
management: [],
administration: [],
network: [
CONSTANTS.appQuery,
CONSTANTS.filters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { TimelineType } from '../../../../common/types/timeline';

import { HostsTableType } from '../../../hosts/store/model';
import { NetworkRouteType } from '../../../network/pages/navigation/types';
import { AdministrationSubTab as AdministrationType } from '../../../management/types';
import { FlowTarget } from '../../../graphql/types';

export type SiemRouteType = HostsTableType | NetworkRouteType | TimelineType;
export type SiemRouteType = HostsTableType | NetworkRouteType | TimelineType | AdministrationType;
export interface RouteSpyState {
pageName: string;
detailName: string | undefined;
Expand All @@ -38,6 +39,10 @@ export interface TimelineRouteSpyState extends RouteSpyState {
tabName: TimelineType | undefined;
}

export interface AdministrationRouteSpyState extends RouteSpyState {
tabName: AdministrationType | undefined;
}

export type RouteSpyAction =
| {
type: 'updateSearch';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ManagementStoreGlobalNamespace, ManagementSubTab } from '../types';
import { ManagementStoreGlobalNamespace, AdministrationSubTab } from '../types';
import { APP_ID } from '../../../common/constants';
import { SecurityPageName } from '../../app/types';

// --[ ROUTING ]---------------------------------------------------------------------------
export const MANAGEMENT_APP_ID = `${APP_ID}:${SecurityPageName.management}`;
export const MANAGEMENT_APP_ID = `${APP_ID}:${SecurityPageName.administration}`;
export const MANAGEMENT_ROUTING_ROOT_PATH = '';
export const MANAGEMENT_ROUTING_HOSTS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.hosts})`;
export const MANAGEMENT_ROUTING_POLICIES_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})`;
export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})/:policyId`;
export const MANAGEMENT_ROUTING_HOSTS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.hosts})`;
export const MANAGEMENT_ROUTING_POLICIES_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.policies})`;
export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId`;

// --[ STORE ]---------------------------------------------------------------------------
/** The SIEM global store namespace where the management state will be mounted */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
MANAGEMENT_ROUTING_POLICIES_PATH,
MANAGEMENT_ROUTING_POLICY_DETAILS_PATH,
} from './constants';
import { ManagementSubTab } from '../types';
import { AdministrationSubTab } from '../types';
import { appendSearch } from '../../common/components/link_to/helpers';
import { HostIndexUIQueryParams } from '../pages/endpoint_hosts/types';

Expand Down Expand Up @@ -47,7 +47,7 @@ export const getHostListPath = (

if (name === 'hostList') {
return `${generatePath(MANAGEMENT_ROUTING_HOSTS_PATH, {
tabName: ManagementSubTab.hosts,
tabName: AdministrationSubTab.hosts,
})}${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`;
}
return `${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`;
Expand All @@ -65,17 +65,17 @@ export const getHostDetailsPath = (
const urlSearch = `${urlQueryParams && !isEmpty(search) ? '&' : ''}${search ?? ''}`;

return `${generatePath(MANAGEMENT_ROUTING_HOSTS_PATH, {
tabName: ManagementSubTab.hosts,
tabName: AdministrationSubTab.hosts,
})}${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`;
};

export const getPoliciesPath = (search?: string) =>
`${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, {
tabName: ManagementSubTab.policies,
tabName: AdministrationSubTab.policies,
})}${appendSearch(search)}`;

export const getPolicyDetailPath = (policyId: string, search?: string) =>
`${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, {
tabName: ManagementSubTab.policies,
tabName: AdministrationSubTab.policies,
policyId,
})}${appendSearch(search)}`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { i18n } from '@kbn/i18n';

export const HOSTS_TAB = i18n.translate('xpack.securitySolution.hostsTab', {
defaultMessage: 'Hosts',
});

export const POLICIES_TAB = i18n.translate('xpack.securitySolution.policiesTab', {
defaultMessage: 'Policies',
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import React, { memo, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { useParams } from 'react-router-dom';
import { PageView, PageViewProps } from '../../common/components/endpoint/page_view';
import { ManagementSubTab } from '../types';
import { AdministrationSubTab } from '../types';
import { SecurityPageName } from '../../app/types';
import { useFormatUrl } from '../../common/components/link_to';
import { getHostListPath, getPoliciesPath } from '../common/routing';
import { useNavigateByRouterEventHandler } from '../../common/hooks/endpoint/use_navigate_by_router_event_handler';

export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => {
const { formatUrl, search } = useFormatUrl(SecurityPageName.management);
const { tabName } = useParams<{ tabName: ManagementSubTab }>();
const { formatUrl, search } = useFormatUrl(SecurityPageName.administration);
const { tabName } = useParams<{ tabName: AdministrationSubTab }>();

const goToEndpoint = useNavigateByRouterEventHandler(
getHostListPath({ name: 'hostList' }, search)
Expand All @@ -30,20 +30,20 @@ export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) =>
}
return [
{
name: i18n.translate('xpack.securitySolution.managementTabs.endpoints', {
name: i18n.translate('xpack.securitySolution.managementTabs.hosts', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you created a translations.ts module under management/common/ where both Host and Policies tab names are defined - should they be used here as well?

defaultMessage: 'Hosts',
}),
id: ManagementSubTab.hosts,
isSelected: tabName === ManagementSubTab.hosts,
id: AdministrationSubTab.hosts,
isSelected: tabName === AdministrationSubTab.hosts,
href: formatUrl(getHostListPath({ name: 'hostList' })),
onClick: goToEndpoint,
},
{
name: i18n.translate('xpack.securitySolution.managementTabs.policies', {
defaultMessage: 'Policies',
}),
id: ManagementSubTab.policies,
isSelected: tabName === ManagementSubTab.policies,
id: AdministrationSubTab.policies,
isSelected: tabName === AdministrationSubTab.policies,
href: formatUrl(getPoliciesPath()),
onClick: goToPolicies,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
const policyStatus = useHostSelector(
policyResponseStatus
) as keyof typeof POLICY_STATUS_TO_HEALTH_COLOR;
const { formatUrl } = useFormatUrl(SecurityPageName.management);
const { formatUrl } = useFormatUrl(SecurityPageName.administration);

const detailsResultsUpper = useMemo(() => {
return [
Expand Down Expand Up @@ -106,7 +106,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
path: agentDetailsWithFlyoutPath,
state: {
onDoneNavigateTo: [
'securitySolution:management',
'securitySolution:administration',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should actually be replaced with the constant we have in management/common/constants: MANAGEMENT_APP_ID

{
path: getHostDetailsPath({ name: 'hostDetails', selected_host: details.host.id }),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ const PolicyResponseFlyoutPanel = memo<{
const responseAttentionCount = useHostSelector(policyResponseFailedOrWarningActionCount);
const loading = useHostSelector(policyResponseLoading);
const error = useHostSelector(policyResponseError);
const { formatUrl } = useFormatUrl(SecurityPageName.management);
const { formatUrl } = useFormatUrl(SecurityPageName.administration);
const [detailsUri, detailsRoutePath] = useMemo(
() => [
formatUrl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const HostList = () => {
policyItemsLoading,
endpointPackageVersion,
} = useHostSelector(selector);
const { formatUrl, search } = useFormatUrl(SecurityPageName.management);
const { formatUrl, search } = useFormatUrl(SecurityPageName.administration);

const dispatch = useDispatch<(a: HostAction) => void>();

Expand Down Expand Up @@ -127,12 +127,12 @@ export const HostList = () => {
}`,
state: {
onCancelNavigateTo: [
'securitySolution:management',
'securitySolution:administration',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here and other places below - use MANAGEMENT_APP_ID

{ path: getHostListPath({ name: 'hostList' }) },
],
onCancelUrl: formatUrl(getHostListPath({ name: 'hostList' })),
onSaveNavigateTo: [
'securitySolution:management',
'securitySolution:administration',
{ path: getHostListPath({ name: 'hostList' }) },
],
},
Expand All @@ -145,7 +145,7 @@ export const HostList = () => {
path: `#/configs/${selectedPolicyId}?openEnrollmentFlyout=true`,
state: {
onDoneNavigateTo: [
'securitySolution:management',
'securitySolution:administration',
{ path: getHostListPath({ name: 'hostList' }) },
],
},
Expand Down Expand Up @@ -422,7 +422,7 @@ export const HostList = () => {
</>
)}
{renderTableOrEmptyState}
<SpyRoute pageName={SecurityPageName.management} />
<SpyRoute pageName={SecurityPageName.administration} />
</ManagementPageView>
);
};
Loading