Skip to content

Commit

Permalink
Fixed tests and added security assistant
Browse files Browse the repository at this point in the history
  • Loading branch information
YulNaumenko committed Aug 21, 2023
1 parent 966ab0c commit a8c0e50
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 77 deletions.
203 changes: 132 additions & 71 deletions x-pack/packages/security-solution/features/src/app_features.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,143 @@
* 2.0.
*/

import { AppFeatures } from '.';
import type { AppFeatureKeys, ExperimentalFeatures } from '@kbn/security-solution-plugin/common';
import type { PluginSetupContract } from '@kbn/features-plugin/server';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';

const SECURITY_BASE_CONFIG = {
foo: 'foo',
import { AppFeatures } from './app_features';
import type {
AppFeatureKey,
AppFeaturesConfig,
AppSubFeaturesMap,
BaseKibanaFeatureConfig,
} from './types';

const category = {
id: 'security',
label: 'Security app category',
};

const SECURITY_APP_FEATURE_CONFIG = {
'test-base-feature': {
privileges: {
all: {
ui: ['test-capability'],
api: ['test-capability'],
const baseKibanaFeature: BaseKibanaFeatureConfig = {
id: 'FEATURE_ID',
name: 'Base Feature',
order: 1100,
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
privileges: {
all: {
api: ['api-read', 'api-write'],
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
savedObject: {
all: [],
read: [],
},
read: {
ui: ['test-capability'],
api: ['test-capability'],
ui: ['write', 'read'],
},
read: {
api: ['api-read'],
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
savedObject: {
all: [],
read: [],
},
ui: ['read'],
},
},
category,
};

const CASES_BASE_CONFIG = {
bar: 'bar',
const priviledges = {
privileges: {
all: {
api: ['api-read', 'api-write', 'test-capability'],
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
savedObject: {
all: [],
read: [],
},
ui: ['write', 'read', 'test-capability'],
},
read: {
api: ['api-read', 'test-capability'],
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
savedObject: {
all: [],
read: [],
},
ui: ['read', 'test-capability'],
},
},
};

const CASES_APP_FEATURE_CONFIG = {
'test-cases-feature': {
privileges: {
all: {
ui: ['test-cases-capability'],
api: ['test-cases-capability'],
const SECURITY_APP_FEATURE_CONFIG: AppFeaturesConfig<string> = new Map();
SECURITY_APP_FEATURE_CONFIG.set('test-base-feature' as AppFeatureKey, {
privileges: {
all: {
ui: ['test-capability'],
api: ['test-capability'],
},
read: {
ui: ['test-capability'],
api: ['test-capability'],
},
},
});

const CASES_BASE_CONFIG = {
privileges: {
all: {
api: ['api-read', 'api-write', 'test-cases-capability'],
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
savedObject: {
all: [],
read: [],
},
read: {
ui: ['test-cases-capability'],
api: ['test-cases-capability'],
ui: ['write', 'read', 'test-cases-capability'],
},
read: {
api: ['api-read', 'test-cases-capability'],
app: ['FEATURE_ID', 'kibana'],
catalogue: ['APP_ID'],
savedObject: {
all: [],
read: [],
},
ui: ['read', 'test-cases-capability'],
},
},
};

jest.mock('@kbn/security-solution-plugin/server/lib/app_features/security_kibana_features', () => {
return {
getSecurityBaseKibanaFeature: jest.fn(() => SECURITY_BASE_CONFIG),
getSecurityBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
getSecurityAppFeaturesConfig: jest.fn(() => SECURITY_APP_FEATURE_CONFIG),
};
const CASES_APP_FEATURE_CONFIG: AppFeaturesConfig<string> = new Map();
CASES_APP_FEATURE_CONFIG.set('test-cases-feature' as AppFeatureKey, {
privileges: {
all: {
ui: ['test-cases-capability'],
api: ['test-cases-capability'],
},
read: {
ui: ['test-cases-capability'],
api: ['test-cases-capability'],
},
},
});
jest.mock(
'@kbn/security-solution-plugin/server/lib/app_features/security_kibana_sub_features',
() => {
return {
securitySubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
};
}
);

jest.mock(
'@kbn/security-solution-plugin/server/lib/app_features/security_cases_kibana_features',
() => {
return {
getCasesBaseKibanaFeature: jest.fn(() => CASES_BASE_CONFIG),
getCasesBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
getCasesAppFeaturesConfig: jest.fn(() => CASES_APP_FEATURE_CONFIG),
};
}
);

jest.mock(
'@kbn/security-solution-plugin/server/lib/app_features/security_cases_kibana_sub_features',
() => {
return {
casesSubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
};
}
);

const securityKibanaSubFeatures = {
securitySubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
};

const securityCasesKibanaFeatures = {
getCasesBaseKibanaFeature: jest.fn(() => CASES_BASE_CONFIG),
getCasesBaseKibanaSubFeatureIds: jest.fn(() => ['subFeature1']),
getCasesAppFeaturesConfig: jest.fn(() => CASES_APP_FEATURE_CONFIG),
};

const securityCasesKibanaSubFeatures = {
casesSubFeaturesMap: new Map([['subFeature1', { baz: 'baz' }]]),
};

describe('AppFeatures', () => {
it('should register enabled kibana features', () => {
Expand All @@ -91,18 +150,19 @@ describe('AppFeatures', () => {
getKibanaFeatures: jest.fn(),
} as unknown as PluginSetupContract;

const appFeatureKeys = ['test-base-feature'] as unknown as AppFeatureKeys;

const appFeatures = new AppFeatures(
loggingSystemMock.create().get('mock'),
[] as unknown as ExperimentalFeatures
securityKibanaSubFeatures.securitySubFeaturesMap as unknown as AppSubFeaturesMap<string>,
baseKibanaFeature,
['subFeature1']
);
appFeatures.init(featuresSetup);
appFeatures.set(appFeatureKeys);
appFeatures.setConfig(SECURITY_APP_FEATURE_CONFIG);

expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
...SECURITY_BASE_CONFIG,
...SECURITY_APP_FEATURE_CONFIG['test-base-feature'],
...baseKibanaFeature,
...SECURITY_APP_FEATURE_CONFIG.get('test-base-feature' as AppFeatureKey),
...priviledges,
subFeatures: [{ baz: 'baz' }],
});
});
Expand All @@ -112,19 +172,20 @@ describe('AppFeatures', () => {
registerKibanaFeature: jest.fn(),
} as unknown as PluginSetupContract;

const appFeatureKeys = ['test-cases-feature'] as unknown as AppFeatureKeys;

const appFeatures = new AppFeatures(
loggingSystemMock.create().get('mock'),
[] as unknown as ExperimentalFeatures
securityCasesKibanaSubFeatures.casesSubFeaturesMap as unknown as AppSubFeaturesMap<string>,
baseKibanaFeature,
['subFeature1']
);
appFeatures.init(featuresSetup);
appFeatures.set(appFeatureKeys);
appFeatures.setConfig(CASES_APP_FEATURE_CONFIG);

expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
...CASES_BASE_CONFIG,
...CASES_APP_FEATURE_CONFIG['test-cases-feature'],
...baseKibanaFeature,
...CASES_APP_FEATURE_CONFIG.get('test-cases-feature' as AppFeatureKey),
subFeatures: [{ baz: 'baz' }],
...CASES_BASE_CONFIG,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class AppFeatures<T extends string = string, S extends string = string> {
'Cannot sync kibana features as featuresSetup is not present. Did you call init?'
);
}

const completeAppFeatureConfig = this.featureConfigMerger.mergeAppFeatureConfigs(
this.baseKibanaFeature,
this.baseKibanaSubFeatureIds,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,21 @@ export enum AppFeatureCasesKey {
casesConnectors = 'cases_connectors',
}

export enum AppFeatureAssistantKey {
/**
* Enables Elastic AI Assistant
*/
assistant = 'assistant',
}

// Merges the two enums.
export const AppFeatureKey = { ...AppFeatureSecurityKey, ...AppFeatureCasesKey };
export const AppFeatureKey = {
...AppFeatureSecurityKey,
...AppFeatureCasesKey,
...AppFeatureAssistantKey,
};
// We need to merge the value and the type and export both to replicate how enum works.
export type AppFeatureKey = AppFeatureSecurityKey | AppFeatureCasesKey;
export type AppFeatureKey = AppFeatureSecurityKey | AppFeatureCasesKey | AppFeatureAssistantKey;

export const ALL_APP_FEATURE_KEYS = Object.freeze(Object.values(AppFeatureKey));

Expand All @@ -89,3 +100,8 @@ export enum SecuritySubFeatureId {
export enum CasesSubFeatureId {
deleteCases = 'deleteCasesSubFeature',
}

/** Sub-features IDs for Security Assistant */
export enum AssistantSubFeatureId {
createConversation = 'createConversationSubFeature',
}
7 changes: 7 additions & 0 deletions x-pack/packages/security-solution/features/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import type {
AppFeatureCasesKey,
SecuritySubFeatureId,
CasesSubFeatureId,
AssistantSubFeatureId,
AppFeatureAssistantKey,
} from './app_features_keys';

export { AppFeatureKey };
Expand Down Expand Up @@ -44,4 +46,9 @@ export type AppFeaturesCasesConfig = Map<
AppFeatureKibanaConfig<CasesSubFeatureId>
>;

export type AppFeaturesSecurityAssistantConfig = Map<
AppFeatureAssistantKey,
AppFeatureKibanaConfig<AssistantSubFeatureId>
>;

export type AppSubFeaturesMap<T extends string = string> = Map<T, SubFeatureConfig>;
2 changes: 0 additions & 2 deletions x-pack/plugins/security_solution/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,6 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S

registerDeepLinksUpdater(this.appUpdater$);

console.log(core.application.capabilities.siem);

const baseLinksPermissions: LinksPermissions = {
experimentalFeatures: this.experimentalFeatures,
upselling: upsellingService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@ import {
} from './security_cases_kibana_features';
import { casesSubFeaturesMap } from './security_cases_kibana_sub_features';
import { securitySubFeaturesMap } from './security_kibana_sub_features';
import {
getAssistantBaseKibanaFeature,
getAssistantBaseKibanaSubFeatureIds,
} from '../app_features/security_assistant_kibana_features';
import { assistantSubFeaturesMap } from '../app_features/security_assistant_kibana_sub_features';

export class AppFeaturesService {
private securityAppFeatures: AppFeatures;
private casesAppFeatures: AppFeatures;
private securityAssistantAppFeatures: AppFeatures;
private appFeatures?: Set<AppFeatureKey>;

constructor(
Expand All @@ -55,11 +61,22 @@ export class AppFeaturesService {
securityCasesBaseKibanaFeature,
securityCasesBaseKibanaSubFeatureIds
);

// register security assistant Kibana features
const securityAssistantBaseKibanaFeature = getAssistantBaseKibanaFeature();
const securityAssistantBaseKibanaSubFeatureIds = getAssistantBaseKibanaSubFeatureIds();
this.securityAssistantAppFeatures = new AppFeatures(
this.logger,
assistantSubFeaturesMap,
securityAssistantBaseKibanaFeature,
securityAssistantBaseKibanaSubFeatureIds
);
}

public init(featuresSetup: FeaturesPluginSetup) {
this.securityAppFeatures.init(featuresSetup);
this.casesAppFeatures.init(featuresSetup);
this.securityAssistantAppFeatures.init(featuresSetup);
}

public setAppFeaturesConfigurator(configurator: AppFeaturesConfigurator) {
Expand All @@ -69,10 +86,14 @@ export class AppFeaturesService {
const casesAppFeaturesConfig = configurator.cases();
this.casesAppFeatures.setConfig(casesAppFeaturesConfig);

const securityAssistantAppFeaturesConfig = configurator.securityAssistant();
this.securityAssistantAppFeatures.setConfig(securityAssistantAppFeaturesConfig);

this.appFeatures = new Set<AppFeatureKey>(
Object.freeze([
...securityAppFeaturesConfig.keys(),
...casesAppFeaturesConfig.keys(),
...securityAssistantAppFeaturesConfig.keys(),
]) as readonly AppFeatureKey[]
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import type {
AppFeaturesConfig,
SecuritySubFeatureId,
CasesSubFeatureId,
AssistantSubFeatureId,
} from '@kbn/security-solution-features';
import type { ExperimentalFeatures } from '../../../common';

export interface AppFeaturesConfigurator {
security: (experimentalFlags: ExperimentalFeatures) => AppFeaturesConfig<SecuritySubFeatureId>;
cases: () => AppFeaturesConfig<CasesSubFeatureId>;
securityAssistant: () => AppFeaturesConfig<AssistantSubFeatureId>;
}
Loading

0 comments on commit a8c0e50

Please sign in to comment.