[SECURITY SOLUTION] [CASES] Allow cases to be there when security solutions privileges is none (#113573)

* allow case to itself when security solutions privileges is none

* bring back the right owner for cases

* bring no privilege msg when needed it

* fix types

* fix test

* adding test

* review

* deepLinks generation fixed

* register home solution with old app id

* fix get deep links

* fix home link

* fix unit test

* add test

* fix telemetry

Co-authored-by: semd <sergi.massaneda@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Xavier Mouligneau 2021-10-25 18:16:52 -04:00 committed by GitHub
parent 106183551a
commit 436c74a9ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 800 additions and 447 deletions

View file

@ -94,7 +94,7 @@ pageLoadAssetSize:
expressionShape: 34008
interactiveSetup: 80000
expressionTagcloud: 27505
securitySolution: 231753
securitySolution: 273763
customIntegrations: 28810
expressionMetricVis: 23121
visTypeMetric: 23332

View file

@ -156,7 +156,7 @@ export const applicationUsageSchema = {
security_login: commonSchema,
security_logout: commonSchema,
security_overwritten_session: commonSchema,
securitySolution: commonSchema,
securitySolutionUI: commonSchema,
siem: commonSchema,
space_selector: commonSchema,
uptime: commonSchema,

View file

@ -5018,7 +5018,7 @@
}
}
},
"securitySolution": {
"securitySolutionUI": {
"properties": {
"appId": {
"type": "keyword",

View file

@ -12,6 +12,7 @@ import { ENABLE_CASE_CONNECTOR } from '../../cases/common';
import { METADATA_TRANSFORMS_PATTERN } from './endpoint/constants';
export const APP_ID = 'securitySolution';
export const APP_UI_ID = 'securitySolutionUI';
export const CASES_FEATURE_ID = 'securitySolutionCases';
export const SERVER_APP_ID = 'siem';
export const APP_NAME = 'Security';

View file

@ -8,7 +8,7 @@ import { getDeepLinks, PREMIUM_DEEP_LINK_IDS } from '.';
import { AppDeepLink, Capabilities } from '../../../../../../src/core/public';
import { SecurityPageName } from '../types';
import { mockGlobalState } from '../../common/mock';
import { CASES_FEATURE_ID } from '../../../common/constants';
import { CASES_FEATURE_ID, SERVER_APP_ID } from '../../../common/constants';
const findDeepLink = (id: string, deepLinks: AppDeepLink[]): AppDeepLink | null =>
deepLinks.reduce((deepLinkFound: AppDeepLink | null, deepLink) => {
@ -24,10 +24,11 @@ const findDeepLink = (id: string, deepLinks: AppDeepLink[]): AppDeepLink | null
return null;
}, null);
const basicLicense = 'basic';
const platinumLicense = 'platinum';
describe('deepLinks', () => {
it('should return a subset of links for basic license and the full set for platinum', () => {
const basicLicense = 'basic';
const platinumLicense = 'platinum';
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense);
const platinumLinks = getDeepLinks(mockGlobalState.app.enableExperimental, platinumLicense);
@ -57,26 +58,25 @@ describe('deepLinks', () => {
});
it('should return case links for basic license with only read_cases capabilities', () => {
const basicLicense = 'basic';
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
[SERVER_APP_ID]: { show: true },
} as unknown as Capabilities);
expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeTruthy();
});
it('should return case links with NO deepLinks for basic license with only read_cases capabilities', () => {
const basicLicense = 'basic';
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
[SERVER_APP_ID]: { show: true },
} as unknown as Capabilities);
expect(findDeepLink(SecurityPageName.case, basicLinks)?.deepLinks?.length === 0).toBeTruthy();
});
it('should return case links with deepLinks for basic license with crud_cases capabilities', () => {
const basicLicense = 'basic';
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
[SERVER_APP_ID]: { show: true },
} as unknown as Capabilities);
expect(
@ -84,17 +84,32 @@ describe('deepLinks', () => {
).toBeTruthy();
});
it('should return case links with deepLinks for basic license with crud_cases capabilities and security disabled', () => {
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, platinumLicense, {
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
[SERVER_APP_ID]: { show: false },
} as unknown as Capabilities);
expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeTruthy();
});
it('should return NO case links for basic license with NO read_cases capabilities', () => {
const basicLicense = 'basic';
const basicLinks = getDeepLinks(mockGlobalState.app.enableExperimental, basicLicense, {
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
[SERVER_APP_ID]: { show: true },
} as unknown as Capabilities);
expect(findDeepLink(SecurityPageName.case, basicLinks)).toBeFalsy();
});
it('should return empty links for any license', () => {
const emptyDeepLinks = getDeepLinks(
mockGlobalState.app.enableExperimental,
basicLicense,
{} as unknown as Capabilities
);
expect(emptyDeepLinks.length).toBe(0);
});
it('should return case links for basic license with undefined capabilities', () => {
const basicLicense = 'basic';
const basicLinks = getDeepLinks(
mockGlobalState.app.enableExperimental,
basicLicense,
@ -105,7 +120,6 @@ describe('deepLinks', () => {
});
it('should return case deepLinks for basic license with undefined capabilities', () => {
const basicLicense = 'basic';
const basicLinks = getDeepLinks(
mockGlobalState.app.enableExperimental,
basicLicense,

View file

@ -6,16 +6,11 @@
*/
import { i18n } from '@kbn/i18n';
import { Subject } from 'rxjs';
import { isEmpty } from 'lodash';
import { LicenseType } from '../../../../licensing/common/types';
import { SecurityPageName } from '../types';
import {
AppDeepLink,
ApplicationStart,
AppNavLinkStatus,
AppUpdater,
} from '../../../../../../src/core/public';
import { AppDeepLink, ApplicationStart, AppNavLinkStatus } from '../../../../../../src/core/public';
import {
OVERVIEW,
DETECT,
@ -50,6 +45,7 @@ import {
UEBA_PATH,
CASES_FEATURE_ID,
HOST_ISOLATION_EXCEPTIONS_PATH,
SERVER_APP_ID,
} from '../../../common/constants';
import { ExperimentalFeatures } from '../../../common/experimental_features';
@ -356,25 +352,18 @@ export function getDeepLinks(
): AppDeepLink[] {
const isPremium = isPremiumLicense(licenseType);
/**
* Recursive DFS function to filter deepLinks by permissions (licence and capabilities).
* Checks "end" deepLinks with no children first, the other parent deepLinks will be included if
* they still have children deepLinks after filtering
*/
const filterDeepLinks = (deepLinks: AppDeepLink[]): AppDeepLink[] => {
return deepLinks
.filter((deepLink) => {
if (!isPremium && PREMIUM_DEEP_LINK_IDS.has(deepLink.id)) {
return false;
}
if (deepLink.id === SecurityPageName.case) {
return capabilities == null || capabilities[CASES_FEATURE_ID].read_cases === true;
}
if (deepLink.id === SecurityPageName.ueba) {
return enableExperimental.uebaEnabled;
}
return true;
})
.map((deepLink) => {
if (
deepLink.id === SecurityPageName.case &&
capabilities != null &&
capabilities[CASES_FEATURE_ID].crud_cases === false
capabilities[CASES_FEATURE_ID]?.crud_cases === false
) {
return {
...deepLink,
@ -388,6 +377,21 @@ export function getDeepLinks(
};
}
return deepLink;
})
.filter((deepLink) => {
if (!isPremium && PREMIUM_DEEP_LINK_IDS.has(deepLink.id)) {
return false;
}
if (deepLink.path && deepLink.path.startsWith(CASES_PATH)) {
return capabilities == null || capabilities[CASES_FEATURE_ID]?.read_cases === true;
}
if (deepLink.id === SecurityPageName.ueba) {
return enableExperimental.uebaEnabled;
}
if (!isEmpty(deepLink.deepLinks)) {
return true;
}
return capabilities == null || capabilities[SERVER_APP_ID]?.show === true;
});
};
@ -402,18 +406,3 @@ export function isPremiumLicense(licenseType?: LicenseType): boolean {
licenseType === 'trial'
);
}
export function updateGlobalNavigation({
capabilities,
updater$,
enableExperimental,
}: {
capabilities: ApplicationStart['capabilities'];
updater$: Subject<AppUpdater>;
enableExperimental: ExperimentalFeatures;
}) {
updater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent showing main nav link
deepLinks: getDeepLinks(enableExperimental, undefined, capabilities),
}));
}

View file

@ -78,8 +78,12 @@ export const SecuritySolutionTemplateWrapper: React.FC<SecuritySolutionPageWrapp
const showEmptyState = useShowPagesWithEmptyView();
const emptyStateProps = showEmptyState ? NO_DATA_PAGE_TEMPLATE_PROPS : {};
// StyledKibanaPageTemplate is a styled EuiPageTemplate. Security solution currently passes the header and page content as the children of StyledKibanaPageTemplate, as opposed to using the pageHeader prop, which may account for any style discrepancies, such as the bottom border not extending the full width of the page, between EuiPageTemplate and the security solution pages.
/*
* StyledKibanaPageTemplate is a styled EuiPageTemplate. Security solution currently passes the header
* and page content as the children of StyledKibanaPageTemplate, as opposed to using the pageHeader prop,
* which may account for any style discrepancies, such as the bottom border not extending the full width of the page,
* between EuiPageTemplate and the security solution pages.
*/
return (
<StyledKibanaPageTemplate
$isTimelineBottomBarVisible={isTimelineBottomBarVisible}

View file

@ -7,8 +7,7 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Redirect, Route, Switch } from 'react-router-dom';
import { OVERVIEW_PATH } from '../../common/constants';
import { Route, Switch } from 'react-router-dom';
import { NotFoundPage } from './404';
import { SecurityApp } from './app';
@ -22,7 +21,7 @@ export const renderApp = ({
services,
store,
usageCollection,
subPlugins,
subPluginRoutes,
}: RenderAppProps): (() => void) => {
const ApplicationUsageTrackingProvider =
usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment;
@ -36,25 +35,9 @@ export const renderApp = ({
>
<ApplicationUsageTrackingProvider>
<Switch>
{[
...subPlugins.overview.routes,
...subPlugins.alerts.routes,
...subPlugins.rules.routes,
...subPlugins.exceptions.routes,
...subPlugins.hosts.routes,
...subPlugins.network.routes,
// will be undefined if enabledExperimental.uebaEnabled === false
...(subPlugins.ueba != null ? subPlugins.ueba.routes : []),
...subPlugins.timelines.routes,
...subPlugins.cases.routes,
...subPlugins.management.routes,
].map((route, index) => (
<Route key={`route-${index}`} {...route} />
))}
<Route path="" exact>
<Redirect to={OVERVIEW_PATH} />
</Route>
{subPluginRoutes.map((route, index) => {
return <Route key={`route-${index}`} {...route} />;
})}
<Route>
<NotFoundPage />
</Route>

View file

@ -0,0 +1,47 @@
/*
* 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 { EuiPageTemplate } from '@elastic/eui';
import { SecuritySolutionPageWrapper } from '../common/components/page_wrapper';
import { EmptyPage } from '../common/components/empty_page';
import { useKibana } from '../common/lib/kibana';
import * as i18n from './translations';
interface NoPrivilegesPageProps {
subPluginKey: string;
}
export const NoPrivilegesPage = React.memo<NoPrivilegesPageProps>(({ subPluginKey }) => {
const { docLinks } = useKibana().services;
const emptyPageActions = useMemo(
() => ({
feature: {
icon: 'documents',
label: i18n.GO_TO_DOCUMENTATION,
url: `${docLinks.links.siem.privileges}`,
target: '_blank',
},
}),
[docLinks]
);
return (
<SecuritySolutionPageWrapper>
<EuiPageTemplate template="centeredContent">
<EmptyPage
actions={emptyPageActions}
message={i18n.NO_PERMISSIONS_MSG(subPluginKey)}
data-test-subj="no_feature_permissions-alerts"
title={i18n.NO_PERMISSIONS_TITLE}
/>
</EuiPageTemplate>
</SecuritySolutionPageWrapper>
);
});
NoPrivilegesPage.displayName = 'NoPrivilegePage';

View file

@ -80,3 +80,21 @@ export const INVESTIGATE = i18n.translate('xpack.securitySolution.navigation.inv
export const MANAGE = i18n.translate('xpack.securitySolution.navigation.manage', {
defaultMessage: 'Manage',
});
export const GO_TO_DOCUMENTATION = i18n.translate(
'xpack.securitySolution.goToDocumentationButton',
{
defaultMessage: 'View documentation',
}
);
export const NO_PERMISSIONS_MSG = (subPluginKey: string) =>
i18n.translate('xpack.securitySolution.noPermissionsMessage', {
values: { subPluginKey },
defaultMessage:
'To view {subPluginKey}, you must update privileges. For more information, contact your Kibana administrator.',
});
export const NO_PERMISSIONS_TITLE = i18n.translate('xpack.securitySolution.noPermissionsTitle', {
defaultMessage: 'Privileges required',
});

View file

@ -18,7 +18,7 @@ import {
import { RouteProps } from 'react-router-dom';
import { AppMountParameters } from '../../../../../src/core/public';
import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public';
import { StartedSubPlugins, StartServices } from '../types';
import { StartServices } from '../types';
/**
* The React properties used to render `SecurityApp` as well as the `element` to render it into.
@ -26,7 +26,7 @@ import { StartedSubPlugins, StartServices } from '../types';
export interface RenderAppProps extends AppMountParameters {
services: StartServices;
store: Store<State, Action>;
subPlugins: StartedSubPlugins;
subPluginRoutes: RouteProps[];
usageCollection?: UsageCollectionSetup;
}

View file

@ -15,7 +15,7 @@ import {
} from '../../../common/components/link_to';
import { SecurityPageName } from '../../../app/types';
import { useKibana } from '../../../common/lib/kibana';
import { APP_ID } from '../../../../common/constants';
import { APP_ID, APP_UI_ID } from '../../../../common/constants';
export interface AllCasesNavProps {
detailName: string;
@ -36,7 +36,7 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
const goToCreateCase = useCallback(
async (ev) => {
ev.preventDefault();
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCreateCaseUrl(urlSearch),
});
@ -47,7 +47,7 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
const goToCaseConfigure = useCallback(
async (ev) => {
ev.preventDefault();
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getConfigureCasesUrl(urlSearch),
});
@ -61,7 +61,7 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
return formatUrl(getCaseDetailsUrl({ id: detailName, subCaseId }));
},
onClick: async ({ detailName, subCaseId, search }: AllCasesNavProps) => {
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseDetailsUrl({ id: detailName, search, subCaseId }),
});

View file

@ -19,7 +19,7 @@ import { Case, CaseViewRefreshPropInterface } from '../../../../../cases/common'
import { TimelineId } from '../../../../common/types/timeline';
import { SecurityPageName } from '../../../app/types';
import { useKibana } from '../../../common/lib/kibana';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
import { timelineActions } from '../../../timelines/store/timeline';
import { useSourcererScope } from '../../../common/containers/sourcerer';
import { SourcererScopeName } from '../../../common/store/sourcerer/model';
@ -153,7 +153,7 @@ export const CaseView = React.memo(
if (e) {
e.preventDefault();
}
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: allCasesLink,
});
@ -165,7 +165,7 @@ export const CaseView = React.memo(
if (e) {
e.preventDefault();
}
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseDetailsUrl({ id: caseId }),
});
@ -178,7 +178,7 @@ export const CaseView = React.memo(
if (e) {
e.preventDefault();
}
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getConfigureCasesUrl(search),
});
@ -193,7 +193,7 @@ export const CaseView = React.memo(
if (e) {
e.preventDefault();
}
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
path: getEndpointDetailsPath({
name: 'endpointActivityLog',
selected_endpoint: endpointId,
@ -207,7 +207,7 @@ export const CaseView = React.memo(
if (e) {
e.preventDefault();
}
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId ?? ''),
});

View file

@ -16,7 +16,7 @@ import { Create } from '.';
import { useKibana } from '../../../common/lib/kibana';
import { Case } from '../../../../../cases/public/containers/types';
import { basicCase } from '../../../../../cases/public/containers/mock';
import { APP_ID, SecurityPageName } from '../../../../common/constants';
import { APP_ID, APP_UI_ID, SecurityPageName } from '../../../../common/constants';
import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search';
jest.mock('../use_insert_timeline');
@ -71,7 +71,7 @@ describe('Create case', () => {
);
await waitFor(() =>
expect(mockNavigateToApp).toHaveBeenCalledWith(APP_ID, {
expect(mockNavigateToApp).toHaveBeenCalledWith(APP_UI_ID, {
path: `?${mockRes}`,
deepLinkId: SecurityPageName.case,
})
@ -96,7 +96,7 @@ describe('Create case', () => {
);
await waitFor(() =>
expect(mockNavigateToApp).toHaveBeenNthCalledWith(1, APP_ID, {
expect(mockNavigateToApp).toHaveBeenNthCalledWith(1, APP_UI_ID, {
path: `/basic-case-id?${mockRes}`,
deepLinkId: SecurityPageName.case,
})

View file

@ -11,7 +11,7 @@ import { getCaseDetailsUrl, getCaseUrl } from '../../../common/components/link_t
import { useKibana } from '../../../common/lib/kibana';
import * as timelineMarkdownPlugin from '../../../common/components/markdown_editor/plugins/timeline';
import { useInsertTimeline } from '../use_insert_timeline';
import { APP_ID } from '../../../../common/constants';
import { APP_ID, APP_UI_ID } from '../../../../common/constants';
import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search';
import { navTabs } from '../../../app/home/home_navigations';
import { SecurityPageName } from '../../../app/types';
@ -24,7 +24,7 @@ export const Create = React.memo(() => {
const search = useGetUrlSearch(navTabs.case);
const onSuccess = useCallback(
async ({ id }) =>
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseDetailsUrl({ id, search }),
}),
@ -32,7 +32,7 @@ export const Create = React.memo(() => {
);
const handleSetIsCancel = useCallback(
async () =>
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseUrl(search),
}),

View file

@ -16,7 +16,7 @@ import { useGetUserCasesPermissions, useKibana } from '../../common/lib/kibana';
import { getCaseUrl } from '../../common/components/link_to';
import { navTabs } from '../../app/home/home_navigations';
import { CaseView } from '../components/case_view';
import { APP_ID } from '../../../common/constants';
import { APP_UI_ID } from '../../../common/constants';
import { Case } from '../../../../cases/common';
export const CaseDetailsPage = React.memo(() => {
@ -32,7 +32,7 @@ export const CaseDetailsPage = React.memo(() => {
useEffect(() => {
if (userPermissions != null && !userPermissions.read) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseUrl(search),
});

View file

@ -18,7 +18,7 @@ import { navTabs } from '../../app/home/home_navigations';
import { CaseHeaderPage } from '../components/case_header_page';
import { WhitePageWrapper, SectionWrapper } from '../components/wrappers';
import * as i18n from './translations';
import { APP_ID } from '../../../common/constants';
import { APP_ID, APP_UI_ID } from '../../../common/constants';
const ConfigureCasesPageComponent: React.FC = () => {
const {
@ -39,7 +39,7 @@ const ConfigureCasesPageComponent: React.FC = () => {
useEffect(() => {
if (userPermissions != null && !userPermissions.read) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseUrl(search),
});

View file

@ -17,7 +17,7 @@ import { navTabs } from '../../app/home/home_navigations';
import { CaseHeaderPage } from '../components/case_header_page';
import { Create } from '../components/create';
import * as i18n from './translations';
import { APP_ID } from '../../../common/constants';
import { APP_UI_ID } from '../../../common/constants';
export const CreateCasePage = React.memo(() => {
const userPermissions = useGetUserCasesPermissions();
@ -37,7 +37,7 @@ export const CreateCasePage = React.memo(() => {
useEffect(() => {
if (userPermissions != null && !userPermissions.crud) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseUrl(search),
});

View file

@ -13,7 +13,7 @@ import { getCaseDetailsUrl, getCreateCaseUrl } from '../../common/components/lin
import { RouteSpyState } from '../../common/utils/route/types';
import * as i18n from './translations';
import { GetUrlForApp } from '../../common/components/navigation/types';
import { APP_ID } from '../../../common/constants';
import { APP_UI_ID } from '../../../common/constants';
import { SecurityPageName } from '../../app/types';
export const getBreadcrumbs = (
@ -26,7 +26,7 @@ export const getBreadcrumbs = (
let breadcrumb = [
{
text: i18n.PAGE_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: queryParameters,
}),
@ -37,7 +37,7 @@ export const getBreadcrumbs = (
...breadcrumb,
{
text: i18n.CREATE_BC_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCreateCaseUrl(queryParameters),
}),
@ -48,7 +48,7 @@ export const getBreadcrumbs = (
...breadcrumb,
{
text: params.state?.caseTitle ?? '',
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseDetailsUrl({ id: params.detailName, search: queryParameters }),
}),

View file

@ -8,7 +8,7 @@
import React, { memo, MouseEventHandler } from 'react';
import { EuiLink, EuiLinkProps, EuiButton, EuiButtonProps } from '@elastic/eui';
import { useNavigateToAppEventHandler } from '../../hooks/endpoint/use_navigate_to_app_event_handler';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
export type LinkToAppProps = (EuiLinkProps | EuiButtonProps) & {
/** the app id - normally the value of the `id` in that plugin's `kibana.json` */
@ -30,7 +30,7 @@ export type LinkToAppProps = (EuiLinkProps | EuiButtonProps) & {
*/
export const LinkToApp = memo<LinkToAppProps>(
({
appId = APP_ID,
appId = APP_UI_ID,
deepLinkId,
appPath: path,
appState: state,

View file

@ -14,7 +14,7 @@ import * as i18n from './translations';
import { TimelineEventsDetailsItem } from '../../../../common';
import { LinkAnchor } from '../links';
import { useKibana } from '../../lib/kibana';
import { APP_ID, SecurityPageName } from '../../../../common/constants';
import { APP_UI_ID, SecurityPageName } from '../../../../common/constants';
import { EVENT_DETAILS_PLACEHOLDER } from '../../../timelines/components/side_panel/event_details/translations';
import { getFieldValue } from '../../../detections/components/host_isolation/helpers';
@ -64,7 +64,7 @@ export const ReasonComponent: React.FC<Props> = ({ eventId, data }) => {
data-test-subj="ruleName"
onClick={(ev: { preventDefault: () => void }) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId),
});

View file

@ -16,7 +16,7 @@ import {
import React, { useMemo, useCallback, SyntheticEvent } from 'react';
import { isNil } from 'lodash/fp';
import { IP_REPUTATION_LINKS_SETTING, APP_ID } from '../../../../common/constants';
import { IP_REPUTATION_LINKS_SETTING, APP_UI_ID } from '../../../../common/constants';
import {
DefaultFieldRendererOverflow,
DEFAULT_MORE_MAX_HEIGHT,
@ -56,7 +56,7 @@ const UebaDetailsLinkComponent: React.FC<{
const goToUebaDetails = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.ueba,
path: getUebaDetailsUrl(encodeURIComponent(hostName), search),
});
@ -99,7 +99,7 @@ const HostDetailsLinkComponent: React.FC<{
const goToHostDetails = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
path: getHostDetailsUrl(encodeURIComponent(hostName), search),
});
@ -183,7 +183,7 @@ const NetworkDetailsLinkComponent: React.FC<{
const goToNetworkDetails = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.network,
path: getNetworkDetailsUrl(encodeURIComponent(encodeIpv6(ip)), flowTarget, search),
});
@ -229,7 +229,7 @@ const CaseDetailsLinkComponent: React.FC<{
const goToCaseDetails = useCallback(
async (ev) => {
ev.preventDefault();
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseDetailsUrl({ id: detailName, search, subCaseId }),
});
@ -257,7 +257,7 @@ export const CreateCaseLink = React.memo<{ children: React.ReactNode }>(({ child
const goToCreateCase = useCallback(
async (ev) => {
ev.preventDefault();
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCreateCaseUrl(search),
});

View file

@ -158,11 +158,11 @@ describe('Navigation Breadcrumbs', () => {
);
expect(breadcrumbs).toEqual([
{
href: 'securitySolution/overview',
href: 'securitySolutionUI/overview',
text: 'Security',
},
{
href: "securitySolution/hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
text: 'Hosts',
},
{
@ -178,10 +178,10 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Network',
href: "securitySolution/network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: 'Flows',
@ -196,10 +196,10 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Timelines',
href: "securitySolution/timelines?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/timelines?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
]);
});
@ -210,14 +210,14 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Hosts',
href: "securitySolution/hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: 'siem-kibana',
href: "securitySolution/hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{ text: 'Authentications', href: '' },
]);
@ -229,14 +229,14 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Network',
href: "securitySolution/network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: ipv4,
href: `securitySolution/network/ip/${ipv4}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
href: `securitySolutionUI/network/ip/${ipv4}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
},
{ text: 'Flows', href: '' },
]);
@ -248,14 +248,14 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Network',
href: "securitySolution/network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: ipv6,
href: `securitySolution/network/ip/${ipv6Encoded}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
href: `securitySolutionUI/network/ip/${ipv6Encoded}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
},
{ text: 'Flows', href: '' },
]);
@ -267,7 +267,7 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Alerts',
href: '',
@ -281,7 +281,7 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Exceptions',
href: '',
@ -295,10 +295,10 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Rules',
href: "securitySolution/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
]);
});
@ -309,10 +309,10 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Rules',
href: "securitySolution/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: 'Create',
@ -335,14 +335,14 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Rules',
href: "securitySolution/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: mockRuleName,
href: `securitySolution/rules/id/${mockDetailName}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
href: `securitySolutionUI/rules/id/${mockDetailName}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
},
]);
});
@ -361,14 +361,14 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Rules',
href: "securitySolution/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/rules?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: 'ALERT_RULE_NAME',
href: `securitySolution/rules/id/${mockDetailName}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
href: `securitySolutionUI/rules/id/${mockDetailName}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
},
{
text: 'Edit',
@ -383,10 +383,10 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Cases',
href: "securitySolution/case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
]);
});
@ -403,14 +403,14 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Cases',
href: "securitySolution/case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
},
{
text: sampleCase.name,
href: `securitySolution/case/${sampleCase.id}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
href: `securitySolutionUI/case/${sampleCase.id}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`,
},
]);
});
@ -420,7 +420,7 @@ describe('Navigation Breadcrumbs', () => {
getUrlForAppMock
);
expect(breadcrumbs).toEqual([
{ text: 'Security', href: 'securitySolution/overview' },
{ text: 'Security', href: 'securitySolutionUI/overview' },
{
text: 'Endpoints',
href: '',
@ -442,17 +442,17 @@ describe('Navigation Breadcrumbs', () => {
expect(setBreadcrumbsMock).toBeCalledWith([
expect.objectContaining({
text: 'Security',
href: 'securitySolution/overview',
href: 'securitySolutionUI/overview',
onClick: expect.any(Function),
}),
expect.objectContaining({
text: 'Hosts',
href: "securitySolution/hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
onClick: expect.any(Function),
}),
expect.objectContaining({
text: 'siem-kibana',
href: "securitySolution/hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
href: "securitySolutionUI/hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))",
onClick: expect.any(Function),
}),
{

View file

@ -9,7 +9,7 @@ import { getOr, omit } from 'lodash/fp';
import { useDispatch } from 'react-redux';
import { ChromeBreadcrumb } from '../../../../../../../../src/core/public';
import { APP_NAME, APP_ID } from '../../../../../common/constants';
import { APP_NAME, APP_UI_ID } from '../../../../../common/constants';
import { StartServices } from '../../../../types';
import { getBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../../hosts/pages/details/utils';
import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../../network/pages/details';
@ -92,7 +92,7 @@ export const getBreadcrumbsForRoute = (
getUrlForApp: GetUrlForApp
): ChromeBreadcrumb[] | null => {
const spyState: RouteSpyState = omit('navTabs', object);
const overviewPath = getUrlForApp(APP_ID, { deepLinkId: SecurityPageName.overview });
const overviewPath = getUrlForApp(APP_UI_ID, { deepLinkId: SecurityPageName.overview });
const siemRootBreadcrumb: ChromeBreadcrumb = {
text: APP_NAME,
href: getAppOverviewUrl(overviewPath),

View file

@ -116,7 +116,7 @@ describe('Table Navigation', () => {
`EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]`
);
expect(firstTab.props().href).toBe(
`/app/securitySolution/hosts/siem-window/authentications${SEARCH_QUERY}`
`/app/securitySolutionUI/hosts/siem-window/authentications${SEARCH_QUERY}`
);
});
});

View file

@ -18,6 +18,7 @@ import { UrlInputsModel } from '../../../store/inputs/model';
import { useRouteSpy } from '../../../utils/route/use_route_spy';
import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features';
import { TestProviders } from '../../../mock';
import { CASES_FEATURE_ID } from '../../../../../common/constants';
import { useCanSeeHostIsolationExceptionsMenu } from '../../../../management/pages/host_isolation_exceptions/view/hooks';
jest.mock('../../../lib/kibana/kibana_react');
@ -88,9 +89,10 @@ describe('useSecuritySolutionNavigation', () => {
`${appId}/${options?.deepLinkId ?? ''}${options?.path ?? ''}`,
capabilities: {
siem: {
crud_alerts: true,
read_alerts: true,
show: true,
crud: true,
},
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
},
},
chrome: {
@ -114,10 +116,10 @@ describe('useSecuritySolutionNavigation', () => {
"id": "main",
"items": Array [
Object {
"data-href": "securitySolution/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-overview",
"disabled": false,
"href": "securitySolution/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/overview?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "overview",
"isSelected": false,
"name": "Overview",
@ -130,30 +132,30 @@ describe('useSecuritySolutionNavigation', () => {
"id": "detect",
"items": Array [
Object {
"data-href": "securitySolution/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-alerts",
"disabled": false,
"href": "securitySolution/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/alerts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "alerts",
"isSelected": false,
"name": "Alerts",
"onClick": [Function],
},
Object {
"data-href": "securitySolution/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-rules",
"disabled": false,
"href": "securitySolution/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/rules?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "rules",
"isSelected": false,
"name": "Rules",
"onClick": [Function],
},
Object {
"data-href": "securitySolution/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-exceptions",
"disabled": false,
"href": "securitySolution/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/exceptions?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "exceptions",
"isSelected": false,
"name": "Exceptions",
@ -166,20 +168,20 @@ describe('useSecuritySolutionNavigation', () => {
"id": "explore",
"items": Array [
Object {
"data-href": "securitySolution/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-hosts",
"disabled": false,
"href": "securitySolution/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/hosts?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "hosts",
"isSelected": true,
"name": "Hosts",
"onClick": [Function],
},
Object {
"data-href": "securitySolution/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-network",
"disabled": false,
"href": "securitySolution/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/network?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "network",
"isSelected": false,
"name": "Network",
@ -192,10 +194,10 @@ describe('useSecuritySolutionNavigation', () => {
"id": "investigate",
"items": Array [
Object {
"data-href": "securitySolution/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-timelines",
"disabled": false,
"href": "securitySolution/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/timelines?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "timelines",
"isSelected": false,
"name": "Timelines",
@ -208,40 +210,40 @@ describe('useSecuritySolutionNavigation', () => {
"id": "manage",
"items": Array [
Object {
"data-href": "securitySolution/endpoints",
"data-href": "securitySolutionUI/endpoints",
"data-test-subj": "navigation-endpoints",
"disabled": false,
"href": "securitySolution/endpoints",
"href": "securitySolutionUI/endpoints",
"id": "endpoints",
"isSelected": false,
"name": "Endpoints",
"onClick": [Function],
},
Object {
"data-href": "securitySolution/trusted_apps",
"data-href": "securitySolutionUI/trusted_apps",
"data-test-subj": "navigation-trusted_apps",
"disabled": false,
"href": "securitySolution/trusted_apps",
"href": "securitySolutionUI/trusted_apps",
"id": "trusted_apps",
"isSelected": false,
"name": "Trusted applications",
"onClick": [Function],
},
Object {
"data-href": "securitySolution/event_filters",
"data-href": "securitySolutionUI/event_filters",
"data-test-subj": "navigation-event_filters",
"disabled": false,
"href": "securitySolution/event_filters",
"href": "securitySolutionUI/event_filters",
"id": "event_filters",
"isSelected": false,
"name": "Event filters",
"onClick": [Function],
},
Object {
"data-href": "securitySolution/host_isolation_exceptions",
"data-href": "securitySolutionUI/host_isolation_exceptions",
"data-test-subj": "navigation-host_isolation_exceptions",
"disabled": false,
"href": "securitySolution/host_isolation_exceptions",
"href": "securitySolutionUI/host_isolation_exceptions",
"id": "host_isolation_exceptions",
"isSelected": false,
"name": "Host isolation exceptions",
@ -299,10 +301,10 @@ describe('useSecuritySolutionNavigation', () => {
);
expect(caseNavItem).toMatchInlineSnapshot(`
Object {
"data-href": "securitySolution/case?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-href": "securitySolutionUI/case?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"data-test-subj": "navigation-case",
"disabled": false,
"href": "securitySolution/case?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"href": "securitySolutionUI/case?query=(language:kuery,query:'host.name:%22security-solution-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
"id": "case",
"isSelected": false,
"name": "Cases",

View file

@ -11,6 +11,7 @@ import { EuiSideNavItemType } from '@elastic/eui/src/components/side_nav/side_na
import { securityNavGroup } from '../../../../app/home/home_navigations';
import { getSearch } from '../helpers';
import { PrimaryNavigationItemsProps } from './types';
import { useKibana } from '../../../lib/kibana/kibana_react';
import { useGetUserCasesPermissions } from '../../../lib/kibana';
import { useNavigation } from '../../../lib/kibana/hooks';
import { NavTab } from '../types';
@ -64,34 +65,52 @@ export const usePrimaryNavigationItems = ({
function usePrimaryNavigationItemsToDisplay(navTabs: Record<string, NavTab>) {
const hasCasesReadPermissions = useGetUserCasesPermissions()?.read;
const canSeeHostIsolationExceptions = useCanSeeHostIsolationExceptionsMenu();
return useMemo(() => {
return [
{
id: 'main',
name: '',
items: [navTabs.overview],
},
{
...securityNavGroup.detect,
items: [navTabs.alerts, navTabs.rules, navTabs.exceptions],
},
{
...securityNavGroup.explore,
items: [navTabs.hosts, navTabs.network, ...(navTabs.ueba != null ? [navTabs.ueba] : [])],
},
{
...securityNavGroup.investigate,
items: hasCasesReadPermissions ? [navTabs.timelines, navTabs.case] : [navTabs.timelines],
},
{
...securityNavGroup.manage,
items: [
navTabs.endpoints,
navTabs.trusted_apps,
navTabs.event_filters,
...(canSeeHostIsolationExceptions ? [navTabs.host_isolation_exceptions] : []),
],
},
];
}, [navTabs, hasCasesReadPermissions, canSeeHostIsolationExceptions]);
const uiCapabilities = useKibana().services.application.capabilities;
return useMemo(
() =>
uiCapabilities.siem.show
? [
{
id: 'main',
name: '',
items: [navTabs.overview],
},
{
...securityNavGroup.detect,
items: [navTabs.alerts, navTabs.rules, navTabs.exceptions],
},
{
...securityNavGroup.explore,
items: [
navTabs.hosts,
navTabs.network,
...(navTabs.ueba != null ? [navTabs.ueba] : []),
],
},
{
...securityNavGroup.investigate,
items: hasCasesReadPermissions
? [navTabs.timelines, navTabs.case]
: [navTabs.timelines],
},
{
...securityNavGroup.manage,
items: [
navTabs.endpoints,
navTabs.trusted_apps,
navTabs.event_filters,
...(canSeeHostIsolationExceptions ? [navTabs.host_isolation_exceptions] : []),
],
},
]
: hasCasesReadPermissions
? [
{
...securityNavGroup.investigate,
items: [navTabs.case],
},
]
: [],
[uiCapabilities.siem.show, navTabs, hasCasesReadPermissions, canSeeHostIsolationExceptions]
);
}

View file

@ -40,15 +40,16 @@ export const UserPrivilegesProvider = ({
kibanaCapabilities,
children,
}: UserPrivilegesProviderProps) => {
const listPrivileges = useFetchListPrivileges();
const detectionEnginePrivileges = useFetchDetectionEnginePrivileges();
const endpointPrivileges = useEndpointPrivileges();
const [kibanaSecuritySolutionsPrivileges, setKibanaSecuritySolutionsPrivileges] = useState({
crud: false,
read: false,
});
const crud: boolean = kibanaCapabilities[SERVER_APP_ID].crud === true;
const read: boolean = kibanaCapabilities[SERVER_APP_ID].show === true;
const [kibanaSecuritySolutionsPrivileges, setKibanaSecuritySolutionsPrivileges] = useState({
crud,
read,
});
const listPrivileges = useFetchListPrivileges(read);
const detectionEnginePrivileges = useFetchDetectionEnginePrivileges(read);
const endpointPrivileges = useEndpointPrivileges();
useEffect(() => {
setKibanaSecuritySolutionsPrivileges((currPrivileges) => {

View file

@ -17,7 +17,7 @@ import {
createStartServicesMock,
createWithKibanaMock,
} from '../kibana_react.mock';
import { APP_ID } from '../../../../../common/constants';
import { APP_UI_ID } from '../../../../../common/constants';
const mockStartServicesMock = createStartServicesMock();
export const KibanaServices = { get: jest.fn(), getKibanaVersion: jest.fn(() => '8.0.0') };
@ -65,7 +65,7 @@ export const useGetUserCasesPermissions = jest.fn();
export const useAppUrl = jest.fn().mockReturnValue({
getAppUrl: jest
.fn()
.mockImplementation(({ appId = APP_ID, ...options }) =>
.mockImplementation(({ appId = APP_UI_ID, ...options }) =>
mockStartServicesMock.application.getUrlForApp(appId, options)
),
});

View file

@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n';
import { camelCase, isArray, isObject } from 'lodash';
import { set } from '@elastic/safer-lodash-set';
import {
APP_ID,
APP_UI_ID,
CASES_FEATURE_ID,
DEFAULT_DATE_FORMAT,
DEFAULT_DATE_FORMAT_TZ,
@ -174,7 +174,7 @@ export const useAppUrl = () => {
const getAppUrl = useCallback(
({
appId = APP_ID,
appId = APP_UI_ID,
...options
}: {
appId?: string;
@ -197,7 +197,7 @@ export const useNavigateTo = () => {
const navigateTo = useCallback(
({
url,
appId = APP_ID,
appId = APP_UI_ID,
...options
}: {
url?: string;

View file

@ -22,7 +22,7 @@ import { createStartServicesMock } from '../../lib/kibana/kibana_react.mock';
import { SUB_PLUGINS_REDUCER, mockGlobalState, createSecuritySolutionStorageMock } from '..';
import { ExperimentalFeatures } from '../../../../common/experimental_features';
import { PLUGIN_ID } from '../../../../../fleet/common';
import { APP_ID, APP_PATH } from '../../../../common/constants';
import { APP_UI_ID, APP_PATH } from '../../../../common/constants';
import { KibanaContextProvider, KibanaServices } from '../../lib/kibana';
import { getDeepLinks } from '../../../app/deep_links';
import { fleetGetPackageListHttpMock } from '../../../management/pages/mocks';
@ -176,7 +176,7 @@ const createCoreStartMock = (
switch (appId) {
case PLUGIN_ID:
return '/app/fleet';
case APP_ID:
case APP_UI_ID:
return `${APP_PATH}${
deepLinkId && deepLinkPaths[deepLinkId] ? deepLinkPaths[deepLinkId] : ''
}${path ?? ''}`;
@ -186,7 +186,7 @@ const createCoreStartMock = (
});
coreStart.application.navigateToApp.mockImplementation((appId, { deepLinkId, path } = {}) => {
if (appId === APP_ID) {
if (appId === APP_UI_ID) {
history.push(
`${deepLinkId && deepLinkPaths[deepLinkId] ? deepLinkPaths[deepLinkId] : ''}${path ?? ''}`
);

View file

@ -26,6 +26,7 @@ import { FieldHook } from '../../shared_imports';
import { SUB_PLUGINS_REDUCER } from './utils';
import { createSecuritySolutionStorageMock, localStorageMock } from './mock_local_storage';
import { UserPrivilegesProvider } from '../components/user_privileges';
import { CASES_FEATURE_ID } from '../../../common/constants';
const state: State = mockGlobalState;
@ -76,7 +77,10 @@ const TestProvidersWithPrivilegesComponent: React.FC<Props> = ({
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
<UserPrivilegesProvider
kibanaCapabilities={
{ siem: { crud_alerts: true, read_alerts: true } } as unknown as Capabilities
{
siem: { show: true, crud: true },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
} as unknown as Capabilities
}
>
<DragDropContext onDragEnd={onDragEnd}>{children}</DragDropContext>

View file

@ -122,7 +122,7 @@ describe('AlertsHistogramPanel', () => {
preventDefault: jest.fn(),
});
expect(mockNavigateToApp).toBeCalledWith('securitySolution', {
expect(mockNavigateToApp).toBeCalledWith('securitySolutionUI', {
deepLinkId: SecurityPageName.alerts,
path: '',
});

View file

@ -14,7 +14,7 @@ import { isEmpty } from 'lodash/fp';
import uuid from 'uuid';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../../common/constants';
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../../common/constants';
import type { UpdateDateRange } from '../../../../common/components/charts/common';
import type { LegendItem } from '../../../../common/components/charts/draggable_legend_item';
import { escapeDataProviderId } from '../../../../common/components/drag_and_drop/helpers';
@ -147,7 +147,7 @@ export const AlertsHistogramPanel = memo<AlertsHistogramPanelProps>(
const goToDetectionEngine = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.alerts,
path: getDetectionEngineUrl(urlSearch),
});

View file

@ -8,7 +8,7 @@
import { useMemo } from 'react';
import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana';
import { TimelineId, TimelineNonEcsData } from '../../../../../common';
import { APP_ID } from '../../../../../common/constants';
import { APP_UI_ID } from '../../../../../common/constants';
import { useInsertTimeline } from '../../../../cases/components/use_insert_timeline';
import { Ecs } from '../../../../../common/ecs';
@ -39,7 +39,7 @@ export const useAddToCaseActions = ({
event: { data: nonEcsData ?? [], ecs: ecsData, _id: ecsData?._id },
useInsertTimeline: insertTimelineHook,
casePermissions,
appId: APP_ID,
appId: APP_UI_ID,
onClose: afterCaseSelection,
}
: null,

View file

@ -30,7 +30,7 @@ import { getRulesUrl } from '../../../../common/components/link_to/redirect_to_d
import { getToolTipContent } from '../../../../common/utils/privileges';
import { useBoolState } from '../../../../common/hooks/use_bool_state';
import { useKibana } from '../../../../common/lib/kibana';
import { APP_ID, SecurityPageName } from '../../../../../common/constants';
import { APP_UI_ID, SecurityPageName } from '../../../../../common/constants';
const MyEuiButtonIcon = styled(EuiButtonIcon)`
&.euiButtonIcon {
@ -62,7 +62,7 @@ const RuleActionsOverflowComponent = ({
const [, dispatchToaster] = useStateToaster();
const onRuleDeletedCallback = useCallback(() => {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRulesUrl(),
});

View file

@ -35,7 +35,7 @@ import { RuleActionsField } from '../rule_actions_field';
import { useKibana } from '../../../../common/lib/kibana';
import { getSchema } from './schema';
import * as I18n from './translations';
import { APP_ID } from '../../../../../common/constants';
import { APP_UI_ID } from '../../../../../common/constants';
import { useManageCaseAction } from './use_manage_case_action';
interface StepRuleActionsProps extends RuleStepProps {
@ -80,7 +80,7 @@ const StepRuleActionsComponent: FC<StepRuleActionsProps> = ({
} = useKibana();
const kibanaAbsoluteUrl = useMemo(
() =>
application.getUrlForApp(`${APP_ID}`, {
application.getUrlForApp(`${APP_UI_ID}`, {
absolute: true,
}),
[application]

View file

@ -69,9 +69,7 @@ describe('useUserInfo', () => {
const wrapper = ({ children }: { children: JSX.Element }) => (
<TestProviders>
<UserPrivilegesProvider
kibanaCapabilities={
{ siem: { crud_alerts: true, read_alerts: true } } as unknown as Capabilities
}
kibanaCapabilities={{ siem: { show: true, crud: true } } as unknown as Capabilities}
>
<ManageUserInfo>{children}</ManageUserInfo>
</UserPrivilegesProvider>

View file

@ -13,7 +13,7 @@ import * as i18n from './translations';
export const useFetchPrivileges = () => useAsync(withOptionalSignal(getUserPrivilege));
export const useFetchDetectionEnginePrivileges = () => {
export const useFetchDetectionEnginePrivileges = (isAppAvailable: boolean = true) => {
const { start, ...detectionEnginePrivileges } = useFetchPrivileges();
const { addError } = useAppToasts();
const abortCtrlRef = useRef(new AbortController());
@ -21,12 +21,12 @@ export const useFetchDetectionEnginePrivileges = () => {
useEffect(() => {
const { loading, result, error } = detectionEnginePrivileges;
if (!loading && !(result || error)) {
if (isAppAvailable && !loading && !(result || error)) {
abortCtrlRef.current.abort();
abortCtrlRef.current = new AbortController();
start({ signal: abortCtrlRef.current.signal });
}
}, [start, detectionEnginePrivileges]);
}, [start, detectionEnginePrivileges, isAppAvailable]);
useEffect(() => {
return () => {

View file

@ -22,7 +22,7 @@ interface ListPrivileges {
};
}
export const useFetchListPrivileges = () => {
export const useFetchListPrivileges = (isAppAvailable: boolean = true) => {
const http = useHttp();
const { lists } = useKibana().services;
const { start: fetchListPrivileges, ...listPrivileges } = useReadListPrivileges();
@ -32,12 +32,12 @@ export const useFetchListPrivileges = () => {
useEffect(() => {
const { loading, result, error } = listPrivileges;
if (lists && !loading && !(result || error)) {
if (isAppAvailable && lists && !loading && !(result || error)) {
abortCtrlRef.current.abort();
abortCtrlRef.current = new AbortController();
fetchListPrivileges({ http, signal: abortCtrlRef.current.signal });
}
}, [http, lists, fetchListPrivileges, listPrivileges]);
}, [http, lists, fetchListPrivileges, listPrivileges, isAppAvailable]);
useEffect(() => {
return () => {

View file

@ -7,7 +7,7 @@
import React, { Dispatch } from 'react';
import { NavigateToAppOptions } from '../../../../../../../../../src/core/public';
import { APP_ID } from '../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../common/constants';
import { BulkAction } from '../../../../../../common/detection_engine/schemas/common/schemas';
import { CreateRulesSchema } from '../../../../../../common/detection_engine/schemas/request';
import { SecurityPageName } from '../../../../../app/types';
@ -37,7 +37,7 @@ export const editRuleAction = (
ruleId: string,
navigateToApp: (appId: string, options?: NavigateToAppOptions | undefined) => Promise<void>
) => {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getEditRuleUrl(ruleId ?? ''),
});

View file

@ -42,7 +42,7 @@ import { getToolTipContent, canEditRuleWithActions } from '../../../../../common
import { PopoverTooltip } from './popover_tooltip';
import { TagsDisplay } from './tag_display';
import { getRuleStatusText } from '../../../../../../common/detection_engine/utils';
import { APP_ID, SecurityPageName } from '../../../../../../common/constants';
import { APP_UI_ID, SecurityPageName } from '../../../../../../common/constants';
import { DocLinksStart, NavigateToAppOptions } from '../../../../../../../../../src/core/public';
export const getActions = (
@ -164,7 +164,7 @@ export const getColumns = ({
data-test-subj="ruleName"
onClick={(ev: { preventDefault: () => void }) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(item.id),
});
@ -329,7 +329,7 @@ export const getMonitoringColumns = (
data-test-subj="ruleName"
onClick={(ev: { preventDefault: () => void }) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(item.id),
});

View file

@ -47,7 +47,7 @@ import { formatRule, stepIsValid } from './helpers';
import * as i18n from './translations';
import { SecurityPageName } from '../../../../../app/types';
import { ruleStepsOrder } from '../utils';
import { APP_ID } from '../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../common/constants';
import { useKibana } from '../../../../../common/lib/kibana';
const formHookNoop = async (): Promise<undefined> => undefined;
@ -269,7 +269,7 @@ const CreateRulePageComponent: React.FC = () => {
if (ruleName && ruleId) {
displaySuccessToast(i18n.SUCCESSFULLY_CREATED_RULES(ruleName), dispatchToaster);
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId),
});
@ -284,13 +284,13 @@ const CreateRulePageComponent: React.FC = () => {
needsListsConfiguration
)
) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.alerts,
path: getDetectionEngineUrl(),
});
return null;
} else if (!userHasPermissions(canUserCRUD)) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRulesUrl(),
});

View file

@ -89,7 +89,7 @@ import { LinkButton } from '../../../../../common/components/links';
import { useFormatUrl } from '../../../../../common/components/link_to';
import { ExceptionsViewer } from '../../../../../common/components/exceptions/viewer';
import {
APP_ID,
APP_UI_ID,
DEFAULT_INDEX_PATTERN,
DEFAULT_INDEX_PATTERN_EXPERIMENTAL,
} from '../../../../../../common/constants';
@ -574,7 +574,7 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
const goToEditRule = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getEditRuleUrl(ruleId ?? ''),
});
@ -683,7 +683,7 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
needsListsConfiguration
)
) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.alerts,
path: getDetectionEngineUrl(),
});

View file

@ -56,7 +56,7 @@ import * as i18n from './translations';
import { SecurityPageName } from '../../../../../app/types';
import { ruleStepsOrder } from '../utils';
import { useKibana } from '../../../../../common/lib/kibana';
import { APP_ID } from '../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../common/constants';
const formHookNoop = async (): Promise<undefined> => undefined;
@ -300,7 +300,7 @@ const EditRulePageComponent: FC = () => {
const goToDetailsRule = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId ?? ''),
});
@ -318,7 +318,7 @@ const EditRulePageComponent: FC = () => {
if (isSaved) {
displaySuccessToast(i18n.SUCCESSFULLY_SAVED_RULE(rule?.name ?? ''), dispatchToaster);
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId ?? ''),
});
@ -333,13 +333,13 @@ const EditRulePageComponent: FC = () => {
needsListsConfiguration
)
) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.alerts,
path: getDetectionEngineUrl(),
});
return null;
} else if (!userHasPermissions(canUserCRUD)) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId ?? ''),
});

View file

@ -36,7 +36,7 @@ import { useFormatUrl } from '../../../../common/components/link_to';
import { NeedAdminForUpdateRulesCallOut } from '../../../components/callouts/need_admin_for_update_callout';
import { MlJobCompatibilityCallout } from '../../../components/callouts/ml_job_compatibility_callout';
import { MissingPrivilegesCallOut } from '../../../components/callouts/missing_privileges_callout';
import { APP_ID } from '../../../../../common/constants';
import { APP_UI_ID } from '../../../../../common/constants';
import { useKibana } from '../../../../common/lib/kibana';
type Func = () => Promise<void>;
@ -125,7 +125,7 @@ const RulesPageComponent: React.FC = () => {
const goToNewRule = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, { deepLinkId: SecurityPageName.rules, path: getCreateRuleUrl() });
navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.rules, path: getCreateRuleUrl() });
},
[navigateToApp]
);
@ -156,7 +156,7 @@ const RulesPageComponent: React.FC = () => {
needsListsConfiguration
)
) {
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.alerts,
path: getDetectionEngineUrl(),
});

View file

@ -16,7 +16,7 @@ import * as i18nRules from './translations';
import { RouteSpyState } from '../../../../common/utils/route/types';
import { GetUrlForApp } from '../../../../common/components/navigation/types';
import { SecurityPageName } from '../../../../app/types';
import { APP_ID, RULES_PATH } from '../../../../../common/constants';
import { APP_UI_ID, RULES_PATH } from '../../../../../common/constants';
import { RuleStep, RuleStepsOrder } from './types';
export const ruleStepsOrder: RuleStepsOrder = [
@ -32,7 +32,7 @@ const getRulesBreadcrumb = (pathname: string, search: string[], getUrlForApp: Ge
if (tabPath === 'rules') {
return {
text: i18nRules.PAGE_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRulesUrl(!isEmpty(search[0]) ? search[0] : ''),
}),
@ -64,7 +64,7 @@ export const getBreadcrumbs = (
...breadcrumb,
{
text: params.state.ruleName,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''),
}),

View file

@ -1,62 +0,0 @@
/*
* 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 { parseRoute, getHostRiskIndex } from './helpers';
describe('public helpers parseRoute', () => {
it('should properly parse hash route', () => {
const hashSearch =
'?timerange=(global:(linkTo:!(timeline),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)),timeline:(linkTo:!(global),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)))';
const hashLocation = {
hash: `#/detections/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit${hashSearch}`,
pathname: '/app/siem',
search: '',
};
expect(parseRoute(hashLocation)).toEqual({
pageName: 'detections',
path: `/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit${hashSearch}`,
search: hashSearch,
});
});
it('should properly parse non-hash route', () => {
const nonHashLocation = {
hash: '',
pathname: '/app/security/detections/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit',
search:
'?timerange=(global:(linkTo:!(timeline),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)),timeline:(linkTo:!(global),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)))',
};
expect(parseRoute(nonHashLocation)).toEqual({
pageName: 'detections',
path: `/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit${nonHashLocation.search}`,
search: nonHashLocation.search,
});
});
it('should properly parse non-hash subplugin route', () => {
const nonHashLocation = {
hash: '',
pathname: '/app/security/detections',
search:
'?timerange=(global:(linkTo:!(timeline),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)),timeline:(linkTo:!(global),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)))',
};
expect(parseRoute(nonHashLocation)).toEqual({
pageName: 'detections',
path: `${nonHashLocation.search}`,
search: nonHashLocation.search,
});
});
});
describe('public helpers export getHostRiskIndex', () => {
it('should properly return index if space is specified', () => {
expect(getHostRiskIndex('testName')).toEqual('ml_host_risk_score_latest_testName');
});
});

View file

@ -0,0 +1,276 @@
/*
* 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 from 'react';
import { shallow } from 'enzyme';
import { Capabilities } from '../../../../src/core/public';
import { CASES_FEATURE_ID, SERVER_APP_ID } from '../common/constants';
import {
parseRoute,
getHostRiskIndex,
isSubPluginAvailable,
getSubPluginRoutesByCapabilities,
RedirectRoute,
} from './helpers';
import { StartedSubPlugins } from './types';
describe('public helpers parseRoute', () => {
it('should properly parse hash route', () => {
const hashSearch =
'?timerange=(global:(linkTo:!(timeline),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)),timeline:(linkTo:!(global),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)))';
const hashLocation = {
hash: `#/detections/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit${hashSearch}`,
pathname: '/app/siem',
search: '',
};
expect(parseRoute(hashLocation)).toEqual({
pageName: 'detections',
path: `/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit${hashSearch}`,
search: hashSearch,
});
});
it('should properly parse non-hash route', () => {
const nonHashLocation = {
hash: '',
pathname: '/app/security/detections/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit',
search:
'?timerange=(global:(linkTo:!(timeline),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)),timeline:(linkTo:!(global),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)))',
};
expect(parseRoute(nonHashLocation)).toEqual({
pageName: 'detections',
path: `/rules/id/78acc090-bbaa-4a86-916b-ea44784324ae/edit${nonHashLocation.search}`,
search: nonHashLocation.search,
});
});
it('should properly parse non-hash subplugin route', () => {
const nonHashLocation = {
hash: '',
pathname: '/app/security/detections',
search:
'?timerange=(global:(linkTo:!(timeline),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)),timeline:(linkTo:!(global),timerange:(from:%272020-09-06T11:43:55.814Z%27,fromStr:now-24h,kind:relative,to:%272020-09-07T11:43:55.814Z%27,toStr:now)))',
};
expect(parseRoute(nonHashLocation)).toEqual({
pageName: 'detections',
path: `${nonHashLocation.search}`,
search: nonHashLocation.search,
});
});
});
describe('public helpers export getHostRiskIndex', () => {
it('should properly return index if space is specified', () => {
expect(getHostRiskIndex('testName')).toEqual('ml_host_risk_score_latest_testName');
});
});
describe('#getSubPluginRoutesByCapabilities', () => {
const mockRender = () => null;
const mockSubPlugins = {
alerts: { routes: [{ path: 'alerts', render: mockRender }] },
cases: { routes: [{ path: 'cases', render: mockRender }] },
} as unknown as StartedSubPlugins;
it('cases routes should return NoPrivilegesPage component when cases plugin is NOT available ', () => {
const routes = getSubPluginRoutesByCapabilities(mockSubPlugins, {
[SERVER_APP_ID]: { show: true, crud: false },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities);
const casesRoute = routes.find((r) => r.path === 'cases');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const CasesView = (casesRoute?.component ?? mockRender) as React.ComponentType<any>;
expect(shallow(<CasesView />)).toMatchInlineSnapshot(`
<NoPrivilegePage
subPluginKey="cases"
/>
`);
});
it('alerts should return NoPrivilegesPage component when siem plugin is NOT available ', () => {
const routes = getSubPluginRoutesByCapabilities(mockSubPlugins, {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
} as unknown as Capabilities);
const alertsRoute = routes.find((r) => r.path === 'alerts');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const AlertsView = (alertsRoute?.component ?? mockRender) as React.ComponentType<any>;
expect(shallow(<AlertsView />)).toMatchInlineSnapshot(`
<NoPrivilegePage
subPluginKey="alerts"
/>
`);
});
it('should return NoPrivilegesPage for each route when both plugins are NOT available ', () => {
const routes = getSubPluginRoutesByCapabilities(mockSubPlugins, {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities);
const casesRoute = routes.find((r) => r.path === 'cases');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const CasesView = (casesRoute?.component ?? mockRender) as React.ComponentType<any>;
const alertsRoute = routes.find((r) => r.path === 'alerts');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const AlertsView = (alertsRoute?.component ?? mockRender) as React.ComponentType<any>;
expect(shallow(<AlertsView />)).toMatchInlineSnapshot(`
<NoPrivilegePage
subPluginKey="alerts"
/>
`);
expect(shallow(<CasesView />)).toMatchInlineSnapshot(`
<NoPrivilegePage
subPluginKey="cases"
/>
`);
});
});
describe('#isSubPluginAvailable', () => {
it('plugin outsides of cases should be available if siem privilege is all and independently of cases privileges', () => {
expect(
isSubPluginAvailable('pluginKey', {
[SERVER_APP_ID]: { show: true, crud: true },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities)
).toBeTruthy();
});
it('plugin outsides of cases should be available if siem privilege is read and independently of cases privileges', () => {
expect(
isSubPluginAvailable('pluginKey', {
[SERVER_APP_ID]: { show: true, crud: false },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities)
).toBeTruthy();
});
it('plugin outsides of cases should NOT be available if siem privilege is none and independently of cases privileges', () => {
expect(
isSubPluginAvailable('pluginKey', {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities)
).toBeFalsy();
});
it('cases plugin should be available if cases privilege is all and independently of siem privileges', () => {
expect(
isSubPluginAvailable('cases', {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
} as unknown as Capabilities)
).toBeTruthy();
});
it('cases plugin should be available if cases privilege is read and independently of siem privileges', () => {
expect(
isSubPluginAvailable('cases', {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
} as unknown as Capabilities)
).toBeTruthy();
});
it('cases plugin should NOT be available if cases privilege is none independently of siem privileges', () => {
expect(
isSubPluginAvailable('pluginKey', {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities)
).toBeFalsy();
});
});
describe('RedirectRoute', () => {
it('RedirectRoute should redirect to overview page when siem and case privileges are all', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: true, crud: true },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/overview"
/>
`);
});
it('RedirectRoute should redirect to overview page when siem and case privileges are read', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: true, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/overview"
/>
`);
});
it('RedirectRoute should redirect to overview page when siem and case privileges are off', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: false, crud_cases: false },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/overview"
/>
`);
});
it('RedirectRoute should redirect to overview page when siem privilege is read and case privilege is all', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: true, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/overview"
/>
`);
});
it('RedirectRoute should redirect to overview page when siem privilege is read and case privilege is read', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: true, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/overview"
/>
`);
});
it('RedirectRoute should redirect to cases page when siem privilege is none and case privilege is read', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: false },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/cases"
/>
`);
});
it('RedirectRoute should redirect to cases page when siem privilege is none and case privilege is all', () => {
const mockCapabilitities = {
[SERVER_APP_ID]: { show: false, crud: false },
[CASES_FEATURE_ID]: { read_cases: true, crud_cases: true },
} as unknown as Capabilities;
expect(shallow(<RedirectRoute capabilities={mockCapabilitities} />)).toMatchInlineSnapshot(`
<Redirect
to="/cases"
/>
`);
});
});

View file

@ -6,24 +6,30 @@
*/
import { isEmpty } from 'lodash/fp';
import { matchPath } from 'react-router-dom';
import React from 'react';
import { matchPath, RouteProps, Redirect } from 'react-router-dom';
import { CoreStart } from '../../../../src/core/public';
import { Capabilities, CoreStart } from '../../../../src/core/public';
import {
ALERTS_PATH,
APP_ID,
APP_UI_ID,
EXCEPTIONS_PATH,
RULES_PATH,
UEBA_PATH,
RISKY_HOSTS_INDEX_PREFIX,
SERVER_APP_ID,
CASES_FEATURE_ID,
OVERVIEW_PATH,
CASES_PATH,
} from '../common/constants';
import {
FactoryQueryTypes,
StrategyResponseType,
} from '../common/search_strategy/security_solution';
import { TimelineEqlResponse } from '../common/search_strategy/timeline';
import { NoPrivilegesPage } from './app/no_privileges';
import { SecurityPageName } from './app/types';
import { InspectResponse } from './types';
import { CASES_SUB_PLUGIN_KEY, InspectResponse, StartedSubPlugins } from './types';
export const parseRoute = (location: Pick<Location, 'hash' | 'pathname' | 'search'>) => {
if (!isEmpty(location.hash)) {
@ -59,49 +65,49 @@ export const manageOldSiemRoutes = async (coreStart: CoreStart) => {
switch (pageName) {
case SecurityPageName.overview:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.overview,
replace: true,
path,
});
break;
case 'ml-hosts':
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
replace: true,
path: `/ml-hosts${path}`,
});
break;
case SecurityPageName.hosts:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
replace: true,
path,
});
break;
case 'ml-network':
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.network,
replace: true,
path: `/ml-network${path}`,
});
break;
case SecurityPageName.network:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.network,
replace: true,
path,
});
break;
case SecurityPageName.timelines:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.timelines,
replace: true,
path,
});
break;
case SecurityPageName.case:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
replace: true,
path,
@ -109,28 +115,28 @@ export const manageOldSiemRoutes = async (coreStart: CoreStart) => {
break;
case SecurityPageName.detections:
case SecurityPageName.alerts:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.alerts,
replace: true,
path,
});
break;
case SecurityPageName.rules:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
replace: true,
path,
});
break;
case SecurityPageName.exceptions:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.exceptions,
replace: true,
path,
});
break;
default:
application.navigateToApp(APP_ID, {
application.navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.overview,
replace: true,
path,
@ -158,3 +164,47 @@ export const isDetectionsPath = (pathname: string): boolean => {
export const getHostRiskIndex = (spaceId: string): string => {
return `${RISKY_HOSTS_INDEX_PREFIX}${spaceId}`;
};
export const getSubPluginRoutesByCapabilities = (
subPlugins: StartedSubPlugins,
capabilities: Capabilities
): RouteProps[] => {
return [
...Object.entries(subPlugins).reduce<RouteProps[]>((acc, [key, value]) => {
if (isSubPluginAvailable(key, capabilities)) {
return [...acc, ...value.routes];
}
return [
...acc,
...value.routes.map((route: RouteProps) => ({
path: route.path,
component: () => <NoPrivilegesPage subPluginKey={key} />,
})),
];
}, []),
{
path: '',
component: () => <RedirectRoute capabilities={capabilities} />,
},
];
};
export const isSubPluginAvailable = (pluginKey: string, capabilities: Capabilities): boolean => {
if (CASES_SUB_PLUGIN_KEY === pluginKey) {
return capabilities[CASES_FEATURE_ID].read_cases === true;
}
return capabilities[SERVER_APP_ID].show === true;
};
export const RedirectRoute = React.memo<{ capabilities: Capabilities }>(({ capabilities }) => {
const overviewAvailable = isSubPluginAvailable('overview', capabilities);
const casesAvailable = isSubPluginAvailable(CASES_SUB_PLUGIN_KEY, capabilities);
if (overviewAvailable) {
return <Redirect to={OVERVIEW_PATH} />;
}
if (casesAvailable) {
return <Redirect to={CASES_PATH} />;
}
return <Redirect to={OVERVIEW_PATH} />;
});
RedirectRoute.displayName = 'RedirectRoute';

View file

@ -15,7 +15,7 @@ import { getHostDetailsUrl } from '../../../common/components/link_to/redirect_t
import * as i18n from '../translations';
import { HostRouteSpyState } from '../../../common/utils/route/types';
import { GetUrlForApp } from '../../../common/components/navigation/types';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
import { SecurityPageName } from '../../../app/types';
export const type = hostsModel.HostsType.details;
@ -37,7 +37,7 @@ export const getBreadcrumbs = (
let breadcrumb = [
{
text: i18n.PAGE_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
path: !isEmpty(search[0]) ? search[0] : '',
deepLinkId: SecurityPageName.hosts,
}),
@ -49,7 +49,7 @@ export const getBreadcrumbs = (
...breadcrumb,
{
text: params.detailName,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
path: getHostDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''),
deepLinkId: SecurityPageName.hosts,
}),

View file

@ -13,7 +13,7 @@ import {
ContextMenuWithRouterSupportProps,
} from './context_menu_with_router_support';
import { act, fireEvent, waitForElementToBeRemoved } from '@testing-library/react';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
describe('When using the ContextMenuWithRouterSupport component', () => {
let appTestContext: AppContextTestRender;
@ -42,7 +42,7 @@ describe('When using the ContextMenuWithRouterSupport component', () => {
},
{
children: 'click me 2',
navigateAppId: APP_ID,
navigateAppId: APP_UI_ID,
navigateOptions: {
path: '/one/two/three',
},
@ -126,7 +126,7 @@ describe('When using the ContextMenuWithRouterSupport component', () => {
});
expect(appTestContext.coreStart.application.navigateToApp).toHaveBeenCalledWith(
APP_ID,
APP_UI_ID,
expect.objectContaining({ path: '/one/two/three' })
);
});

View file

@ -7,7 +7,7 @@
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { APP_ID } from '../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../common/constants';
import { pagePathGetters } from '../../../../../../../fleet/public';
import { getEndpointDetailsPath } from '../../../../common/routing';
import { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/types';
@ -67,7 +67,7 @@ export const useEndpointActionItems = (
'data-test-subj': 'unIsolateLink',
icon: 'logoSecurity',
key: 'unIsolateHost',
navigateAppId: APP_ID,
navigateAppId: APP_UI_ID,
navigateOptions: {
path: endpointUnIsolatePath,
},
@ -85,7 +85,7 @@ export const useEndpointActionItems = (
'data-test-subj': 'isolateLink',
icon: 'logoSecurity',
key: 'isolateHost',
navigateAppId: APP_ID,
navigateAppId: APP_UI_ID,
navigateOptions: {
path: endpointIsolatePath,
},
@ -105,7 +105,7 @@ export const useEndpointActionItems = (
'data-test-subj': 'hostLink',
icon: 'logoSecurity',
key: 'hostDetailsLink',
navigateAppId: APP_ID,
navigateAppId: APP_UI_ID,
navigateOptions: { path: `/hosts/${endpointHostName}` },
href: getAppUrl({ path: `/hosts/${endpointHostName}` }),
children: (

View file

@ -28,7 +28,7 @@ import {
import { PolicyDetailsRouteState } from '../../../../../common/endpoint/types';
import { getEndpointListPath } from '../../../common/routing';
import { useAppUrl } from '../../../../common/lib/kibana';
import { APP_ID } from '../../../../../common/constants';
import { APP_UI_ID } from '../../../../../common/constants';
export const PolicyDetails = React.memo(() => {
// TODO: Remove this and related code when removing FF
@ -68,7 +68,7 @@ export const PolicyDetails = React.memo(() => {
),
backButtonUrl: getAppUrl({ path: endpointListPath }),
onBackButtonNavigateTo: [
APP_ID,
APP_UI_ID,
{
path: endpointListPath,
},

View file

@ -118,7 +118,7 @@ describe('Policy Form Layout', () => {
cancelbutton.simulate('click', { button: 0 });
const navigateToAppMockedCalls = coreStart.application.navigateToApp.mock.calls;
expect(navigateToAppMockedCalls[navigateToAppMockedCalls.length - 1]).toEqual([
'securitySolution',
'securitySolutionUI',
{ path: endpointListPath },
]);
});

View file

@ -35,7 +35,7 @@ import { SpyRoute } from '../../../../../../common/utils/route/spy_routes';
import { SecurityPageName } from '../../../../../../app/types';
import { getEndpointListPath } from '../../../../../common/routing';
import { useNavigateToAppEventHandler } from '../../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { PolicyDetailsRouteState } from '../../../../../../../common/endpoint/types';
import { SecuritySolutionPageWrapper } from '../../../../../../common/components/page_wrapper';
import { PolicyDetailsForm } from '../../policy_details_form';
@ -65,7 +65,7 @@ export const PolicyFormLayout = React.memo(() => {
const routingOnCancelNavigateTo = routeState?.onCancelNavigateTo;
const navigateToAppArguments = useMemo((): Parameters<ApplicationStart['navigateToApp']> => {
return routingOnCancelNavigateTo ?? [APP_ID, { path: hostListRouterPath }];
return routingOnCancelNavigateTo ?? [APP_UI_ID, { path: hostListRouterPath }];
}, [hostListRouterPath, routingOnCancelNavigateTo]);
// Handle showing update statuses

View file

@ -15,7 +15,7 @@ import { ConfigForm } from '../../components/config_form';
import { RadioButtons } from '../components/radio_buttons';
import { UserNotification } from '../components/user_notification';
import { ProtectionSwitch } from '../components/protection_switch';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { LinkToApp } from '../../../../../../common/components/endpoint/link_to_app';
import { SecurityPageName } from '../../../../../../app/types';
@ -51,7 +51,7 @@ export const BehaviorProtection = React.memo(() => {
defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page."
values={{
detectionRulesLink: (
<LinkToApp appId={APP_ID} deepLinkId={SecurityPageName.rules}>
<LinkToApp appId={APP_UI_ID} deepLinkId={SecurityPageName.rules}>
<FormattedMessage
id="xpack.securitySolution.endpoint.policy.details.detectionRulesLink"
defaultMessage="related detection rules"

View file

@ -9,7 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { SecurityPageName } from '../../../../../../app/types';
import { Immutable, OperatingSystem } from '../../../../../../../common/endpoint/types';
import { MalwareProtectionOSes, OS } from '../../../types';
@ -54,7 +54,7 @@ export const MalwareProtections = React.memo(() => {
defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page."
values={{
detectionRulesLink: (
<LinkToApp appId={APP_ID} deepLinkId={SecurityPageName.rules}>
<LinkToApp appId={APP_UI_ID} deepLinkId={SecurityPageName.rules}>
<FormattedMessage
id="xpack.securitySolution.endpoint.policy.details.detectionRulesLink"
defaultMessage="related detection rules"

View file

@ -9,7 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { SecurityPageName } from '../../../../../../app/types';
import { Immutable, OperatingSystem } from '../../../../../../../common/endpoint/types';
import { MemoryProtectionOSes, OS } from '../../../types';
@ -51,7 +51,7 @@ export const MemoryProtection = React.memo(() => {
defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page."
values={{
detectionRulesLink: (
<LinkToApp appId={APP_ID} deepLinkId={SecurityPageName.rules}>
<LinkToApp appId={APP_UI_ID} deepLinkId={SecurityPageName.rules}>
<FormattedMessage
id="xpack.securitySolution.endpoint.policy.details.detectionRulesLink"
defaultMessage="related detection rules"

View file

@ -9,7 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { SecurityPageName } from '../../../../../../app/types';
import { Immutable, OperatingSystem } from '../../../../../../../common/endpoint/types';
import { RansomwareProtectionOSes, OS } from '../../../types';
@ -52,7 +52,7 @@ export const Ransomware = React.memo(() => {
defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page."
values={{
detectionRulesLink: (
<LinkToApp appId={APP_ID} deepLinkId={SecurityPageName.rules}>
<LinkToApp appId={APP_UI_ID} deepLinkId={SecurityPageName.rules}>
<FormattedMessage
id="xpack.securitySolution.endpoint.policy.details.detectionRulesLink"
defaultMessage="related detection rules"

View file

@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { useNavigateToAppEventHandler } from '../../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
import { useAppUrl } from '../../../../../../common/lib/kibana/hooks';
import { getPolicyTrustedAppsPath, getTrustedAppsListPath } from '../../../../../common/routing';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
export const useGetLinkTo = (policyId: string, policyName: string) => {
const { getAppUrl } = useAppUrl();
@ -35,19 +35,19 @@ export const useGetLinkTo = (policyId: string, policyName: string) => {
}
),
onBackButtonNavigateTo: [
APP_ID,
APP_UI_ID,
{
path: policyTrustedAppsPath,
},
],
backButtonUrl: getAppUrl({
appId: APP_ID,
appId: APP_UI_ID,
path: policyTrustedAppsPath,
}),
};
}, [getAppUrl, policyName, policyTrustedAppsPath]);
const onClickHandler = useNavigateToAppEventHandler(APP_ID, {
const onClickHandler = useNavigateToAppEventHandler(APP_UI_ID, {
state: policyTrustedAppRouteState,
path: toRoutePath,
});

View file

@ -20,7 +20,7 @@ import {
isLoadedResourceState,
} from '../../../../../state';
import { fireEvent, within, act, waitFor } from '@testing-library/react';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import {
EndpointPrivileges,
useEndpointPrivileges,
@ -203,7 +203,7 @@ describe('when rendering the PolicyTrustedAppsList', () => {
});
expect(appTestContext.coreStart.application.navigateToApp).toHaveBeenCalledWith(
APP_ID,
APP_UI_ID,
expect.objectContaining({
path: '/administration/trusted_apps?filter=89f72d8a-05b5-4350-8cad-0dc3661d6e67',
})

View file

@ -33,7 +33,7 @@ import {
} from '../../../../../common/routing';
import { Immutable, TrustedApp } from '../../../../../../../common/endpoint/types';
import { useAppUrl, useToasts } from '../../../../../../common/lib/kibana';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { ContextMenuItemNavByRouterProps } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router';
import { ArtifactEntryCollapsibleCardProps } from '../../../../../components/artifact_entry_card';
import { useTestIdGenerator } from '../../../../../components/hooks/use_test_id_generator';
@ -130,7 +130,7 @@ export const PolicyTrustedAppsList = memo<PolicyTrustedAppsListProps>(
const policyDetailsPath = getPolicyDetailPath(trustedAppAssignedPolicyId);
const thisPolicyMenuProps: ContextMenuItemNavByRouterProps = {
navigateAppId: APP_ID,
navigateAppId: APP_UI_ID,
navigateOptions: {
path: policyDetailsPath,
},
@ -150,8 +150,8 @@ export const PolicyTrustedAppsList = memo<PolicyTrustedAppsListProps>(
'xpack.securitySolution.endpoint.policy.trustedApps.list.viewAction',
{ defaultMessage: 'View full details' }
),
href: getAppUrl({ appId: APP_ID, path: viewUrlPath }),
navigateAppId: APP_ID,
href: getAppUrl({ appId: APP_UI_ID, path: viewUrlPath }),
navigateAppId: APP_UI_ID,
navigateOptions: { path: viewUrlPath },
'data-test-subj': getTestId('viewFullDetailsAction'),
},

View file

@ -37,7 +37,7 @@ import {
ArtifactEntryCardProps,
} from '../../../../../components/artifact_entry_card';
import { AppAction } from '../../../../../../common/store/actions';
import { APP_ID } from '../../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../../common/constants';
import { useAppUrl } from '../../../../../../common/lib/kibana';
export interface PaginationBarProps {
@ -115,7 +115,7 @@ export const TrustedAppsGrid = memo(() => {
backLink: {
label: BACK_TO_TRUSTED_APPS_LABEL,
navigateTo: [
APP_ID,
APP_UI_ID,
{
path: currentPagePath,
},
@ -123,7 +123,7 @@ export const TrustedAppsGrid = memo(() => {
href: getAppUrl({ path: currentPagePath }),
},
onCancelNavigateTo: [
APP_ID,
APP_UI_ID,
{
path: currentPagePath,
},
@ -131,6 +131,7 @@ export const TrustedAppsGrid = memo(() => {
};
policyToNavOptionsMap[policyId] = {
navigateAppId: APP_UI_ID,
navigateOptions: {
path: policyDetailsPath,
state: routeState,

View file

@ -15,7 +15,7 @@ import * as i18n from '../translations';
import { NetworkRouteType } from '../navigation/types';
import { NetworkRouteSpyState } from '../../../common/utils/route/types';
import { GetUrlForApp } from '../../../common/components/navigation/types';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
import { SecurityPageName } from '../../../app/types';
export const type = networkModel.NetworkType.details;
@ -36,7 +36,7 @@ export const getBreadcrumbs = (
let breadcrumb = [
{
text: i18n.PAGE_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.network,
path: !isEmpty(search[0]) ? search[0] : '',
}),
@ -47,7 +47,7 @@ export const getBreadcrumbs = (
...breadcrumb,
{
text: decodeIpv6(params.detailName),
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.network,
path: getNetworkDetailsUrl(
params.detailName,

View file

@ -9,7 +9,7 @@ import numeral from '@elastic/numeral';
import React, { useEffect, useMemo, useCallback } from 'react';
import { Position } from '@elastic/charts';
import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants';
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants';
import { SHOWING, UNIT } from '../../../common/components/alerts_viewer/translations';
import { MatrixHistogram } from '../../../common/components/matrix_histogram';
import { useKibana, useUiSetting$ } from '../../../common/lib/kibana';
@ -68,7 +68,7 @@ const AlertsByCategoryComponent: React.FC<Props> = ({
const goToHostAlerts = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
path: getTabsOnHostsUrl(HostsTableType.alerts, urlSearch),
});

View file

@ -9,15 +9,15 @@ import React, { memo } from 'react';
import { EuiCallOut, EuiButton, EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { useKibana } from '../../../common/lib/kibana';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
import { getEndpointListPath } from '../../../management/common/routing';
import { useNavigateToAppEventHandler } from '../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
export const EndpointNotice = memo<{ onDismiss: () => void }>(({ onDismiss }) => {
const { getUrlForApp } = useKibana().services.application;
const endpointsPath = getEndpointListPath({ name: 'endpointList' });
const endpointsLink = getUrlForApp(APP_ID, { path: endpointsPath });
const handleGetStartedClick = useNavigateToAppEventHandler(APP_ID, {
const endpointsLink = getUrlForApp(APP_UI_ID, { path: endpointsPath });
const handleGetStartedClick = useNavigateToAppEventHandler(APP_UI_ID, {
path: endpointsPath,
});

View file

@ -10,7 +10,7 @@ import numeral from '@elastic/numeral';
import React, { useEffect, useMemo, useCallback } from 'react';
import uuid from 'uuid';
import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants';
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants';
import { SHOWING, UNIT } from '../../../common/components/events_viewer/translations';
import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts';
import { MatrixHistogram } from '../../../common/components/matrix_histogram';
@ -101,7 +101,7 @@ const EventsByDatasetComponent: React.FC<Props> = ({
const goToHostEvents = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
path: getTabsOnHostsUrl(HostsTableType.events, urlSearch),
});

View file

@ -11,7 +11,7 @@ import numeral from '@elastic/numeral';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useMemo, useCallback } from 'react';
import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants';
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants';
import { ESQuery } from '../../../../common/typed_json';
import { ID as OverviewHostQueryId, useHostOverview } from '../../containers/overview_host';
import { HeaderSection } from '../../../common/components/header_section';
@ -57,7 +57,7 @@ const OverviewHostComponent: React.FC<OverviewHostProps> = ({
const goToHost = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
path: getHostDetailsUrl('allHosts', urlSearch),
});

View file

@ -138,7 +138,7 @@ describe('OverviewNetwork', () => {
preventDefault: jest.fn(),
});
expect(mockNavigateToApp).toBeCalledWith('securitySolution', {
expect(mockNavigateToApp).toBeCalledWith('securitySolutionUI', {
path: '',
deepLinkId: SecurityPageName.network,
});

View file

@ -11,7 +11,7 @@ import numeral from '@elastic/numeral';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useMemo, useCallback } from 'react';
import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants';
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants';
import { ESQuery } from '../../../../common/typed_json';
import { HeaderSection } from '../../../common/components/header_section';
import { useUiSetting$, useKibana } from '../../../common/lib/kibana';
@ -59,7 +59,7 @@ const OverviewNetworkComponent: React.FC<OverviewNetworkProps> = ({
const goToNetwork = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.network,
path: getNetworkUrl(urlSearch),
});

View file

@ -7,7 +7,7 @@
import React, { useCallback } from 'react';
import { EuiButtonEmpty, EuiText } from '@elastic/eui';
import { APP_ID, SecurityPageName } from '../../../../common/constants';
import { APP_UI_ID, SecurityPageName } from '../../../../common/constants';
import { useKibana } from '../../../common/lib/kibana';
export const NavigateToHost: React.FC<{ name: string }> = ({ name }): JSX.Element => {
@ -27,7 +27,7 @@ export const NavigateToHost: React.FC<{ name: string }> = ({ name }): JSX.Elemen
query: { match_phrase: { 'host.name': name } },
},
]);
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.hosts,
});
},

View file

@ -14,7 +14,7 @@ import {
} from '../../../common/components/link_to/redirect_to_case';
import { useFormatUrl } from '../../../common/components/link_to';
import { useGetUserCasesPermissions, useKibana } from '../../../common/lib/kibana';
import { APP_ID } from '../../../../common/constants';
import { APP_ID, APP_UI_ID } from '../../../../common/constants';
import { SecurityPageName } from '../../../app/types';
import { AllCasesNavProps } from '../../../cases/components/all_cases';
@ -33,7 +33,7 @@ const RecentCasesComponent = () => {
href: formatUrl(getCaseUrl()),
onClick: async (e) => {
e?.preventDefault();
return navigateToApp(APP_ID, { deepLinkId: SecurityPageName.case });
return navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.case });
},
},
caseDetailsNavigation: {
@ -42,7 +42,7 @@ const RecentCasesComponent = () => {
},
onClick: async ({ detailName, subCaseId, search }, e) => {
e?.preventDefault();
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCaseDetailsUrl({ id: detailName, search, subCaseId }),
});
@ -52,7 +52,7 @@ const RecentCasesComponent = () => {
href: formatUrl(getCreateCaseUrl()),
onClick: async (e) => {
e?.preventDefault();
return navigateToApp(APP_ID, {
return navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCreateCaseUrl(),
});

View file

@ -25,7 +25,7 @@ import { LoadingPlaceholders } from '../loading_placeholders';
import { useTimelineStatus } from '../../../timelines/components/open_timeline/use_timeline_status';
import { useKibana } from '../../../common/lib/kibana';
import { SecurityPageName } from '../../../app/types';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
import { useFormatUrl } from '../../../common/components/link_to';
import { LinkAnchor } from '../../../common/components/links';
import { Direction } from '../../../../common/search_strategy';
@ -61,7 +61,7 @@ const StatefulRecentTimelinesComponent: React.FC<Props> = ({ filterBy }) => {
const goToTimelines = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.timelines,
});
},

View file

@ -37,16 +37,16 @@ import { SOLUTION_NAME } from './common/translations';
import {
APP_ID,
OVERVIEW_PATH,
APP_OVERVIEW_PATH,
APP_UI_ID,
APP_PATH,
DEFAULT_INDEX_KEY,
APP_ICON_SOLUTION,
DETECTION_ENGINE_INDEX_URL,
SERVER_APP_ID,
} from '../common/constants';
import { getDeepLinks, updateGlobalNavigation } from './app/deep_links';
import { manageOldSiemRoutes } from './helpers';
import { getDeepLinks } from './app/deep_links';
import { getSubPluginRoutesByCapabilities, manageOldSiemRoutes } from './helpers';
import {
IndexFieldsStrategyRequest,
IndexFieldsStrategyResponse,
@ -98,7 +98,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
{
usageCollection: plugins.usageCollection,
},
APP_ID
APP_UI_ID
);
if (plugins.home) {
@ -110,7 +110,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
'Prevent, collect, detect, and respond to threats for unified protection across your infrastructure.',
}),
icon: 'logoSecurity',
path: APP_OVERVIEW_PATH,
path: APP_PATH,
order: 300,
});
}
@ -133,13 +133,12 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
})();
core.application.register({
id: APP_ID,
id: APP_UI_ID,
title: SOLUTION_NAME,
appRoute: APP_PATH,
category: DEFAULT_APP_CATEGORIES.security,
navLinkStatus: AppNavLinkStatus.hidden,
searchable: true,
defaultPath: OVERVIEW_PATH,
updater$: this.appUpdater$,
euiIconType: APP_ICON_SOLUTION,
deepLinks: getDeepLinks(this.experimentalFeatures),
@ -152,7 +151,10 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
services: await startServices,
store: await this.store(coreStart, startPlugins, subPlugins),
usageCollection: plugins.usageCollection,
subPlugins,
subPluginRoutes: getSubPluginRoutesByCapabilities(
subPlugins,
coreStart.application.capabilities
),
});
},
});
@ -232,11 +234,14 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
}
});
} else {
updateGlobalNavigation({
capabilities: core.application.capabilities,
updater$: this.appUpdater$,
enableExperimental: this.experimentalFeatures,
});
this.appUpdater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // workaround to prevent main navLink to switch to visible after update. should not be needed
deepLinks: getDeepLinks(
this.experimentalFeatures,
undefined,
core.application.capabilities
),
}));
}
return {};
@ -313,9 +318,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
return {
overview: subPlugins.overview.start(),
alerts: subPlugins.alerts.start(storage),
cases: subPlugins.cases.start(),
rules: subPlugins.rules.start(storage),
exceptions: subPlugins.exceptions.start(storage),
cases: subPlugins.cases.start(),
hosts: subPlugins.hosts.start(storage),
network: subPlugins.network.start(storage),
ueba: subPlugins.ueba.start(storage),
@ -358,9 +363,11 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
// }
// );
// signal = { name: indexName[0] };
signal = await coreStart.http.fetch(DETECTION_ENGINE_INDEX_URL, {
method: 'GET',
});
if (coreStart.application.capabilities[SERVER_APP_ID].read === true) {
signal = await coreStart.http.fetch(DETECTION_ENGINE_INDEX_URL, {
method: 'GET',
});
}
} catch {
signal = { name: null };
}

View file

@ -62,7 +62,7 @@ describe('AddToCaseButton', () => {
wrapper.find(`[data-test-subj="attach-timeline-case-button"]`).first().simulate('click');
wrapper.find(`[data-test-subj="attach-timeline-existing-case"]`).first().simulate('click');
expect(navigateToApp).toHaveBeenCalledWith('securitySolution', {
expect(navigateToApp).toHaveBeenCalledWith('securitySolutionUI', {
path: '/create',
deepLinkId: SecurityPageName.case,
});
@ -84,7 +84,7 @@ describe('AddToCaseButton', () => {
wrapper.find(`[data-test-subj="attach-timeline-case-button"]`).first().simulate('click');
wrapper.find(`[data-test-subj="attach-timeline-existing-case"]`).first().simulate('click');
expect(navigateToApp).toHaveBeenCalledWith('securitySolution', {
expect(navigateToApp).toHaveBeenCalledWith('securitySolutionUI', {
path: '/case-id',
deepLinkId: SecurityPageName.case,
});

View file

@ -11,7 +11,7 @@ import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { Case, SubCase } from '../../../../../../cases/common';
import { APP_ID } from '../../../../../common/constants';
import { APP_ID, APP_UI_ID } from '../../../../../common/constants';
import { timelineSelectors } from '../../../../timelines/store/timeline';
import { setInsertTimeline, showTimeline } from '../../../store/timeline/actions';
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
@ -55,7 +55,7 @@ const AddToCaseButtonComponent: React.FC<Props> = ({ timelineId }) => {
const onRowClick = useCallback(
async (theCase?: Case | SubCase) => {
openCaseModal(false);
await navigateToApp(APP_ID, {
await navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: theCase != null ? getCaseDetailsUrl({ id: theCase.id }) : getCreateCaseUrl(),
});
@ -90,7 +90,7 @@ const AddToCaseButtonComponent: React.FC<Props> = ({ timelineId }) => {
const handleNewCaseClick = useCallback(() => {
handlePopoverClose();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.case,
path: getCreateCaseUrl(),
}).then(() => {

View file

@ -33,21 +33,6 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re
opacity: 1;
}
.c4 {
padding: 16px;
background: rgba(250,251,253,0.9);
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 1000;
}
.c5 {
height: 100%;
}
.c2 dt {
font-size: 12px !important;
}
@ -75,6 +60,21 @@ exports[`Expandable Host Component ExpandableHostDetails: rendering it should re
z-index: 2;
}
.c4 {
padding: 16px;
background: rgba(250,251,253,0.9);
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 1000;
}
.c5 {
height: 100%;
}
<ExpandableHostDetails
contextID="text-context"
hostName="testHostName"

View file

@ -30,7 +30,7 @@ import * as i18n from './translations';
import { SecurityPageName } from '../../../../../app/types';
import { useFormatUrl } from '../../../../../common/components/link_to';
import { useKibana } from '../../../../../common/lib/kibana';
import { APP_ID } from '../../../../../../common/constants';
import { APP_UI_ID } from '../../../../../../common/constants';
import { LinkAnchor } from '../../../../../common/components/links';
const EventModuleFlexItem = styled(EuiFlexItem)`
@ -73,7 +73,7 @@ export const RenderRuleName: React.FC<RenderRuleNameProps> = ({
const goToRuleDetails = useCallback(
(ev) => {
ev.preventDefault();
navigateToApp(APP_ID, {
navigateToApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId ?? '', search),
});
@ -83,7 +83,7 @@ export const RenderRuleName: React.FC<RenderRuleNameProps> = ({
const href = useMemo(
() =>
getUrlForApp(APP_ID, {
getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.rules,
path: getRuleDetailsUrl(ruleId ?? '', search),
}),

View file

@ -18,7 +18,7 @@ import { TimelinesPage } from './timelines_page';
import { PAGE_TITLE } from './translations';
import { appendSearch } from '../../common/components/link_to/helpers';
import { GetUrlForApp } from '../../common/components/navigation/types';
import { APP_ID, TIMELINES_PATH } from '../../../common/constants';
import { APP_UI_ID, TIMELINES_PATH } from '../../../common/constants';
import { SecurityPageName } from '../../app/types';
const timelinesPagePath = `${TIMELINES_PATH}/:tabName(${TimelineType.default}|${TimelineType.template})`;
@ -31,7 +31,7 @@ export const getBreadcrumbs = (
): ChromeBreadcrumb[] => [
{
text: PAGE_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
deepLinkId: SecurityPageName.timelines,
path: !isEmpty(search[0]) ? search[0] : '',
}),

View file

@ -87,11 +87,12 @@ export interface AppObservableLibs {
export type InspectResponse = Inspect & { response: string[] };
export const CASES_SUB_PLUGIN_KEY = 'cases';
export interface SubPlugins {
alerts: Detections;
rules: Rules;
exceptions: Exceptions;
cases: Cases;
[CASES_SUB_PLUGIN_KEY]: Cases;
hosts: Hosts;
network: Network;
ueba: Ueba;
@ -105,7 +106,7 @@ export interface StartedSubPlugins {
alerts: ReturnType<Detections['start']>;
rules: ReturnType<Rules['start']>;
exceptions: ReturnType<Exceptions['start']>;
cases: ReturnType<Cases['start']>;
[CASES_SUB_PLUGIN_KEY]: ReturnType<Cases['start']>;
hosts: ReturnType<Hosts['start']>;
network: ReturnType<Network['start']>;
ueba: ReturnType<Ueba['start']>;

View file

@ -15,7 +15,7 @@ import { getUebaDetailsUrl } from '../../../common/components/link_to/redirect_t
import * as i18n from '../translations';
import { UebaRouteSpyState } from '../../../common/utils/route/types';
import { GetUrlForApp } from '../../../common/components/navigation/types';
import { APP_ID } from '../../../../common/constants';
import { APP_UI_ID } from '../../../../common/constants';
import { SecurityPageName } from '../../../app/types';
export const type = uebaModel.UebaType.details;
@ -35,7 +35,7 @@ export const getBreadcrumbs = (
let breadcrumb = [
{
text: i18n.PAGE_TITLE,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
path: !isEmpty(search[0]) ? search[0] : '',
deepLinkId: SecurityPageName.ueba,
}),
@ -47,7 +47,7 @@ export const getBreadcrumbs = (
...breadcrumb,
{
text: params.detailName,
href: getUrlForApp(APP_ID, {
href: getUrlForApp(APP_UI_ID, {
path: getUebaDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''),
deepLinkId: SecurityPageName.ueba,
}),