mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[8.x] [Security Solution] Migration of Alert Page controls for non-default Spaces. (#200058) (#200093)
# Backport This will backport the following commits from `main` to `8.x`: - [[Security Solution] Migration of Alert Page controls for non-default Spaces. (#200058)](https://github.com/elastic/kibana/pull/200058) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Jatin Kathuria","email":"jatin.kathuria@elastic.co"},"sourceCommit":{"committedDate":"2024-11-13T20:52:00Z","message":"[Security Solution] Migration of Alert Page controls for non-default Spaces. (#200058)\n\n## Summary\r\n\r\nRecently, we created a PR to migrate the alert page filters controls to\r\n`8.16`. Unfortunately, it does not do migration for non-default spaces\r\nso any users upgrading to `8.16` will face the issue where Alert page\r\nerrors out as shown in below screenshot.\r\n\r\n\r\n\r\n\r\n\r\n## Desk Testing\r\n\r\n1. Checkout to `v8.15` branch by running `git checkout 8.15`. \r\n2. Create a new space and go to that space.\r\n3. Go to the alert page and do some modifications to the page controls.\r\nThis store `v8.15` page controls in local storage.\r\n - You can, for example, delete one page control.\r\n - Change selected value for one page control.\r\n - Additionally, you can also add a custom control.\r\n4. Checkout `main` now and repeat the above steps.\r\n5. Your changes should be retained on the alert page and there should\r\nnot be any error.","sha":"b7ca7228315393c6672f638982dbac5196c9ad90","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Threat Hunting:Investigations","backport:prev-minor"],"title":"[Security Solution] Migration of Alert Page controls for non-default Spaces.","number":200058,"url":"https://github.com/elastic/kibana/pull/200058","mergeCommit":{"message":"[Security Solution] Migration of Alert Page controls for non-default Spaces. (#200058)\n\n## Summary\r\n\r\nRecently, we created a PR to migrate the alert page filters controls to\r\n`8.16`. Unfortunately, it does not do migration for non-default spaces\r\nso any users upgrading to `8.16` will face the issue where Alert page\r\nerrors out as shown in below screenshot.\r\n\r\n\r\n\r\n\r\n\r\n## Desk Testing\r\n\r\n1. Checkout to `v8.15` branch by running `git checkout 8.15`. \r\n2. Create a new space and go to that space.\r\n3. Go to the alert page and do some modifications to the page controls.\r\nThis store `v8.15` page controls in local storage.\r\n - You can, for example, delete one page control.\r\n - Change selected value for one page control.\r\n - Additionally, you can also add a custom control.\r\n4. Checkout `main` now and repeat the above steps.\r\n5. Your changes should be retained on the alert page and there should\r\nnot be any error.","sha":"b7ca7228315393c6672f638982dbac5196c9ad90"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/200058","number":200058,"mergeCommit":{"message":"[Security Solution] Migration of Alert Page controls for non-default Spaces. (#200058)\n\n## Summary\r\n\r\nRecently, we created a PR to migrate the alert page filters controls to\r\n`8.16`. Unfortunately, it does not do migration for non-default spaces\r\nso any users upgrading to `8.16` will face the issue where Alert page\r\nerrors out as shown in below screenshot.\r\n\r\n\r\n\r\n\r\n\r\n## Desk Testing\r\n\r\n1. Checkout to `v8.15` branch by running `git checkout 8.15`. \r\n2. Create a new space and go to that space.\r\n3. Go to the alert page and do some modifications to the page controls.\r\nThis store `v8.15` page controls in local storage.\r\n - You can, for example, delete one page control.\r\n - Change selected value for one page control.\r\n - Additionally, you can also add a custom control.\r\n4. Checkout `main` now and repeat the above steps.\r\n5. Your changes should be retained on the alert page and there should\r\nnot be any error.","sha":"b7ca7228315393c6672f638982dbac5196c9ad90"}}]}] BACKPORT--> Co-authored-by: Jatin Kathuria <jatin.kathuria@elastic.co>
This commit is contained in:
parent
6b2ebb4a4f
commit
255086dc66
6 changed files with 110 additions and 43 deletions
|
@ -12,6 +12,7 @@ import { getDataTablesInStorageByIds } from '../timelines/containers/local_stora
|
|||
import { routes } from './routes';
|
||||
import type { SecuritySubPlugin } from '../app/types';
|
||||
import { runDetectionMigrations } from './migrations';
|
||||
import type { StartPlugins } from '../types';
|
||||
|
||||
export const DETECTIONS_TABLE_IDS: TableIdLiteral[] = [
|
||||
TableId.alertsOnRuleDetailsPage,
|
||||
|
@ -21,8 +22,8 @@ export const DETECTIONS_TABLE_IDS: TableIdLiteral[] = [
|
|||
export class Detections {
|
||||
public setup() {}
|
||||
|
||||
public start(storage: Storage): SecuritySubPlugin {
|
||||
runDetectionMigrations();
|
||||
public async start(storage: Storage, plugins: StartPlugins): Promise<SecuritySubPlugin> {
|
||||
await runDetectionMigrations(storage, plugins);
|
||||
|
||||
return {
|
||||
storageDataTables: {
|
||||
|
|
|
@ -5,16 +5,21 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import { migrateAlertPageControlsTo816 } from '../timelines/containers/local_storage/migrate_alert_page_controls';
|
||||
import type { StartPlugins } from '../types';
|
||||
|
||||
type LocalStorageMigrator = (storage: Storage) => void;
|
||||
/* Migrator could be sync or async */
|
||||
type LocalStorageMigrator = (storage: Storage, plugins: StartPlugins) => void | Promise<void>;
|
||||
|
||||
const runLocalStorageMigration = (fn: LocalStorageMigrator) => {
|
||||
const storage = new Storage(localStorage);
|
||||
fn(storage);
|
||||
const getLocalStorageMigrationRunner = (storage: Storage, plugins: StartPlugins) => {
|
||||
const runLocalStorageMigration = async (fn: LocalStorageMigrator) => {
|
||||
await fn(storage, plugins);
|
||||
};
|
||||
return runLocalStorageMigration;
|
||||
};
|
||||
|
||||
export const runDetectionMigrations = () => {
|
||||
runLocalStorageMigration(migrateAlertPageControlsTo816);
|
||||
export const runDetectionMigrations = async (storage: Storage, plugins: StartPlugins) => {
|
||||
const runLocalStorageMigration = getLocalStorageMigrationRunner(storage, plugins);
|
||||
await runLocalStorageMigration(migrateAlertPageControlsTo816);
|
||||
};
|
||||
|
|
|
@ -256,8 +256,9 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
plugins: StartPlugins
|
||||
): Promise<StartedSubPlugins> {
|
||||
const subPlugins = await this.createSubPlugins();
|
||||
const alerts = await subPlugins.alerts.start(storage, plugins);
|
||||
return {
|
||||
alerts: subPlugins.alerts.start(storage),
|
||||
alerts,
|
||||
attackDiscovery: subPlugins.attackDiscovery.start(),
|
||||
cases: subPlugins.cases.start(),
|
||||
cloudDefend: subPlugins.cloudDefend.start(),
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import {
|
||||
PAGE_FILTER_STORAGE_KEY,
|
||||
GET_PAGE_FILTER_STORAGE_KEY,
|
||||
migrateAlertPageControlsTo816,
|
||||
} from './migrate_alert_page_controls';
|
||||
import type { StartPlugins } from '../../../types';
|
||||
|
||||
const OLD_FORMAT = {
|
||||
viewMode: 'view',
|
||||
|
@ -216,40 +217,94 @@ const NEW_FORMAT = {
|
|||
};
|
||||
const storage = new Storage(localStorage);
|
||||
|
||||
const mockPlugins = {
|
||||
spaces: {
|
||||
getActiveSpace: jest.fn().mockResolvedValue({ id: 'default' }),
|
||||
},
|
||||
} as unknown as StartPlugins;
|
||||
|
||||
describe('migrateAlertPageControlsTo816', () => {
|
||||
beforeEach(() => {
|
||||
storage.clear();
|
||||
});
|
||||
it('should migrate the old format to the new format', () => {
|
||||
storage.set(PAGE_FILTER_STORAGE_KEY, OLD_FORMAT);
|
||||
migrateAlertPageControlsTo816(storage);
|
||||
const migrated = storage.get(PAGE_FILTER_STORAGE_KEY);
|
||||
expect(migrated).toMatchObject(NEW_FORMAT);
|
||||
describe('Default space', () => {
|
||||
beforeEach(() => {
|
||||
if (mockPlugins.spaces?.getActiveSpace) {
|
||||
mockPlugins.spaces.getActiveSpace = jest.fn().mockResolvedValue({ id: 'default' });
|
||||
}
|
||||
});
|
||||
it('should migrate the old format to the new format', async () => {
|
||||
storage.set(GET_PAGE_FILTER_STORAGE_KEY(), OLD_FORMAT);
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY());
|
||||
expect(migrated).toMatchObject(NEW_FORMAT);
|
||||
});
|
||||
|
||||
it('should be a no-op if the new format already exists', async () => {
|
||||
storage.set(GET_PAGE_FILTER_STORAGE_KEY(), NEW_FORMAT);
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY());
|
||||
expect(migrated).toMatchObject(NEW_FORMAT);
|
||||
});
|
||||
|
||||
it('should be a no-op if no value is present in localstorage for page filters ', async () => {
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY());
|
||||
expect(migrated).toBeNull();
|
||||
});
|
||||
|
||||
it('should convert custom old format correctly', async () => {
|
||||
const MODIFIED_OLD_FORMAT = structuredClone(OLD_FORMAT);
|
||||
MODIFIED_OLD_FORMAT.panels['0'].explicitInput.hideExists = true;
|
||||
MODIFIED_OLD_FORMAT.chainingSystem = 'NONE';
|
||||
storage.set(GET_PAGE_FILTER_STORAGE_KEY(), MODIFIED_OLD_FORMAT);
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY());
|
||||
const EXPECTED_NEW_FORMAT = structuredClone(NEW_FORMAT);
|
||||
EXPECTED_NEW_FORMAT.initialChildControlState['0'].hideExists = true;
|
||||
EXPECTED_NEW_FORMAT.chainingSystem = 'NONE';
|
||||
expect(migrated).toMatchObject(EXPECTED_NEW_FORMAT);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a no-op if the new format already exists', () => {
|
||||
storage.set(PAGE_FILTER_STORAGE_KEY, NEW_FORMAT);
|
||||
migrateAlertPageControlsTo816(storage);
|
||||
const migrated = storage.get(PAGE_FILTER_STORAGE_KEY);
|
||||
expect(migrated).toMatchObject(NEW_FORMAT);
|
||||
});
|
||||
describe('Non Default space', () => {
|
||||
const nonDefaultSpaceId = 'space1';
|
||||
beforeEach(() => {
|
||||
if (mockPlugins.spaces?.getActiveSpace) {
|
||||
mockPlugins.spaces.getActiveSpace = jest.fn().mockResolvedValue({ id: nonDefaultSpaceId });
|
||||
}
|
||||
});
|
||||
it('should migrate the old format to the new format', async () => {
|
||||
storage.set(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId), OLD_FORMAT);
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId));
|
||||
expect(migrated).toMatchObject(NEW_FORMAT);
|
||||
});
|
||||
|
||||
it('should be a no-op if no value is present in localstorage for page filters ', () => {
|
||||
migrateAlertPageControlsTo816(storage);
|
||||
const migrated = storage.get(PAGE_FILTER_STORAGE_KEY);
|
||||
expect(migrated).toBeNull();
|
||||
});
|
||||
it('should be a no-op if the new format already exists', async () => {
|
||||
storage.set(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId), NEW_FORMAT);
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId));
|
||||
expect(migrated).toMatchObject(NEW_FORMAT);
|
||||
});
|
||||
|
||||
it('should convert custom old format correctly', () => {
|
||||
const MODIFIED_OLD_FORMAT = structuredClone(OLD_FORMAT);
|
||||
MODIFIED_OLD_FORMAT.panels['0'].explicitInput.hideExists = true;
|
||||
MODIFIED_OLD_FORMAT.chainingSystem = 'NONE';
|
||||
storage.set(PAGE_FILTER_STORAGE_KEY, MODIFIED_OLD_FORMAT);
|
||||
migrateAlertPageControlsTo816(storage);
|
||||
const migrated = storage.get(PAGE_FILTER_STORAGE_KEY);
|
||||
const EXPECTED_NEW_FORMAT = structuredClone(NEW_FORMAT);
|
||||
EXPECTED_NEW_FORMAT.initialChildControlState['0'].hideExists = true;
|
||||
EXPECTED_NEW_FORMAT.chainingSystem = 'NONE';
|
||||
expect(migrated).toMatchObject(EXPECTED_NEW_FORMAT);
|
||||
it('should be a no-op if no value is present in localstorage for page filters ', async () => {
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId));
|
||||
expect(migrated).toBeNull();
|
||||
});
|
||||
|
||||
it('should convert custom old format correctly', async () => {
|
||||
const MODIFIED_OLD_FORMAT = structuredClone(OLD_FORMAT);
|
||||
MODIFIED_OLD_FORMAT.panels['0'].explicitInput.hideExists = true;
|
||||
MODIFIED_OLD_FORMAT.chainingSystem = 'NONE';
|
||||
storage.set(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId), MODIFIED_OLD_FORMAT);
|
||||
await migrateAlertPageControlsTo816(storage, mockPlugins);
|
||||
const migrated = storage.get(GET_PAGE_FILTER_STORAGE_KEY(nonDefaultSpaceId));
|
||||
const EXPECTED_NEW_FORMAT = structuredClone(NEW_FORMAT);
|
||||
EXPECTED_NEW_FORMAT.initialChildControlState['0'].hideExists = true;
|
||||
EXPECTED_NEW_FORMAT.chainingSystem = 'NONE';
|
||||
expect(migrated).toMatchObject(EXPECTED_NEW_FORMAT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
import type { DefaultControlState, ControlGroupRuntimeState } from '@kbn/controls-plugin/common';
|
||||
import type { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { StartPlugins } from '../../../types';
|
||||
|
||||
export const PAGE_FILTER_STORAGE_KEY = 'siem.default.pageFilters';
|
||||
export const GET_PAGE_FILTER_STORAGE_KEY = (spaceId: string = 'default') =>
|
||||
`siem.${spaceId}.pageFilters`;
|
||||
|
||||
interface OldFormat {
|
||||
viewMode: string;
|
||||
|
@ -96,8 +98,11 @@ interface NewFormatExplicitInput {
|
|||
* This migration script is to migrate the old format to the new format.
|
||||
*
|
||||
*/
|
||||
export function migrateAlertPageControlsTo816(storage: Storage) {
|
||||
const oldFormat: OldFormat = storage.get(PAGE_FILTER_STORAGE_KEY);
|
||||
export async function migrateAlertPageControlsTo816(storage: Storage, plugins: StartPlugins) {
|
||||
const space = await plugins.spaces?.getActiveSpace();
|
||||
const spaceId = space?.id ?? 'default';
|
||||
const storageKey = GET_PAGE_FILTER_STORAGE_KEY(spaceId);
|
||||
const oldFormat: OldFormat = storage.get(GET_PAGE_FILTER_STORAGE_KEY(spaceId));
|
||||
if (oldFormat && Object.keys(oldFormat).includes('panels')) {
|
||||
// Only run when it is old format
|
||||
const newFormat: ControlGroupRuntimeState<NewFormatExplicitInput & DefaultControlState> = {
|
||||
|
@ -131,6 +136,6 @@ export function migrateAlertPageControlsTo816(storage: Storage) {
|
|||
};
|
||||
}
|
||||
|
||||
storage.set(PAGE_FILTER_STORAGE_KEY, newFormat);
|
||||
storage.set(storageKey, newFormat);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,7 +248,7 @@ export interface SubPlugins {
|
|||
// TODO: find a better way to defined these types
|
||||
export interface StartedSubPlugins {
|
||||
[CASES_SUB_PLUGIN_KEY]: ReturnType<Cases['start']>;
|
||||
alerts: ReturnType<Detections['start']>;
|
||||
alerts: Awaited<ReturnType<Detections['start']>>;
|
||||
attackDiscovery: ReturnType<AttackDiscovery['start']>;
|
||||
cloudDefend: ReturnType<CloudDefend['start']>;
|
||||
cloudSecurityPosture: ReturnType<CloudSecurityPosture['start']>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue