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