mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Refactor AppFeatures to ProductFeatures (#177005)
## Summary This PR renames `AppFeatures` -> `ProductFeatures`. This module is responsible for managing the Security _features_ that are enabled according to the _product_ type used. After talking with different teams we agreed it would be more intuitive and easier to understand if we named it `ProductFeatures`, since `AppFeatures` is too vague and generic. This refactoring does not introduce any change in the application behavior. Internal docs will also be updated. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5ae321f8f5
commit
872929bd60
72 changed files with 1063 additions and 962 deletions
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { securityDefaultAppFeaturesConfig } from './src/security/app_feature_config';
|
||||
export { getCasesDefaultAppFeaturesConfig } from './src/cases/app_feature_config';
|
||||
export { assistantDefaultAppFeaturesConfig } from './src/assistant/app_feature_config';
|
||||
export { securityDefaultProductFeaturesConfig } from './src/security/product_feature_config';
|
||||
export { getCasesDefaultProductFeaturesConfig } from './src/cases/product_feature_config';
|
||||
export { assistantDefaultProductFeaturesConfig } from './src/assistant/product_feature_config';
|
||||
|
||||
export { createEnabledAppFeaturesConfigMap } from './src/helpers';
|
||||
export { createEnabledProductFeaturesConfigMap } from './src/helpers';
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './src/app_features_keys';
|
||||
export * from './src/product_features_keys';
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
export { AppFeaturesPrivilegeId, AppFeaturesPrivileges } from './src/app_features_privileges';
|
||||
export {
|
||||
ProductFeaturesPrivilegeId,
|
||||
ProductFeaturesPrivileges,
|
||||
} from './src/product_features_privileges';
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { AssistantSubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureParams } from '../types';
|
||||
import type { AssistantSubFeatureId } from '../product_features_keys';
|
||||
import type { ProductFeatureParams } from '../types';
|
||||
import { getAssistantBaseKibanaFeature } from './kibana_features';
|
||||
import {
|
||||
getAssistantBaseKibanaSubFeatureIds,
|
||||
assistantSubFeaturesMap,
|
||||
} from './kibana_sub_features';
|
||||
|
||||
export const getAssistantFeature = (): AppFeatureParams<AssistantSubFeatureId> => ({
|
||||
export const getAssistantFeature = (): ProductFeatureParams<AssistantSubFeatureId> => ({
|
||||
baseKibanaFeature: getAssistantBaseKibanaFeature(),
|
||||
baseKibanaSubFeatureIds: getAssistantBaseKibanaSubFeatureIds(),
|
||||
subFeaturesMap: assistantSubFeaturesMap,
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AssistantSubFeatureId } from '../app_features_keys';
|
||||
import { AppFeatureAssistantKey } from '../app_features_keys';
|
||||
import type { AppFeatureKibanaConfig } from '../types';
|
||||
import type { AssistantSubFeatureId } from '../product_features_keys';
|
||||
import { ProductFeatureAssistantKey } from '../product_features_keys';
|
||||
import type { ProductFeatureKibanaConfig } from '../types';
|
||||
|
||||
/**
|
||||
* App features privileges configuration for the Security Assistant Kibana Feature app.
|
||||
|
@ -19,11 +19,11 @@ import type { AppFeatureKibanaConfig } from '../types';
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const assistantDefaultAppFeaturesConfig: Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
export const assistantDefaultProductFeaturesConfig: Record<
|
||||
ProductFeatureAssistantKey,
|
||||
ProductFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
> = {
|
||||
[AppFeatureAssistantKey.assistant]: {
|
||||
[ProductFeatureAssistantKey.assistant]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['ai-assistant'],
|
|
@ -4,15 +4,15 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { CasesSubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureParams } from '../types';
|
||||
import type { CasesSubFeatureId } from '../product_features_keys';
|
||||
import type { ProductFeatureParams } from '../types';
|
||||
import { getCasesBaseKibanaFeature } from './kibana_features';
|
||||
import { getCasesBaseKibanaSubFeatureIds, getCasesSubFeaturesMap } from './kibana_sub_features';
|
||||
import type { CasesFeatureParams } from './types';
|
||||
|
||||
export const getCasesFeature = (
|
||||
params: CasesFeatureParams
|
||||
): AppFeatureParams<CasesSubFeatureId> => ({
|
||||
): ProductFeatureParams<CasesSubFeatureId> => ({
|
||||
baseKibanaFeature: getCasesBaseKibanaFeature(params),
|
||||
baseKibanaSubFeatureIds: getCasesBaseKibanaSubFeatureIds(),
|
||||
subFeaturesMap: getCasesSubFeaturesMap(params),
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { CasesSubFeatureId } from '../app_features_keys';
|
||||
import { CasesSubFeatureId } from '../product_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { CasesFeatureParams } from './types';
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AppFeatureCasesKey } from '../app_features_keys';
|
||||
import { ProductFeatureCasesKey } from '../product_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { DefaultCasesAppFeaturesConfig } from './types';
|
||||
import type { DefaultCasesProductFeaturesConfig } from './types';
|
||||
|
||||
/**
|
||||
* App features privileges configuration for the Security Cases Kibana Feature app.
|
||||
|
@ -19,14 +19,14 @@ import type { DefaultCasesAppFeaturesConfig } from './types';
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const getCasesDefaultAppFeaturesConfig = ({
|
||||
export const getCasesDefaultProductFeaturesConfig = ({
|
||||
apiTags,
|
||||
uiCapabilities,
|
||||
}: {
|
||||
apiTags: { connectors: string };
|
||||
uiCapabilities: { connectors: string };
|
||||
}): DefaultCasesAppFeaturesConfig => ({
|
||||
[AppFeatureCasesKey.casesConnectors]: {
|
||||
}): DefaultCasesProductFeaturesConfig => ({
|
||||
[ProductFeatureCasesKey.casesConnectors]: {
|
||||
privileges: {
|
||||
all: {
|
||||
api: [apiTags.connectors],
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { CasesUiCapabilities, CasesApiTags } from '@kbn/cases-plugin/common';
|
||||
import type { AppFeatureCasesKey, CasesSubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureKibanaConfig } from '../types';
|
||||
import type { ProductFeatureCasesKey, CasesSubFeatureId } from '../product_features_keys';
|
||||
import type { ProductFeatureKibanaConfig } from '../types';
|
||||
|
||||
export interface CasesFeatureParams {
|
||||
uiCapabilities: CasesUiCapabilities;
|
||||
|
@ -15,7 +15,7 @@ export interface CasesFeatureParams {
|
|||
savedObjects: { files: string[] };
|
||||
}
|
||||
|
||||
export type DefaultCasesAppFeaturesConfig = Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
export type DefaultCasesProductFeaturesConfig = Record<
|
||||
ProductFeatureCasesKey,
|
||||
ProductFeatureKibanaConfig<CasesSubFeatureId>
|
||||
>;
|
||||
|
|
|
@ -5,26 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys, AppFeatureKeyType, AppFeatureKibanaConfig } from './types';
|
||||
import type {
|
||||
ProductFeatureKeys,
|
||||
ProductFeatureKeyType,
|
||||
ProductFeatureKibanaConfig,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Creates the AppFeaturesConfig Map from the given appFeatures object and a set of enabled appFeatures keys.
|
||||
* Creates the ProductFeaturesConfig Map from the given productFeatures object and a set of enabled productFeatures keys.
|
||||
*/
|
||||
export const createEnabledAppFeaturesConfigMap = <
|
||||
K extends AppFeatureKeyType,
|
||||
export const createEnabledProductFeaturesConfigMap = <
|
||||
K extends ProductFeatureKeyType,
|
||||
T extends string = string
|
||||
>(
|
||||
appFeatures: Record<K, AppFeatureKibanaConfig<T>>,
|
||||
enabledAppFeaturesKeys: AppFeatureKeys
|
||||
) => {
|
||||
return new Map(
|
||||
Object.entries<AppFeatureKibanaConfig<T>>(appFeatures).reduce<
|
||||
Array<[K, AppFeatureKibanaConfig<T>]>
|
||||
productFeatures: Record<K, ProductFeatureKibanaConfig<T>>,
|
||||
enabledProductFeaturesKeys: ProductFeatureKeys
|
||||
) =>
|
||||
new Map(
|
||||
Object.entries<ProductFeatureKibanaConfig<T>>(productFeatures).reduce<
|
||||
Array<[K, ProductFeatureKibanaConfig<T>]>
|
||||
>((acc, [key, value]) => {
|
||||
if (enabledAppFeaturesKeys.includes(key as K)) {
|
||||
if (enabledProductFeaturesKeys.includes(key as K)) {
|
||||
acc.push([key as K, value]);
|
||||
}
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export enum AppFeatureSecurityKey {
|
||||
export enum ProductFeatureSecurityKey {
|
||||
/** Enables Advanced Insights (Entity Risk, GenAI) */
|
||||
advancedInsights = 'advanced_insights',
|
||||
/**
|
||||
|
@ -65,14 +65,14 @@ export enum AppFeatureSecurityKey {
|
|||
externalRuleActions = 'external_rule_actions',
|
||||
}
|
||||
|
||||
export enum AppFeatureCasesKey {
|
||||
export enum ProductFeatureCasesKey {
|
||||
/**
|
||||
* Enables Cases Connectors
|
||||
*/
|
||||
casesConnectors = 'cases_connectors',
|
||||
}
|
||||
|
||||
export enum AppFeatureAssistantKey {
|
||||
export enum ProductFeatureAssistantKey {
|
||||
/**
|
||||
* Enables Elastic AI Assistant
|
||||
*/
|
||||
|
@ -80,15 +80,18 @@ export enum AppFeatureAssistantKey {
|
|||
}
|
||||
|
||||
// Merges the two enums.
|
||||
export const AppFeatureKey = {
|
||||
...AppFeatureSecurityKey,
|
||||
...AppFeatureCasesKey,
|
||||
...AppFeatureAssistantKey,
|
||||
export const ProductFeatureKey = {
|
||||
...ProductFeatureSecurityKey,
|
||||
...ProductFeatureCasesKey,
|
||||
...ProductFeatureAssistantKey,
|
||||
};
|
||||
// We need to merge the value and the type and export both to replicate how enum works.
|
||||
export type AppFeatureKeyType = AppFeatureSecurityKey | AppFeatureCasesKey | AppFeatureAssistantKey;
|
||||
export type ProductFeatureKeyType =
|
||||
| ProductFeatureSecurityKey
|
||||
| ProductFeatureCasesKey
|
||||
| ProductFeatureAssistantKey;
|
||||
|
||||
export const ALL_APP_FEATURE_KEYS = Object.freeze(Object.values(AppFeatureKey));
|
||||
export const ALL_PRODUCT_FEATURE_KEYS = Object.freeze(Object.values(ProductFeatureKey));
|
||||
|
||||
/** Sub-features IDs for Security */
|
||||
export enum SecuritySubFeatureId {
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { APP_ID } from './constants';
|
||||
|
||||
export enum AppFeaturesPrivilegeId {
|
||||
export enum ProductFeaturesPrivilegeId {
|
||||
endpointExceptions = 'endpoint_exceptions',
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@ export enum AppFeaturesPrivilegeId {
|
|||
* using a different Kibana feature configuration (sub-feature, main feature privilege, etc)
|
||||
* in each offering type (ess, serverless)
|
||||
*/
|
||||
export const AppFeaturesPrivileges = {
|
||||
[AppFeaturesPrivilegeId.endpointExceptions]: {
|
||||
export const ProductFeaturesPrivileges = {
|
||||
[ProductFeaturesPrivilegeId.endpointExceptions]: {
|
||||
all: {
|
||||
ui: ['showEndpointExceptions', 'crudEndpointExceptions'],
|
||||
api: [`${APP_ID}-showEndpointExceptions`, `${APP_ID}-crudEndpointExceptions`],
|
|
@ -4,15 +4,15 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { SecuritySubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureParams } from '../types';
|
||||
import type { SecuritySubFeatureId } from '../product_features_keys';
|
||||
import type { ProductFeatureParams } from '../types';
|
||||
import { getSecurityBaseKibanaFeature } from './kibana_features';
|
||||
import { securitySubFeaturesMap, getSecurityBaseKibanaSubFeatureIds } from './kibana_sub_features';
|
||||
import type { SecurityFeatureParams } from './types';
|
||||
|
||||
export const getSecurityFeature = (
|
||||
params: SecurityFeatureParams
|
||||
): AppFeatureParams<SecuritySubFeatureId> => ({
|
||||
): ProductFeatureParams<SecuritySubFeatureId> => ({
|
||||
baseKibanaFeature: getSecurityBaseKibanaFeature(params),
|
||||
baseKibanaSubFeatureIds: getSecurityBaseKibanaSubFeatureIds(params),
|
||||
subFeaturesMap: securitySubFeaturesMap,
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import { EXCEPTION_LIST_NAMESPACE_AGNOSTIC } from '@kbn/securitysolution-list-constants';
|
||||
import { AppFeaturesPrivilegeId, AppFeaturesPrivileges } from '../app_features_privileges';
|
||||
import {
|
||||
ProductFeaturesPrivilegeId,
|
||||
ProductFeaturesPrivileges,
|
||||
} from '../product_features_privileges';
|
||||
|
||||
import { SecuritySubFeatureId } from '../app_features_keys';
|
||||
import { SecuritySubFeatureId } from '../product_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { SecurityFeatureParams } from './types';
|
||||
|
||||
|
@ -583,7 +586,7 @@ const endpointExceptionsSubFeature: SubFeatureConfig = {
|
|||
all: [],
|
||||
read: [],
|
||||
},
|
||||
...AppFeaturesPrivileges[AppFeaturesPrivilegeId.endpointExceptions].all,
|
||||
...ProductFeaturesPrivileges[ProductFeaturesPrivilegeId.endpointExceptions].all,
|
||||
},
|
||||
{
|
||||
id: 'endpoint_exceptions_read',
|
||||
|
@ -593,7 +596,7 @@ const endpointExceptionsSubFeature: SubFeatureConfig = {
|
|||
all: [],
|
||||
read: [],
|
||||
},
|
||||
...AppFeaturesPrivileges[AppFeaturesPrivilegeId.endpointExceptions].read,
|
||||
...ProductFeaturesPrivileges[ProductFeaturesPrivilegeId.endpointExceptions].read,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AppFeatureSecurityKey, SecuritySubFeatureId } from '../app_features_keys';
|
||||
import { ProductFeatureSecurityKey, SecuritySubFeatureId } from '../product_features_keys';
|
||||
import { APP_ID } from '../constants';
|
||||
import type { DefaultSecurityAppFeaturesConfig } from './types';
|
||||
import type { DefaultSecurityProductFeaturesConfig } from './types';
|
||||
|
||||
/**
|
||||
* App features privileges configuration for the Security Solution Kibana Feature app.
|
||||
|
@ -19,8 +19,8 @@ import type { DefaultSecurityAppFeaturesConfig } from './types';
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig = {
|
||||
[AppFeatureSecurityKey.advancedInsights]: {
|
||||
export const securityDefaultProductFeaturesConfig: DefaultSecurityProductFeaturesConfig = {
|
||||
[ProductFeatureSecurityKey.advancedInsights]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['entity-analytics'],
|
||||
|
@ -32,7 +32,7 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig
|
|||
},
|
||||
},
|
||||
},
|
||||
[AppFeatureSecurityKey.investigationGuide]: {
|
||||
[ProductFeatureSecurityKey.investigationGuide]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['investigation-guide'],
|
||||
|
@ -43,7 +43,7 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig
|
|||
},
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.threatIntelligence]: {
|
||||
[ProductFeatureSecurityKey.threatIntelligence]: {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['threat-intelligence'],
|
||||
|
@ -56,18 +56,18 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig
|
|||
},
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointHostManagement]: {
|
||||
[ProductFeatureSecurityKey.endpointHostManagement]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.endpointList],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointPolicyManagement]: {
|
||||
[ProductFeatureSecurityKey.endpointPolicyManagement]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.policyManagement],
|
||||
},
|
||||
|
||||
// Adds no additional kibana feature controls
|
||||
[AppFeatureSecurityKey.endpointPolicyProtections]: {},
|
||||
[ProductFeatureSecurityKey.endpointPolicyProtections]: {},
|
||||
|
||||
[AppFeatureSecurityKey.endpointArtifactManagement]: {
|
||||
[ProductFeatureSecurityKey.endpointArtifactManagement]: {
|
||||
subFeatureIds: [
|
||||
SecuritySubFeatureId.trustedApplications,
|
||||
SecuritySubFeatureId.blocklist,
|
||||
|
@ -87,7 +87,7 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig
|
|||
],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.endpointResponseActions]: {
|
||||
[ProductFeatureSecurityKey.endpointResponseActions]: {
|
||||
subFeatureIds: [
|
||||
SecuritySubFeatureId.hostIsolationExceptions,
|
||||
SecuritySubFeatureId.responseActionsHistory,
|
||||
|
@ -105,8 +105,9 @@ export const securityDefaultAppFeaturesConfig: DefaultSecurityAppFeaturesConfig
|
|||
],
|
||||
},
|
||||
|
||||
[AppFeatureSecurityKey.osqueryAutomatedResponseActions]: {},
|
||||
[AppFeatureSecurityKey.endpointProtectionUpdates]: {},
|
||||
[AppFeatureSecurityKey.endpointAgentTamperProtection]: {},
|
||||
[AppFeatureSecurityKey.externalRuleActions]: {},
|
||||
// Product features without RBAC
|
||||
[ProductFeatureSecurityKey.osqueryAutomatedResponseActions]: {},
|
||||
[ProductFeatureSecurityKey.endpointProtectionUpdates]: {},
|
||||
[ProductFeatureSecurityKey.endpointAgentTamperProtection]: {},
|
||||
[ProductFeatureSecurityKey.externalRuleActions]: {},
|
||||
};
|
|
@ -5,16 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureSecurityKey, SecuritySubFeatureId } from '../app_features_keys';
|
||||
import type { AppFeatureKibanaConfig } from '../types';
|
||||
import type { ProductFeatureSecurityKey, SecuritySubFeatureId } from '../product_features_keys';
|
||||
import type { ProductFeatureKibanaConfig } from '../types';
|
||||
|
||||
export interface SecurityFeatureParams {
|
||||
experimentalFeatures: Record<string, boolean>;
|
||||
savedObjects: string[];
|
||||
}
|
||||
|
||||
export type DefaultSecurityAppFeaturesConfig = Omit<
|
||||
Record<AppFeatureSecurityKey, AppFeatureKibanaConfig<SecuritySubFeatureId>>,
|
||||
AppFeatureSecurityKey.endpointExceptions
|
||||
// | add not default security app features here
|
||||
export type DefaultSecurityProductFeaturesConfig = Omit<
|
||||
Record<ProductFeatureSecurityKey, ProductFeatureKibanaConfig<SecuritySubFeatureId>>,
|
||||
ProductFeatureSecurityKey.endpointExceptions
|
||||
// | add not generic security app features here
|
||||
>;
|
||||
|
|
|
@ -12,48 +12,48 @@ import type {
|
|||
} from '@kbn/features-plugin/common';
|
||||
import type { RecursivePartial } from '@kbn/utility-types';
|
||||
import type {
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKeyType,
|
||||
AppFeatureSecurityKey,
|
||||
ProductFeatureAssistantKey,
|
||||
ProductFeatureCasesKey,
|
||||
ProductFeatureKeyType,
|
||||
ProductFeatureSecurityKey,
|
||||
AssistantSubFeatureId,
|
||||
CasesSubFeatureId,
|
||||
SecuritySubFeatureId,
|
||||
} from './app_features_keys';
|
||||
} from './product_features_keys';
|
||||
|
||||
export type { AppFeatureKeyType };
|
||||
export type AppFeatureKeys = AppFeatureKeyType[];
|
||||
export type { ProductFeatureKeyType };
|
||||
export type ProductFeatureKeys = ProductFeatureKeyType[];
|
||||
|
||||
// Features types
|
||||
export type BaseKibanaFeatureConfig = Omit<KibanaFeatureConfig, 'subFeatures'>;
|
||||
export type SubFeaturesPrivileges = RecursivePartial<SubFeaturePrivilegeConfig>;
|
||||
export type AppFeatureKibanaConfig<T extends string = string> =
|
||||
export type ProductFeatureKibanaConfig<T extends string = string> =
|
||||
RecursivePartial<BaseKibanaFeatureConfig> & {
|
||||
subFeatureIds?: T[];
|
||||
subFeaturesPrivileges?: SubFeaturesPrivileges[];
|
||||
};
|
||||
export type AppFeaturesConfig<T extends string = string> = Map<
|
||||
AppFeatureKeyType,
|
||||
AppFeatureKibanaConfig<T>
|
||||
export type ProductFeaturesConfig<T extends string = string> = Map<
|
||||
ProductFeatureKeyType,
|
||||
ProductFeatureKibanaConfig<T>
|
||||
>;
|
||||
|
||||
export type AppFeaturesSecurityConfig = Map<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
export type ProductFeaturesSecurityConfig = Map<
|
||||
ProductFeatureSecurityKey,
|
||||
ProductFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
>;
|
||||
export type AppFeaturesCasesConfig = Map<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
export type ProductFeaturesCasesConfig = Map<
|
||||
ProductFeatureCasesKey,
|
||||
ProductFeatureKibanaConfig<CasesSubFeatureId>
|
||||
>;
|
||||
|
||||
export type AppFeaturesAssistantConfig = Map<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
export type ProductFeaturesAssistantConfig = Map<
|
||||
ProductFeatureAssistantKey,
|
||||
ProductFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
>;
|
||||
|
||||
export type AppSubFeaturesMap<T extends string = string> = Map<T, SubFeatureConfig>;
|
||||
|
||||
export interface AppFeatureParams<T extends string = string> {
|
||||
export interface ProductFeatureParams<T extends string = string> {
|
||||
baseKibanaFeature: BaseKibanaFeatureConfig;
|
||||
baseKibanaSubFeatureIds: T[];
|
||||
subFeaturesMap: AppSubFeaturesMap<T>;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { ENDPOINT_PRIVILEGES, FleetAuthz } from '@kbn/fleet-plugin/common';
|
||||
|
||||
import { omit } from 'lodash';
|
||||
import type { AppFeaturesService } from '../../../../server/lib/app_features_service';
|
||||
import type { ProductFeaturesService } from '../../../../server/lib/product_features_service';
|
||||
import { RESPONSE_CONSOLE_ACTION_COMMANDS_TO_REQUIRED_AUTHZ } from '../response_actions/constants';
|
||||
import type { LicenseService } from '../../../license';
|
||||
import type { EndpointAuthz } from '../../types/authz';
|
||||
|
@ -22,19 +22,19 @@ import type { MaybeImmutable } from '../../types';
|
|||
* level, use `calculateEndpointAuthz()`
|
||||
*
|
||||
* @param fleetAuthz
|
||||
* @param appFeatureService
|
||||
* @param productFeatureService
|
||||
*/
|
||||
function hasAuthFactory(fleetAuthz: FleetAuthz, appFeatureService?: AppFeaturesService) {
|
||||
function hasAuthFactory(fleetAuthz: FleetAuthz, productFeatureService?: ProductFeaturesService) {
|
||||
return function hasAuth(
|
||||
privilege: keyof typeof ENDPOINT_PRIVILEGES,
|
||||
{ action }: { action?: string } = {}
|
||||
): boolean {
|
||||
// Product features control
|
||||
if (appFeatureService) {
|
||||
if (productFeatureService) {
|
||||
// Only server side has to check this, to prevent "superuser" role from being allowed to use product gated APIs.
|
||||
// UI side does not need to check this. Capabilities list is correct for superuser.
|
||||
const actionToCheck = action ?? appFeatureService.getApiActionName(privilege);
|
||||
if (!appFeatureService.isActionRegistered(actionToCheck)) {
|
||||
const actionToCheck = action ?? productFeatureService.getApiActionName(privilege);
|
||||
if (!productFeatureService.isActionRegistered(actionToCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -58,9 +58,9 @@ export const calculateEndpointAuthz = (
|
|||
licenseService: LicenseService,
|
||||
fleetAuthz: FleetAuthz,
|
||||
userRoles: MaybeImmutable<string[]> = [],
|
||||
appFeaturesService?: AppFeaturesService // only exists on the server side
|
||||
productFeaturesService?: ProductFeaturesService // only exists on the server side
|
||||
): EndpointAuthz => {
|
||||
const hasAuth = hasAuthFactory(fleetAuthz, appFeaturesService);
|
||||
const hasAuth = hasAuthFactory(fleetAuthz, productFeaturesService);
|
||||
|
||||
const isPlatinumPlusLicense = licenseService.isPlatinumPlus();
|
||||
const isEnterpriseLicense = licenseService.isEnterprise();
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { EuiCode, EuiEmptyPrompt } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useIsMounted } from '@kbn/securitysolution-hook-utils';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { useUpsellingComponent } from '../../../common/hooks/use_upselling';
|
||||
import { ResponseActionFormField } from './osquery_response_action_form_field';
|
||||
import type { ArrayItem } from '../../../shared_imports';
|
||||
|
@ -28,7 +28,9 @@ export const OsqueryResponseAction = React.memo((props: OsqueryResponseActionPro
|
|||
const isMounted = useIsMounted();
|
||||
|
||||
// serverless component that is returned when users do not have Endpoint.Complete tier
|
||||
const UpsellingComponent = useUpsellingComponent(AppFeatureKey.osqueryAutomatedResponseActions);
|
||||
const UpsellingComponent = useUpsellingComponent(
|
||||
ProductFeatureKey.osqueryAutomatedResponseActions
|
||||
);
|
||||
|
||||
if (osquery) {
|
||||
const { disabled, permissionDenied } = osquery.fetchInstallationStatus();
|
||||
|
|
|
@ -50,7 +50,7 @@ import { calculateEndpointAuthz } from '../../common/endpoint/service/authz';
|
|||
import type { FeatureUsageService } from './services/feature_usage/service';
|
||||
import type { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
import type { ActionCreateService } from './services/actions/create/types';
|
||||
import type { AppFeaturesService } from '../lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from '../lib/product_features_service/product_features_service';
|
||||
|
||||
export interface EndpointAppContextServiceSetupContract {
|
||||
securitySolutionRequestContextFactory: IRequestContextFactory;
|
||||
|
@ -79,7 +79,7 @@ export interface EndpointAppContextServiceStartContract {
|
|||
messageSigningService: MessageSigningServiceInterface | undefined;
|
||||
actionCreateService: ActionCreateService | undefined;
|
||||
esClient: ElasticsearchClient;
|
||||
appFeaturesService: AppFeaturesService;
|
||||
productFeaturesService: ProductFeaturesService;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
}
|
||||
|
||||
|
@ -117,17 +117,17 @@ export class EndpointAppContextService {
|
|||
featureUsageService,
|
||||
endpointMetadataService,
|
||||
esClient,
|
||||
appFeaturesService,
|
||||
productFeaturesService,
|
||||
savedObjectsClient,
|
||||
} = dependencies;
|
||||
|
||||
registerIngestCallback(
|
||||
'agentPolicyCreate',
|
||||
getAgentPolicyCreateCallback(logger, appFeaturesService)
|
||||
getAgentPolicyCreateCallback(logger, productFeaturesService)
|
||||
);
|
||||
registerIngestCallback(
|
||||
'agentPolicyUpdate',
|
||||
getAgentPolicyUpdateCallback(logger, appFeaturesService)
|
||||
getAgentPolicyUpdateCallback(logger, productFeaturesService)
|
||||
);
|
||||
|
||||
registerIngestCallback(
|
||||
|
@ -140,7 +140,7 @@ export class EndpointAppContextService {
|
|||
licenseService,
|
||||
exceptionListsClient,
|
||||
this.setupDependencies.cloud,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -158,7 +158,7 @@ export class EndpointAppContextService {
|
|||
endpointMetadataService,
|
||||
this.setupDependencies.cloud,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -194,7 +194,7 @@ export class EndpointAppContextService {
|
|||
}
|
||||
|
||||
public async getEndpointAuthz(request: KibanaRequest): Promise<EndpointAuthz> {
|
||||
if (!this.startDependencies?.appFeaturesService) {
|
||||
if (!this.startDependencies?.productFeaturesService) {
|
||||
throw new EndpointAppContentServicesNotStartedError();
|
||||
}
|
||||
const fleetAuthz = await this.getFleetAuthzService().fromRequest(request);
|
||||
|
@ -203,7 +203,7 @@ export class EndpointAppContextService {
|
|||
this.getLicenseService(),
|
||||
fleetAuthz,
|
||||
userRoles,
|
||||
this.startDependencies.appFeaturesService
|
||||
this.startDependencies.productFeaturesService
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,25 +9,25 @@ import { createMockEndpointAppContextServiceStartContract } from '../mocks';
|
|||
import type { Logger } from '@kbn/logging';
|
||||
import type { EndpointInternalFleetServicesInterface } from '../services/fleet';
|
||||
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../../lib/app_features_service/mocks';
|
||||
import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service';
|
||||
import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks';
|
||||
import { turnOffAgentPolicyFeatures } from './turn_off_agent_policy_features';
|
||||
|
||||
describe('Turn Off Agent Policy Features Migration', () => {
|
||||
let fleetServices: EndpointInternalFleetServicesInterface;
|
||||
let appFeatureService: AppFeaturesService;
|
||||
let productFeatureService: ProductFeaturesService;
|
||||
let logger: Logger;
|
||||
|
||||
const callTurnOffAgentPolicyFeatures = () =>
|
||||
turnOffAgentPolicyFeatures(fleetServices, appFeatureService, logger);
|
||||
turnOffAgentPolicyFeatures(fleetServices, productFeatureService, logger);
|
||||
|
||||
beforeEach(() => {
|
||||
const endpointContextStartContract = createMockEndpointAppContextServiceStartContract();
|
||||
|
||||
({ logger } = endpointContextStartContract);
|
||||
|
||||
appFeatureService = endpointContextStartContract.appFeaturesService;
|
||||
productFeatureService = endpointContextStartContract.productFeaturesService;
|
||||
fleetServices = endpointContextStartContract.endpointFleetServicesFactory.asInternalUser();
|
||||
});
|
||||
|
||||
|
@ -46,8 +46,8 @@ describe('Turn Off Agent Policy Features Migration', () => {
|
|||
|
||||
describe('and `agentTamperProtection` is disabled', () => {
|
||||
beforeEach(() => {
|
||||
appFeatureService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_agent_tamper_protection')
|
||||
productFeatureService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_agent_tamper_protection')
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -6,27 +6,27 @@
|
|||
*/
|
||||
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import type { EndpointInternalFleetServicesInterface } from '../services/fleet';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service';
|
||||
|
||||
export const turnOffAgentPolicyFeatures = async (
|
||||
fleetServices: EndpointInternalFleetServicesInterface,
|
||||
appFeaturesService: AppFeaturesService,
|
||||
productFeaturesService: ProductFeaturesService,
|
||||
logger: Logger
|
||||
): Promise<void> => {
|
||||
const log = logger.get('endpoint', 'agentPolicyFeatures');
|
||||
|
||||
if (appFeaturesService.isEnabled(AppFeatureSecurityKey.endpointAgentTamperProtection)) {
|
||||
if (productFeaturesService.isEnabled(ProductFeatureSecurityKey.endpointAgentTamperProtection)) {
|
||||
log.info(
|
||||
`App feature [${AppFeatureSecurityKey.endpointAgentTamperProtection}] is enabled. Nothing to do!`
|
||||
`App feature [${ProductFeatureSecurityKey.endpointAgentTamperProtection}] is enabled. Nothing to do!`
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(
|
||||
`App feature [${AppFeatureSecurityKey.endpointAgentTamperProtection}] is disabled. Checking fleet agent policies for compliance`
|
||||
`App feature [${ProductFeatureSecurityKey.endpointAgentTamperProtection}] is disabled. Checking fleet agent policies for compliance`
|
||||
);
|
||||
|
||||
const { agentPolicy: agentPolicyService, internalSoClient } = fleetServices;
|
||||
|
|
|
@ -10,24 +10,24 @@ import type { Logger } from '@kbn/logging';
|
|||
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
|
||||
import type { EndpointInternalFleetServicesInterface } from '../services/fleet';
|
||||
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { turnOffPolicyProtectionsIfNotSupported } from './turn_off_policy_protections';
|
||||
import { FleetPackagePolicyGenerator } from '../../../common/endpoint/data_generators/fleet_package_policy_generator';
|
||||
import type { PolicyData } from '../../../common/endpoint/types';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { PromiseResolvedValue } from '../../../common/endpoint/types/utility_types';
|
||||
import { ensureOnlyEventCollectionIsAllowed } from '../../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../../lib/app_features_service/mocks';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service';
|
||||
import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks';
|
||||
|
||||
describe('Turn Off Policy Protections Migration', () => {
|
||||
let esClient: ElasticsearchClient;
|
||||
let fleetServices: EndpointInternalFleetServicesInterface;
|
||||
let appFeatureService: AppFeaturesService;
|
||||
let productFeatureService: ProductFeaturesService;
|
||||
let logger: Logger;
|
||||
|
||||
const callTurnOffPolicyProtections = () =>
|
||||
turnOffPolicyProtectionsIfNotSupported(esClient, fleetServices, appFeatureService, logger);
|
||||
turnOffPolicyProtectionsIfNotSupported(esClient, fleetServices, productFeatureService, logger);
|
||||
|
||||
const generatePolicyMock = (
|
||||
policyGenerator: FleetPackagePolicyGenerator,
|
||||
|
@ -60,7 +60,7 @@ describe('Turn Off Policy Protections Migration', () => {
|
|||
|
||||
({ esClient, logger } = endpointContextStartContract);
|
||||
|
||||
appFeatureService = endpointContextStartContract.appFeaturesService;
|
||||
productFeatureService = endpointContextStartContract.productFeaturesService;
|
||||
fleetServices = endpointContextStartContract.endpointFleetServicesFactory.asInternalUser();
|
||||
});
|
||||
|
||||
|
@ -89,8 +89,8 @@ describe('Turn Off Policy Protections Migration', () => {
|
|||
policyGenerator = new FleetPackagePolicyGenerator('seed');
|
||||
const packagePolicyListSrv = fleetServices.packagePolicy.list as jest.Mock;
|
||||
|
||||
appFeatureService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_protection_updates')
|
||||
productFeatureService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_protection_updates')
|
||||
);
|
||||
|
||||
page1Items = [
|
||||
|
@ -160,8 +160,8 @@ describe('Turn Off Policy Protections Migration', () => {
|
|||
policyGenerator = new FleetPackagePolicyGenerator('seed');
|
||||
const packagePolicyListSrv = fleetServices.packagePolicy.list as jest.Mock;
|
||||
|
||||
appFeatureService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
productFeatureService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
);
|
||||
|
||||
page1Items = [
|
||||
|
@ -253,8 +253,8 @@ describe('Turn Off Policy Protections Migration', () => {
|
|||
policyGenerator = new FleetPackagePolicyGenerator('seed');
|
||||
const packagePolicyListSrv = fleetServices.packagePolicy.list as jest.Mock;
|
||||
|
||||
appFeatureService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter(
|
||||
productFeatureService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter(
|
||||
(key) => key !== 'endpoint_policy_protections' && key !== 'endpoint_protection_updates'
|
||||
)
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
|
||||
import type { UpdatePackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import type { AuthenticatedUser } from '@kbn/security-plugin/common';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
ensureOnlyEventCollectionIsAllowed,
|
||||
isPolicySetToEventCollectionOnly,
|
||||
|
@ -16,33 +16,33 @@ import {
|
|||
import type { PolicyData } from '../../../common/endpoint/types';
|
||||
import type { EndpointInternalFleetServicesInterface } from '../services/fleet';
|
||||
import { getPolicyDataForUpdate } from '../../../common/endpoint/service/policy';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service';
|
||||
|
||||
export const turnOffPolicyProtectionsIfNotSupported = async (
|
||||
esClient: ElasticsearchClient,
|
||||
fleetServices: EndpointInternalFleetServicesInterface,
|
||||
appFeaturesService: AppFeaturesService,
|
||||
productFeaturesService: ProductFeaturesService,
|
||||
logger: Logger
|
||||
): Promise<void> => {
|
||||
const log = logger.get('endpoint', 'policyProtections');
|
||||
|
||||
const isProtectionUpdatesFeatureEnabled = appFeaturesService.isEnabled(
|
||||
AppFeatureSecurityKey.endpointProtectionUpdates
|
||||
const isProtectionUpdatesFeatureEnabled = productFeaturesService.isEnabled(
|
||||
ProductFeatureSecurityKey.endpointProtectionUpdates
|
||||
);
|
||||
|
||||
const isPolicyProtectionsEnabled = appFeaturesService.isEnabled(
|
||||
AppFeatureSecurityKey.endpointPolicyProtections
|
||||
const isPolicyProtectionsEnabled = productFeaturesService.isEnabled(
|
||||
ProductFeatureSecurityKey.endpointPolicyProtections
|
||||
);
|
||||
|
||||
if (isPolicyProtectionsEnabled) {
|
||||
log.info(
|
||||
`App feature [${AppFeatureSecurityKey.endpointPolicyProtections}] is enabled. Nothing to do!`
|
||||
`App feature [${ProductFeatureSecurityKey.endpointPolicyProtections}] is enabled. Nothing to do!`
|
||||
);
|
||||
}
|
||||
|
||||
if (isProtectionUpdatesFeatureEnabled) {
|
||||
log.info(
|
||||
`App feature [${AppFeatureSecurityKey.endpointProtectionUpdates}] is enabled. Nothing to do!`
|
||||
`App feature [${ProductFeatureSecurityKey.endpointProtectionUpdates}] is enabled. Nothing to do!`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -52,13 +52,13 @@ export const turnOffPolicyProtectionsIfNotSupported = async (
|
|||
|
||||
if (!isPolicyProtectionsEnabled) {
|
||||
log.info(
|
||||
`App feature [${AppFeatureSecurityKey.endpointPolicyProtections}] is disabled. Checking endpoint integration policies for compliance`
|
||||
`App feature [${ProductFeatureSecurityKey.endpointPolicyProtections}] is disabled. Checking endpoint integration policies for compliance`
|
||||
);
|
||||
}
|
||||
|
||||
if (!isProtectionUpdatesFeatureEnabled) {
|
||||
log.info(
|
||||
`App feature [${AppFeatureSecurityKey.endpointProtectionUpdates}] is disabled. Checking endpoint integration policies for compliance`
|
||||
`App feature [${ProductFeatureSecurityKey.endpointProtectionUpdates}] is disabled. Checking endpoint integration policies for compliance`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ import type { EndpointAuthz } from '../../common/endpoint/types/authz';
|
|||
import { EndpointFleetServicesFactory } from './services/fleet';
|
||||
import { createLicenseServiceMock } from '../../common/license/mocks';
|
||||
import { createFeatureUsageServiceMock } from './services/feature_usage/mocks';
|
||||
import { createAppFeaturesServiceMock } from '../lib/app_features_service/mocks';
|
||||
import { createProductFeaturesServiceMock } from '../lib/product_features_service/mocks';
|
||||
|
||||
/**
|
||||
* Creates a mocked EndpointAppContext.
|
||||
|
@ -170,7 +170,7 @@ export const createMockEndpointAppContextServiceStartContract =
|
|||
savedObjectsStart
|
||||
);
|
||||
const experimentalFeatures = config.experimentalFeatures;
|
||||
const appFeaturesService = createAppFeaturesServiceMock(
|
||||
const productFeaturesService = createProductFeaturesServiceMock(
|
||||
undefined,
|
||||
experimentalFeatures,
|
||||
undefined,
|
||||
|
@ -224,7 +224,7 @@ export const createMockEndpointAppContextServiceStartContract =
|
|||
actionCreateService: undefined,
|
||||
createFleetActionsClient: jest.fn((_) => fleetActionsClientMock),
|
||||
esClient: elasticsearchClientMock.createElasticsearchClient(),
|
||||
appFeaturesService,
|
||||
productFeaturesService,
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ import { createPackagePolicyServiceMock } from '@kbn/fleet-plugin/server/mocks';
|
|||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import { listMock } from '@kbn/lists-plugin/server/mocks';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
import {
|
||||
createPackagePolicyWithInitialManifestMock,
|
||||
createPackagePolicyWithManifestMock,
|
||||
|
@ -28,8 +28,8 @@ import { createEndpointArtifactClientMock, getManifestClientMock } from '../mock
|
|||
import type { ManifestManagerContext } from './manifest_manager';
|
||||
import { ManifestManager } from './manifest_manager';
|
||||
import { parseExperimentalConfigValue } from '../../../../../common/experimental_features';
|
||||
import { createAppFeaturesServiceMock } from '../../../../lib/app_features_service/mocks';
|
||||
import type { AppFeaturesService } from '../../../../lib/app_features_service/app_features_service';
|
||||
import { createProductFeaturesServiceMock } from '../../../../lib/product_features_service/mocks';
|
||||
import type { ProductFeaturesService } from '../../../../lib/product_features_service/product_features_service';
|
||||
|
||||
export const createExceptionListResponse = (data: ExceptionListItemSchema[], total?: number) => ({
|
||||
data,
|
||||
|
@ -71,28 +71,28 @@ export interface ManifestManagerMockOptions {
|
|||
exceptionListClient: ExceptionListClient;
|
||||
packagePolicyService: jest.Mocked<PackagePolicyClient>;
|
||||
savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
appFeaturesService: AppFeaturesService;
|
||||
productFeaturesService: ProductFeaturesService;
|
||||
}
|
||||
|
||||
export const buildManifestManagerMockOptions = (
|
||||
opts: Partial<ManifestManagerMockOptions>,
|
||||
customAppFeatures?: AppFeatureKeys
|
||||
customProductFeatures?: ProductFeatureKeys
|
||||
): ManifestManagerMockOptions => {
|
||||
const savedObjectMock = savedObjectsClientMock.create();
|
||||
return {
|
||||
exceptionListClient: listMock.getExceptionListClient(savedObjectMock),
|
||||
packagePolicyService: createPackagePolicyServiceMock(),
|
||||
savedObjectsClient: savedObjectMock,
|
||||
appFeaturesService: createAppFeaturesServiceMock(customAppFeatures),
|
||||
productFeaturesService: createProductFeaturesServiceMock(customProductFeatures),
|
||||
...opts,
|
||||
};
|
||||
};
|
||||
|
||||
export const buildManifestManagerContextMock = (
|
||||
opts: Partial<ManifestManagerMockOptions>,
|
||||
customAppFeatures?: AppFeatureKeys
|
||||
customProductFeatures?: ProductFeatureKeys
|
||||
): ManifestManagerContext => {
|
||||
const fullOpts = buildManifestManagerMockOptions(opts, customAppFeatures);
|
||||
const fullOpts = buildManifestManagerMockOptions(opts, customProductFeatures);
|
||||
|
||||
return {
|
||||
...fullOpts,
|
||||
|
|
|
@ -37,7 +37,7 @@ import type { EndpointArtifactClientInterface } from '../artifact_client';
|
|||
import { InvalidInternalManifestError } from '../errors';
|
||||
import { EndpointError } from '../../../../../common/endpoint/errors';
|
||||
import type { Artifact } from '@kbn/fleet-plugin/server';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types/src/response/exception_list_item_schema';
|
||||
import {
|
||||
createFetchAllArtifactsIterableMock,
|
||||
|
@ -775,7 +775,7 @@ describe('ManifestManager', () => {
|
|||
tags: ['policy:all'],
|
||||
});
|
||||
const context = buildManifestManagerContextMock({}, [
|
||||
AppFeatureSecurityKey.endpointArtifactManagement,
|
||||
ProductFeatureSecurityKey.endpointArtifactManagement,
|
||||
]);
|
||||
const manifestManager = new ManifestManager(context);
|
||||
|
||||
|
@ -859,8 +859,8 @@ describe('ManifestManager', () => {
|
|||
tags: ['policy:all'],
|
||||
});
|
||||
const context = buildManifestManagerContextMock({}, [
|
||||
AppFeatureSecurityKey.endpointArtifactManagement,
|
||||
AppFeatureSecurityKey.endpointResponseActions,
|
||||
ProductFeatureSecurityKey.endpointArtifactManagement,
|
||||
ProductFeatureSecurityKey.endpointResponseActions,
|
||||
]);
|
||||
const manifestManager = new ManifestManager(context);
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ import type { PackagePolicy } from '@kbn/fleet-plugin/common';
|
|||
import type { Artifact, PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { ExceptionListClient } from '@kbn/lists-plugin/server';
|
||||
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { stringify } from '../../../utils/stringify';
|
||||
import { QueueProcessor } from '../../../utils/queue_processor';
|
||||
import type { AppFeaturesService } from '../../../../lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from '../../../../lib/product_features_service/product_features_service';
|
||||
import type { ExperimentalFeatures } from '../../../../../common';
|
||||
import type { ManifestSchemaVersion } from '../../../../../common/endpoint/schema/common';
|
||||
import {
|
||||
|
@ -81,7 +81,7 @@ export interface ManifestManagerContext {
|
|||
experimentalFeatures: ExperimentalFeatures;
|
||||
packagerTaskPackagePolicyUpdateBatchSize: number;
|
||||
esClient: ElasticsearchClient;
|
||||
appFeaturesService: AppFeaturesService;
|
||||
productFeaturesService: ProductFeaturesService;
|
||||
}
|
||||
|
||||
const getArtifactIds = (manifest: ManifestSchema) =>
|
||||
|
@ -103,7 +103,7 @@ export class ManifestManager {
|
|||
protected cachedExceptionsListsByOs: Map<string, ExceptionListItemSchema[]>;
|
||||
protected packagerTaskPackagePolicyUpdateBatchSize: number;
|
||||
protected esClient: ElasticsearchClient;
|
||||
protected appFeaturesService: AppFeaturesService;
|
||||
protected productFeaturesService: ProductFeaturesService;
|
||||
|
||||
constructor(context: ManifestManagerContext) {
|
||||
this.artifactClient = context.artifactClient;
|
||||
|
@ -117,7 +117,7 @@ export class ManifestManager {
|
|||
this.packagerTaskPackagePolicyUpdateBatchSize =
|
||||
context.packagerTaskPackagePolicyUpdateBatchSize;
|
||||
this.esClient = context.esClient;
|
||||
this.appFeaturesService = context.appFeaturesService;
|
||||
this.productFeaturesService = context.productFeaturesService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,9 +151,9 @@ export class ManifestManager {
|
|||
let itemsByListId: ExceptionListItemSchema[] = [];
|
||||
if (
|
||||
(listId === ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id &&
|
||||
this.appFeaturesService.isEnabled(AppFeatureKey.endpointResponseActions)) ||
|
||||
this.productFeaturesService.isEnabled(ProductFeatureKey.endpointResponseActions)) ||
|
||||
(listId !== ENDPOINT_ARTIFACT_LISTS.hostIsolationExceptions.id &&
|
||||
this.appFeaturesService.isEnabled(AppFeatureKey.endpointArtifactManagement))
|
||||
this.productFeaturesService.isEnabled(ProductFeatureKey.endpointArtifactManagement))
|
||||
) {
|
||||
itemsByListId = await getAllItemsFromEndpointExceptionList({
|
||||
elClient,
|
||||
|
|
|
@ -32,7 +32,10 @@ import {
|
|||
getPackagePolicyUpdateCallback,
|
||||
} from './fleet_integration';
|
||||
import type { KibanaRequest, Logger } from '@kbn/core/server';
|
||||
import { ALL_APP_FEATURE_KEYS, AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
ALL_PRODUCT_FEATURE_KEYS,
|
||||
ProductFeatureSecurityKey,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import { requestContextMock } from '../lib/detection_engine/routes/__mocks__';
|
||||
import { requestContextFactoryMock } from '../request_context_factory.mock';
|
||||
import type { EndpointAppContextServiceStartContract } from '../endpoint/endpoint_app_context_services';
|
||||
|
@ -60,8 +63,8 @@ import { createMockPolicyData } from '../endpoint/services/feature_usage/mocks';
|
|||
import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../common/endpoint/service/artifacts/constants';
|
||||
import { ENDPOINT_EVENT_FILTERS_LIST_ID } from '@kbn/securitysolution-list-constants';
|
||||
import { disableProtections } from '../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeaturesService } from '../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../lib/app_features_service/mocks';
|
||||
import type { ProductFeaturesService } from '../lib/product_features_service/product_features_service';
|
||||
import { createProductFeaturesServiceMock } from '../lib/product_features_service/mocks';
|
||||
import * as moment from 'moment';
|
||||
import type { PostAgentPolicyCreateCallback } from '@kbn/fleet-plugin/server/types';
|
||||
|
||||
|
@ -87,7 +90,7 @@ describe('ingest_integration tests ', () => {
|
|||
});
|
||||
const generator = new EndpointDocGenerator();
|
||||
const cloudService = cloudMock.createSetup();
|
||||
let appFeaturesService: AppFeaturesService;
|
||||
let productFeaturesService: ProductFeaturesService;
|
||||
|
||||
beforeEach(() => {
|
||||
endpointAppContextMock = createMockEndpointAppContextServiceStartContract();
|
||||
|
@ -96,7 +99,7 @@ describe('ingest_integration tests ', () => {
|
|||
licenseEmitter = new Subject();
|
||||
licenseService = new LicenseService();
|
||||
licenseService.start(licenseEmitter);
|
||||
appFeaturesService = endpointAppContextMock.appFeaturesService;
|
||||
productFeaturesService = endpointAppContextMock.productFeaturesService;
|
||||
|
||||
jest
|
||||
.spyOn(endpointAppContextMock.endpointMetadataService, 'getFleetEndpointPackagePolicy')
|
||||
|
@ -153,7 +156,7 @@ describe('ingest_integration tests ', () => {
|
|||
licenseService,
|
||||
exceptionListClient,
|
||||
cloudService,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
|
||||
return callback(
|
||||
|
@ -389,15 +392,15 @@ describe('ingest_integration tests ', () => {
|
|||
});
|
||||
|
||||
describe('agent policy update callback', () => {
|
||||
it('AppFeature disabled - returns an error if higher tier features are turned on in the policy', async () => {
|
||||
it('ProductFeature disabled - returns an error if higher tier features are turned on in the policy', async () => {
|
||||
const logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter(
|
||||
(key) => key !== AppFeatureSecurityKey.endpointAgentTamperProtection
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter(
|
||||
(key) => key !== ProductFeatureSecurityKey.endpointAgentTamperProtection
|
||||
)
|
||||
);
|
||||
const callback = getAgentPolicyUpdateCallback(logger, appFeaturesService);
|
||||
const callback = getAgentPolicyUpdateCallback(logger, productFeaturesService);
|
||||
|
||||
const policyConfig = generator.generateAgentPolicy();
|
||||
policyConfig.is_protected = true;
|
||||
|
@ -406,15 +409,15 @@ describe('ingest_integration tests ', () => {
|
|||
'Agent Tamper Protection is not allowed in current environment'
|
||||
);
|
||||
});
|
||||
it('AppFeature disabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
it('ProductFeature disabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
const logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter(
|
||||
(key) => key !== AppFeatureSecurityKey.endpointAgentTamperProtection
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter(
|
||||
(key) => key !== ProductFeatureSecurityKey.endpointAgentTamperProtection
|
||||
)
|
||||
);
|
||||
const callback = getAgentPolicyUpdateCallback(logger, appFeaturesService);
|
||||
const callback = getAgentPolicyUpdateCallback(logger, productFeaturesService);
|
||||
|
||||
const policyConfig = generator.generateAgentPolicy();
|
||||
|
||||
|
@ -422,10 +425,10 @@ describe('ingest_integration tests ', () => {
|
|||
|
||||
expect(updatedPolicyConfig).toEqual(policyConfig);
|
||||
});
|
||||
it('AppFeature enabled - returns agent policy if higher tier features are turned on in the policy', async () => {
|
||||
it('ProductFeature enabled - returns agent policy if higher tier features are turned on in the policy', async () => {
|
||||
const logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
|
||||
const callback = getAgentPolicyUpdateCallback(logger, appFeaturesService);
|
||||
const callback = getAgentPolicyUpdateCallback(logger, productFeaturesService);
|
||||
|
||||
const policyConfig = generator.generateAgentPolicy();
|
||||
policyConfig.is_protected = true;
|
||||
|
@ -434,10 +437,10 @@ describe('ingest_integration tests ', () => {
|
|||
|
||||
expect(updatedPolicyConfig).toEqual(policyConfig);
|
||||
});
|
||||
it('AppFeature enabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
it('ProductFeature enabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
const logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
|
||||
const callback = getAgentPolicyUpdateCallback(logger, appFeaturesService);
|
||||
const callback = getAgentPolicyUpdateCallback(logger, productFeaturesService);
|
||||
const policyConfig = generator.generateAgentPolicy();
|
||||
|
||||
const updatedPolicyConfig = await callback(policyConfig);
|
||||
|
@ -453,17 +456,17 @@ describe('ingest_integration tests ', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
logger = loggingSystemMock.create().get('ingest_integration.test');
|
||||
callback = getAgentPolicyCreateCallback(logger, appFeaturesService);
|
||||
callback = getAgentPolicyCreateCallback(logger, productFeaturesService);
|
||||
policyConfig = generator.generateAgentPolicy();
|
||||
});
|
||||
|
||||
it('AppFeature disabled - returns an error if higher tier features are turned on in the policy', async () => {
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter(
|
||||
(key) => key !== AppFeatureSecurityKey.endpointAgentTamperProtection
|
||||
it('ProductFeature disabled - returns an error if higher tier features are turned on in the policy', async () => {
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter(
|
||||
(key) => key !== ProductFeatureSecurityKey.endpointAgentTamperProtection
|
||||
)
|
||||
);
|
||||
callback = getAgentPolicyCreateCallback(logger, appFeaturesService);
|
||||
callback = getAgentPolicyCreateCallback(logger, productFeaturesService);
|
||||
policyConfig.is_protected = true;
|
||||
|
||||
await expect(() => callback(policyConfig)).rejects.toThrow(
|
||||
|
@ -471,26 +474,26 @@ describe('ingest_integration tests ', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('AppFeature disabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter(
|
||||
(key) => key !== AppFeatureSecurityKey.endpointAgentTamperProtection
|
||||
it('ProductFeature disabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter(
|
||||
(key) => key !== ProductFeatureSecurityKey.endpointAgentTamperProtection
|
||||
)
|
||||
);
|
||||
callback = getAgentPolicyCreateCallback(logger, appFeaturesService);
|
||||
callback = getAgentPolicyCreateCallback(logger, productFeaturesService);
|
||||
const updatedPolicyConfig = await callback(policyConfig);
|
||||
|
||||
expect(updatedPolicyConfig).toEqual(policyConfig);
|
||||
});
|
||||
|
||||
it('AppFeature enabled - returns agent policy if higher tier features are turned on in the policy', async () => {
|
||||
it('ProductFeature enabled - returns agent policy if higher tier features are turned on in the policy', async () => {
|
||||
policyConfig.is_protected = true;
|
||||
const updatedPolicyConfig = await callback(policyConfig);
|
||||
|
||||
expect(updatedPolicyConfig).toEqual(policyConfig);
|
||||
});
|
||||
|
||||
it('AppFeature enabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
it('ProductFeature enabled - returns agent policy if higher tier features are turned off in the policy', async () => {
|
||||
const updatedPolicyConfig = await callback(policyConfig);
|
||||
|
||||
expect(updatedPolicyConfig).toEqual(policyConfig);
|
||||
|
@ -514,7 +517,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -533,7 +536,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -558,9 +561,9 @@ describe('ingest_integration tests ', () => {
|
|||
|
||||
const validDateYesterday = moment.utc().subtract(1, 'day');
|
||||
|
||||
it('should throw if endpointProtectionUpdates appFeature is disabled and user modifies global_manifest_version', () => {
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_protection_updates')
|
||||
it('should throw if endpointProtectionUpdates productFeature is disabled and user modifies global_manifest_version', () => {
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_protection_updates')
|
||||
);
|
||||
const callback = getPackagePolicyUpdateCallback(
|
||||
endpointAppContextMock.logger,
|
||||
|
@ -569,7 +572,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value.global_manifest_version = '2023-01-01';
|
||||
|
@ -624,7 +627,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = {
|
||||
|
@ -684,7 +687,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = {
|
||||
|
@ -722,7 +725,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
policyConfig.inputs[0]!.config!.policy.value = mockPolicy;
|
||||
|
@ -736,9 +739,9 @@ describe('ingest_integration tests ', () => {
|
|||
expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy);
|
||||
});
|
||||
|
||||
it('should turn off protections if endpointPolicyProtections appFeature is disabled', async () => {
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
it('should turn off protections if endpointPolicyProtections productFeature is disabled', async () => {
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
);
|
||||
const callback = getPackagePolicyUpdateCallback(
|
||||
endpointAppContextMock.logger,
|
||||
|
@ -747,7 +750,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
|
||||
const updatedPolicy = await callback(
|
||||
|
@ -826,7 +829,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
|
||||
|
@ -863,7 +866,7 @@ describe('ingest_integration tests ', () => {
|
|||
endpointAppContextMock.endpointMetadataService,
|
||||
cloudService,
|
||||
esClient,
|
||||
appFeaturesService
|
||||
productFeaturesService
|
||||
);
|
||||
const policyConfig = generator.generatePolicyPackagePolicy();
|
||||
// values should be updated
|
||||
|
|
|
@ -24,12 +24,12 @@ import type {
|
|||
} from '@kbn/fleet-plugin/common';
|
||||
import type { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import type { InfoResponse } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import type {
|
||||
PostAgentPolicyCreateCallback,
|
||||
PostAgentPolicyUpdateCallback,
|
||||
} from '@kbn/fleet-plugin/server/types';
|
||||
import { validatePolicyAgainstAppFeatures } from './handlers/validate_policy_against_app_features';
|
||||
import { validatePolicyAgainstProductFeatures } from './handlers/validate_policy_against_product_features';
|
||||
import { validateEndpointPackagePolicy } from './handlers/validate_endpoint_package_policy';
|
||||
import {
|
||||
isPolicySetToEventCollectionOnly,
|
||||
|
@ -51,7 +51,7 @@ import { notifyProtectionFeatureUsage } from './notify_protection_feature_usage'
|
|||
import type { AnyPolicyCreateConfig } from './types';
|
||||
import { ENDPOINT_INTEGRATION_CONFIG_KEY } from './constants';
|
||||
import { createEventFilters } from './handlers/create_event_filters';
|
||||
import type { AppFeaturesService } from '../lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from '../lib/product_features_service/product_features_service';
|
||||
import { removeProtectionUpdatesNote } from './handlers/remove_protection_updates_note';
|
||||
|
||||
const isEndpointPackagePolicy = <T extends { package?: { name: string } }>(
|
||||
|
@ -90,7 +90,7 @@ export const getPackagePolicyCreateCallback = (
|
|||
licenseService: LicenseService,
|
||||
exceptionsClient: ExceptionListClient | undefined,
|
||||
cloud: CloudSetup,
|
||||
appFeatures: AppFeaturesService
|
||||
productFeatures: ProductFeaturesService
|
||||
): PostPackagePolicyCreateCallback => {
|
||||
return async (
|
||||
newPackagePolicy,
|
||||
|
@ -111,7 +111,7 @@ export const getPackagePolicyCreateCallback = (
|
|||
}
|
||||
|
||||
if (newPackagePolicy?.inputs) {
|
||||
validatePolicyAgainstAppFeatures(newPackagePolicy.inputs, appFeatures);
|
||||
validatePolicyAgainstProductFeatures(newPackagePolicy.inputs, productFeatures);
|
||||
validateEndpointPackagePolicy(newPackagePolicy.inputs);
|
||||
}
|
||||
// Optional endpoint integration configuration
|
||||
|
@ -163,7 +163,7 @@ export const getPackagePolicyCreateCallback = (
|
|||
endpointIntegrationConfig,
|
||||
cloud,
|
||||
esClientInfo,
|
||||
appFeatures
|
||||
productFeatures
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -199,7 +199,7 @@ export const getPackagePolicyUpdateCallback = (
|
|||
endpointMetadataService: EndpointMetadataService,
|
||||
cloud: CloudSetup,
|
||||
esClient: ElasticsearchClient,
|
||||
appFeatures: AppFeaturesService
|
||||
productFeatures: ProductFeaturesService
|
||||
): PutPackagePolicyUpdateCallback => {
|
||||
return async (newPackagePolicy: NewPackagePolicy): Promise<UpdatePackagePolicy> => {
|
||||
if (!isEndpointPackagePolicy(newPackagePolicy)) {
|
||||
|
@ -218,7 +218,7 @@ export const getPackagePolicyUpdateCallback = (
|
|||
);
|
||||
|
||||
// Validate that Endpoint Security policy uses only enabled App Features
|
||||
validatePolicyAgainstAppFeatures(endpointIntegrationData.inputs, appFeatures);
|
||||
validatePolicyAgainstProductFeatures(endpointIntegrationData.inputs, productFeatures);
|
||||
|
||||
validateEndpointPackagePolicy(endpointIntegrationData.inputs);
|
||||
|
||||
|
@ -258,11 +258,11 @@ export const getPackagePolicyUpdateCallback = (
|
|||
// If no Policy Protection allowed (ex. serverless)
|
||||
const eventsOnlyPolicy = isPolicySetToEventCollectionOnly(newEndpointPackagePolicy);
|
||||
if (
|
||||
!appFeatures.isEnabled(AppFeatureSecurityKey.endpointPolicyProtections) &&
|
||||
!productFeatures.isEnabled(ProductFeatureSecurityKey.endpointPolicyProtections) &&
|
||||
!eventsOnlyPolicy.isOnlyCollectingEvents
|
||||
) {
|
||||
logger.warn(
|
||||
`Endpoint integration policy [${endpointIntegrationData.id}][${endpointIntegrationData.name}] adjusted due to [endpointPolicyProtections] appFeature not being enabled. Trigger [${eventsOnlyPolicy.message}]`
|
||||
`Endpoint integration policy [${endpointIntegrationData.id}][${endpointIntegrationData.name}] adjusted due to [endpointPolicyProtections] productFeature not being enabled. Trigger [${eventsOnlyPolicy.message}]`
|
||||
);
|
||||
|
||||
endpointIntegrationData.inputs[0].config.policy.value =
|
||||
|
@ -317,12 +317,12 @@ const throwAgentTamperProtectionUnavailableError = (
|
|||
|
||||
export const getAgentPolicyCreateCallback = (
|
||||
logger: Logger,
|
||||
appFeatures: AppFeaturesService
|
||||
productFeatures: ProductFeaturesService
|
||||
): PostAgentPolicyCreateCallback => {
|
||||
return async (agentPolicy: NewAgentPolicy): Promise<NewAgentPolicy> => {
|
||||
if (
|
||||
agentPolicy.is_protected &&
|
||||
!appFeatures.isEnabled(AppFeatureSecurityKey.endpointAgentTamperProtection)
|
||||
!productFeatures.isEnabled(ProductFeatureSecurityKey.endpointAgentTamperProtection)
|
||||
) {
|
||||
throwAgentTamperProtectionUnavailableError(logger, agentPolicy.name, agentPolicy.id);
|
||||
}
|
||||
|
@ -332,12 +332,12 @@ export const getAgentPolicyCreateCallback = (
|
|||
|
||||
export const getAgentPolicyUpdateCallback = (
|
||||
logger: Logger,
|
||||
appFeatures: AppFeaturesService
|
||||
productFeatures: ProductFeaturesService
|
||||
): PostAgentPolicyUpdateCallback => {
|
||||
return async (agentPolicy: Partial<AgentPolicy>): Promise<Partial<AgentPolicy>> => {
|
||||
if (
|
||||
agentPolicy.is_protected &&
|
||||
!appFeatures.isEnabled(AppFeatureSecurityKey.endpointAgentTamperProtection)
|
||||
!productFeatures.isEnabled(ProductFeatureSecurityKey.endpointAgentTamperProtection)
|
||||
) {
|
||||
throwAgentTamperProtectionUnavailableError(logger, agentPolicy.name, agentPolicy.id);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Subject } from 'rxjs';
|
|||
import type { ILicense } from '@kbn/licensing-plugin/common/types';
|
||||
import { licenseMock } from '@kbn/licensing-plugin/common/licensing.mock';
|
||||
import { cloudMock } from '@kbn/cloud-plugin/server/mocks';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { LicenseService } from '../../../common/license';
|
||||
import { createDefaultPolicy } from './create_default_policy';
|
||||
import { ProtectionModes } from '../../../common/endpoint/types';
|
||||
|
@ -20,8 +20,8 @@ import type {
|
|||
PolicyCreateCloudConfig,
|
||||
PolicyCreateEndpointConfig,
|
||||
} from '../types';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import { createAppFeaturesServiceMock } from '../../lib/app_features_service/mocks';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service';
|
||||
import { createProductFeaturesServiceMock } from '../../lib/product_features_service/mocks';
|
||||
|
||||
describe('Create Default Policy tests ', () => {
|
||||
const cloud = cloudMock.createSetup();
|
||||
|
@ -31,7 +31,7 @@ describe('Create Default Policy tests ', () => {
|
|||
const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold', uid: '' } });
|
||||
let licenseEmitter: Subject<ILicense>;
|
||||
let licenseService: LicenseService;
|
||||
let appFeaturesService: AppFeaturesService;
|
||||
let productFeaturesService: ProductFeaturesService;
|
||||
|
||||
const createDefaultPolicyCallback = async (
|
||||
config: AnyPolicyCreateConfig | undefined
|
||||
|
@ -39,7 +39,7 @@ describe('Create Default Policy tests ', () => {
|
|||
const esClientInfo = await elasticsearchServiceMock.createClusterClient().asInternalUser.info();
|
||||
esClientInfo.cluster_name = '';
|
||||
esClientInfo.cluster_uuid = '';
|
||||
return createDefaultPolicy(licenseService, config, cloud, esClientInfo, appFeaturesService);
|
||||
return createDefaultPolicy(licenseService, config, cloud, esClientInfo, productFeaturesService);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -47,7 +47,7 @@ describe('Create Default Policy tests ', () => {
|
|||
licenseService = new LicenseService();
|
||||
licenseService.start(licenseEmitter);
|
||||
licenseEmitter.next(Platinum); // set license level to platinum
|
||||
appFeaturesService = createAppFeaturesServiceMock();
|
||||
productFeaturesService = createProductFeaturesServiceMock();
|
||||
});
|
||||
|
||||
describe('When no config is set', () => {
|
||||
|
@ -210,9 +210,9 @@ describe('Create Default Policy tests ', () => {
|
|||
expect(policy).toMatchObject(defaultPolicy);
|
||||
});
|
||||
|
||||
it('should set policy to event collection only if endpointPolicyProtections appFeature is disabled', async () => {
|
||||
appFeaturesService = createAppFeaturesServiceMock(
|
||||
ALL_APP_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
it('should set policy to event collection only if endpointPolicyProtections productFeature is disabled', async () => {
|
||||
productFeaturesService = createProductFeaturesServiceMock(
|
||||
ALL_PRODUCT_FEATURE_KEYS.filter((key) => key !== 'endpoint_policy_protections')
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type { CloudSetup } from '@kbn/cloud-plugin/server';
|
||||
import type { InfoResponse } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
policyFactory as policyConfigFactory,
|
||||
policyFactoryWithoutPaidFeatures as policyConfigFactoryWithoutPaidFeatures,
|
||||
|
@ -25,7 +25,7 @@ import {
|
|||
disableProtections,
|
||||
ensureOnlyEventCollectionIsAllowed,
|
||||
} from '../../../common/endpoint/models/policy_config_helpers';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service/product_features_service';
|
||||
|
||||
/**
|
||||
* Create the default endpoint policy based on the current license and configuration type
|
||||
|
@ -35,7 +35,7 @@ export const createDefaultPolicy = (
|
|||
config: AnyPolicyCreateConfig | undefined,
|
||||
cloud: CloudSetup,
|
||||
esClientInfo: InfoResponse,
|
||||
appFeatures: AppFeaturesService
|
||||
productFeatures: ProductFeaturesService
|
||||
): PolicyConfig => {
|
||||
// Pass license and cloud information to use in Policy creation
|
||||
const factoryPolicy = policyConfigFactory(
|
||||
|
@ -57,7 +57,7 @@ export const createDefaultPolicy = (
|
|||
}
|
||||
|
||||
// If no Policy Protection allowed (ex. serverless)
|
||||
if (!appFeatures.isEnabled(AppFeatureSecurityKey.endpointPolicyProtections)) {
|
||||
if (!productFeatures.isEnabled(ProductFeatureSecurityKey.endpointPolicyProtections)) {
|
||||
defaultPolicyPerType = ensureOnlyEventCollectionIsAllowed(defaultPolicyPerType);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +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 type { NewPackagePolicyInput } from '@kbn/fleet-plugin/common';
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/src/app_features_keys';
|
||||
import type { AppFeaturesService } from '../../lib/app_features_service';
|
||||
|
||||
export const validatePolicyAgainstAppFeatures = (
|
||||
inputs: NewPackagePolicyInput[],
|
||||
appFeaturesService: AppFeaturesService
|
||||
): void => {
|
||||
const input = inputs.find((i) => i.type === 'endpoint');
|
||||
if (input?.config?.policy?.value?.global_manifest_version) {
|
||||
const globalManifestVersion = input.config.policy.value.global_manifest_version;
|
||||
if (
|
||||
globalManifestVersion !== 'latest' &&
|
||||
!appFeaturesService.isEnabled(AppFeatureSecurityKey.endpointProtectionUpdates)
|
||||
) {
|
||||
const appFeatureError: Error & { statusCode?: number; apiPassThrough?: boolean } = new Error(
|
||||
'To modify protection updates, you must add at least Endpoint Complete to your project.'
|
||||
);
|
||||
appFeatureError.statusCode = 403;
|
||||
appFeatureError.apiPassThrough = true;
|
||||
throw appFeatureError;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 type { NewPackagePolicyInput } from '@kbn/fleet-plugin/common';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import type { ProductFeaturesService } from '../../lib/product_features_service';
|
||||
|
||||
export const validatePolicyAgainstProductFeatures = (
|
||||
inputs: NewPackagePolicyInput[],
|
||||
productFeaturesService: ProductFeaturesService
|
||||
): void => {
|
||||
const input = inputs.find((i) => i.type === 'endpoint');
|
||||
if (input?.config?.policy?.value?.global_manifest_version) {
|
||||
const globalManifestVersion = input.config.policy.value.global_manifest_version;
|
||||
if (
|
||||
globalManifestVersion !== 'latest' &&
|
||||
!productFeaturesService.isEnabled(ProductFeatureSecurityKey.endpointProtectionUpdates)
|
||||
) {
|
||||
const productFeatureError: Error & { statusCode?: number; apiPassThrough?: boolean } =
|
||||
new Error(
|
||||
'To modify protection updates, you must add at least Endpoint Complete to your project.'
|
||||
);
|
||||
productFeatureError.statusCode = 403;
|
||||
productFeatureError.apiPassThrough = true;
|
||||
throw productFeatureError;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,245 +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 { AppFeaturesService } from './app_features_service';
|
||||
import { AppFeatures } from './app_features';
|
||||
import type { AppFeaturesConfig, BaseKibanaFeatureConfig } from '@kbn/security-solution-features';
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import type { ExperimentalFeatures } from '../../../common';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
import type { AppFeaturesConfigurator } from './types';
|
||||
import type {
|
||||
AssistantSubFeatureId,
|
||||
CasesSubFeatureId,
|
||||
SecuritySubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { httpServiceMock } from '@kbn/core-http-server-mocks';
|
||||
import type {
|
||||
KibanaRequest,
|
||||
LifecycleResponseFactory,
|
||||
OnPostAuthHandler,
|
||||
} from '@kbn/core-http-server';
|
||||
|
||||
jest.mock('./app_features');
|
||||
const MockedAppFeatures = AppFeatures as unknown as jest.MockedClass<typeof AppFeatures>;
|
||||
|
||||
const appFeature = {
|
||||
subFeaturesMap: new Map(),
|
||||
baseKibanaFeature: {} as BaseKibanaFeatureConfig,
|
||||
baseKibanaSubFeatureIds: [],
|
||||
};
|
||||
const mockGetFeature = jest.fn().mockReturnValue(appFeature);
|
||||
jest.mock('@kbn/security-solution-features/app_features', () => ({
|
||||
getAssistantFeature: () => mockGetFeature(),
|
||||
getCasesFeature: () => mockGetFeature(),
|
||||
getSecurityFeature: () => mockGetFeature(),
|
||||
}));
|
||||
|
||||
describe('AppFeaturesService', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should create AppFeatureService instance', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
|
||||
expect(mockGetFeature).toHaveBeenCalledTimes(3);
|
||||
expect(MockedAppFeatures).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should init all AppFeatures when initialized', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
|
||||
const featuresSetup = featuresPluginMock.createSetup();
|
||||
appFeaturesService.init(featuresSetup);
|
||||
|
||||
expect(MockedAppFeatures.mock.instances[0].init).toHaveBeenCalledWith(featuresSetup);
|
||||
expect(MockedAppFeatures.mock.instances[1].init).toHaveBeenCalledWith(featuresSetup);
|
||||
expect(MockedAppFeatures.mock.instances[2].init).toHaveBeenCalledWith(featuresSetup);
|
||||
});
|
||||
|
||||
it('should configure AppFeatures', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
|
||||
const featuresSetup = featuresPluginMock.createSetup();
|
||||
appFeaturesService.init(featuresSetup);
|
||||
|
||||
const mockSecurityConfig = new Map() as AppFeaturesConfig<SecuritySubFeatureId>;
|
||||
const mockCasesConfig = new Map() as AppFeaturesConfig<CasesSubFeatureId>;
|
||||
const mockAssistantConfig = new Map() as AppFeaturesConfig<AssistantSubFeatureId>;
|
||||
|
||||
const configurator: AppFeaturesConfigurator = {
|
||||
security: jest.fn(() => mockSecurityConfig),
|
||||
cases: jest.fn(() => mockCasesConfig),
|
||||
securityAssistant: jest.fn(() => mockAssistantConfig),
|
||||
};
|
||||
appFeaturesService.setAppFeaturesConfigurator(configurator);
|
||||
|
||||
expect(configurator.security).toHaveBeenCalled();
|
||||
expect(configurator.cases).toHaveBeenCalled();
|
||||
expect(configurator.securityAssistant).toHaveBeenCalled();
|
||||
|
||||
expect(MockedAppFeatures.mock.instances[0].setConfig).toHaveBeenCalledWith(mockSecurityConfig);
|
||||
expect(MockedAppFeatures.mock.instances[1].setConfig).toHaveBeenCalledWith(mockCasesConfig);
|
||||
expect(MockedAppFeatures.mock.instances[2].setConfig).toHaveBeenCalledWith(mockAssistantConfig);
|
||||
});
|
||||
|
||||
it('should return isEnabled for enabled features', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
|
||||
const featuresSetup = featuresPluginMock.createSetup();
|
||||
appFeaturesService.init(featuresSetup);
|
||||
|
||||
const mockSecurityConfig = new Map([
|
||||
[AppFeatureKey.advancedInsights, {}],
|
||||
[AppFeatureKey.endpointExceptions, {}],
|
||||
]) as AppFeaturesConfig<SecuritySubFeatureId>;
|
||||
const mockCasesConfig = new Map([
|
||||
[AppFeatureKey.casesConnectors, {}],
|
||||
]) as AppFeaturesConfig<CasesSubFeatureId>;
|
||||
const mockAssistantConfig = new Map([
|
||||
[AppFeatureKey.assistant, {}],
|
||||
]) as AppFeaturesConfig<AssistantSubFeatureId>;
|
||||
|
||||
const configurator: AppFeaturesConfigurator = {
|
||||
security: jest.fn(() => mockSecurityConfig),
|
||||
cases: jest.fn(() => mockCasesConfig),
|
||||
securityAssistant: jest.fn(() => mockAssistantConfig),
|
||||
};
|
||||
appFeaturesService.setAppFeaturesConfigurator(configurator);
|
||||
|
||||
expect(appFeaturesService.isEnabled(AppFeatureKey.advancedInsights)).toEqual(true);
|
||||
expect(appFeaturesService.isEnabled(AppFeatureKey.endpointExceptions)).toEqual(true);
|
||||
expect(appFeaturesService.isEnabled(AppFeatureKey.casesConnectors)).toEqual(true);
|
||||
expect(appFeaturesService.isEnabled(AppFeatureKey.assistant)).toEqual(true);
|
||||
expect(appFeaturesService.isEnabled(AppFeatureKey.externalRuleActions)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should call isApiPrivilegeEnabled for api actions', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
|
||||
appFeaturesService.isApiPrivilegeEnabled('writeEndpointExceptions');
|
||||
|
||||
expect(MockedAppFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-writeEndpointExceptions'
|
||||
);
|
||||
expect(MockedAppFeatures.mock.instances[1].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-writeEndpointExceptions'
|
||||
);
|
||||
expect(MockedAppFeatures.mock.instances[2].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-writeEndpointExceptions'
|
||||
);
|
||||
});
|
||||
|
||||
describe('registerApiAccessControl', () => {
|
||||
const mockHttpSetup = httpServiceMock.createSetupContract();
|
||||
let lastRegisteredFn: OnPostAuthHandler;
|
||||
mockHttpSetup.registerOnPostAuth.mockImplementation((fn) => {
|
||||
lastRegisteredFn = fn;
|
||||
});
|
||||
|
||||
const getReq = (tags: string[] = []) =>
|
||||
({
|
||||
route: { options: { tags } },
|
||||
url: { pathname: '', search: '' },
|
||||
} as unknown as KibanaRequest);
|
||||
const res = { notFound: jest.fn() } as unknown as LifecycleResponseFactory;
|
||||
const toolkit = { next: jest.fn() };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should register api authorization http route interceptor', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
appFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
expect(mockHttpSetup.registerOnPostAuth).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should authorize when no tag matches', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
appFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
lastRegisteredFn(getReq(['access:something', 'access:securitySolution']), res, toolkit);
|
||||
|
||||
expect(MockedAppFeatures.mock.instances[0].isActionRegistered).not.toHaveBeenCalled();
|
||||
expect(res.notFound).not.toHaveBeenCalled();
|
||||
expect(toolkit.next).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should check when tag matches and return not found when not action registered', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
appFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
(MockedAppFeatures.mock.instances[0].isActionRegistered as jest.Mock).mockReturnValueOnce(
|
||||
false
|
||||
);
|
||||
lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit);
|
||||
|
||||
expect(MockedAppFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-foo'
|
||||
);
|
||||
expect(res.notFound).toHaveBeenCalledTimes(1);
|
||||
expect(toolkit.next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check when tag matches and continue when action registered', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
appFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
(MockedAppFeatures.mock.instances[0].isActionRegistered as jest.Mock).mockReturnValueOnce(
|
||||
true
|
||||
);
|
||||
lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit);
|
||||
|
||||
expect(MockedAppFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-foo'
|
||||
);
|
||||
expect(res.notFound).not.toHaveBeenCalled();
|
||||
expect(toolkit.next).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should check when appFeature tag when it matches and return not found when not enabled', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
appFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
appFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(false);
|
||||
|
||||
lastRegisteredFn(getReq(['securitySolutionAppFeature:foo']), res, toolkit);
|
||||
|
||||
expect(appFeaturesService.isEnabled).toHaveBeenCalledWith('foo');
|
||||
expect(res.notFound).toHaveBeenCalledTimes(1);
|
||||
expect(toolkit.next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check when appFeature tag when it matches and continue when enabled', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const appFeaturesService = new AppFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
appFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
appFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(true);
|
||||
|
||||
lastRegisteredFn(getReq(['securitySolutionAppFeature:foo']), res, toolkit);
|
||||
|
||||
expect(appFeaturesService.isEnabled).toHaveBeenCalledWith('foo');
|
||||
expect(res.notFound).not.toHaveBeenCalled();
|
||||
expect(toolkit.next).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,4 +4,4 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
export { AppFeaturesService } from './app_features_service';
|
||||
export { ProductFeaturesService } from './product_features_service';
|
|
@ -10,12 +10,12 @@ import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-p
|
|||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { allowedExperimentalValues, type ExperimentalFeatures } from '../../../common';
|
||||
import { AppFeaturesService } from './app_features_service';
|
||||
import { ProductFeaturesService } from './product_features_service';
|
||||
|
||||
jest.mock('@kbn/security-solution-features/app_features', () => ({
|
||||
jest.mock('@kbn/security-solution-features/product_features', () => ({
|
||||
getSecurityFeature: jest.fn(() => ({
|
||||
baseKibanaFeature: {},
|
||||
baseKibanaSubFeatureIds: [],
|
||||
|
@ -33,19 +33,19 @@ jest.mock('@kbn/security-solution-features/app_features', () => ({
|
|||
})),
|
||||
}));
|
||||
|
||||
export const createAppFeaturesServiceMock = (
|
||||
export const createProductFeaturesServiceMock = (
|
||||
/** What features keys should be enabled. Default is all */
|
||||
enabledFeatureKeys: AppFeatureKeys = [...ALL_APP_FEATURE_KEYS],
|
||||
enabledFeatureKeys: ProductFeatureKeys = [...ALL_PRODUCT_FEATURE_KEYS],
|
||||
experimentalFeatures: ExperimentalFeatures = { ...allowedExperimentalValues },
|
||||
featuresPluginSetupContract: FeaturesPluginSetup = featuresPluginMock.createSetup(),
|
||||
logger: Logger = loggingSystemMock.create().get('appFeatureMock')
|
||||
logger: Logger = loggingSystemMock.create().get('productFeatureMock')
|
||||
) => {
|
||||
const appFeaturesService = new AppFeaturesService(logger, experimentalFeatures);
|
||||
const productFeaturesService = new ProductFeaturesService(logger, experimentalFeatures);
|
||||
|
||||
appFeaturesService.init(featuresPluginSetupContract);
|
||||
productFeaturesService.init(featuresPluginSetupContract);
|
||||
|
||||
if (enabledFeatureKeys) {
|
||||
appFeaturesService.setAppFeaturesConfigurator({
|
||||
productFeaturesService.setProductFeaturesConfigurator({
|
||||
security: jest.fn().mockReturnValue(
|
||||
new Map(
|
||||
enabledFeatureKeys.map((key) => [
|
||||
|
@ -106,5 +106,5 @@ export const createAppFeaturesServiceMock = (
|
|||
});
|
||||
}
|
||||
|
||||
return appFeaturesService;
|
||||
return productFeaturesService;
|
||||
};
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
import type { PluginSetupContract } from '@kbn/features-plugin/server';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { AppFeatures } from './app_features';
|
||||
import { ProductFeatures } from './product_features';
|
||||
import type {
|
||||
AppFeatureKeyType,
|
||||
AppFeatureKibanaConfig,
|
||||
ProductFeatureKeyType,
|
||||
ProductFeatureKibanaConfig,
|
||||
BaseKibanaFeatureConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import type { SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
|
@ -102,11 +102,11 @@ const subFeaturesMap = new Map([
|
|||
]);
|
||||
|
||||
// app features configs
|
||||
const testSubFeaturePrivilegeConfig: AppFeatureKibanaConfig = {
|
||||
const testSubFeaturePrivilegeConfig: ProductFeatureKibanaConfig = {
|
||||
subFeatureIds: [SUB_FEATURE.name],
|
||||
};
|
||||
|
||||
const testFeaturePrivilegeConfig: AppFeatureKibanaConfig = {
|
||||
const testFeaturePrivilegeConfig: ProductFeatureKibanaConfig = {
|
||||
privileges: {
|
||||
all: {
|
||||
ui: ['test-action'],
|
||||
|
@ -144,7 +144,7 @@ const expectedBaseWithTestConfigPrivileges = {
|
|||
},
|
||||
};
|
||||
|
||||
describe('AppFeatures', () => {
|
||||
describe('ProductFeatures', () => {
|
||||
describe('setConfig', () => {
|
||||
it('should register base kibana features', () => {
|
||||
const featuresSetup = {
|
||||
|
@ -152,14 +152,14 @@ describe('AppFeatures', () => {
|
|||
getKibanaFeatures: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(new Map());
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(new Map());
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
...baseKibanaFeature,
|
||||
|
@ -173,15 +173,15 @@ describe('AppFeatures', () => {
|
|||
getKibanaFeatures: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(
|
||||
new Map([['test-feature' as AppFeatureKeyType, testFeaturePrivilegeConfig]])
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(
|
||||
new Map([['test-feature' as ProductFeatureKeyType, testFeaturePrivilegeConfig]])
|
||||
);
|
||||
|
||||
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith({
|
||||
|
@ -197,17 +197,17 @@ describe('AppFeatures', () => {
|
|||
getKibanaFeatures: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(
|
||||
new Map([
|
||||
['test-feature' as AppFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as AppFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
['test-feature' as ProductFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as ProductFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
])
|
||||
);
|
||||
|
||||
|
@ -224,17 +224,17 @@ describe('AppFeatures', () => {
|
|||
getKibanaFeatures: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[SUB_FEATURE_2.name]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(
|
||||
new Map([
|
||||
['test-feature' as AppFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as AppFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
['test-feature' as ProductFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as ProductFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
])
|
||||
);
|
||||
|
||||
|
@ -252,25 +252,25 @@ describe('AppFeatures', () => {
|
|||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(new Map());
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(new Map());
|
||||
|
||||
expect(appFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:test-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('ui:test-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature1-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature2-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:test-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('ui:test-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature1-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature2-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should register config privilege actions', () => {
|
||||
|
@ -278,27 +278,27 @@ describe('AppFeatures', () => {
|
|||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(
|
||||
new Map([['test-feature' as AppFeatureKeyType, testFeaturePrivilegeConfig]])
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(
|
||||
new Map([['test-feature' as ProductFeatureKeyType, testFeaturePrivilegeConfig]])
|
||||
);
|
||||
|
||||
expect(appFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:test-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:test-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature1-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature2-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:test-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:test-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature1-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature2-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should register config sub-feature privilege actions', () => {
|
||||
|
@ -306,30 +306,30 @@ describe('AppFeatures', () => {
|
|||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(
|
||||
new Map([
|
||||
['test-feature' as AppFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as AppFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
['test-feature' as ProductFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as ProductFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
])
|
||||
);
|
||||
|
||||
expect(appFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:test-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:test-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature1-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature2-action')).toEqual(false);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:test-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:test-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature1-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature2-action')).toEqual(false);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should register default and config sub-feature privilege actions', () => {
|
||||
|
@ -337,30 +337,30 @@ describe('AppFeatures', () => {
|
|||
registerKibanaFeature: jest.fn(),
|
||||
} as unknown as PluginSetupContract;
|
||||
|
||||
const appFeatures = new AppFeatures(
|
||||
const productFeatures = new ProductFeatures(
|
||||
loggingSystemMock.create().get('mock'),
|
||||
subFeaturesMap,
|
||||
baseKibanaFeature,
|
||||
[SUB_FEATURE_2.name]
|
||||
);
|
||||
appFeatures.init(featuresSetup);
|
||||
appFeatures.setConfig(
|
||||
productFeatures.init(featuresSetup);
|
||||
productFeatures.setConfig(
|
||||
new Map([
|
||||
['test-feature' as AppFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as AppFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
['test-feature' as ProductFeatureKeyType, testFeaturePrivilegeConfig],
|
||||
['test-sub-feature' as ProductFeatureKeyType, testSubFeaturePrivilegeConfig],
|
||||
])
|
||||
);
|
||||
|
||||
expect(appFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:test-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:test-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature1-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('api:subFeature2-action')).toEqual(true);
|
||||
expect(appFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:api-read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:read')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:api-write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:write')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:test-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:test-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature1-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature1-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('api:subFeature2-action')).toEqual(true);
|
||||
expect(productFeatures.isActionRegistered('ui:subFeature2-action')).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,14 +12,14 @@ import type {
|
|||
PluginSetupContract as FeaturesPluginSetup,
|
||||
} from '@kbn/features-plugin/server';
|
||||
import type {
|
||||
AppFeaturesConfig,
|
||||
ProductFeaturesConfig,
|
||||
AppSubFeaturesMap,
|
||||
BaseKibanaFeatureConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import { AppFeaturesConfigMerger } from './app_features_config_merger';
|
||||
import { ProductFeaturesConfigMerger } from './product_features_config_merger';
|
||||
|
||||
export class AppFeatures<T extends string = string, S extends string = string> {
|
||||
private featureConfigMerger: AppFeaturesConfigMerger;
|
||||
export class ProductFeatures<T extends string = string, S extends string = string> {
|
||||
private featureConfigMerger: ProductFeaturesConfigMerger;
|
||||
private featuresSetup?: FeaturesPluginSetup;
|
||||
private readonly registeredActions: Set<string>;
|
||||
|
||||
|
@ -29,7 +29,7 @@ export class AppFeatures<T extends string = string, S extends string = string> {
|
|||
private readonly baseKibanaFeature: BaseKibanaFeatureConfig,
|
||||
private readonly baseKibanaSubFeatureIds: T[]
|
||||
) {
|
||||
this.featureConfigMerger = new AppFeaturesConfigMerger(this.logger, subFeaturesMap);
|
||||
this.featureConfigMerger = new ProductFeaturesConfigMerger(this.logger, subFeaturesMap);
|
||||
this.registeredActions = new Set();
|
||||
}
|
||||
|
||||
|
@ -37,22 +37,22 @@ export class AppFeatures<T extends string = string, S extends string = string> {
|
|||
this.featuresSetup = featuresSetup;
|
||||
}
|
||||
|
||||
public setConfig(appFeatureConfig: AppFeaturesConfig<S>) {
|
||||
public setConfig(productFeatureConfig: ProductFeaturesConfig<S>) {
|
||||
if (this.featuresSetup == null) {
|
||||
throw new Error(
|
||||
'Cannot sync kibana features as featuresSetup is not present. Did you call init?'
|
||||
);
|
||||
}
|
||||
|
||||
const completeAppFeatureConfig = this.featureConfigMerger.mergeAppFeatureConfigs(
|
||||
const completeProductFeatureConfig = this.featureConfigMerger.mergeProductFeatureConfigs(
|
||||
this.baseKibanaFeature,
|
||||
this.baseKibanaSubFeatureIds,
|
||||
Array.from(appFeatureConfig.values())
|
||||
Array.from(productFeatureConfig.values())
|
||||
);
|
||||
|
||||
this.logger.debug(JSON.stringify(completeAppFeatureConfig));
|
||||
this.featuresSetup.registerKibanaFeature(completeAppFeatureConfig);
|
||||
this.addRegisteredActions(completeAppFeatureConfig);
|
||||
this.logger.debug(JSON.stringify(completeProductFeatureConfig));
|
||||
this.featuresSetup.registerKibanaFeature(completeProductFeatureConfig);
|
||||
this.addRegisteredActions(completeProductFeatureConfig);
|
||||
}
|
||||
|
||||
private addRegisteredActions(config: KibanaFeatureConfig) {
|
|
@ -6,9 +6,9 @@
|
|||
*/
|
||||
|
||||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { AppFeaturesConfigMerger } from './app_features_config_merger';
|
||||
import { ProductFeaturesConfigMerger } from './product_features_config_merger';
|
||||
import type { Logger } from '@kbn/core/server';
|
||||
import type { AppFeatureKibanaConfig } from '@kbn/security-solution-features';
|
||||
import type { ProductFeatureKibanaConfig } from '@kbn/security-solution-features';
|
||||
import type { KibanaFeatureConfig, SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
|
||||
const category = {
|
||||
|
@ -138,8 +138,8 @@ export const subFeaturesMap = Object.freeze(
|
|||
|
||||
const mockLogger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
describe('AppFeaturesConfigMerger', () => {
|
||||
const merger = new AppFeaturesConfigMerger(mockLogger, subFeaturesMap);
|
||||
describe('ProductFeaturesConfigMerger', () => {
|
||||
const merger = new ProductFeaturesConfigMerger(mockLogger, subFeaturesMap);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -147,7 +147,7 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
|
||||
describe('main privileges', () => {
|
||||
it('should merge enabled main privileges into base config', () => {
|
||||
const enabledAppFeaturesConfigs: AppFeatureKibanaConfig[] = [
|
||||
const enabledProductFeaturesConfigs: ProductFeatureKibanaConfig[] = [
|
||||
{
|
||||
privileges: {
|
||||
all: {
|
||||
|
@ -179,10 +179,10 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const merged = merger.mergeAppFeatureConfigs(
|
||||
const merged = merger.mergeProductFeatureConfigs(
|
||||
baseKibanaFeature,
|
||||
[],
|
||||
enabledAppFeaturesConfigs
|
||||
enabledProductFeaturesConfigs
|
||||
);
|
||||
|
||||
expect(merged).toEqual({
|
||||
|
@ -227,21 +227,25 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
it('adds base subFeatures in the correct order', () => {
|
||||
const baseKibanaSubFeatureIds = ['subFeature2', 'subFeature3', 'subFeature1'];
|
||||
|
||||
const merged = merger.mergeAppFeatureConfigs(baseKibanaFeature, baseKibanaSubFeatureIds, []);
|
||||
const merged = merger.mergeProductFeatureConfigs(
|
||||
baseKibanaFeature,
|
||||
baseKibanaSubFeatureIds,
|
||||
[]
|
||||
);
|
||||
expect(merged.subFeatures).toEqual([subFeature1, subFeature2, subFeature3]);
|
||||
});
|
||||
|
||||
it('should merge enabled subFeatures into base config in the correct order', () => {
|
||||
const enabledAppFeaturesConfigs: AppFeatureKibanaConfig[] = [
|
||||
const enabledProductFeaturesConfigs: ProductFeatureKibanaConfig[] = [
|
||||
{
|
||||
subFeatureIds: ['subFeature3', 'subFeature1'],
|
||||
},
|
||||
];
|
||||
|
||||
const merged = merger.mergeAppFeatureConfigs(
|
||||
const merged = merger.mergeProductFeatureConfigs(
|
||||
baseKibanaFeature,
|
||||
['subFeature2'],
|
||||
enabledAppFeaturesConfigs
|
||||
enabledProductFeaturesConfigs
|
||||
);
|
||||
|
||||
expect(merged).toEqual({
|
||||
|
@ -253,7 +257,7 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
|
||||
describe('subFeaturePrivileges', () => {
|
||||
it('should merge enabled subFeatures with extra subFeaturePrivileges into base config in the correct order', () => {
|
||||
const enabledAppFeaturesConfigs: AppFeatureKibanaConfig[] = [
|
||||
const enabledProductFeaturesConfigs: ProductFeatureKibanaConfig[] = [
|
||||
{
|
||||
subFeaturesPrivileges: [
|
||||
{
|
||||
|
@ -273,10 +277,10 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const merged = merger.mergeAppFeatureConfigs(
|
||||
const merged = merger.mergeProductFeatureConfigs(
|
||||
baseKibanaFeature,
|
||||
['subFeature2'],
|
||||
enabledAppFeaturesConfigs
|
||||
enabledProductFeaturesConfigs
|
||||
);
|
||||
expect(merged).toEqual({
|
||||
...baseKibanaFeature,
|
||||
|
@ -323,7 +327,7 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
|
||||
it('should warn if there are subFeaturesPrivileges for a subFeature id that is not found', () => {
|
||||
const subFeaturesPrivilegesId = 'sub-feature-1_all';
|
||||
const enabledAppFeaturesConfigs: AppFeatureKibanaConfig[] = [
|
||||
const enabledProductFeaturesConfigs: ProductFeatureKibanaConfig[] = [
|
||||
{
|
||||
subFeaturesPrivileges: [
|
||||
{
|
||||
|
@ -335,10 +339,10 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const merged = merger.mergeAppFeatureConfigs(
|
||||
const merged = merger.mergeProductFeatureConfigs(
|
||||
baseKibanaFeature,
|
||||
['subFeature2', 'subFeature3'],
|
||||
enabledAppFeaturesConfigs
|
||||
enabledProductFeaturesConfigs
|
||||
);
|
||||
|
||||
expect(mockLogger.warn).toHaveBeenCalledWith(
|
||||
|
@ -349,7 +353,7 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
});
|
||||
|
||||
it('should merge everything at the same time', () => {
|
||||
const enabledAppFeaturesConfigs: AppFeatureKibanaConfig[] = [
|
||||
const enabledProductFeaturesConfigs: ProductFeatureKibanaConfig[] = [
|
||||
{
|
||||
privileges: {
|
||||
all: {
|
||||
|
@ -395,10 +399,10 @@ describe('AppFeaturesConfigMerger', () => {
|
|||
];
|
||||
const baseKibanaSubFeatureIds = ['subFeature2'];
|
||||
|
||||
const merged = merger.mergeAppFeatureConfigs(
|
||||
const merged = merger.mergeProductFeatureConfigs(
|
||||
baseKibanaFeature,
|
||||
baseKibanaSubFeatureIds,
|
||||
enabledAppFeaturesConfigs
|
||||
enabledProductFeaturesConfigs
|
||||
);
|
||||
|
||||
expect(merged).toEqual({
|
|
@ -9,28 +9,28 @@ import { cloneDeep, isArray, mergeWith, uniq } from 'lodash';
|
|||
import type { Logger } from '@kbn/core/server';
|
||||
import type { KibanaFeatureConfig, SubFeatureConfig } from '@kbn/features-plugin/common';
|
||||
import type {
|
||||
AppFeatureKibanaConfig,
|
||||
ProductFeatureKibanaConfig,
|
||||
BaseKibanaFeatureConfig,
|
||||
SubFeaturesPrivileges,
|
||||
} from '@kbn/security-solution-features';
|
||||
|
||||
export class AppFeaturesConfigMerger<T extends string = string> {
|
||||
export class ProductFeaturesConfigMerger<T extends string = string> {
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly subFeaturesMap: Map<T, SubFeatureConfig>
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Merges `appFeaturesConfigs` into `kibanaFeatureConfig`.
|
||||
* Merges `productFeaturesConfigs` into `kibanaFeatureConfig`.
|
||||
* @param kibanaFeatureConfig the KibanaFeatureConfig to merge into
|
||||
* @param kibanaSubFeatureIds
|
||||
* @param appFeaturesConfigs the AppFeatureKibanaConfig to merge
|
||||
* @param productFeaturesConfigs the ProductFeatureKibanaConfig to merge
|
||||
* @returns mergedKibanaFeatureConfig the merged KibanaFeatureConfig
|
||||
* */
|
||||
public mergeAppFeatureConfigs(
|
||||
public mergeProductFeatureConfigs(
|
||||
kibanaFeatureConfig: BaseKibanaFeatureConfig,
|
||||
kibanaSubFeatureIds: T[],
|
||||
appFeaturesConfigs: AppFeatureKibanaConfig[]
|
||||
productFeaturesConfigs: ProductFeatureKibanaConfig[]
|
||||
): KibanaFeatureConfig {
|
||||
const mergedKibanaFeatureConfig = cloneDeep(kibanaFeatureConfig) as KibanaFeatureConfig;
|
||||
const subFeaturesPrivilegesToMerge: SubFeaturesPrivileges[] = [];
|
||||
|
@ -38,9 +38,9 @@ export class AppFeaturesConfigMerger<T extends string = string> {
|
|||
kibanaSubFeatureIds.map((id) => [id, true])
|
||||
);
|
||||
|
||||
appFeaturesConfigs.forEach((appFeatureConfig) => {
|
||||
const { subFeaturesPrivileges, subFeatureIds, ...appFeatureConfigToMerge } =
|
||||
cloneDeep(appFeatureConfig);
|
||||
productFeaturesConfigs.forEach((productFeatureConfig) => {
|
||||
const { subFeaturesPrivileges, subFeatureIds, ...productFeatureConfigToMerge } =
|
||||
cloneDeep(productFeatureConfig);
|
||||
|
||||
subFeatureIds?.forEach((subFeatureId) => {
|
||||
enabledSubFeaturesIndexed[subFeatureId] = true;
|
||||
|
@ -49,7 +49,7 @@ export class AppFeaturesConfigMerger<T extends string = string> {
|
|||
if (subFeaturesPrivileges) {
|
||||
subFeaturesPrivilegesToMerge.push(...subFeaturesPrivileges);
|
||||
}
|
||||
mergeWith(mergedKibanaFeatureConfig, appFeatureConfigToMerge, featureConfigMerger);
|
||||
mergeWith(mergedKibanaFeatureConfig, productFeatureConfigToMerge, featureConfigMerger);
|
||||
});
|
||||
|
||||
// generate sub features configs from enabled sub feature ids, preserving map order
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* 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 { ProductFeaturesService } from './product_features_service';
|
||||
import { ProductFeatures } from './product_features';
|
||||
import type {
|
||||
ProductFeaturesConfig,
|
||||
BaseKibanaFeatureConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import type { ExperimentalFeatures } from '../../../common';
|
||||
import { featuresPluginMock } from '@kbn/features-plugin/server/mocks';
|
||||
import type { ProductFeaturesConfigurator } from './types';
|
||||
import type {
|
||||
AssistantSubFeatureId,
|
||||
CasesSubFeatureId,
|
||||
SecuritySubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import { httpServiceMock } from '@kbn/core-http-server-mocks';
|
||||
import type {
|
||||
KibanaRequest,
|
||||
LifecycleResponseFactory,
|
||||
OnPostAuthHandler,
|
||||
} from '@kbn/core-http-server';
|
||||
|
||||
jest.mock('./product_features');
|
||||
const MockedProductFeatures = ProductFeatures as unknown as jest.MockedClass<
|
||||
typeof ProductFeatures
|
||||
>;
|
||||
|
||||
const productFeature = {
|
||||
subFeaturesMap: new Map(),
|
||||
baseKibanaFeature: {} as BaseKibanaFeatureConfig,
|
||||
baseKibanaSubFeatureIds: [],
|
||||
};
|
||||
const mockGetFeature = jest.fn().mockReturnValue(productFeature);
|
||||
jest.mock('@kbn/security-solution-features/product_features', () => ({
|
||||
getAssistantFeature: () => mockGetFeature(),
|
||||
getCasesFeature: () => mockGetFeature(),
|
||||
getSecurityFeature: () => mockGetFeature(),
|
||||
}));
|
||||
|
||||
describe('ProductFeaturesService', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should create ProductFeatureService instance', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
new ProductFeaturesService(loggerMock.create(), experimentalFeatures);
|
||||
|
||||
expect(mockGetFeature).toHaveBeenCalledTimes(3);
|
||||
expect(MockedProductFeatures).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should init all ProductFeatures when initialized', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
|
||||
const featuresSetup = featuresPluginMock.createSetup();
|
||||
productFeaturesService.init(featuresSetup);
|
||||
|
||||
expect(MockedProductFeatures.mock.instances[0].init).toHaveBeenCalledWith(featuresSetup);
|
||||
expect(MockedProductFeatures.mock.instances[1].init).toHaveBeenCalledWith(featuresSetup);
|
||||
expect(MockedProductFeatures.mock.instances[2].init).toHaveBeenCalledWith(featuresSetup);
|
||||
});
|
||||
|
||||
it('should configure ProductFeatures', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
|
||||
const featuresSetup = featuresPluginMock.createSetup();
|
||||
productFeaturesService.init(featuresSetup);
|
||||
|
||||
const mockSecurityConfig = new Map() as ProductFeaturesConfig<SecuritySubFeatureId>;
|
||||
const mockCasesConfig = new Map() as ProductFeaturesConfig<CasesSubFeatureId>;
|
||||
const mockAssistantConfig = new Map() as ProductFeaturesConfig<AssistantSubFeatureId>;
|
||||
|
||||
const configurator: ProductFeaturesConfigurator = {
|
||||
security: jest.fn(() => mockSecurityConfig),
|
||||
cases: jest.fn(() => mockCasesConfig),
|
||||
securityAssistant: jest.fn(() => mockAssistantConfig),
|
||||
};
|
||||
productFeaturesService.setProductFeaturesConfigurator(configurator);
|
||||
|
||||
expect(configurator.security).toHaveBeenCalled();
|
||||
expect(configurator.cases).toHaveBeenCalled();
|
||||
expect(configurator.securityAssistant).toHaveBeenCalled();
|
||||
|
||||
expect(MockedProductFeatures.mock.instances[0].setConfig).toHaveBeenCalledWith(
|
||||
mockSecurityConfig
|
||||
);
|
||||
expect(MockedProductFeatures.mock.instances[1].setConfig).toHaveBeenCalledWith(mockCasesConfig);
|
||||
expect(MockedProductFeatures.mock.instances[2].setConfig).toHaveBeenCalledWith(
|
||||
mockAssistantConfig
|
||||
);
|
||||
});
|
||||
|
||||
it('should return isEnabled for enabled features', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
|
||||
const featuresSetup = featuresPluginMock.createSetup();
|
||||
productFeaturesService.init(featuresSetup);
|
||||
|
||||
const mockSecurityConfig = new Map([
|
||||
[ProductFeatureKey.advancedInsights, {}],
|
||||
[ProductFeatureKey.endpointExceptions, {}],
|
||||
]) as ProductFeaturesConfig<SecuritySubFeatureId>;
|
||||
const mockCasesConfig = new Map([
|
||||
[ProductFeatureKey.casesConnectors, {}],
|
||||
]) as ProductFeaturesConfig<CasesSubFeatureId>;
|
||||
const mockAssistantConfig = new Map([
|
||||
[ProductFeatureKey.assistant, {}],
|
||||
]) as ProductFeaturesConfig<AssistantSubFeatureId>;
|
||||
|
||||
const configurator: ProductFeaturesConfigurator = {
|
||||
security: jest.fn(() => mockSecurityConfig),
|
||||
cases: jest.fn(() => mockCasesConfig),
|
||||
securityAssistant: jest.fn(() => mockAssistantConfig),
|
||||
};
|
||||
productFeaturesService.setProductFeaturesConfigurator(configurator);
|
||||
|
||||
expect(productFeaturesService.isEnabled(ProductFeatureKey.advancedInsights)).toEqual(true);
|
||||
expect(productFeaturesService.isEnabled(ProductFeatureKey.endpointExceptions)).toEqual(true);
|
||||
expect(productFeaturesService.isEnabled(ProductFeatureKey.casesConnectors)).toEqual(true);
|
||||
expect(productFeaturesService.isEnabled(ProductFeatureKey.assistant)).toEqual(true);
|
||||
expect(productFeaturesService.isEnabled(ProductFeatureKey.externalRuleActions)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should call isApiPrivilegeEnabled for api actions', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
|
||||
productFeaturesService.isApiPrivilegeEnabled('writeEndpointExceptions');
|
||||
|
||||
expect(MockedProductFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-writeEndpointExceptions'
|
||||
);
|
||||
expect(MockedProductFeatures.mock.instances[1].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-writeEndpointExceptions'
|
||||
);
|
||||
expect(MockedProductFeatures.mock.instances[2].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-writeEndpointExceptions'
|
||||
);
|
||||
});
|
||||
|
||||
describe('registerApiAccessControl', () => {
|
||||
const mockHttpSetup = httpServiceMock.createSetupContract();
|
||||
let lastRegisteredFn: OnPostAuthHandler;
|
||||
mockHttpSetup.registerOnPostAuth.mockImplementation((fn) => {
|
||||
lastRegisteredFn = fn;
|
||||
});
|
||||
|
||||
const getReq = (tags: string[] = []) =>
|
||||
({
|
||||
route: { options: { tags } },
|
||||
url: { pathname: '', search: '' },
|
||||
} as unknown as KibanaRequest);
|
||||
const res = { notFound: jest.fn() } as unknown as LifecycleResponseFactory;
|
||||
const toolkit = { next: jest.fn() };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should register api authorization http route interceptor', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
productFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
expect(mockHttpSetup.registerOnPostAuth).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should authorize when no tag matches', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
productFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
lastRegisteredFn(getReq(['access:something', 'access:securitySolution']), res, toolkit);
|
||||
|
||||
expect(MockedProductFeatures.mock.instances[0].isActionRegistered).not.toHaveBeenCalled();
|
||||
expect(res.notFound).not.toHaveBeenCalled();
|
||||
expect(toolkit.next).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should check when tag matches and return not found when not action registered', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
productFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
(MockedProductFeatures.mock.instances[0].isActionRegistered as jest.Mock).mockReturnValueOnce(
|
||||
false
|
||||
);
|
||||
lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit);
|
||||
|
||||
expect(MockedProductFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-foo'
|
||||
);
|
||||
expect(res.notFound).toHaveBeenCalledTimes(1);
|
||||
expect(toolkit.next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check when tag matches and continue when action registered', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
productFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
(MockedProductFeatures.mock.instances[0].isActionRegistered as jest.Mock).mockReturnValueOnce(
|
||||
true
|
||||
);
|
||||
lastRegisteredFn(getReq(['access:securitySolution-foo']), res, toolkit);
|
||||
|
||||
expect(MockedProductFeatures.mock.instances[0].isActionRegistered).toHaveBeenCalledWith(
|
||||
'api:securitySolution-foo'
|
||||
);
|
||||
expect(res.notFound).not.toHaveBeenCalled();
|
||||
expect(toolkit.next).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should check when productFeature tag when it matches and return not found when not enabled', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
productFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
productFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(false);
|
||||
|
||||
lastRegisteredFn(getReq(['securitySolutionProductFeature:foo']), res, toolkit);
|
||||
|
||||
expect(productFeaturesService.isEnabled).toHaveBeenCalledWith('foo');
|
||||
expect(res.notFound).toHaveBeenCalledTimes(1);
|
||||
expect(toolkit.next).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check when productFeature tag when it matches and continue when enabled', () => {
|
||||
const experimentalFeatures = {} as ExperimentalFeatures;
|
||||
const productFeaturesService = new ProductFeaturesService(
|
||||
loggerMock.create(),
|
||||
experimentalFeatures
|
||||
);
|
||||
productFeaturesService.registerApiAccessControl(mockHttpSetup);
|
||||
|
||||
productFeaturesService.isEnabled = jest.fn().mockReturnValueOnce(true);
|
||||
|
||||
lastRegisteredFn(getReq(['securitySolutionProductFeature:foo']), res, toolkit);
|
||||
|
||||
expect(productFeaturesService.isEnabled).toHaveBeenCalledWith('foo');
|
||||
expect(res.notFound).not.toHaveBeenCalled();
|
||||
expect(toolkit.next).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -14,24 +14,24 @@
|
|||
import type { HttpServiceSetup, Logger } from '@kbn/core/server';
|
||||
import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import type { ProductFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import {
|
||||
getAssistantFeature,
|
||||
getCasesFeature,
|
||||
getSecurityFeature,
|
||||
} from '@kbn/security-solution-features/app_features';
|
||||
} from '@kbn/security-solution-features/product_features';
|
||||
import type { ExperimentalFeatures } from '../../../common';
|
||||
import { APP_ID } from '../../../common';
|
||||
import { AppFeatures } from './app_features';
|
||||
import type { AppFeaturesConfigurator } from './types';
|
||||
import { ProductFeatures } from './product_features';
|
||||
import type { ProductFeaturesConfigurator } from './types';
|
||||
import { securityDefaultSavedObjects } from './security_saved_objects';
|
||||
import { casesApiTags, casesUiCapabilities } from './cases_privileges';
|
||||
|
||||
export class AppFeaturesService {
|
||||
private securityAppFeatures: AppFeatures;
|
||||
private casesAppFeatures: AppFeatures;
|
||||
private securityAssistantAppFeatures: AppFeatures;
|
||||
private appFeatures?: Set<AppFeatureKeyType>;
|
||||
export class ProductFeaturesService {
|
||||
private securityProductFeatures: ProductFeatures;
|
||||
private casesProductFeatures: ProductFeatures;
|
||||
private securityAssistantProductFeatures: ProductFeatures;
|
||||
private productFeatures?: Set<ProductFeatureKeyType>;
|
||||
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
|
@ -41,7 +41,7 @@ export class AppFeaturesService {
|
|||
savedObjects: securityDefaultSavedObjects,
|
||||
experimentalFeatures: this.experimentalFeatures,
|
||||
});
|
||||
this.securityAppFeatures = new AppFeatures(
|
||||
this.securityProductFeatures = new ProductFeatures(
|
||||
this.logger,
|
||||
securityFeature.subFeaturesMap,
|
||||
securityFeature.baseKibanaFeature,
|
||||
|
@ -53,7 +53,7 @@ export class AppFeaturesService {
|
|||
apiTags: casesApiTags,
|
||||
savedObjects: { files: filesSavedObjectTypes },
|
||||
});
|
||||
this.casesAppFeatures = new AppFeatures(
|
||||
this.casesProductFeatures = new ProductFeatures(
|
||||
this.logger,
|
||||
casesFeature.subFeaturesMap,
|
||||
casesFeature.baseKibanaFeature,
|
||||
|
@ -61,7 +61,7 @@ export class AppFeaturesService {
|
|||
);
|
||||
|
||||
const assistantFeature = getAssistantFeature();
|
||||
this.securityAssistantAppFeatures = new AppFeatures(
|
||||
this.securityAssistantProductFeatures = new ProductFeatures(
|
||||
this.logger,
|
||||
assistantFeature.subFeaturesMap,
|
||||
assistantFeature.baseKibanaFeature,
|
||||
|
@ -70,44 +70,44 @@ export class AppFeaturesService {
|
|||
}
|
||||
|
||||
public init(featuresSetup: FeaturesPluginSetup) {
|
||||
this.securityAppFeatures.init(featuresSetup);
|
||||
this.casesAppFeatures.init(featuresSetup);
|
||||
this.securityAssistantAppFeatures.init(featuresSetup);
|
||||
this.securityProductFeatures.init(featuresSetup);
|
||||
this.casesProductFeatures.init(featuresSetup);
|
||||
this.securityAssistantProductFeatures.init(featuresSetup);
|
||||
}
|
||||
|
||||
public setAppFeaturesConfigurator(configurator: AppFeaturesConfigurator) {
|
||||
const securityAppFeaturesConfig = configurator.security();
|
||||
this.securityAppFeatures.setConfig(securityAppFeaturesConfig);
|
||||
public setProductFeaturesConfigurator(configurator: ProductFeaturesConfigurator) {
|
||||
const securityProductFeaturesConfig = configurator.security();
|
||||
this.securityProductFeatures.setConfig(securityProductFeaturesConfig);
|
||||
|
||||
const casesAppFeaturesConfig = configurator.cases();
|
||||
this.casesAppFeatures.setConfig(casesAppFeaturesConfig);
|
||||
const casesProductFeaturesConfig = configurator.cases();
|
||||
this.casesProductFeatures.setConfig(casesProductFeaturesConfig);
|
||||
|
||||
const securityAssistantAppFeaturesConfig = configurator.securityAssistant();
|
||||
this.securityAssistantAppFeatures.setConfig(securityAssistantAppFeaturesConfig);
|
||||
const securityAssistantProductFeaturesConfig = configurator.securityAssistant();
|
||||
this.securityAssistantProductFeatures.setConfig(securityAssistantProductFeaturesConfig);
|
||||
|
||||
this.appFeatures = new Set<AppFeatureKeyType>(
|
||||
this.productFeatures = new Set<ProductFeatureKeyType>(
|
||||
Object.freeze([
|
||||
...securityAppFeaturesConfig.keys(),
|
||||
...casesAppFeaturesConfig.keys(),
|
||||
...securityAssistantAppFeaturesConfig.keys(),
|
||||
]) as readonly AppFeatureKeyType[]
|
||||
...securityProductFeaturesConfig.keys(),
|
||||
...casesProductFeaturesConfig.keys(),
|
||||
...securityAssistantProductFeaturesConfig.keys(),
|
||||
]) as readonly ProductFeatureKeyType[]
|
||||
);
|
||||
}
|
||||
|
||||
public isEnabled(appFeatureKey: AppFeatureKeyType): boolean {
|
||||
if (!this.appFeatures) {
|
||||
throw new Error('AppFeatures has not yet been configured');
|
||||
public isEnabled(productFeatureKey: ProductFeatureKeyType): boolean {
|
||||
if (!this.productFeatures) {
|
||||
throw new Error('ProductFeatures has not yet been configured');
|
||||
}
|
||||
return this.appFeatures.has(appFeatureKey);
|
||||
return this.productFeatures.has(productFeatureKey);
|
||||
}
|
||||
|
||||
public getApiActionName = (apiPrivilege: string) => `api:${APP_ID}-${apiPrivilege}`;
|
||||
|
||||
public isActionRegistered(action: string) {
|
||||
return (
|
||||
this.securityAppFeatures.isActionRegistered(action) ||
|
||||
this.casesAppFeatures.isActionRegistered(action) ||
|
||||
this.securityAssistantAppFeatures.isActionRegistered(action)
|
||||
this.securityProductFeatures.isActionRegistered(action) ||
|
||||
this.casesProductFeatures.isActionRegistered(action) ||
|
||||
this.securityAssistantProductFeatures.isActionRegistered(action)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -116,13 +116,13 @@ export class AppFeaturesService {
|
|||
}
|
||||
|
||||
public registerApiAccessControl(http: HttpServiceSetup) {
|
||||
// The `securitySolutionAppFeature:` prefix is used for AppFeature based control.
|
||||
// Should be used only by routes that do not need RBAC, only direct appFeature control.
|
||||
const APP_FEATURE_TAG_PREFIX = 'securitySolutionAppFeature:';
|
||||
// The `securitySolutionProductFeature:` prefix is used for ProductFeature based control.
|
||||
// Should be used only by routes that do not need RBAC, only direct productFeature control.
|
||||
const APP_FEATURE_TAG_PREFIX = 'securitySolutionProductFeature:';
|
||||
// The "access:securitySolution-" prefix is used for API action based control.
|
||||
// Should be used by routes that need RBAC, extending the `access:` role privilege check from the security plugin.
|
||||
// An additional check is performed to ensure the privilege has been registered by the appFeature service,
|
||||
// preventing full access (`*`) roles, such as superuser, from bypassing appFeature controls.
|
||||
// An additional check is performed to ensure the privilege has been registered by the productFeature service,
|
||||
// preventing full access (`*`) roles, such as superuser, from bypassing productFeature controls.
|
||||
const API_ACTION_TAG_PREFIX = `access:${APP_ID}-`;
|
||||
|
||||
http.registerOnPostAuth((request, response, toolkit) => {
|
||||
|
@ -130,7 +130,7 @@ export class AppFeaturesService {
|
|||
let isEnabled = true;
|
||||
if (tag.startsWith(APP_FEATURE_TAG_PREFIX)) {
|
||||
isEnabled = this.isEnabled(
|
||||
tag.substring(APP_FEATURE_TAG_PREFIX.length) as AppFeatureKeyType
|
||||
tag.substring(APP_FEATURE_TAG_PREFIX.length) as ProductFeatureKeyType
|
||||
);
|
||||
} else if (tag.startsWith(API_ACTION_TAG_PREFIX)) {
|
||||
isEnabled = this.isApiPrivilegeEnabled(tag.substring(API_ACTION_TAG_PREFIX.length));
|
|
@ -5,15 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeaturesConfig } from '@kbn/security-solution-features';
|
||||
import type { ProductFeaturesConfig } from '@kbn/security-solution-features';
|
||||
import type {
|
||||
SecuritySubFeatureId,
|
||||
CasesSubFeatureId,
|
||||
AssistantSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
|
||||
export interface AppFeaturesConfigurator {
|
||||
security: () => AppFeaturesConfig<SecuritySubFeatureId>;
|
||||
cases: () => AppFeaturesConfig<CasesSubFeatureId>;
|
||||
securityAssistant: () => AppFeaturesConfig<AssistantSubFeatureId>;
|
||||
export interface ProductFeaturesConfigurator {
|
||||
security: () => ProductFeaturesConfig<SecuritySubFeatureId>;
|
||||
cases: () => ProductFeaturesConfig<CasesSubFeatureId>;
|
||||
securityAssistant: () => ProductFeaturesConfig<AssistantSubFeatureId>;
|
||||
}
|
|
@ -120,7 +120,7 @@ import {
|
|||
ENDPOINT_SEARCH_STRATEGY,
|
||||
} from '../common/endpoint/constants';
|
||||
|
||||
import { AppFeaturesService } from './lib/app_features_service/app_features_service';
|
||||
import { ProductFeaturesService } from './lib/product_features_service/product_features_service';
|
||||
import { registerRiskScoringTask } from './lib/entity_analytics/risk_score/tasks/risk_scoring_task';
|
||||
import { registerProtectionUpdatesNoteRoutes } from './endpoint/routes/protection_updates_note';
|
||||
import {
|
||||
|
@ -139,7 +139,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
private readonly config: ConfigType;
|
||||
private readonly logger: Logger;
|
||||
private readonly appClientFactory: AppClientFactory;
|
||||
private readonly appFeaturesService: AppFeaturesService;
|
||||
private readonly productFeaturesService: ProductFeaturesService;
|
||||
|
||||
private readonly ruleMonitoringService: IRuleMonitoringService;
|
||||
private readonly endpointAppContextService = new EndpointAppContextService();
|
||||
|
@ -163,7 +163,10 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
this.config = serverConfig;
|
||||
this.logger = context.logger.get();
|
||||
this.appClientFactory = new AppClientFactory();
|
||||
this.appFeaturesService = new AppFeaturesService(this.logger, this.config.experimentalFeatures);
|
||||
this.productFeaturesService = new ProductFeaturesService(
|
||||
this.logger,
|
||||
this.config.experimentalFeatures
|
||||
);
|
||||
|
||||
this.ruleMonitoringService = createRuleMonitoringService(this.config, this.logger);
|
||||
this.telemetryEventsSender = new TelemetryEventsSender(this.logger);
|
||||
|
@ -188,12 +191,12 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
): SecuritySolutionPluginSetup {
|
||||
this.logger.debug('plugin setup');
|
||||
|
||||
const { appClientFactory, appFeaturesService, pluginContext, config, logger } = this;
|
||||
const { appClientFactory, productFeaturesService, pluginContext, config, logger } = this;
|
||||
const experimentalFeatures = config.experimentalFeatures;
|
||||
|
||||
initSavedObjects(core.savedObjects);
|
||||
initUiSettings(core.uiSettings, experimentalFeatures, config.enableUiSettingsValidations);
|
||||
appFeaturesService.init(plugins.features);
|
||||
productFeaturesService.init(plugins.features);
|
||||
|
||||
events.forEach((eventConfig) => core.analytics.registerEventType(eventConfig));
|
||||
|
||||
|
@ -220,7 +223,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
kibanaBranch: pluginContext.env.packageInfo.branch,
|
||||
});
|
||||
|
||||
appFeaturesService.registerApiAccessControl(core.http);
|
||||
productFeaturesService.registerApiAccessControl(core.http);
|
||||
const router = core.http.createRouter<SecuritySolutionRequestHandlerContext>();
|
||||
core.http.registerRouteHandlerContext<SecuritySolutionRequestHandlerContext, typeof APP_ID>(
|
||||
APP_ID,
|
||||
|
@ -494,8 +497,8 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
featureUsageService.setup(plugins.licensing);
|
||||
|
||||
return {
|
||||
setAppFeaturesConfigurator:
|
||||
appFeaturesService.setAppFeaturesConfigurator.bind(appFeaturesService),
|
||||
setProductFeaturesConfigurator:
|
||||
productFeaturesService.setProductFeaturesConfigurator.bind(productFeaturesService),
|
||||
experimentalFeatures: { ...config.experimentalFeatures },
|
||||
};
|
||||
}
|
||||
|
@ -504,7 +507,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
core: SecuritySolutionPluginCoreStartDependencies,
|
||||
plugins: SecuritySolutionPluginStartDependencies
|
||||
): SecuritySolutionPluginStart {
|
||||
const { config, logger, appFeaturesService } = this;
|
||||
const { config, logger, productFeaturesService } = this;
|
||||
|
||||
this.ruleMonitoringService.start(core, plugins);
|
||||
|
||||
|
@ -567,7 +570,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
experimentalFeatures: config.experimentalFeatures,
|
||||
packagerTaskPackagePolicyUpdateBatchSize: config.packagerTaskPackagePolicyUpdateBatchSize,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
appFeaturesService,
|
||||
productFeaturesService,
|
||||
});
|
||||
|
||||
// Migrate artifacts to fleet and then start the manifest task after that is done
|
||||
|
@ -584,13 +587,13 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
turnOffPolicyProtectionsIfNotSupported(
|
||||
core.elasticsearch.client.asInternalUser,
|
||||
endpointFleetServicesFactory.asInternalUser(),
|
||||
appFeaturesService,
|
||||
productFeaturesService,
|
||||
logger
|
||||
);
|
||||
|
||||
turnOffAgentPolicyFeatures(
|
||||
endpointFleetServicesFactory.asInternalUser(),
|
||||
appFeaturesService,
|
||||
productFeaturesService,
|
||||
logger
|
||||
);
|
||||
});
|
||||
|
@ -636,7 +639,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
),
|
||||
createFleetActionsClient,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
appFeaturesService,
|
||||
productFeaturesService,
|
||||
savedObjectsClient,
|
||||
});
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ import type { SharePluginStart } from '@kbn/share-plugin/server';
|
|||
import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server';
|
||||
import type { PluginSetup as UnifiedSearchServerPluginSetup } from '@kbn/unified-search-plugin/server';
|
||||
import type { ElasticAssistantPluginStart } from '@kbn/elastic-assistant-plugin/server';
|
||||
import type { AppFeaturesService } from './lib/app_features_service/app_features_service';
|
||||
import type { ProductFeaturesService } from './lib/product_features_service/product_features_service';
|
||||
import type { ExperimentalFeatures } from '../common';
|
||||
|
||||
export interface SecuritySolutionPluginSetupDependencies {
|
||||
|
@ -90,7 +90,7 @@ export interface SecuritySolutionPluginSetup {
|
|||
/**
|
||||
* Sets the configurations for app features that are available to the Security Solution
|
||||
*/
|
||||
setAppFeaturesConfigurator: AppFeaturesService['setAppFeaturesConfigurator'];
|
||||
setProductFeaturesConfigurator: ProductFeaturesService['setProductFeaturesConfigurator'];
|
||||
/**
|
||||
* The security solution generic experimental features
|
||||
*/
|
||||
|
|
|
@ -1,22 +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 type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { AppFeaturesConfigurator } from '@kbn/security-solution-plugin/server/lib/app_features_service/types';
|
||||
import { getCasesAppFeaturesConfigurator } from './cases_app_features_config';
|
||||
import { getSecurityAppFeaturesConfigurator } from './security_app_features_config';
|
||||
import { getSecurityAssistantAppFeaturesConfigurator } from './security_assistant_app_features_config';
|
||||
|
||||
export const getProductAppFeaturesConfigurator = (
|
||||
enabledAppFeatureKeys: AppFeatureKeys
|
||||
): AppFeaturesConfigurator => {
|
||||
return {
|
||||
security: getSecurityAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
cases: getCasesAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
securityAssistant: getSecurityAssistantAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
};
|
||||
};
|
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
|
||||
// Just copying all feature keys for now.
|
||||
// We may need a different set of keys in the future if we create serverless-specific appFeatures
|
||||
export const DEFAULT_APP_FEATURES = [...ALL_APP_FEATURE_KEYS];
|
||||
// We may need a different set of keys in the future if we create serverless-specific productFeatures
|
||||
export const DEFAULT_PRODUCT_FEATURES = [...ALL_PRODUCT_FEATURE_KEYS];
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import type { Plugin, CoreSetup } from '@kbn/core/server';
|
||||
import { getProductAppFeaturesConfigurator } from './app_features';
|
||||
import { DEFAULT_APP_FEATURES } from './constants';
|
||||
import { getProductProductFeaturesConfigurator } from './product_features';
|
||||
import { DEFAULT_PRODUCT_FEATURES } from './constants';
|
||||
|
||||
import type {
|
||||
SecuritySolutionEssPluginSetup,
|
||||
|
@ -26,8 +26,9 @@ export class SecuritySolutionEssPlugin
|
|||
>
|
||||
{
|
||||
public setup(_coreSetup: CoreSetup, pluginsSetup: SecuritySolutionEssPluginSetupDeps) {
|
||||
const appFeaturesConfigurator = getProductAppFeaturesConfigurator(DEFAULT_APP_FEATURES);
|
||||
pluginsSetup.securitySolution.setAppFeaturesConfigurator(appFeaturesConfigurator);
|
||||
const productFeaturesConfigurator =
|
||||
getProductProductFeaturesConfigurator(DEFAULT_PRODUCT_FEATURES);
|
||||
pluginsSetup.securitySolution.setProductFeaturesConfigurator(productFeaturesConfigurator);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,26 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesAssistantConfig,
|
||||
ProductFeatureKeys,
|
||||
ProductFeatureKibanaConfig,
|
||||
ProductFeaturesAssistantConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
assistantDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
assistantDefaultProductFeaturesConfig,
|
||||
createEnabledProductFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import type {
|
||||
AppFeatureAssistantKey,
|
||||
ProductFeatureAssistantKey,
|
||||
AssistantSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
|
||||
export const getSecurityAssistantAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesAssistantConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(assistantAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
export const getSecurityAssistantProductFeaturesConfigurator =
|
||||
(enabledProductFeatureKeys: ProductFeatureKeys) => (): ProductFeaturesAssistantConfig => {
|
||||
return createEnabledProductFeaturesConfigMap(
|
||||
assistantProductFeaturesConfig,
|
||||
enabledProductFeatureKeys
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* Maps the ProductFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Assistant app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
|
@ -32,10 +35,10 @@ export const getSecurityAssistantAppFeaturesConfigurator =
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Assistant subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Assistant subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const assistantAppFeaturesConfig: Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
const assistantProductFeaturesConfig: Record<
|
||||
ProductFeatureAssistantKey,
|
||||
ProductFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
> = {
|
||||
...assistantDefaultAppFeaturesConfig,
|
||||
...assistantDefaultProductFeaturesConfig,
|
||||
// ess-specific app features configs here
|
||||
};
|
|
@ -5,14 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesCasesConfig,
|
||||
AppFeatureKeys,
|
||||
ProductFeatureKibanaConfig,
|
||||
ProductFeaturesCasesConfig,
|
||||
ProductFeatureKeys,
|
||||
} from '@kbn/security-solution-features';
|
||||
import type { AppFeatureCasesKey, CasesSubFeatureId } from '@kbn/security-solution-features/keys';
|
||||
import type {
|
||||
ProductFeatureCasesKey,
|
||||
CasesSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
getCasesDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
getCasesDefaultProductFeaturesConfig,
|
||||
createEnabledProductFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
|
||||
import {
|
||||
|
@ -20,13 +23,16 @@ import {
|
|||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
|
||||
export const getCasesAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesCasesConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(casesAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
export const getCasesProductFeaturesConfigurator =
|
||||
(enabledProductFeatureKeys: ProductFeatureKeys) => (): ProductFeaturesCasesConfig => {
|
||||
return createEnabledProductFeaturesConfigMap(
|
||||
casesProductFeaturesConfig,
|
||||
enabledProductFeatureKeys
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* Maps the ProductFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Cases app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
|
@ -34,11 +40,11 @@ export const getCasesAppFeaturesConfigurator =
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Cases subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Cases subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const casesAppFeaturesConfig: Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
const casesProductFeaturesConfig: Record<
|
||||
ProductFeatureCasesKey,
|
||||
ProductFeatureKibanaConfig<CasesSubFeatureId>
|
||||
> = {
|
||||
...getCasesDefaultAppFeaturesConfig({
|
||||
...getCasesDefaultProductFeaturesConfig({
|
||||
apiTags: { connectors: GET_CONNECTORS_CONFIGURE_API_TAG },
|
||||
uiCapabilities: { connectors: CASES_CONNECTORS_CAPABILITY },
|
||||
}),
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { ProductFeaturesConfigurator } from '@kbn/security-solution-plugin/server/lib/product_features_service/types';
|
||||
import { getCasesProductFeaturesConfigurator } from './cases_product_features_config';
|
||||
import { getSecurityProductFeaturesConfigurator } from './security_product_features_config';
|
||||
import { getSecurityAssistantProductFeaturesConfigurator } from './assistant_product_features_config';
|
||||
|
||||
export const getProductProductFeaturesConfigurator = (
|
||||
enabledProductFeatureKeys: ProductFeatureKeys
|
||||
): ProductFeaturesConfigurator => {
|
||||
return {
|
||||
security: getSecurityProductFeaturesConfigurator(enabledProductFeatureKeys),
|
||||
cases: getCasesProductFeaturesConfigurator(enabledProductFeatureKeys),
|
||||
securityAssistant: getSecurityAssistantProductFeaturesConfigurator(enabledProductFeatureKeys),
|
||||
};
|
||||
};
|
|
@ -5,30 +5,33 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesSecurityConfig,
|
||||
ProductFeatureKeys,
|
||||
ProductFeatureKibanaConfig,
|
||||
ProductFeaturesSecurityConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
AppFeatureSecurityKey,
|
||||
ProductFeatureSecurityKey,
|
||||
type SecuritySubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
securityDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
securityDefaultProductFeaturesConfig,
|
||||
createEnabledProductFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import {
|
||||
AppFeaturesPrivilegeId,
|
||||
AppFeaturesPrivileges,
|
||||
ProductFeaturesPrivilegeId,
|
||||
ProductFeaturesPrivileges,
|
||||
} from '@kbn/security-solution-features/privileges';
|
||||
|
||||
export const getSecurityAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesSecurityConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(securityAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
export const getSecurityProductFeaturesConfigurator =
|
||||
(enabledProductFeatureKeys: ProductFeatureKeys) => (): ProductFeaturesSecurityConfig => {
|
||||
return createEnabledProductFeaturesConfigMap(
|
||||
securityProductFeaturesConfig,
|
||||
enabledProductFeatureKeys
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* Maps the ProductFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
|
@ -36,12 +39,12 @@ export const getSecurityAppFeaturesConfigurator =
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const securityAppFeaturesConfig: Record<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
const securityProductFeaturesConfig: Record<
|
||||
ProductFeatureSecurityKey,
|
||||
ProductFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
> = {
|
||||
...securityDefaultAppFeaturesConfig,
|
||||
[AppFeatureSecurityKey.endpointExceptions]: {
|
||||
privileges: AppFeaturesPrivileges[AppFeaturesPrivilegeId.endpointExceptions],
|
||||
...securityDefaultProductFeaturesConfig,
|
||||
[ProductFeatureSecurityKey.endpointExceptions]: {
|
||||
privileges: ProductFeaturesPrivileges[ProductFeaturesPrivilegeId.endpointExceptions],
|
||||
},
|
||||
};
|
|
@ -5,38 +5,41 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
import { ProductFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { SecurityProductLine, SecurityProductTier } from '../config';
|
||||
|
||||
type PliAppFeatures = Readonly<
|
||||
Record<SecurityProductLine, Readonly<Record<SecurityProductTier, Readonly<AppFeatureKeys>>>>
|
||||
type PliProductFeatures = Readonly<
|
||||
Record<SecurityProductLine, Readonly<Record<SecurityProductTier, Readonly<ProductFeatureKeys>>>>
|
||||
>;
|
||||
|
||||
export const PLI_APP_FEATURES: PliAppFeatures = {
|
||||
export const PLI_PRODUCT_FEATURES: PliProductFeatures = {
|
||||
security: {
|
||||
essentials: [AppFeatureKey.endpointHostManagement, AppFeatureKey.endpointPolicyManagement],
|
||||
essentials: [
|
||||
ProductFeatureKey.endpointHostManagement,
|
||||
ProductFeatureKey.endpointPolicyManagement,
|
||||
],
|
||||
complete: [
|
||||
AppFeatureKey.advancedInsights,
|
||||
AppFeatureKey.assistant,
|
||||
AppFeatureKey.investigationGuide,
|
||||
AppFeatureKey.threatIntelligence,
|
||||
AppFeatureKey.casesConnectors,
|
||||
AppFeatureKey.externalRuleActions,
|
||||
ProductFeatureKey.advancedInsights,
|
||||
ProductFeatureKey.assistant,
|
||||
ProductFeatureKey.investigationGuide,
|
||||
ProductFeatureKey.threatIntelligence,
|
||||
ProductFeatureKey.casesConnectors,
|
||||
ProductFeatureKey.externalRuleActions,
|
||||
],
|
||||
},
|
||||
endpoint: {
|
||||
essentials: [
|
||||
AppFeatureKey.endpointPolicyProtections,
|
||||
AppFeatureKey.endpointArtifactManagement,
|
||||
AppFeatureKey.endpointExceptions,
|
||||
ProductFeatureKey.endpointPolicyProtections,
|
||||
ProductFeatureKey.endpointArtifactManagement,
|
||||
ProductFeatureKey.endpointExceptions,
|
||||
],
|
||||
complete: [
|
||||
AppFeatureKey.endpointResponseActions,
|
||||
AppFeatureKey.osqueryAutomatedResponseActions,
|
||||
AppFeatureKey.endpointAgentTamperProtection,
|
||||
AppFeatureKey.endpointExceptions,
|
||||
AppFeatureKey.endpointProtectionUpdates,
|
||||
ProductFeatureKey.endpointResponseActions,
|
||||
ProductFeatureKey.osqueryAutomatedResponseActions,
|
||||
ProductFeatureKey.endpointAgentTamperProtection,
|
||||
ProductFeatureKey.endpointExceptions,
|
||||
ProductFeatureKey.endpointProtectionUpdates,
|
||||
],
|
||||
},
|
||||
cloud: {
|
||||
|
|
|
@ -4,44 +4,44 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { getProductAppFeatures } from './pli_features';
|
||||
import { getProductProductFeatures } from './pli_features';
|
||||
import * as pliConfig from './pli_config';
|
||||
import { ProductLine, ProductTier } from '../product';
|
||||
|
||||
describe('getProductAppFeatures', () => {
|
||||
describe('getProductProductFeatures', () => {
|
||||
it('should return the essentials PLIs features', () => {
|
||||
// @ts-ignore reassigning readonly value for testing
|
||||
pliConfig.PLI_APP_FEATURES = {
|
||||
pliConfig.PLI_PRODUCT_FEATURES = {
|
||||
security: {
|
||||
essentials: ['foo'],
|
||||
complete: ['baz'],
|
||||
},
|
||||
};
|
||||
|
||||
const appFeatureKeys = getProductAppFeatures([
|
||||
const productFeatureKeys = getProductProductFeatures([
|
||||
{ product_line: ProductLine.security, product_tier: ProductTier.essentials },
|
||||
]);
|
||||
|
||||
expect(appFeatureKeys).toEqual(['foo']);
|
||||
expect(productFeatureKeys).toEqual(['foo']);
|
||||
});
|
||||
|
||||
it('should return the complete PLIs features, which includes essentials', () => {
|
||||
// @ts-ignore reassigning readonly value for testing
|
||||
pliConfig.PLI_APP_FEATURES = {
|
||||
pliConfig.PLI_PRODUCT_FEATURES = {
|
||||
security: {
|
||||
essentials: ['foo'],
|
||||
complete: ['baz'],
|
||||
},
|
||||
};
|
||||
|
||||
const appFeatureKeys = getProductAppFeatures([
|
||||
const productFeatureKeys = getProductProductFeatures([
|
||||
{ product_line: ProductLine.security, product_tier: ProductTier.complete },
|
||||
]);
|
||||
|
||||
expect(appFeatureKeys).toEqual(['foo', 'baz']);
|
||||
expect(productFeatureKeys).toEqual(['foo', 'baz']);
|
||||
});
|
||||
|
||||
it('returns an empty object if no PLIs are enabled', () => {
|
||||
expect(getProductAppFeatures([])).toEqual([]);
|
||||
expect(getProductProductFeatures([])).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,24 +5,26 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features/src/types';
|
||||
import type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { SecurityProductTypes } from '../config';
|
||||
import { ProductTier } from '../product';
|
||||
import { PLI_APP_FEATURES } from './pli_config';
|
||||
import { PLI_PRODUCT_FEATURES } from './pli_config';
|
||||
|
||||
/**
|
||||
* Returns the U (union) of all PLIs from the enabled productTypes in a single array.
|
||||
*/
|
||||
export const getProductAppFeatures = (productTypes: SecurityProductTypes): AppFeatureKeys => {
|
||||
const appFeatureKeys = productTypes.reduce<AppFeatureKeys>(
|
||||
(appFeatures, { product_line: line, product_tier: tier }) => {
|
||||
export const getProductProductFeatures = (
|
||||
productTypes: SecurityProductTypes
|
||||
): ProductFeatureKeys => {
|
||||
const productFeatureKeys = productTypes.reduce<ProductFeatureKeys>(
|
||||
(productFeatures, { product_line: line, product_tier: tier }) => {
|
||||
if (tier === ProductTier.complete) {
|
||||
appFeatures.push(...PLI_APP_FEATURES[line][ProductTier.essentials]);
|
||||
productFeatures.push(...PLI_PRODUCT_FEATURES[line][ProductTier.essentials]);
|
||||
}
|
||||
appFeatures.push(...PLI_APP_FEATURES[line][tier]);
|
||||
return appFeatures;
|
||||
productFeatures.push(...PLI_PRODUCT_FEATURES[line][tier]);
|
||||
return productFeatures;
|
||||
},
|
||||
[]
|
||||
);
|
||||
return appFeatureKeys;
|
||||
return productFeatureKeys;
|
||||
};
|
||||
|
|
|
@ -5,26 +5,26 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import { PLI_APP_FEATURES } from '../../../common/pli/pli_config';
|
||||
import type { ProductFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import { PLI_PRODUCT_FEATURES } from '../../../common/pli/pli_config';
|
||||
|
||||
export const getProductTypeByPLI = (requiredPLI: AppFeatureKeyType): string | null => {
|
||||
if (PLI_APP_FEATURES.security.essentials.includes(requiredPLI)) {
|
||||
export const getProductTypeByPLI = (requiredPLI: ProductFeatureKeyType): string | null => {
|
||||
if (PLI_PRODUCT_FEATURES.security.essentials.includes(requiredPLI)) {
|
||||
return 'Security Essentials';
|
||||
}
|
||||
if (PLI_APP_FEATURES.security.complete.includes(requiredPLI)) {
|
||||
if (PLI_PRODUCT_FEATURES.security.complete.includes(requiredPLI)) {
|
||||
return 'Security Complete';
|
||||
}
|
||||
if (PLI_APP_FEATURES.endpoint.essentials.includes(requiredPLI)) {
|
||||
if (PLI_PRODUCT_FEATURES.endpoint.essentials.includes(requiredPLI)) {
|
||||
return 'Endpoint Essentials';
|
||||
}
|
||||
if (PLI_APP_FEATURES.endpoint.complete.includes(requiredPLI)) {
|
||||
if (PLI_PRODUCT_FEATURES.endpoint.complete.includes(requiredPLI)) {
|
||||
return 'Endpoint Complete';
|
||||
}
|
||||
if (PLI_APP_FEATURES.cloud.essentials.includes(requiredPLI)) {
|
||||
if (PLI_PRODUCT_FEATURES.cloud.essentials.includes(requiredPLI)) {
|
||||
return 'Cloud Essentials';
|
||||
}
|
||||
if (PLI_APP_FEATURES.cloud.complete.includes(requiredPLI)) {
|
||||
if (PLI_PRODUCT_FEATURES.cloud.complete.includes(requiredPLI)) {
|
||||
return 'Cloud Complete';
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
import { EuiEmptyPrompt, EuiIcon } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { memo } from 'react';
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features/keys';
|
||||
import type { ProductFeatureKeyType } from '@kbn/security-solution-features/keys';
|
||||
import { getProductTypeByPLI } from '../../hooks/use_product_type_by_pli';
|
||||
|
||||
const EndpointExceptionsDetailsUpselling: React.FC<{ requiredPLI: AppFeatureKeyType }> = memo(
|
||||
const EndpointExceptionsDetailsUpselling: React.FC<{ requiredPLI: ProductFeatureKeyType }> = memo(
|
||||
({ requiredPLI }) => {
|
||||
const productTypeRequired = getProductTypeByPLI(requiredPLI);
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ import { EuiEmptyPrompt, EuiIcon } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React from 'react';
|
||||
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import type { ProductFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import { getProductTypeByPLI } from '../hooks/use_product_type_by_pli';
|
||||
|
||||
const OsqueryResponseActionsUpsellingSection: React.FC<{ requiredPLI: AppFeatureKeyType }> =
|
||||
const OsqueryResponseActionsUpsellingSection: React.FC<{ requiredPLI: ProductFeatureKeyType }> =
|
||||
React.memo(({ requiredPLI }) => {
|
||||
const productTypeRequired = getProductTypeByPLI(requiredPLI);
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
import React from 'react';
|
||||
import { EuiEmptyPrompt, EuiIcon } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import type { ProductFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { getProductTypeByPLI } from '../hooks/use_product_type_by_pli';
|
||||
|
||||
const ThreatIntelligencePaywall: React.FC<{ requiredPLI: AppFeatureKeyType }> = React.memo(
|
||||
const ThreatIntelligencePaywall: React.FC<{ requiredPLI: ProductFeatureKeyType }> = React.memo(
|
||||
function PaywallComponent({ requiredPLI }) {
|
||||
const productTypeRequired = getProductTypeByPLI(requiredPLI);
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ import {
|
|||
} from './register_upsellings';
|
||||
import { ProductLine, ProductTier } from '../../common/product';
|
||||
import type { SecurityProductTypes } from '../../common/config';
|
||||
import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import { ALL_PRODUCT_FEATURE_KEYS } from '@kbn/security-solution-features/keys';
|
||||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import { mockServices } from '../common/services/__mocks__/services.mock';
|
||||
|
||||
const mockGetProductAppFeatures = jest.fn();
|
||||
const mockGetProductProductFeatures = jest.fn();
|
||||
jest.mock('../../common/pli/pli_features', () => ({
|
||||
getProductAppFeatures: () => mockGetProductAppFeatures(),
|
||||
getProductProductFeatures: () => mockGetProductProductFeatures(),
|
||||
}));
|
||||
|
||||
const allProductTypes: SecurityProductTypes = [
|
||||
|
@ -30,7 +30,7 @@ const allProductTypes: SecurityProductTypes = [
|
|||
|
||||
describe('registerUpsellings', () => {
|
||||
it('should not register anything when all PLIs features are enabled', () => {
|
||||
mockGetProductAppFeatures.mockReturnValue(ALL_APP_FEATURE_KEYS);
|
||||
mockGetProductProductFeatures.mockReturnValue(ALL_PRODUCT_FEATURE_KEYS);
|
||||
|
||||
const setPages = jest.fn();
|
||||
const setSections = jest.fn();
|
||||
|
@ -54,7 +54,7 @@ describe('registerUpsellings', () => {
|
|||
});
|
||||
|
||||
it('should register all upsellings pages, sections and messages when PLIs features are disabled', () => {
|
||||
mockGetProductAppFeatures.mockReturnValue([]);
|
||||
mockGetProductProductFeatures.mockReturnValue([]);
|
||||
|
||||
const setPages = jest.fn();
|
||||
const setSections = jest.fn();
|
||||
|
|
|
@ -15,8 +15,8 @@ import type {
|
|||
import type { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import React from 'react';
|
||||
import { UPGRADE_INVESTIGATION_GUIDE } from '@kbn/security-solution-upselling/messages';
|
||||
import { AppFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { AppFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import { ProductFeatureKey } from '@kbn/security-solution-features/keys';
|
||||
import type { ProductFeatureKeyType } from '@kbn/security-solution-features';
|
||||
import {
|
||||
EndpointAgentTamperProtectionLazy,
|
||||
EndpointPolicyProtectionsLazy,
|
||||
|
@ -24,7 +24,7 @@ import {
|
|||
RuleDetailsEndpointExceptionsLazy,
|
||||
} from './sections/endpoint_management';
|
||||
import type { SecurityProductTypes } from '../../common/config';
|
||||
import { getProductAppFeatures } from '../../common/pli/pli_features';
|
||||
import { getProductProductFeatures } from '../../common/pli/pli_features';
|
||||
import {
|
||||
EndpointExceptionsDetailsUpsellingLazy,
|
||||
EntityAnalyticsUpsellingLazy,
|
||||
|
@ -36,12 +36,12 @@ import type { Services } from '../common/services';
|
|||
import { withServicesProvider } from '../common/services';
|
||||
|
||||
interface UpsellingsConfig {
|
||||
pli: AppFeatureKeyType;
|
||||
pli: ProductFeatureKeyType;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
|
||||
interface UpsellingsMessageConfig {
|
||||
pli: AppFeatureKeyType;
|
||||
pli: ProductFeatureKeyType;
|
||||
message: string;
|
||||
id: UpsellingMessageId;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ export const registerUpsellings = (
|
|||
productTypes: SecurityProductTypes,
|
||||
services: Services
|
||||
) => {
|
||||
const enabledPLIsSet = new Set(getProductAppFeatures(productTypes));
|
||||
const enabledPLIsSet = new Set(getProductProductFeatures(productTypes));
|
||||
|
||||
const upsellingPagesToRegister = upsellingPages.reduce<PageUpsellings>(
|
||||
(pageUpsellings, { pageName, pli, component }) => {
|
||||
|
@ -97,25 +97,25 @@ export const upsellingPages: UpsellingPages = [
|
|||
// It is highly advisable to make use of lazy loaded components to minimize bundle size.
|
||||
{
|
||||
pageName: SecurityPageName.entityAnalytics,
|
||||
pli: AppFeatureKey.advancedInsights,
|
||||
pli: ProductFeatureKey.advancedInsights,
|
||||
component: () => (
|
||||
<EntityAnalyticsUpsellingLazy
|
||||
requiredProduct={getProductTypeByPLI(AppFeatureKey.advancedInsights) ?? undefined}
|
||||
requiredProduct={getProductTypeByPLI(ProductFeatureKey.advancedInsights) ?? undefined}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
pageName: SecurityPageName.threatIntelligence,
|
||||
pli: AppFeatureKey.threatIntelligence,
|
||||
pli: ProductFeatureKey.threatIntelligence,
|
||||
component: () => (
|
||||
<ThreatIntelligencePaywallLazy requiredPLI={AppFeatureKey.threatIntelligence} />
|
||||
<ThreatIntelligencePaywallLazy requiredPLI={ProductFeatureKey.threatIntelligence} />
|
||||
),
|
||||
},
|
||||
{
|
||||
pageName: SecurityPageName.exceptions,
|
||||
pli: AppFeatureKey.endpointExceptions,
|
||||
pli: ProductFeatureKey.endpointExceptions,
|
||||
component: () => (
|
||||
<EndpointExceptionsDetailsUpsellingLazy requiredPLI={AppFeatureKey.endpointExceptions} />
|
||||
<EndpointExceptionsDetailsUpsellingLazy requiredPLI={ProductFeatureKey.endpointExceptions} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
@ -125,31 +125,31 @@ export const upsellingSections: UpsellingSections = [
|
|||
// It is highly advisable to make use of lazy loaded components to minimize bundle size.
|
||||
{
|
||||
id: 'osquery_automated_response_actions',
|
||||
pli: AppFeatureKey.osqueryAutomatedResponseActions,
|
||||
pli: ProductFeatureKey.osqueryAutomatedResponseActions,
|
||||
component: () => (
|
||||
<OsqueryResponseActionsUpsellingSectionLazy
|
||||
requiredPLI={AppFeatureKey.osqueryAutomatedResponseActions}
|
||||
requiredPLI={ProductFeatureKey.osqueryAutomatedResponseActions}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'endpoint_agent_tamper_protection',
|
||||
pli: AppFeatureKey.endpointAgentTamperProtection,
|
||||
pli: ProductFeatureKey.endpointAgentTamperProtection,
|
||||
component: EndpointAgentTamperProtectionLazy,
|
||||
},
|
||||
{
|
||||
id: 'endpointPolicyProtections',
|
||||
pli: AppFeatureKey.endpointPolicyProtections,
|
||||
pli: ProductFeatureKey.endpointPolicyProtections,
|
||||
component: EndpointPolicyProtectionsLazy,
|
||||
},
|
||||
{
|
||||
id: 'ruleDetailsEndpointExceptions',
|
||||
pli: AppFeatureKey.endpointExceptions,
|
||||
pli: ProductFeatureKey.endpointExceptions,
|
||||
component: RuleDetailsEndpointExceptionsLazy,
|
||||
},
|
||||
{
|
||||
id: 'endpoint_protection_updates',
|
||||
pli: AppFeatureKey.endpointProtectionUpdates,
|
||||
pli: ProductFeatureKey.endpointProtectionUpdates,
|
||||
component: EndpointProtectionUpdatesLazy,
|
||||
},
|
||||
];
|
||||
|
@ -158,9 +158,9 @@ export const upsellingSections: UpsellingSections = [
|
|||
export const upsellingMessages: UpsellingMessages = [
|
||||
{
|
||||
id: 'investigation_guide',
|
||||
pli: AppFeatureKey.investigationGuide,
|
||||
pli: ProductFeatureKey.investigationGuide,
|
||||
message: UPGRADE_INVESTIGATION_GUIDE(
|
||||
getProductTypeByPLI(AppFeatureKey.investigationGuide) ?? ''
|
||||
getProductTypeByPLI(ProductFeatureKey.investigationGuide) ?? ''
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,27 +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 type { AppFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { AppFeaturesConfigurator } from '@kbn/security-solution-plugin/server/lib/app_features_service/types';
|
||||
import type { ServerlessSecurityConfig } from '../config';
|
||||
import { getCasesAppFeaturesConfigurator } from './cases_app_features_config';
|
||||
import { getSecurityAppFeaturesConfigurator } from './security_app_features_config';
|
||||
import { getSecurityAssistantAppFeaturesConfigurator } from './security_assistant_app_features_config';
|
||||
|
||||
export const getProductAppFeaturesConfigurator = (
|
||||
enabledAppFeatureKeys: AppFeatureKeys,
|
||||
config: ServerlessSecurityConfig
|
||||
): AppFeaturesConfigurator => {
|
||||
return {
|
||||
security: getSecurityAppFeaturesConfigurator(
|
||||
enabledAppFeatureKeys,
|
||||
config.experimentalFeatures
|
||||
),
|
||||
cases: getCasesAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
securityAssistant: getSecurityAssistantAppFeaturesConfigurator(enabledAppFeatureKeys),
|
||||
};
|
||||
};
|
|
@ -14,7 +14,7 @@ import type {
|
|||
} from '@kbn/core/server';
|
||||
|
||||
import { SECURITY_PROJECT_SETTINGS } from '@kbn/serverless-security-settings';
|
||||
import { getProductAppFeatures } from '../common/pli/pli_features';
|
||||
import { getProductProductFeatures } from '../common/pli/pli_features';
|
||||
|
||||
import type { ServerlessSecurityConfig } from './config';
|
||||
import { createConfig } from './config';
|
||||
|
@ -26,7 +26,7 @@ import type {
|
|||
} from './types';
|
||||
import { SecurityUsageReportingTask } from './task_manager/usage_reporting_task';
|
||||
import { cloudSecurityMetringTaskProperties } from './cloud_security/cloud_security_metering_task_config';
|
||||
import { getProductAppFeaturesConfigurator } from './app_features';
|
||||
import { getProductProductFeaturesConfigurator } from './product_features';
|
||||
import { METERING_TASK as ENDPOINT_METERING_TASK } from './endpoint/constants/metering';
|
||||
import {
|
||||
endpointMeteringService,
|
||||
|
@ -55,7 +55,7 @@ export class SecuritySolutionServerlessPlugin
|
|||
|
||||
public setup(coreSetup: CoreSetup, pluginsSetup: SecuritySolutionServerlessPluginSetupDeps) {
|
||||
this.config = createConfig(this.initializerContext, pluginsSetup.securitySolution);
|
||||
const enabledAppFeatures = getProductAppFeatures(this.config.productTypes);
|
||||
const enabledProductFeatures = getProductProductFeatures(this.config.productTypes);
|
||||
|
||||
// securitySolutionEss plugin should always be disabled when securitySolutionServerless is enabled.
|
||||
// This check is an additional layer of security to prevent double registrations when
|
||||
|
@ -64,14 +64,17 @@ export class SecuritySolutionServerlessPlugin
|
|||
if (shouldRegister) {
|
||||
const productTypesStr = JSON.stringify(this.config.productTypes, null, 2);
|
||||
this.logger.info(`Security Solution running with product types:\n${productTypesStr}`);
|
||||
const appFeaturesConfigurator = getProductAppFeaturesConfigurator(
|
||||
enabledAppFeatures,
|
||||
const productFeaturesConfigurator = getProductProductFeaturesConfigurator(
|
||||
enabledProductFeatures,
|
||||
this.config
|
||||
);
|
||||
pluginsSetup.securitySolution.setAppFeaturesConfigurator(appFeaturesConfigurator);
|
||||
pluginsSetup.securitySolution.setProductFeaturesConfigurator(productFeaturesConfigurator);
|
||||
}
|
||||
|
||||
enableRuleActions({ actions: pluginsSetup.actions, appFeatureKeys: enabledAppFeatures });
|
||||
enableRuleActions({
|
||||
actions: pluginsSetup.actions,
|
||||
productFeatureKeys: enabledProductFeatures,
|
||||
});
|
||||
|
||||
this.cloudSecurityUsageReportingTask = new SecurityUsageReportingTask({
|
||||
core: coreSetup,
|
||||
|
|
|
@ -5,26 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesAssistantConfig,
|
||||
ProductFeatureKeys,
|
||||
ProductFeatureKibanaConfig,
|
||||
ProductFeaturesAssistantConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
assistantDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
assistantDefaultProductFeaturesConfig,
|
||||
createEnabledProductFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import type {
|
||||
AppFeatureAssistantKey,
|
||||
ProductFeatureAssistantKey,
|
||||
AssistantSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
|
||||
export const getSecurityAssistantAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesAssistantConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(assistantAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
export const getSecurityAssistantProductFeaturesConfigurator =
|
||||
(enabledProductFeatureKeys: ProductFeatureKeys) => (): ProductFeaturesAssistantConfig => {
|
||||
return createEnabledProductFeaturesConfigMap(
|
||||
assistantProductFeaturesConfig,
|
||||
enabledProductFeatureKeys
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* Maps the ProductFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Assistant app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
|
@ -32,10 +35,10 @@ export const getSecurityAssistantAppFeaturesConfigurator =
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Assistant subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Assistant subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const assistantAppFeaturesConfig: Record<
|
||||
AppFeatureAssistantKey,
|
||||
AppFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
const assistantProductFeaturesConfig: Record<
|
||||
ProductFeatureAssistantKey,
|
||||
ProductFeatureKibanaConfig<AssistantSubFeatureId>
|
||||
> = {
|
||||
...assistantDefaultAppFeaturesConfig,
|
||||
...assistantDefaultProductFeaturesConfig,
|
||||
// serverless-specific app features configs here
|
||||
};
|
|
@ -5,27 +5,33 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesCasesConfig,
|
||||
AppFeatureKeys,
|
||||
ProductFeatureKibanaConfig,
|
||||
ProductFeaturesCasesConfig,
|
||||
ProductFeatureKeys,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
getCasesDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
getCasesDefaultProductFeaturesConfig,
|
||||
createEnabledProductFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import type { AppFeatureCasesKey, CasesSubFeatureId } from '@kbn/security-solution-features/keys';
|
||||
import type {
|
||||
ProductFeatureCasesKey,
|
||||
CasesSubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
CASES_CONNECTORS_CAPABILITY,
|
||||
GET_CONNECTORS_CONFIGURE_API_TAG,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
|
||||
export const getCasesAppFeaturesConfigurator =
|
||||
(enabledAppFeatureKeys: AppFeatureKeys) => (): AppFeaturesCasesConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(casesAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
export const getCasesProductFeaturesConfigurator =
|
||||
(enabledProductFeatureKeys: ProductFeatureKeys) => (): ProductFeaturesCasesConfig => {
|
||||
return createEnabledProductFeaturesConfigMap(
|
||||
casesProductFeaturesConfig,
|
||||
enabledProductFeatureKeys
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* Maps the ProductFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security Cases app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
|
@ -33,11 +39,11 @@ export const getCasesAppFeaturesConfigurator =
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Cases subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Cases subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const casesAppFeaturesConfig: Record<
|
||||
AppFeatureCasesKey,
|
||||
AppFeatureKibanaConfig<CasesSubFeatureId>
|
||||
const casesProductFeaturesConfig: Record<
|
||||
ProductFeatureCasesKey,
|
||||
ProductFeatureKibanaConfig<CasesSubFeatureId>
|
||||
> = {
|
||||
...getCasesDefaultAppFeaturesConfig({
|
||||
...getCasesDefaultProductFeaturesConfig({
|
||||
apiTags: { connectors: GET_CONNECTORS_CONFIGURE_API_TAG },
|
||||
uiCapabilities: { connectors: CASES_CONNECTORS_CAPABILITY },
|
||||
}),
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
import type { ProductFeaturesConfigurator } from '@kbn/security-solution-plugin/server/lib/product_features_service/types';
|
||||
import type { ServerlessSecurityConfig } from '../config';
|
||||
import { getCasesProductFeaturesConfigurator } from './cases_product_features_config';
|
||||
import { getSecurityProductFeaturesConfigurator } from './security_product_features_config';
|
||||
import { getSecurityAssistantProductFeaturesConfigurator } from './assistant_product_features_config';
|
||||
|
||||
export const getProductProductFeaturesConfigurator = (
|
||||
enabledProductFeatureKeys: ProductFeatureKeys,
|
||||
config: ServerlessSecurityConfig
|
||||
): ProductFeaturesConfigurator => {
|
||||
return {
|
||||
security: getSecurityProductFeaturesConfigurator(
|
||||
enabledProductFeatureKeys,
|
||||
config.experimentalFeatures
|
||||
),
|
||||
cases: getCasesProductFeaturesConfigurator(enabledProductFeatureKeys),
|
||||
securityAssistant: getSecurityAssistantProductFeaturesConfigurator(enabledProductFeatureKeys),
|
||||
};
|
||||
};
|
|
@ -5,28 +5,34 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
AppFeatureKeys,
|
||||
AppFeatureKibanaConfig,
|
||||
AppFeaturesSecurityConfig,
|
||||
ProductFeatureKeys,
|
||||
ProductFeatureKibanaConfig,
|
||||
ProductFeaturesSecurityConfig,
|
||||
} from '@kbn/security-solution-features';
|
||||
import {
|
||||
securityDefaultAppFeaturesConfig,
|
||||
createEnabledAppFeaturesConfigMap,
|
||||
securityDefaultProductFeaturesConfig,
|
||||
createEnabledProductFeaturesConfigMap,
|
||||
} from '@kbn/security-solution-features/config';
|
||||
import { AppFeatureSecurityKey, SecuritySubFeatureId } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
ProductFeatureSecurityKey,
|
||||
SecuritySubFeatureId,
|
||||
} from '@kbn/security-solution-features/keys';
|
||||
import type { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
|
||||
export const getSecurityAppFeaturesConfigurator =
|
||||
export const getSecurityProductFeaturesConfigurator =
|
||||
(
|
||||
enabledAppFeatureKeys: AppFeatureKeys,
|
||||
enabledProductFeatureKeys: ProductFeatureKeys,
|
||||
_: ExperimentalFeatures // currently un-used, but left here as a convenience for possible future use
|
||||
) =>
|
||||
(): AppFeaturesSecurityConfig => {
|
||||
return createEnabledAppFeaturesConfigMap(securityAppFeaturesConfig, enabledAppFeatureKeys);
|
||||
(): ProductFeaturesSecurityConfig => {
|
||||
return createEnabledProductFeaturesConfigMap(
|
||||
securityProductFeaturesConfig,
|
||||
enabledProductFeatureKeys
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the AppFeatures keys to Kibana privileges that will be merged
|
||||
* Maps the ProductFeatures keys to Kibana privileges that will be merged
|
||||
* into the base privileges config for the Security app.
|
||||
*
|
||||
* Privileges can be added in different ways:
|
||||
|
@ -34,12 +40,12 @@ export const getSecurityAppFeaturesConfigurator =
|
|||
* - `subFeatureIds`: the ids of the sub-features that will be added into the Security subFeatures entry.
|
||||
* - `subFeaturesPrivileges`: the privileges that will be added into the existing Security subFeature with the privilege `id` specified.
|
||||
*/
|
||||
const securityAppFeaturesConfig: Record<
|
||||
AppFeatureSecurityKey,
|
||||
AppFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
const securityProductFeaturesConfig: Record<
|
||||
ProductFeatureSecurityKey,
|
||||
ProductFeatureKibanaConfig<SecuritySubFeatureId>
|
||||
> = {
|
||||
...securityDefaultAppFeaturesConfig,
|
||||
[AppFeatureSecurityKey.endpointExceptions]: {
|
||||
...securityDefaultProductFeaturesConfig,
|
||||
[ProductFeatureSecurityKey.endpointExceptions]: {
|
||||
subFeatureIds: [SecuritySubFeatureId.endpointExceptions],
|
||||
},
|
||||
};
|
|
@ -4,14 +4,14 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { AppFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import { ProductFeatureSecurityKey } from '@kbn/security-solution-features/keys';
|
||||
import {
|
||||
IndexConnectorTypeId,
|
||||
SlackWebhookConnectorTypeId,
|
||||
EmailConnectorTypeId,
|
||||
} from '@kbn/stack-connectors-plugin/server/connector_types';
|
||||
import { EnabledActionTypes } from '@kbn/actions-plugin/server/config';
|
||||
import type { AppFeatureKeys } from '@kbn/security-solution-features/src/types';
|
||||
import type { ProductFeatureKeys } from '@kbn/security-solution-features';
|
||||
|
||||
import type { PluginSetupContract as ActionsPluginSetupContract } from '@kbn/actions-plugin/server';
|
||||
|
||||
|
@ -22,16 +22,16 @@ const INTERNAL_RULE_ACTIONS = [
|
|||
];
|
||||
|
||||
/**
|
||||
* enable rule actions based on AppFeature Config
|
||||
* enable rule actions based on ProductFeature Config
|
||||
*/
|
||||
export const enableRuleActions = ({
|
||||
actions,
|
||||
appFeatureKeys,
|
||||
productFeatureKeys,
|
||||
}: {
|
||||
actions: ActionsPluginSetupContract;
|
||||
appFeatureKeys: AppFeatureKeys;
|
||||
productFeatureKeys: ProductFeatureKeys;
|
||||
}) => {
|
||||
if (appFeatureKeys.includes(AppFeatureSecurityKey.externalRuleActions)) {
|
||||
if (productFeatureKeys.includes(ProductFeatureSecurityKey.externalRuleActions)) {
|
||||
// enables all rule actions
|
||||
actions.setEnabledConnectorTypes([EnabledActionTypes.Any]);
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue