mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] Static config settings for serverless (#167856)
## Summary This PR implements a standard way to have different static settings for the serverless and ess (stateful) environments. It centralizes flags, which were set using different approaches previously, in a single configuration. This aims to make it easier for developers to enable/disable parts of the application in serverless projects. Default: ``` sideNavEnabled: true, ILMEnabled: true, ESQLEnabled: true, ``` Serverless: ``` xpack.securitySolution.offeringSettings: { sideNavEnabled: false, # Internal security side navigation disabled, the serverless global chrome navigation is used instead ILMEnabled: false, # Index Lifecycle Management (ILM) functionalities disabled, not supported by serverless Elasticsearch ESQLEnabled: false, # ES|QL disabled, not supported by serverless Elasticsearch } ``` ### Consume the settings #### Server - Plugin parsed `ConfigType`: `this.config.settings.ESQLEnabled` #### UI - Plugin attribute: `this.configSettings.ESQLEnabled`. - Components can access it from Kibana services: `useKibana().services.configSettings.ESQLEnabled;` --------- Co-authored-by: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com>
This commit is contained in:
parent
6e548c0ce9
commit
c7df950bd7
25 changed files with 161 additions and 68 deletions
|
@ -17,6 +17,12 @@ xpack.securitySolutionServerless.productTypes:
|
|||
{ product_line: 'endpoint', product_tier: 'complete' },
|
||||
]
|
||||
|
||||
xpack.securitySolution.offeringSettings: {
|
||||
sideNavEnabled: false, # Internal security side navigation disabled, the serverless global chrome navigation is used instead
|
||||
ILMEnabled: false, # Index Lifecycle Management (ILM) functionalities disabled, not supported by serverless Elasticsearch
|
||||
ESQLEnabled: false, # ES|QL disabled, not supported by serverless Elasticsearch
|
||||
}
|
||||
|
||||
## Set the home route
|
||||
uiSettings.overrides.defaultRoute: /app/security/get_started
|
||||
|
||||
|
@ -33,10 +39,6 @@ xpack.fleet.internal.registry.spec.max: '3.0'
|
|||
# xpack.fleet.internal.registry.kibanaVersionCheckEnabled: false
|
||||
# xpack.fleet.internal.registry.spec.min: '3.0'
|
||||
|
||||
# Serverless security specific options
|
||||
xpack.securitySolution.enableExperimental:
|
||||
- esqlRulesDisabled
|
||||
|
||||
xpack.ml.ad.enabled: true
|
||||
xpack.ml.dfa.enabled: true
|
||||
xpack.ml.nlp.enabled: false
|
||||
|
|
|
@ -316,6 +316,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.spaces.allowFeatureVisibility (any)',
|
||||
'xpack.securitySolution.enableExperimental (array)',
|
||||
'xpack.securitySolution.prebuiltRulesPackageVersion (string)',
|
||||
'xpack.securitySolution.offeringSettings (record)',
|
||||
'xpack.snapshot_restore.slm_ui.enabled (boolean)',
|
||||
'xpack.snapshot_restore.ui.enabled (boolean)',
|
||||
'xpack.stack_connectors.enableExperimental (array)',
|
||||
|
|
65
x-pack/plugins/security_solution/common/config_settings.ts
Normal file
65
x-pack/plugins/security_solution/common/config_settings.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export interface ConfigSettings {
|
||||
/**
|
||||
* Security solution internal side navigation enabled
|
||||
*/
|
||||
sideNavEnabled: boolean;
|
||||
/**
|
||||
* Index Lifecycle Management (ILM) feature enabled.
|
||||
*/
|
||||
ILMEnabled: boolean;
|
||||
/**
|
||||
* ESQL queries enabled.
|
||||
*/
|
||||
ESQLEnabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of allowed values that can be override in `xpack.securitySolution.offeringSettings`.
|
||||
* This object is then used to validate and parse the value entered.
|
||||
*/
|
||||
export const defaultSettings: ConfigSettings = Object.freeze({
|
||||
sideNavEnabled: true,
|
||||
ILMEnabled: true,
|
||||
ESQLEnabled: true,
|
||||
});
|
||||
|
||||
type ConfigSettingsKey = keyof ConfigSettings;
|
||||
|
||||
/**
|
||||
* Parses the string value used in `xpack.securitySolution.offeringSettings` kibana configuration,
|
||||
*
|
||||
* @param offeringSettings
|
||||
*/
|
||||
export const parseConfigSettings = (
|
||||
offeringSettings: Record<string, boolean>
|
||||
): { settings: ConfigSettings; invalid: string[] } => {
|
||||
const configSettings: Partial<ConfigSettings> = {};
|
||||
const invalidKeys: string[] = [];
|
||||
|
||||
for (const optionKey in offeringSettings) {
|
||||
if (defaultSettings[optionKey as ConfigSettingsKey] == null) {
|
||||
invalidKeys.push(optionKey);
|
||||
} else {
|
||||
configSettings[optionKey as ConfigSettingsKey] = offeringSettings[optionKey];
|
||||
}
|
||||
}
|
||||
|
||||
const settings: ConfigSettings = Object.freeze({
|
||||
...defaultSettings,
|
||||
...configSettings,
|
||||
});
|
||||
|
||||
return {
|
||||
settings,
|
||||
invalid: invalidKeys,
|
||||
};
|
||||
};
|
||||
|
||||
export const getDefaultConfigSettings = (): ConfigSettings => ({ ...defaultSettings });
|
|
@ -6,19 +6,20 @@
|
|||
*/
|
||||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { useSecuritySolutionNavigation } from './use_security_solution_navigation';
|
||||
|
||||
jest.mock('../breadcrumbs', () => ({
|
||||
useBreadcrumbsNav: () => jest.fn(),
|
||||
}));
|
||||
|
||||
const mockIsSidebarEnabled$ = new BehaviorSubject(true);
|
||||
const mockIsSideNavEnabled = jest.fn(() => true);
|
||||
jest.mock('../../../lib/kibana/kibana_react', () => {
|
||||
return {
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
isSidebarEnabled$: mockIsSidebarEnabled$.asObservable(),
|
||||
configSettings: {
|
||||
sideNavEnabled: mockIsSideNavEnabled(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
@ -26,7 +27,6 @@ jest.mock('../../../lib/kibana/kibana_react', () => {
|
|||
|
||||
describe('Security Solution Navigation', () => {
|
||||
beforeEach(() => {
|
||||
mockIsSidebarEnabled$.next(true);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
|
@ -44,7 +44,7 @@ describe('Security Solution Navigation', () => {
|
|||
});
|
||||
|
||||
it('should return undefined props when disabled', () => {
|
||||
mockIsSidebarEnabled$.next(false);
|
||||
mockIsSideNavEnabled.mockReturnValueOnce(false);
|
||||
const { result } = renderHook(useSecuritySolutionNavigation);
|
||||
expect(result.current).toEqual(undefined);
|
||||
});
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { useKibana } from '../../../lib/kibana';
|
||||
|
@ -24,12 +23,11 @@ const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mai
|
|||
});
|
||||
|
||||
export const useSecuritySolutionNavigation = (): KibanaPageTemplateProps['solutionNav'] => {
|
||||
const { isSidebarEnabled$ } = useKibana().services;
|
||||
const isSidebarEnabled = useObservable(isSidebarEnabled$);
|
||||
const { sideNavEnabled } = useKibana().services.configSettings;
|
||||
|
||||
useBreadcrumbsNav();
|
||||
|
||||
if (!isSidebarEnabled) {
|
||||
if (!sideNavEnabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ import { NavigationProvider } from '@kbn/security-solution-navigation';
|
|||
import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks';
|
||||
import { savedSearchPluginMock } from '@kbn/saved-search-plugin/public/mocks';
|
||||
import { contractStartServicesMock } from '../../../mocks';
|
||||
import { getDefaultConfigSettings } from '../../../../common/config_settings';
|
||||
|
||||
const mockUiSettings: Record<string, unknown> = {
|
||||
[DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' },
|
||||
|
@ -126,6 +127,7 @@ export const createStartServicesMock = (
|
|||
return {
|
||||
...core,
|
||||
...contractStartServicesMock,
|
||||
configSettings: getDefaultConfigSettings(),
|
||||
apm,
|
||||
cases,
|
||||
unifiedSearch,
|
||||
|
|
|
@ -19,6 +19,7 @@ export interface ServerApiError {
|
|||
export interface SecuritySolutionUiConfigType {
|
||||
enableExperimental: string[];
|
||||
prebuiltRulesPackageVersion?: string;
|
||||
offeringSettings: Record<string, boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,10 +15,20 @@ import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_ex
|
|||
jest.mock('../../../../common/lib/kibana');
|
||||
|
||||
jest.mock('../../../../common/hooks/use_experimental_features', () => ({
|
||||
useIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(false),
|
||||
useIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(false), // enabled (esqlRulesDisabled = false)
|
||||
}));
|
||||
const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
|
||||
|
||||
const mockESQLEnabled = jest.fn(() => true);
|
||||
jest.mock('../../../../common/lib/kibana', () => {
|
||||
const useKibana = jest.requireActual('../../../../common/lib/kibana').useKibana;
|
||||
return {
|
||||
useKibana: () => ({
|
||||
services: { ...useKibana().services, configSettings: { ESQLEnabled: mockESQLEnabled() } },
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('SelectRuleType', () => {
|
||||
it('renders correctly', () => {
|
||||
const Component = () => {
|
||||
|
@ -187,8 +197,27 @@ describe('SelectRuleType', () => {
|
|||
expect(wrapper.find('[data-test-subj="esqlRuleType"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not render "esql" rule type if its feature disabled', () => {
|
||||
useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
|
||||
it('should not render "esql" rule type if esql setting is disabled', () => {
|
||||
mockESQLEnabled.mockReturnValueOnce(false);
|
||||
const Component = () => {
|
||||
const field = useFormFieldMock();
|
||||
|
||||
return (
|
||||
<SelectRuleType
|
||||
field={field}
|
||||
describedByIds={[]}
|
||||
isUpdateView={false}
|
||||
hasValidLicense={true}
|
||||
isMlAdmin={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const wrapper = shallow(<Component />);
|
||||
expect(wrapper.find('[data-test-subj="esqlRuleType"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not render "esql" rule type if the feature flag is disabled', () => {
|
||||
useIsExperimentalFeatureEnabledMock.mockReturnValue(true); // disabled (esqlRulesDisabled = true)
|
||||
const Component = () => {
|
||||
const field = useFormFieldMock();
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import * as i18n from './translations';
|
|||
import { MlCardDescription } from './ml_card_description';
|
||||
import { TechnicalPreviewBadge } from '../technical_preview_badge';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
|
||||
interface SelectRuleTypeProps {
|
||||
describedByIds: string[];
|
||||
|
@ -49,7 +50,9 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = memo(
|
|||
const setNewTerms = useCallback(() => setType('new_terms'), [setType]);
|
||||
const setEsql = useCallback(() => setType('esql'), [setType]);
|
||||
|
||||
const isEsqlFeatureEnabled = !useIsExperimentalFeatureEnabled('esqlRulesDisabled');
|
||||
const isEsqlSettingEnabled = useKibana().services.configSettings.ESQLEnabled;
|
||||
const isEsqlFeatureFlagEnabled = !useIsExperimentalFeatureEnabled('esqlRulesDisabled');
|
||||
const isEsqlFeatureEnabled = isEsqlSettingEnabled && isEsqlFeatureFlagEnabled;
|
||||
|
||||
const eqlSelectableConfig = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -16,10 +16,8 @@ const upselling = new UpsellingService();
|
|||
|
||||
export const contractStartServicesMock: ContractStartServices = {
|
||||
extraRoutes$: of([]),
|
||||
isSidebarEnabled$: of(true),
|
||||
getComponent$: jest.fn(),
|
||||
upselling,
|
||||
dataQualityPanelConfig: undefined,
|
||||
};
|
||||
|
||||
const setupMock = (): PluginSetup => ({
|
||||
|
@ -27,12 +25,10 @@ const setupMock = (): PluginSetup => ({
|
|||
experimentalFeatures: allowedExperimentalValues, // default values
|
||||
setAppLinksSwitcher: jest.fn(),
|
||||
setDeepLinksFormatter: jest.fn(),
|
||||
setDataQualityPanelConfig: jest.fn(),
|
||||
});
|
||||
|
||||
const startMock = (): PluginStart => ({
|
||||
getNavLinks$: jest.fn(() => new BehaviorSubject<NavigationLink[]>([])),
|
||||
setIsSidebarEnabled: jest.fn(),
|
||||
setComponents: jest.fn(),
|
||||
getBreadcrumbsNav$: jest.fn(
|
||||
() => new BehaviorSubject<BreadcrumbsNav>({ leading: [], trailing: [] })
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
import type { SecuritySubPlugin } from '../app/types';
|
||||
import { routes } from './routes';
|
||||
|
||||
export * from './types';
|
||||
|
||||
export class Overview {
|
||||
public setup() {}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { useKibana as mockUseKibana } from '../../common/lib/kibana/__mocks__';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
|
@ -16,7 +15,6 @@ import { DataQuality } from './data_quality';
|
|||
import { HOT, WARM, UNMANAGED } from './translations';
|
||||
|
||||
const mockedUseKibana = mockUseKibana();
|
||||
const mockIsILMAvailable = of(true);
|
||||
|
||||
jest.mock('../../common/components/landing_page');
|
||||
jest.mock('../../common/lib/kibana', () => {
|
||||
|
@ -51,7 +49,7 @@ jest.mock('../../common/lib/kibana', () => {
|
|||
useCasesAddToNewCaseFlyout: jest.fn(),
|
||||
},
|
||||
},
|
||||
isILMAvailable$: mockIsILMAvailable,
|
||||
configSettings: { ILMEnabled: true },
|
||||
},
|
||||
}),
|
||||
useUiSetting$: () => ['0,0.[000]'],
|
||||
|
|
|
@ -54,10 +54,8 @@ import type {
|
|||
ReportDataQualityCheckAllCompletedParams,
|
||||
ReportDataQualityIndexCheckedParams,
|
||||
} from '../../common/lib/telemetry';
|
||||
import type { DataQualityPanelConfig } from '../types';
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'dataQualityDashboardLastChecked';
|
||||
const defaultDataQualityPanelConfig: DataQualityPanelConfig = { isILMAvailable: true };
|
||||
|
||||
const comboBoxStyle: React.CSSProperties = {
|
||||
width: '322px',
|
||||
|
@ -158,8 +156,8 @@ const DataQualityComponent: React.FC = () => {
|
|||
const [selectedOptions, setSelectedOptions] = useState<EuiComboBoxOptionOption[]>(defaultOptions);
|
||||
const { indicesExist, loading: isSourcererLoading, selectedPatterns } = useSourcererDataView();
|
||||
const { signalIndexName, loading: isSignalIndexNameLoading } = useSignalIndex();
|
||||
const { dataQualityPanelConfig = defaultDataQualityPanelConfig, cases } = useKibana().services;
|
||||
const { isILMAvailable } = dataQualityPanelConfig;
|
||||
const { configSettings, cases } = useKibana().services;
|
||||
const isILMAvailable = configSettings.ILMEnabled;
|
||||
|
||||
const [startDate, setStartTime] = useState<string>();
|
||||
const [endDate, setEndTime] = useState<string>();
|
||||
|
@ -173,7 +171,7 @@ const DataQualityComponent: React.FC = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isILMAvailable === false) {
|
||||
if (!isILMAvailable) {
|
||||
setStartTime(DEFAULT_START_TIME);
|
||||
setEndTime(DEFAULT_END_TIME);
|
||||
}
|
||||
|
|
|
@ -1,10 +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.
|
||||
*/
|
||||
|
||||
export interface DataQualityPanelConfig {
|
||||
isILMAvailable: boolean;
|
||||
}
|
|
@ -57,6 +57,7 @@ import { LazyEndpointCustomAssetsExtension } from './management/pages/policy/vie
|
|||
import type { SecurityAppStore } from './common/store/types';
|
||||
import { PluginContract } from './plugin_contract';
|
||||
import { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service';
|
||||
import { parseConfigSettings, type ConfigSettings } from '../common/config_settings';
|
||||
|
||||
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
|
||||
/**
|
||||
|
@ -84,6 +85,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
private telemetry: TelemetryService;
|
||||
|
||||
readonly experimentalFeatures: ExperimentalFeatures;
|
||||
readonly configSettings: ConfigSettings;
|
||||
private queryService: QueryService = new QueryService();
|
||||
private nowProvider: NowProvider = new NowProvider();
|
||||
|
||||
|
@ -92,6 +94,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
this.experimentalFeatures = parseExperimentalConfigValue(
|
||||
this.config.enableExperimental || []
|
||||
).features;
|
||||
this.configSettings = parseConfigSettings(this.config.offeringSettings ?? {}).settings;
|
||||
this.kibanaVersion = initializerContext.env.packageInfo.version;
|
||||
this.kibanaBranch = initializerContext.env.packageInfo.branch;
|
||||
this.prebuiltRulesPackageVersion = this.config.prebuiltRulesPackageVersion;
|
||||
|
@ -185,6 +188,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
...coreStart,
|
||||
...startPlugins,
|
||||
...this.contract.getStartServices(),
|
||||
configSettings: this.configSettings,
|
||||
apm,
|
||||
savedObjectsTagging: savedObjectsTaggingOss.getTaggingApi(),
|
||||
setHeaderActionMenu: params.setHeaderActionMenu,
|
||||
|
|
|
@ -9,7 +9,6 @@ import { BehaviorSubject } from 'rxjs';
|
|||
import type { RouteProps } from 'react-router-dom';
|
||||
import { UpsellingService } from '@kbn/security-solution-upselling/service';
|
||||
import type { ContractStartServices, PluginSetup, PluginStart } from './types';
|
||||
import type { DataQualityPanelConfig } from './overview/types';
|
||||
import type { AppLinksSwitcher } from './common/links';
|
||||
import type { DeepLinksFormatter } from './common/links/deep_links';
|
||||
import type { ExperimentalFeatures } from '../common/experimental_features';
|
||||
|
@ -18,17 +17,14 @@ import { breadcrumbsNav$ } from './common/breadcrumbs';
|
|||
import { ContractComponentsService } from './contract_components';
|
||||
|
||||
export class PluginContract {
|
||||
public isSidebarEnabled$: BehaviorSubject<boolean>;
|
||||
public componentsService: ContractComponentsService;
|
||||
public upsellingService: UpsellingService;
|
||||
public extraRoutes$: BehaviorSubject<RouteProps[]>;
|
||||
public appLinksSwitcher: AppLinksSwitcher;
|
||||
public deepLinksFormatter?: DeepLinksFormatter;
|
||||
public dataQualityPanelConfig?: DataQualityPanelConfig;
|
||||
|
||||
constructor(private readonly experimentalFeatures: ExperimentalFeatures) {
|
||||
this.extraRoutes$ = new BehaviorSubject<RouteProps[]>([]);
|
||||
this.isSidebarEnabled$ = new BehaviorSubject<boolean>(true);
|
||||
this.componentsService = new ContractComponentsService();
|
||||
this.upsellingService = new UpsellingService();
|
||||
this.appLinksSwitcher = (appLinks) => appLinks;
|
||||
|
@ -37,10 +33,8 @@ export class PluginContract {
|
|||
public getStartServices(): ContractStartServices {
|
||||
return {
|
||||
extraRoutes$: this.extraRoutes$.asObservable(),
|
||||
isSidebarEnabled$: this.isSidebarEnabled$.asObservable(),
|
||||
getComponent$: this.componentsService.getComponent$.bind(this.componentsService),
|
||||
upselling: this.upsellingService,
|
||||
dataQualityPanelConfig: this.dataQualityPanelConfig,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -54,9 +48,6 @@ export class PluginContract {
|
|||
setDeepLinksFormatter: (deepLinksFormatter) => {
|
||||
this.deepLinksFormatter = deepLinksFormatter;
|
||||
},
|
||||
setDataQualityPanelConfig: (dataQualityPanelConfig) => {
|
||||
this.dataQualityPanelConfig = dataQualityPanelConfig;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -64,8 +55,6 @@ export class PluginContract {
|
|||
return {
|
||||
getNavLinks$: () => navLinks$,
|
||||
setExtraRoutes: (extraRoutes) => this.extraRoutes$.next(extraRoutes),
|
||||
setIsSidebarEnabled: (isSidebarEnabled: boolean) =>
|
||||
this.isSidebarEnabled$.next(isSidebarEnabled),
|
||||
setComponents: (components) => {
|
||||
this.componentsService.setComponents(components);
|
||||
},
|
||||
|
|
|
@ -81,8 +81,8 @@ import type { BreadcrumbsNav } from './common/breadcrumbs/types';
|
|||
import type { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service';
|
||||
import type { ExperimentalFeatures } from '../common/experimental_features';
|
||||
import type { DeepLinksFormatter } from './common/links/deep_links';
|
||||
import type { DataQualityPanelConfig } from './overview/types';
|
||||
import type { SetComponents, GetComponent$ } from './contract_components';
|
||||
import type { ConfigSettings } from '../common/config_settings';
|
||||
|
||||
export interface SetupPlugins {
|
||||
cloud?: CloudSetup;
|
||||
|
@ -147,15 +147,14 @@ export interface StartPluginsDependencies extends StartPlugins {
|
|||
|
||||
export interface ContractStartServices {
|
||||
extraRoutes$: Observable<RouteProps[]>;
|
||||
isSidebarEnabled$: Observable<boolean>;
|
||||
getComponent$: GetComponent$;
|
||||
upselling: UpsellingService;
|
||||
dataQualityPanelConfig: DataQualityPanelConfig | undefined;
|
||||
}
|
||||
|
||||
export type StartServices = CoreStart &
|
||||
StartPlugins &
|
||||
ContractStartServices & {
|
||||
configSettings: ConfigSettings;
|
||||
storage: Storage;
|
||||
sessionStorage: Storage;
|
||||
apm: ApmBase;
|
||||
|
@ -181,13 +180,11 @@ export interface PluginSetup {
|
|||
experimentalFeatures: ExperimentalFeatures;
|
||||
setAppLinksSwitcher: (appLinksSwitcher: AppLinksSwitcher) => void;
|
||||
setDeepLinksFormatter: (deepLinksFormatter: DeepLinksFormatter) => void;
|
||||
setDataQualityPanelConfig: (dataQualityPanelConfig: DataQualityPanelConfig) => void;
|
||||
}
|
||||
|
||||
export interface PluginStart {
|
||||
getNavLinks$: () => Observable<NavigationLink[]>;
|
||||
setExtraRoutes: (extraRoutes: RouteProps[]) => void;
|
||||
setIsSidebarEnabled: (isSidebarEnabled: boolean) => void;
|
||||
setComponents: SetComponents;
|
||||
getBreadcrumbsNav$: () => Observable<BreadcrumbsNav>;
|
||||
getUpselling: () => UpsellingService;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../common/constants';
|
||||
import type { ExperimentalFeatures } from '../common/experimental_features';
|
||||
import { parseExperimentalConfigValue } from '../common/experimental_features';
|
||||
import { getDefaultConfigSettings } from '../common/config_settings';
|
||||
import type { ConfigType } from './config';
|
||||
|
||||
export const createMockConfig = (): ConfigType => {
|
||||
|
@ -26,7 +27,7 @@ export const createMockConfig = (): ConfigType => {
|
|||
alertMergeStrategy: 'missingFields',
|
||||
alertIgnoreFields: [],
|
||||
maxUploadResponseActionFileBytes: 26214400,
|
||||
|
||||
settings: getDefaultConfigSettings(),
|
||||
experimentalFeatures: parseExperimentalConfigValue(enableExperimental).features,
|
||||
enabled: true,
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ import type { PluginInitializerContext } from '@kbn/core/server';
|
|||
import { SIGNALS_INDEX_KEY, DEFAULT_SIGNALS_INDEX } from '../common/constants';
|
||||
import type { ExperimentalFeatures } from '../common/experimental_features';
|
||||
import { parseExperimentalConfigValue } from '../common/experimental_features';
|
||||
import { parseConfigSettings, type ConfigSettings } from '../common/config_settings';
|
||||
|
||||
export const configSchema = schema.object({
|
||||
maxRuleImportExportSize: schema.number({ defaultValue: 10000 }),
|
||||
|
@ -121,12 +122,24 @@ export const configSchema = schema.object({
|
|||
defaultValue: 26214400, // 25MB,
|
||||
max: 104857600, // 100MB,
|
||||
}),
|
||||
/**
|
||||
* Defines the settings for a specific offering of the Security Solution app.
|
||||
* They override the default values.
|
||||
* @example
|
||||
* xpack.securitySolution.offeringSettings: {
|
||||
* "ILMEnabled": false,
|
||||
* }
|
||||
*/
|
||||
offeringSettings: schema.recordOf(schema.string(), schema.boolean(), {
|
||||
defaultValue: {},
|
||||
}),
|
||||
});
|
||||
|
||||
export type ConfigSchema = TypeOf<typeof configSchema>;
|
||||
|
||||
export type ConfigType = ConfigSchema & {
|
||||
export type ConfigType = Omit<ConfigSchema, 'offeringSettings'> & {
|
||||
experimentalFeatures: ExperimentalFeatures;
|
||||
settings: ConfigSettings;
|
||||
};
|
||||
|
||||
export const createConfig = (context: PluginInitializerContext): ConfigType => {
|
||||
|
@ -146,8 +159,20 @@ ${invalid.map((key) => ` - ${key}`).join('\n')}
|
|||
`);
|
||||
}
|
||||
|
||||
const { invalid: invalidConfigSettings, settings } = parseConfigSettings(
|
||||
pluginConfig.offeringSettings
|
||||
);
|
||||
|
||||
if (invalidConfigSettings.length) {
|
||||
logger.warn(`Unsupported "xpack.securitySolution.offeringSettings" values detected.
|
||||
The following configuration values are no longer supported and should be removed from the kibana configuration file:
|
||||
${invalidConfigSettings.map((key) => ` - ${key}`).join('\n')}
|
||||
`);
|
||||
}
|
||||
|
||||
return {
|
||||
...pluginConfig,
|
||||
experimentalFeatures,
|
||||
settings,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ export const config: PluginConfigDescriptor<ConfigSchema> = {
|
|||
exposeToBrowser: {
|
||||
enableExperimental: true,
|
||||
prebuiltRulesPackageVersion: true,
|
||||
offeringSettings: true,
|
||||
},
|
||||
schema: configSchema,
|
||||
deprecations: ({ renameFromRoot, unused }) => [
|
||||
|
|
|
@ -421,7 +421,7 @@ export const previewRulesRoute = async (
|
|||
);
|
||||
break;
|
||||
case 'esql':
|
||||
if (config.experimentalFeatures.esqlRulesDisabled) {
|
||||
if (!config.settings.ESQLEnabled || config.experimentalFeatures.esqlRulesDisabled) {
|
||||
throw Error('ES|QL rule type is not supported');
|
||||
}
|
||||
const esqlAlertType = previewRuleTypeWrapper(createEsqlAlertType(ruleOptions));
|
||||
|
|
|
@ -282,7 +282,7 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
const securityRuleTypeWrapper = createSecurityRuleTypeWrapper(securityRuleTypeOptions);
|
||||
|
||||
plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(ruleOptions)));
|
||||
if (!experimentalFeatures.esqlRulesDisabled) {
|
||||
if (config.settings.ESQLEnabled && !experimentalFeatures.esqlRulesDisabled) {
|
||||
plugins.alerting.registerType(securityRuleTypeWrapper(createEsqlAlertType(ruleOptions)));
|
||||
}
|
||||
plugins.alerting.registerType(
|
||||
|
|
|
@ -28,11 +28,8 @@ export class SecuritySolutionEssPlugin
|
|||
{
|
||||
public setup(
|
||||
_core: CoreSetup,
|
||||
setupDeps: SecuritySolutionEssPluginSetupDeps
|
||||
_setupDeps: SecuritySolutionEssPluginSetupDeps
|
||||
): SecuritySolutionEssPluginSetup {
|
||||
const { securitySolution } = setupDeps;
|
||||
securitySolution.setDataQualityPanelConfig({ isILMAvailable: true });
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,7 @@ export const setupNavigation = (
|
|||
};
|
||||
|
||||
export const startNavigation = (services: Services, config: ServerlessSecurityPublicConfig) => {
|
||||
const { serverless, securitySolution, management } = services;
|
||||
securitySolution.setIsSidebarEnabled(false);
|
||||
const { serverless, management } = services;
|
||||
serverless.setProjectHome(APP_PATH);
|
||||
|
||||
const projectNavigationTree = new ProjectNavigationTree(services);
|
||||
|
|
|
@ -29,7 +29,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
...svlSharedConfig.get('kbnTestServer.serverArgs'),
|
||||
'--serverless=security',
|
||||
'--xpack.encryptedSavedObjects.encryptionKey="abcdefghijklmnopqrstuvwxyz123456"',
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['esqlRulesDisabled'])}`,
|
||||
`--xpack.securitySolutionServerless.productTypes=${JSON.stringify([
|
||||
{ product_line: 'security', product_tier: 'complete' },
|
||||
{ product_line: 'endpoint', product_tier: 'complete' },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue