Skip to content

Commit

Permalink
[8.x] [Security Solution][Notes] - move notes management page under m…
Browse files Browse the repository at this point in the history
…anage section instead of timeline (elastic#194250) (elastic#194628)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution][Notes] - move notes management page under manage
section instead of timeline
(elastic#194250)](elastic#194250)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Philippe
Oberti","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-01T18:30:47Z","message":"[Security
Solution][Notes] - move notes management page under manage section
instead of timeline
(elastic#194250)","sha":"39ac875b764019e2c570b175f3cc57e0c02b20ec","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["backport","v9.0.0","Team:Threat
Hunting:Investigations","v8.16.0"],"title":"[Security Solution][Notes] -
move notes management page under manage section instead of
timeline","number":194250,"url":"https://github.com/elastic/kibana/pull/194250","mergeCommit":{"message":"[Security
Solution][Notes] - move notes management page under manage section
instead of timeline
(elastic#194250)","sha":"39ac875b764019e2c570b175f3cc57e0c02b20ec"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194250","number":194250,"mergeCommit":{"message":"[Security
Solution][Notes] - move notes management page under manage section
instead of timeline
(elastic#194250)","sha":"39ac875b764019e2c570b175f3cc57e0c02b20ec"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Philippe Oberti <[email protected]>
  • Loading branch information
kibanamachine and PhilippeOberti authored Oct 1, 2024
1 parent c601fdf commit cef7801
Show file tree
Hide file tree
Showing 29 changed files with 372 additions and 211 deletions.
2 changes: 1 addition & 1 deletion packages/deeplinks/security/deep_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,5 @@ export enum SecurityPageName {
entityAnalyticsManagement = 'entity_analytics-management',
entityAnalyticsAssetClassification = 'entity_analytics-asset-classification',
coverageOverview = 'coverage-overview',
notesManagement = 'notes-management',
notes = 'notes',
}
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const APP_HOST_ISOLATION_EXCEPTIONS_PATH =
export const APP_BLOCKLIST_PATH = `${APP_PATH}${BLOCKLIST_PATH}` as const;
export const APP_RESPONSE_ACTIONS_HISTORY_PATH =
`${APP_PATH}${RESPONSE_ACTIONS_HISTORY_PATH}` as const;
export const NOTES_MANAGEMENT_PATH = `/notes_management` as const;
export const NOTES_PATH = `${MANAGEMENT_PATH}/notes` as const;

// cloud logs to exclude from default index pattern
export const EXCLUDE_ELASTIC_CLOUD_INDICES = ['-*elastic-cloud-logs-*'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
*/

import { SecurityPageName } from '@kbn/security-solution-navigation';
import { cloneDeep, remove } from 'lodash';
import { cloneDeep, remove, find } from 'lodash';
import type { AppLinkItems, LinkItem } from '../../../common/links/types';
import { createInvestigationsLinkFromTimeline } from './sections/investigations_links';
import {
createInvestigationsLinkFromNotes,
createInvestigationsLinkFromTimeline,
updateInvestigationsLinkFromNotes,
} from './sections/investigations_links';
import { mlAppLink } from './sections/ml_links';
import { createAssetsLinkFromManage } from './sections/assets_links';
import { createSettingsLinksFromManage } from './sections/settings_links';
Expand All @@ -26,6 +30,19 @@ export const solutionAppLinksSwitcher = (appLinks: AppLinkItems): AppLinkItems =
solutionAppLinks.push(createInvestigationsLinkFromTimeline(timelineLinkItem));
}

// Remove note link
const investigationsLinkItem = find(solutionAppLinks, { id: SecurityPageName.investigations });
const [noteLinkItem] = remove(solutionAppLinks, { id: SecurityPageName.notes });
if (noteLinkItem) {
if (!investigationsLinkItem) {
solutionAppLinks.push(createInvestigationsLinkFromNotes(noteLinkItem));
} else {
solutionAppLinks.push(
updateInvestigationsLinkFromNotes(investigationsLinkItem, noteLinkItem)
);
}
}

// Remove manage link
const [manageLinkItem] = remove(solutionAppLinks, { id: SecurityPageName.administration });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { ExternalPageName, SecurityPageName } from '@kbn/security-solution-navigation';
import IconFilebeatChart from './icons/filebeat_chart';
import { INVESTIGATIONS_PATH } from '../../../../../common/constants';
import { SERVER_APP_ID } from '../../../../../common';
import type { LinkItem } from '../../../../common/links/types';
Expand All @@ -21,7 +22,7 @@ const investigationsAppLink: LinkItem = {
capabilities: [`${SERVER_APP_ID}.show`],
hideTimeline: true,
skipUrlState: true,
links: [], // timeline link are added in createInvestigationsLinkFromTimeline
links: [], // timeline and note links are added via the methods below
};

export const createInvestigationsLinkFromTimeline = (timelineLink: LinkItem): LinkItem => {
Expand All @@ -33,6 +34,29 @@ export const createInvestigationsLinkFromTimeline = (timelineLink: LinkItem): Li
};
};

export const createInvestigationsLinkFromNotes = (noteLink: LinkItem): LinkItem => {
return {
...investigationsAppLink,
links: [{ ...noteLink, description: i18n.NOTE_DESCRIPTION, landingIcon: IconTimelineLazy }],
};
};

export const updateInvestigationsLinkFromNotes = (
investigationsLink: LinkItem,
noteLink: LinkItem
): LinkItem => {
const currentLinks = investigationsLink.links ?? [];
currentLinks.push({
...noteLink,
description: i18n.NOTE_DESCRIPTION,
landingIcon: IconFilebeatChart,
});
return {
...investigationsLink,
links: currentLinks,
};
};

// navLinks define the navigation links for the Security Solution pages and External pages as well
export const investigationsNavLinks: SolutionNavLink[] = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export const TIMELINE_DESCRIPTION = i18n.translate(
}
);

export const NOTE_DESCRIPTION = i18n.translate(
'xpack.securitySolution.navLinks.investigations.note.title',
{
defaultMessage: 'Oversee, revise and revisit the annotations within each document and timeline',
}
);

export const OSQUERY_TITLE = i18n.translate(
'xpack.securitySolution.navLinks.investigations.osquery.title',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const ENTITY_ANALYTICS_RISK_SCORE = i18n.translate(
}
);

export const NOTES = i18n.translate('xpack.securitySolution.navigation.notesManagement', {
export const NOTES = i18n.translate('xpack.securitySolution.navigation.notes', {
defaultMessage: 'Notes',
});

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/security_solution/public/app_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
import type { CoreStart } from '@kbn/core/public';

import { links as notesLink } from './notes/links';
import { links as attackDiscoveryLinks } from './attack_discovery/links';
import type { AppLinkItems } from './common/links/types';
import { indicatorsLinks } from './threat_intelligence/links';
Expand Down Expand Up @@ -35,6 +36,7 @@ export const appLinks: AppLinkItems = Object.freeze([
rulesLinks,
gettingStartedLinks,
managementLinks,
notesLink,
]);

export const getFilteredLinks = async (
Expand All @@ -55,5 +57,6 @@ export const getFilteredLinks = async (
rulesLinks,
gettingStartedLinks,
managementFilteredLinks,
notesLink,
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { isEmpty } from 'lodash/fp';
import type { TimelineType } from '../../../../common/api/timeline';
import { appendSearch } from './helpers';

export const getTimelineTabsUrl = (tabName: TimelineType | 'notes', search?: string) =>
export const getTimelineTabsUrl = (tabName: TimelineType, search?: string) =>
`/${tabName}${appendSearch(search)}`;

export const getTimelineUrl = (id: string, graphEventId?: string) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
BLOCKLIST,
RESPONSE_ACTIONS_HISTORY,
PROTECTION_UPDATES,
NOTES,
} from '../../app/translations';

const TabNameMappedToI18nKey: Record<AdministrationSubTab, string> = {
Expand All @@ -25,6 +26,7 @@ const TabNameMappedToI18nKey: Record<AdministrationSubTab, string> = {
[AdministrationSubTab.blocklist]: BLOCKLIST,
[AdministrationSubTab.responseActionsHistory]: RESPONSE_ACTIONS_HISTORY,
[AdministrationSubTab.protectionUpdates]: PROTECTION_UPDATES,
[AdministrationSubTab.notes]: NOTES,
};

export function getTrailingBreadcrumbs(params: AdministrationRouteSpyState): ChromeBreadcrumb[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const MANAGEMENT_ROUTING_POLICY_DETAILS_EVENT_FILTERS_PATH = `${MANAGEMEN
export const MANAGEMENT_ROUTING_POLICY_DETAILS_HOST_ISOLATION_EXCEPTIONS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId/hostIsolationExceptions`;
export const MANAGEMENT_ROUTING_POLICY_DETAILS_BLOCKLISTS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId/blocklists`;
export const MANAGEMENT_ROUTING_POLICY_DETAILS_PROTECTION_UPDATES_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId/protectionUpdates`;
export const MANAGEMENT_ROUTING_NOTES_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.notes})`;
/** @deprecated use the paths defined above instead */
export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH_OLD = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId`;
export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_PATH}/:tabName(${AdministrationSubTab.trustedApps})`;
Expand Down
22 changes: 22 additions & 0 deletions x-pack/plugins/security_solution/public/management/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { CoreStart } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';

import IconFilebeatChart from '../app/solution_navigation/links/sections/icons/filebeat_chart';
import { checkArtifactHasData } from './services/exceptions_list/check_artifact_has_data';
import {
calculateEndpointAuthz,
Expand All @@ -22,6 +23,7 @@ import {
EVENT_FILTERS_PATH,
HOST_ISOLATION_EXCEPTIONS_PATH,
MANAGE_PATH,
NOTES_PATH,
POLICIES_PATH,
RESPONSE_ACTIONS_HISTORY_PATH,
SecurityPageName,
Expand All @@ -39,6 +41,7 @@ import {
TRUSTED_APPLICATIONS,
ENTITY_ANALYTICS_RISK_SCORE,
ASSET_CRITICALITY,
NOTES,
} from '../app/translations';
import { licenseService } from '../common/hooks/use_license';
import type { LinkItem } from '../common/links/types';
Expand Down Expand Up @@ -85,6 +88,12 @@ const categories = [
}),
linkIds: [SecurityPageName.cloudDefendPolicies],
},
{
label: i18n.translate('xpack.securitySolution.appLinks.category.investigations', {
defaultMessage: 'Investigations',
}),
linkIds: [SecurityPageName.notes],
},
];

export const links: LinkItem = {
Expand Down Expand Up @@ -215,6 +224,19 @@ export const links: LinkItem = {
hideTimeline: true,
},
cloudDefendLink,
{
id: SecurityPageName.notes,
title: NOTES,
description: i18n.translate('xpack.securitySolution.appLinks.notesDescription', {
defaultMessage:
'Oversee, revise and revisit the annotations within each document and timeline.',
}),
landingIcon: IconFilebeatChart,
path: NOTES_PATH,
skipUrlState: true,
hideTimeline: true,
experimentalKey: 'securitySolutionNotesEnabled',
},
],
};

Expand Down
18 changes: 18 additions & 0 deletions x-pack/plugins/security_solution/public/management/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { Routes, Route } from '@kbn/shared-ux-router';
import { TrackApplicationView } from '@kbn/usage-collection-plugin/public';
import { EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { NotesContainer } from './notes';
import { ManagementEmptyStateWrapper } from '../components/management_empty_state_wrapper';
import {
MANAGEMENT_ROUTING_ENDPOINTS_PATH,
Expand All @@ -20,6 +22,7 @@ import {
MANAGEMENT_ROUTING_TRUSTED_APPS_PATH,
MANAGEMENT_ROUTING_BLOCKLIST_PATH,
MANAGEMENT_ROUTING_RESPONSE_ACTIONS_HISTORY_PATH,
MANAGEMENT_ROUTING_NOTES_PATH,
} from '../common/constants';
import { NotFoundPage } from '../../app/404';
import { EndpointsContainer } from './endpoint_hosts';
Expand Down Expand Up @@ -77,7 +80,18 @@ const ResponseActionsTelemetry = () => (
</TrackApplicationView>
);

const NotesTelemetry = () => (
<TrackApplicationView viewId={SecurityPageName.notes}>
<NotesContainer />
<SpyRoute pageName={SecurityPageName.notes} />
</TrackApplicationView>
);

export const ManagementContainer = memo(() => {
const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
'securitySolutionNotesEnabled'
);

const {
loading,
canReadPolicyManagement,
Expand Down Expand Up @@ -148,6 +162,10 @@ export const ManagementContainer = memo(() => {
hasPrivilege={canReadActionsLogManagement}
/>

{securitySolutionNotesEnabled && (
<Route path={MANAGEMENT_ROUTING_NOTES_PATH} component={NotesTelemetry} />
)}

{canReadEndpointList && (
<Route path={MANAGEMENT_PATH} exact>
<Redirect to={getEndpointListPath({ name: 'endpointList' })} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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 { Routes, Route } from '@kbn/shared-ux-router';
import React from 'react';
import { NoteManagementPage } from '../../../notes';
import { NotFoundPage } from '../../../app/404';
import { MANAGEMENT_ROUTING_NOTES_PATH } from '../../common/constants';

export const NotesContainer = () => {
return (
<Routes>
<Route path={MANAGEMENT_ROUTING_NOTES_PATH} exact component={NoteManagementPage} />
<Route path="*" component={NotFoundPage} />
</Routes>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export enum AdministrationSubTab {
blocklist = 'blocklist',
responseActionsHistory = 'response_actions_history',
protectionUpdates = 'protection_updates',
notes = 'notes',
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,56 +14,10 @@ export const BATCH_ACTIONS = i18n.translate(
}
);

export const CREATED_COLUMN = i18n.translate(
'xpack.securitySolution.notes.management.createdColumnTitle',
{
defaultMessage: 'Created',
}
);

export const CREATED_BY_COLUMN = i18n.translate(
'xpack.securitySolution.notes.management.createdByColumnTitle',
{
defaultMessage: 'Created by',
}
);

export const EVENT_ID_COLUMN = i18n.translate(
'xpack.securitySolution.notes.management.eventIdColumnTitle',
{
defaultMessage: 'View Document',
}
);

export const TIMELINE_ID_COLUMN = i18n.translate(
'xpack.securitySolution.notes.management.timelineColumnTitle',
{
defaultMessage: 'Timeline',
}
);

export const NOTE_CONTENT_COLUMN = i18n.translate(
'xpack.securitySolution.notes.management.noteContentColumnTitle',
{
defaultMessage: 'Note content',
}
);

export const DELETE = i18n.translate('xpack.securitySolution.notes.management.deleteAction', {
defaultMessage: 'Delete',
});

export const DELETE_SINGLE_NOTE_DESCRIPTION = i18n.translate(
'xpack.securitySolution.notes.management.deleteDescription',
{
defaultMessage: 'Delete this note',
}
);

export const TABLE_ERROR = i18n.translate('xpack.securitySolution.notes.management.tableError', {
defaultMessage: 'Unable to load notes',
});

export const DELETE_NOTES_MODAL_TITLE = i18n.translate(
'xpack.securitySolution.notes.management.deleteNotesModalTitle',
{
Expand Down Expand Up @@ -96,13 +50,6 @@ export const REFRESH = i18n.translate('xpack.securitySolution.notes.management.r
defaultMessage: 'Refresh',
});

export const OPEN_TIMELINE = i18n.translate(
'xpack.securitySolution.notes.management.openTimeline',
{
defaultMessage: 'Open timeline',
}
);

export const VIEW_EVENT_IN_TIMELINE = i18n.translate(
'xpack.securitySolution.notes.management.viewEventInTimeline',
{
Expand Down
Loading

0 comments on commit cef7801

Please sign in to comment.