Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Security Solution] Migration of Alert Page controls for non-default Spaces. #200058

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions x-pack/plugins/security_solution/public/detections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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: {
Expand Down
19 changes: 12 additions & 7 deletions x-pack/plugins/security_solution/public/detections/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
3 changes: 2 additions & 1 deletion x-pack/plugins/security_solution/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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', () => {
PhilippeOberti marked this conversation as resolved.
Show resolved Hide resolved
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', () => {
storage.set(PAGE_FILTER_STORAGE_KEY, NEW_FORMAT);
migrateAlertPageControlsTo816(storage);
const migrated = storage.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 ', () => {
migrateAlertPageControlsTo816(storage);
const migrated = storage.get(PAGE_FILTER_STORAGE_KEY);
expect(migrated).toBeNull();
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 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);
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 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 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);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> = {
Expand Down Expand Up @@ -131,6 +136,6 @@ export function migrateAlertPageControlsTo816(storage: Storage) {
};
}

storage.set(PAGE_FILTER_STORAGE_KEY, newFormat);
storage.set(storageKey, newFormat);
}
}
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']>;
Expand Down