diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index d1491ba63e6e6..453e5c174452d 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -94,7 +94,7 @@ pageLoadAssetSize: expressionShape: 34008 interactiveSetup: 80000 expressionTagcloud: 27505 - securitySolution: 231753 + securitySolution: 273763 customIntegrations: 28810 expressionMetricVis: 23121 visTypeMetric: 23332 diff --git a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts index 5f268a6fdfee7..7c112083875d1 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/application_usage/schema.ts @@ -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, diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index f9ca99a26ec19..437d50ad82473 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -5018,7 +5018,7 @@ } } }, - "securitySolution": { + "securitySolutionUI": { "properties": { "appId": { "type": "keyword", diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 515f2beb53980..618497d8ea11b 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -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'; diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts index a3dc6565b19c6..479ff4753dd75 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.test.ts @@ -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, diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index aaa8ce789591f..8daec76f280b2 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -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; - enableExperimental: ExperimentalFeatures; -}) { - updater$.next(() => ({ - navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent showing main nav link - deepLinks: getDeepLinks(enableExperimental, undefined, capabilities), - })); -} diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 1803ab2b67455..8588539c47a60 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -78,8 +78,12 @@ export const SecuritySolutionTemplateWrapper: React.FC void) => { const ApplicationUsageTrackingProvider = usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; @@ -36,25 +35,9 @@ export const renderApp = ({ > - {[ - ...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) => ( - - ))} - - - - + {subPluginRoutes.map((route, index) => { + return ; + })} diff --git a/x-pack/plugins/security_solution/public/app/no_privileges.tsx b/x-pack/plugins/security_solution/public/app/no_privileges.tsx new file mode 100644 index 0000000000000..354e6eaf27198 --- /dev/null +++ b/x-pack/plugins/security_solution/public/app/no_privileges.tsx @@ -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(({ 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 ( + + + + + + ); +}); + +NoPrivilegesPage.displayName = 'NoPrivilegePage'; diff --git a/x-pack/plugins/security_solution/public/app/translations.ts b/x-pack/plugins/security_solution/public/app/translations.ts index e383725a7e40c..7287739566e68 100644 --- a/x-pack/plugins/security_solution/public/app/translations.ts +++ b/x-pack/plugins/security_solution/public/app/translations.ts @@ -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', +}); diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 1942d2f836b1c..52d69d7c4e7d9 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -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; - subPlugins: StartedSubPlugins; + subPluginRoutes: RouteProps[]; usageCollection?: UsageCollectionSetup; } diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 3c788e0553079..81eeae9866b7f 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -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(({ 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(({ 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(({ 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 }), }); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index 47ba9e1e9cb8f..bdf4a1dbc1579 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -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 ?? ''), }); diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx index 42579c6fbc0ac..2ce5f2904cb3b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx @@ -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, }) diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index 72a41acf1d456..6e6a536cd8437 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -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), }), diff --git a/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx b/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx index ea8205cddad59..6f35209ee1c9f 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx @@ -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), }); diff --git a/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx b/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx index c5ed3454f1ca5..b5feb3dc698b7 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx @@ -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), }); diff --git a/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx b/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx index 933e890ea2d9f..2d7d83cb1b50c 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx @@ -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), }); diff --git a/x-pack/plugins/security_solution/public/cases/pages/utils.ts b/x-pack/plugins/security_solution/public/cases/pages/utils.ts index 968712009e110..f791bc677d5c6 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/pages/utils.ts @@ -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 }), }), diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.tsx index 201738d3293b2..dba8dce6d2df7 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.tsx @@ -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( ({ - appId = APP_ID, + appId = APP_UI_ID, deepLinkId, appPath: path, appState: state, diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx index aab0e86681783..88672e5e2f5dc 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/reason.tsx @@ -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 = ({ 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), }); diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index c74791b8b3aa7..8b9188af7725c 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -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), }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 08f2ef2267f97..de934e00c9117 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -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), }), { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts index 7262264d72103..029dc70c398e3 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts @@ -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), diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index 18dd07a99824e..b123a26257683 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -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}` ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx index 396f431a3232d..3db485f87a68f 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/index.test.tsx @@ -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", diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx index a1be69dd077ad..961090a01ba19 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_navigation_items.tsx @@ -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) { 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] + ); } diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx index bc0640296b33d..05ccadeaf67ac 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx @@ -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) => { diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts index 61ce5a8238b52..5975977988a50 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts @@ -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) ), }); diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts index fdaed64ba91d7..411dd5542038b 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/hooks.ts @@ -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; diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index ed2a2252bd0d2..56f5dc28652aa 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -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 ?? ''}` ); diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 975487bb2b384..7ea93bb7ce8fb 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -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 = ({ ({ eui: euiDarkVars, darkMode: true })}> {children} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx index 484cd66575005..54964de684ed7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx @@ -122,7 +122,7 @@ describe('AlertsHistogramPanel', () => { preventDefault: jest.fn(), }); - expect(mockNavigateToApp).toBeCalledWith('securitySolution', { + expect(mockNavigateToApp).toBeCalledWith('securitySolutionUI', { deepLinkId: SecurityPageName.alerts, path: '', }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx index 07fa81f27684c..147d41e8533df 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx @@ -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( const goToDetectionEngine = useCallback( (ev) => { ev.preventDefault(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.alerts, path: getDetectionEngineUrl(urlSearch), }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx index a342b01b038ca..e14c3e916a35e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_to_case_actions.tsx @@ -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, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx index 9f2728e0813f4..eac1c2800955f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx @@ -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(), }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 2c7ca301002e1..72730deec6a19 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -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 = ({ } = useKibana(); const kibanaAbsoluteUrl = useMemo( () => - application.getUrlForApp(`${APP_ID}`, { + application.getUrlForApp(`${APP_UI_ID}`, { absolute: true, }), [application] diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 1a8588017e4d6..0447130e1bd14 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -69,9 +69,7 @@ describe('useUserInfo', () => { const wrapper = ({ children }: { children: JSX.Element }) => ( {children} diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts index 259245d25c401..81fc83f3fdb1a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_detection_engine_privileges.ts @@ -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 () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts index 2d0e6ee0248e7..e1d6a90da1b0a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts +++ b/x-pack/plugins/security_solution/public/detections/components/user_privileges/use_fetch_list_privileges.ts @@ -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 () => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx index 214a7ac24da8a..06e026ba8f6e1 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/actions.tsx @@ -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 ) => { - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.rules, path: getEditRuleUrl(ruleId ?? ''), }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index 6b05ee6403db3..6ca987a8d005c 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -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), }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx index d37acaeb0ffee..db41df644b3dc 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx @@ -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; @@ -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(), }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 774b9463bed69..ca71ac6783f59 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -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 = ({ 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 = ({ needsListsConfiguration ) ) { - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.alerts, path: getDetectionEngineUrl(), }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx index cea97aed28cc1..784290ad80d47 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx @@ -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; @@ -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 ?? ''), }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx index f957f77ac4c1a..15ffd3579614a 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx @@ -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; @@ -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(), }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts index 92c828b6cbf79..d4ce5da4a1413 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts @@ -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] : ''), }), diff --git a/x-pack/plugins/security_solution/public/helpers.test.ts b/x-pack/plugins/security_solution/public/helpers.test.ts deleted file mode 100644 index f2e37cd995a4f..0000000000000 --- a/x-pack/plugins/security_solution/public/helpers.test.ts +++ /dev/null @@ -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'); - }); -}); diff --git a/x-pack/plugins/security_solution/public/helpers.test.tsx b/x-pack/plugins/security_solution/public/helpers.test.tsx new file mode 100644 index 0000000000000..3475ac7c28f7a --- /dev/null +++ b/x-pack/plugins/security_solution/public/helpers.test.tsx @@ -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; + expect(shallow()).toMatchInlineSnapshot(` + + `); + }); + + 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; + expect(shallow()).toMatchInlineSnapshot(` + + `); + }); + + 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; + + 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; + + expect(shallow()).toMatchInlineSnapshot(` + + `); + expect(shallow()).toMatchInlineSnapshot(` + + `); + }); +}); + +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()).toMatchInlineSnapshot(` + + `); + }); + + 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()).toMatchInlineSnapshot(` + + `); + }); + + 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()).toMatchInlineSnapshot(` + + `); + }); + + 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()).toMatchInlineSnapshot(` + + `); + }); + + 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()).toMatchInlineSnapshot(` + + `); + }); + + 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()).toMatchInlineSnapshot(` + + `); + }); + + 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()).toMatchInlineSnapshot(` + + `); + }); +}); diff --git a/x-pack/plugins/security_solution/public/helpers.ts b/x-pack/plugins/security_solution/public/helpers.tsx similarity index 63% rename from x-pack/plugins/security_solution/public/helpers.ts rename to x-pack/plugins/security_solution/public/helpers.tsx index aba46cffee193..066e6a4cb4684 100644 --- a/x-pack/plugins/security_solution/public/helpers.ts +++ b/x-pack/plugins/security_solution/public/helpers.tsx @@ -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) => { 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((acc, [key, value]) => { + if (isSubPluginAvailable(key, capabilities)) { + return [...acc, ...value.routes]; + } + return [ + ...acc, + ...value.routes.map((route: RouteProps) => ({ + path: route.path, + component: () => , + })), + ]; + }, []), + { + path: '', + component: () => , + }, + ]; +}; + +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 ; + } + if (casesAvailable) { + return ; + } + return ; +}); +RedirectRoute.displayName = 'RedirectRoute'; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts b/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts index f4e14605cab47..3a584f7fefb50 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts @@ -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, }), diff --git a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.test.tsx b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.test.tsx index ae343a57c734f..288c0c074e93d 100644 --- a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.test.tsx @@ -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' }) ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 81432edbdd5fe..a766b9a23082a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -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: ( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 65308012df080..6b5d6f03aca28 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -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, }, diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx index 650bf6115c9d9..28f2ecf7eb98e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.test.tsx @@ -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 }, ]); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx index bae2c21242d97..2345deabb5101 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx @@ -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 => { - return routingOnCancelNavigateTo ?? [APP_ID, { path: hostListRouterPath }]; + return routingOnCancelNavigateTo ?? [APP_UI_ID, { path: hostListRouterPath }]; }, [hostListRouterPath, routingOnCancelNavigateTo]); // Handle showing update statuses diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx index 06cf666f2950e..ccb19da4a4ada 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/behavior.tsx @@ -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: ( - + { defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page." values={{ detectionRulesLink: ( - + { defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page." values={{ detectionRulesLink: ( - + { defaultMessage="View {detectionRulesLink}. Prebuilt rules are tagged “Elastic” on the Detection Rules page." values={{ detectionRulesLink: ( - + { 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, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx index 9165aec3bef8d..b136eef9566eb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx @@ -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', }) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx index 89ff6bd099be4..48f66806b46b4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx @@ -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( 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( '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'), }, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx index dd9a15eb3266a..72b52b0f35278 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx @@ -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, diff --git a/x-pack/plugins/security_solution/public/network/pages/details/utils.ts b/x-pack/plugins/security_solution/public/network/pages/details/utils.ts index 637180203c6d1..7bcf79ba4b434 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/network/pages/details/utils.ts @@ -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, diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 11270fe377733..e74e8f82d8244 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -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 = ({ const goToHostAlerts = useCallback( (ev) => { ev.preventDefault(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.hosts, path: getTabsOnHostsUrl(HostsTableType.alerts, urlSearch), }); diff --git a/x-pack/plugins/security_solution/public/overview/components/endpoint_notice/index.tsx b/x-pack/plugins/security_solution/public/overview/components/endpoint_notice/index.tsx index 715663b60c7dc..39193742021f1 100644 --- a/x-pack/plugins/security_solution/public/overview/components/endpoint_notice/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/endpoint_notice/index.tsx @@ -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, }); diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index 7de1d9d9f40c1..562d1d1fd7aad 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -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 = ({ const goToHostEvents = useCallback( (ev) => { ev.preventDefault(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.hosts, path: getTabsOnHostsUrl(HostsTableType.events, urlSearch), }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx index a0307380ce802..723c768190cb5 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx @@ -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 = ({ const goToHost = useCallback( (ev) => { ev.preventDefault(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.hosts, path: getHostDetailsUrl('allHosts', urlSearch), }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx index 08b2392f60488..dfc144be8e5bb 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx @@ -138,7 +138,7 @@ describe('OverviewNetwork', () => { preventDefault: jest.fn(), }); - expect(mockNavigateToApp).toBeCalledWith('securitySolution', { + expect(mockNavigateToApp).toBeCalledWith('securitySolutionUI', { path: '', deepLinkId: SecurityPageName.network, }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx index 214cd7b3f055b..dd779f1656e92 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx @@ -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 = ({ const goToNetwork = useCallback( (ev) => { ev.preventDefault(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.network, path: getNetworkUrl(urlSearch), }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.tsx index 4680aedc0ba60..2c8e281b376d6 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_risky_host_links/navigate_to_host.tsx @@ -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, }); }, diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx index 207c6ef16bd16..e2d7d8264e9f9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx @@ -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(), }); diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx index dcd8783688ca7..ed59918ad4499 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx @@ -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 = ({ filterBy }) => { const goToTimelines = useCallback( (ev) => { ev.preventDefault(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.timelines, }); }, diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index f016c9712c650..6167aa72a47b4 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -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 ({ + 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 { 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, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx index 553b827f2a64c..ff8746f4729d7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx @@ -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 = ({ 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 = ({ timelineId }) => { const handleNewCaseClick = useCallback(() => { handlePopoverClose(); - navigateToApp(APP_ID, { + navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.case, path: getCreateCaseUrl(), }).then(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap index 01ef89cd35c9f..ad149cbcd63d0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/__snapshots__/expandable_host.test.tsx.snap @@ -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%; +} + = ({ 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 = ({ const href = useMemo( () => - getUrlForApp(APP_ID, { + getUrlForApp(APP_UI_ID, { deepLinkId: SecurityPageName.rules, path: getRuleDetailsUrl(ruleId ?? '', search), }), diff --git a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx index 2bf6e1259ff75..58049ef164e15 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx @@ -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] : '', }), diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 61813d1a122b4..376cfbf31afe2 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -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; rules: ReturnType; exceptions: ReturnType; - cases: ReturnType; + [CASES_SUB_PLUGIN_KEY]: ReturnType; hosts: ReturnType; network: ReturnType; ueba: ReturnType; diff --git a/x-pack/plugins/security_solution/public/ueba/pages/details/utils.ts b/x-pack/plugins/security_solution/public/ueba/pages/details/utils.ts index d5f346d3ece64..8fcc0fce0b7a3 100644 --- a/x-pack/plugins/security_solution/public/ueba/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/ueba/pages/details/utils.ts @@ -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, }),