Skip to content

Commit 526b903

Browse files
semdtomsonpl
andauthored
[8.19] [Security Solution] Hide plugins UI via config without disabling them (#222821) (#223126)
# Backport This will backport the following commits from `main` to `8.19`: - [[Security Solution] Hide plugins UI via config without disabling them (#222821)](#222821) <!--- Backport version: 10.0.0 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Sergi Massaneda","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-06-09T16:21:13Z","message":"[Security Solution] Hide plugins UI via config without disabling them (#222821)\n\n## Summary\n\nIntroduces a new config in the security serverless plugin to hide other\nplugins' UI (making them inaccessible).\n\nIt uses the `core.application.registerAppUpdater` API.\n\n@elastic/kibana-visualizations I did some cleanup around the custom\nglobal search's `registerResultProvider`, which seems to be a workaround\nfrom times before `visibleIn` functionalities. This way, the search\nresult depends on the plugin being accessible. My change introduces a\nsmall text update in the search result, though. To me, now it looks more\nconsistent with the rest of the analytics applications. Let me know if\nyou think that's an issue.\n\n| Prev | New |\n|------|-----|\n|\n![Prev](https://github.com/user-attachments/assets/c11e36c5-c101-440d-aa94-05bce288807d)\n|\n![New](https://github.com/user-attachments/assets/39becf16-7e0d-4c93-9cd7-35a1ced8b234)\n|\n\n### Goal\n\nThis is needed by the `AI4DSOC` team to hide some applications outside\nthe Security Solution:\n\n```yaml\nxpack.securitySolutionServerless.inaccessibleApps:\n - dashboards\n - visualize\n - maps\n - lens\n```\n\n---------\n\nCo-authored-by: kibanamachine <[email protected]>\nCo-authored-by: Tomasz Ciecierski <[email protected]>","sha":"81c4fc89993e804ab795fabc67c8ce1fc2be1487","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team: SecuritySolution","backport:version","v9.1.0","v8.19.0"],"title":"[Security Solution] Hide plugins UI via config without disabling them","number":222821,"url":"https://github.com/elastic/kibana/pull/222821","mergeCommit":{"message":"[Security Solution] Hide plugins UI via config without disabling them (#222821)\n\n## Summary\n\nIntroduces a new config in the security serverless plugin to hide other\nplugins' UI (making them inaccessible).\n\nIt uses the `core.application.registerAppUpdater` API.\n\n@elastic/kibana-visualizations I did some cleanup around the custom\nglobal search's `registerResultProvider`, which seems to be a workaround\nfrom times before `visibleIn` functionalities. This way, the search\nresult depends on the plugin being accessible. My change introduces a\nsmall text update in the search result, though. To me, now it looks more\nconsistent with the rest of the analytics applications. Let me know if\nyou think that's an issue.\n\n| Prev | New |\n|------|-----|\n|\n![Prev](https://github.com/user-attachments/assets/c11e36c5-c101-440d-aa94-05bce288807d)\n|\n![New](https://github.com/user-attachments/assets/39becf16-7e0d-4c93-9cd7-35a1ced8b234)\n|\n\n### Goal\n\nThis is needed by the `AI4DSOC` team to hide some applications outside\nthe Security Solution:\n\n```yaml\nxpack.securitySolutionServerless.inaccessibleApps:\n - dashboards\n - visualize\n - maps\n - lens\n```\n\n---------\n\nCo-authored-by: kibanamachine <[email protected]>\nCo-authored-by: Tomasz Ciecierski <[email protected]>","sha":"81c4fc89993e804ab795fabc67c8ce1fc2be1487"}},"sourceBranch":"main","suggestedTargetBranches":["8.19"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/222821","number":222821,"mergeCommit":{"message":"[Security Solution] Hide plugins UI via config without disabling them (#222821)\n\n## Summary\n\nIntroduces a new config in the security serverless plugin to hide other\nplugins' UI (making them inaccessible).\n\nIt uses the `core.application.registerAppUpdater` API.\n\n@elastic/kibana-visualizations I did some cleanup around the custom\nglobal search's `registerResultProvider`, which seems to be a workaround\nfrom times before `visibleIn` functionalities. This way, the search\nresult depends on the plugin being accessible. My change introduces a\nsmall text update in the search result, though. To me, now it looks more\nconsistent with the rest of the analytics applications. Let me know if\nyou think that's an issue.\n\n| Prev | New |\n|------|-----|\n|\n![Prev](https://github.com/user-attachments/assets/c11e36c5-c101-440d-aa94-05bce288807d)\n|\n![New](https://github.com/user-attachments/assets/39becf16-7e0d-4c93-9cd7-35a1ced8b234)\n|\n\n### Goal\n\nThis is needed by the `AI4DSOC` team to hide some applications outside\nthe Security Solution:\n\n```yaml\nxpack.securitySolutionServerless.inaccessibleApps:\n - dashboards\n - visualize\n - maps\n - lens\n```\n\n---------\n\nCo-authored-by: kibanamachine <[email protected]>\nCo-authored-by: Tomasz Ciecierski <[email protected]>","sha":"81c4fc89993e804ab795fabc67c8ce1fc2be1487"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: Tomasz Ciecierski <[email protected]>
1 parent 9b3c35e commit 526b903

File tree

14 files changed

+111
-135
lines changed

14 files changed

+111
-135
lines changed

config/serverless.security.search_ai_lake.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
xpack.osquery.enabled: false
44
xpack.ml.ad.enabled: false
55
xpack.ml.dfa.enabled: false
6+
7+
## Plugins that are inaccessible in the UI
8+
xpack.securitySolutionServerless.inaccessibleApps:
9+
- dashboards
10+
- visualize
11+
- maps
12+
- lens
13+
614
## Disable plugin features
715
xpack.alerting.maintenanceWindow.enabled: false
816
xpack.alerting.rulesSettings.enabled: false

x-pack/platform/plugins/private/translations/translations/fr-FR.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27762,7 +27762,6 @@
2776227762
"xpack.lens.reporting.shareContextMenu.csvReportsButtonLabel": "Téléchargement CSV",
2776327763
"xpack.lens.resetLayerAriaLabel": "Effacer le calque",
2776427764
"xpack.lens.saveDuplicateRejectedDescription": "La confirmation d'enregistrement avec un doublon de titre a été rejetée.",
27765-
"xpack.lens.searchTitle": "Lens : créer des visualisations",
2776627765
"xpack.lens.section.bannerMessagesLabel": "Messages de déclassement",
2776727766
"xpack.lens.section.configPanelLabel": "Panneau de configuration",
2776827767
"xpack.lens.section.dataPanelLabel": "Panneau de données",

x-pack/platform/plugins/private/translations/translations/ja-JP.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27735,7 +27735,6 @@
2773527735
"xpack.lens.reporting.shareContextMenu.csvReportsButtonLabel": "CSVダウンロード",
2773627736
"xpack.lens.resetLayerAriaLabel": "レイヤーをクリア",
2773727737
"xpack.lens.saveDuplicateRejectedDescription": "重複ファイルの保存確認が拒否されました",
27738-
"xpack.lens.searchTitle": "Lens:ビジュアライゼーションを作成",
2773927738
"xpack.lens.section.bannerMessagesLabel": "廃止予定メッセージ",
2774027739
"xpack.lens.section.configPanelLabel": "構成パネル",
2774127740
"xpack.lens.section.dataPanelLabel": "データパネル",

x-pack/platform/plugins/private/translations/translations/zh-CN.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27793,7 +27793,6 @@
2779327793
"xpack.lens.reporting.shareContextMenu.csvReportsButtonLabel": "CSV 下载",
2779427794
"xpack.lens.resetLayerAriaLabel": "清除图层",
2779527795
"xpack.lens.saveDuplicateRejectedDescription": "已拒绝使用重复标题保存确认",
27796-
"xpack.lens.searchTitle": "Lens:创建可视化",
2779727796
"xpack.lens.section.bannerMessagesLabel": "过时消息",
2779827797
"xpack.lens.section.configPanelLabel": "配置面板",
2779927798
"xpack.lens.section.dataPanelLabel": "数据面板",

x-pack/platform/plugins/shared/lens/public/plugin.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import type { ChartType } from '@kbn/visualization-utils';
6969
import type { ServerlessPluginStart } from '@kbn/serverless/public';
7070
import { LicensingPluginStart } from '@kbn/licensing-plugin/public';
7171
import { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public';
72+
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
7273
import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service';
7374
import type {
7475
FormBasedDatasource as FormBasedDatasourceType,
@@ -135,7 +136,6 @@ import { getSaveModalComponent } from './app_plugin/shared/saved_modal_lazy';
135136
import type { SaveModalContainerProps } from './app_plugin/save_modal_container';
136137

137138
import { setupExpressions } from './expressions';
138-
import { getSearchProvider } from './search_provider';
139139
import { OpenInDiscoverDrilldown } from './trigger_actions/open_in_discover_drilldown';
140140
import { ChartInfoApi } from './chart_info_api';
141141
import { type LensAppLocator, LensAppLocatorDefinition } from '../common/locator/locator';
@@ -476,7 +476,9 @@ export class LensPlugin {
476476
core.application.register({
477477
id: APP_ID,
478478
title: NOT_INTERNATIONALIZED_PRODUCT_NAME,
479-
visibleIn: [],
479+
visibleIn: ['globalSearch'],
480+
category: DEFAULT_APP_CATEGORIES.kibana,
481+
euiIconType: 'logoKibana',
480482
mount: async (params: AppMountParameters) => {
481483
const { core: coreStart, plugins: deps } = startServices();
482484

@@ -511,20 +513,6 @@ export class LensPlugin {
511513
},
512514
});
513515

514-
if (globalSearch) {
515-
globalSearch.registerResultProvider(
516-
getSearchProvider(
517-
core.getStartServices().then(
518-
([
519-
{
520-
application: { capabilities },
521-
},
522-
]) => capabilities
523-
)
524-
)
525-
);
526-
}
527-
528516
urlForwarding.forwardApp(APP_ID, APP_ID);
529517

530518
// Note: this overwrites a method defined above

x-pack/platform/plugins/shared/lens/public/search_provider.ts

Lines changed: 0 additions & 74 deletions
This file was deleted.

x-pack/solutions/security/plugins/security_solution_serverless/common/config.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ export const productTypes = schema.arrayOf<SecurityProductType>(productType, {
3535
});
3636
export type SecurityProductTypes = TypeOf<typeof productTypes>;
3737

38-
export const configSchema = schema.object({
39-
enabled: schema.boolean({ defaultValue: false }),
38+
const commonConfigSchemaProps = {
4039
productTypes,
4140
/**
4241
* For internal use. A list of string values (comma delimited) that will enable experimental
@@ -53,6 +52,19 @@ export const configSchema = schema.object({
5352
enableExperimental: schema.arrayOf(schema.string(), {
5453
defaultValue: () => [],
5554
}),
56-
});
55+
/**
56+
* A list of APP IDs that are not accessible in the serverless environment.
57+
* This is used to disable the apps in the UI and prevent users from accessing them.
58+
*/
59+
inaccessibleApps: schema.arrayOf(schema.string(), { defaultValue: [] }),
60+
};
61+
62+
export const commonConfigSchema = schema.object(commonConfigSchemaProps);
63+
64+
export type ServerlessSecurityPublicConfig = TypeOf<typeof commonConfigSchema>;
5765

58-
export type ServerlessSecurityConfigSchema = TypeOf<typeof configSchema>;
66+
// This is used to expose the common config schema properties to the browser
67+
// so that they can be used in the client-side code.
68+
export const exposeToBrowser = Object.fromEntries(
69+
Object.keys(commonConfigSchemaProps).map((prop) => [prop, true])
70+
);

x-pack/solutions/security/plugins/security_solution_serverless/public/plugin.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,23 @@
55
* 2.0.
66
*/
77

8-
import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';
8+
import type {
9+
AppUpdater,
10+
CoreSetup,
11+
CoreStart,
12+
Plugin,
13+
PluginInitializerContext,
14+
} from '@kbn/core/public';
15+
import { BehaviorSubject } from 'rxjs';
16+
import { AppStatus } from '@kbn/core-application-browser';
917

1018
import { getDashboardsLandingCallout } from './components/dashboards_landing_callout';
19+
import type { ServerlessSecurityPublicConfig } from '../common/config';
1120
import type {
1221
SecuritySolutionServerlessPluginSetup,
1322
SecuritySolutionServerlessPluginStart,
1423
SecuritySolutionServerlessPluginSetupDeps,
1524
SecuritySolutionServerlessPluginStartDeps,
16-
ServerlessSecurityPublicConfig,
1725
} from './types';
1826
import { registerUpsellings } from './upselling';
1927
import { createServices } from './common/services/create_services';
@@ -44,20 +52,20 @@ export class SecuritySolutionServerlessPlugin
4452
}
4553

4654
public setup(
47-
_core: CoreSetup,
55+
core: CoreSetup,
4856
setupDeps: SecuritySolutionServerlessPluginSetupDeps
4957
): SecuritySolutionServerlessPluginSetup {
5058
const { securitySolution } = setupDeps;
51-
const { productTypes } = this.config;
59+
const { productTypes, enableExperimental, inaccessibleApps } = this.config;
5260

5361
this.experimentalFeatures = parseExperimentalConfigValue(
54-
this.config.enableExperimental,
62+
enableExperimental,
5563
securitySolution.experimentalFeatures
5664
).features;
5765

5866
securitySolution.setProductFeatureKeys(getEnabledProductFeatures(productTypes));
59-
6067
setupDeps.discover.showInlineTopNav();
68+
updateInaccessibleApps(inaccessibleApps, core);
6169

6270
return {};
6371
}
@@ -85,3 +93,23 @@ export class SecuritySolutionServerlessPlugin
8593

8694
public stop() {}
8795
}
96+
97+
/**
98+
* Disables apps that are inaccessible based on the provided configuration.
99+
* It updates the app status to 'inaccessible' for those apps.
100+
* The apps will still execute their lifecycle methods, but it will remain inaccessible in the UI.
101+
*/
102+
const updateInaccessibleApps = (inaccessibleApps: string[], core: CoreSetup) => {
103+
if (!inaccessibleApps?.length) {
104+
return;
105+
}
106+
107+
const inaccessibleAppsSet = new Set(inaccessibleApps);
108+
const appUpdater$ = new BehaviorSubject<AppUpdater>((app) => {
109+
if (inaccessibleAppsSet.has(app.id)) {
110+
return { status: AppStatus.inaccessible };
111+
}
112+
});
113+
114+
core.application.registerAppUpdater(appUpdater$);
115+
};

x-pack/solutions/security/plugins/security_solution_serverless/public/types.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/pu
1515
import type { CloudStart } from '@kbn/cloud-plugin/public';
1616
import type { DiscoverSetup } from '@kbn/discover-plugin/public';
1717
import type { IntegrationAssistantPluginStart } from '@kbn/integration-assistant-plugin/public';
18-
import type { ServerlessSecurityConfigSchema } from '../common/config';
1918

2019
// eslint-disable-next-line @typescript-eslint/no-empty-interface
2120
export interface SecuritySolutionServerlessPluginSetup {}
@@ -39,8 +38,3 @@ export interface SecuritySolutionServerlessPluginStartDeps {
3938
cloud: CloudStart;
4039
integrationAssistant?: IntegrationAssistantPluginStart;
4140
}
42-
43-
export type ServerlessSecurityPublicConfig = Pick<
44-
ServerlessSecurityConfigSchema,
45-
'productTypes' | 'enableExperimental'
46-
>;

x-pack/solutions/security/plugins/security_solution_serverless/server/config.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
import { schema, type TypeOf } from '@kbn/config-schema';
99
import type { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server';
1010
import type { SecuritySolutionPluginSetup } from '@kbn/security-solution-plugin/server/plugin_contract';
11+
1112
import { USAGE_SERVICE_USAGE_URL } from './constants';
12-
import { productTypes } from '../common/config';
13+
import { commonConfigSchema, exposeToBrowser } from '../common/config';
1314
import type { ExperimentalFeatures } from '../common/experimental_features';
1415
import { parseExperimentalConfigValue } from '../common/experimental_features';
1516

16-
export const configSchema = schema.object({
17+
export const serverConfigSchema = schema.object({
1718
enabled: schema.boolean({ defaultValue: false }),
18-
productTypes,
1919
/**
2020
* Usage Reporting: the interval between runs of the endpoint task
2121
*/
@@ -25,41 +25,24 @@ export const configSchema = schema.object({
2525
/**
2626
* Usage Reporting: the interval between runs of the cloud security task
2727
*/
28-
2928
cloudSecurityUsageReportingTaskInterval: schema.string({ defaultValue: '30m' }),
3029

31-
/**
32-
* Usage Reporting: timeout value for how long the task should run.
33-
*/
34-
usageReportingTaskTimeout: schema.string({ defaultValue: '1m' }),
35-
3630
/**
3731
* Usage Reporting: the URL to send usage data to
3832
*/
3933
usageReportingApiUrl: schema.string({ defaultValue: USAGE_SERVICE_USAGE_URL }),
34+
4035
/**
41-
* For internal use. A list of string values (comma delimited) that will enable experimental
42-
* type of functionality that is not yet released. Valid values for this settings need to
43-
* be defined in:
44-
* `x-pack/solutions/security/plugins/security_solution_serverless/common/experimental_features.ts`
45-
* under the `allowedExperimentalValues` object
46-
*
47-
* @example
48-
* xpack.securitySolutionServerless.enableExperimental:
49-
* - someCrazyServerlessFeature
50-
* - someEvenCrazierServerlessFeature
36+
* Usage Reporting: timeout value for how long the task should run.
5137
*/
52-
enableExperimental: schema.arrayOf(schema.string(), {
53-
defaultValue: () => [],
54-
}),
38+
usageReportingTaskTimeout: schema.string({ defaultValue: '1m' }),
5539
});
40+
const configSchema = schema.allOf([commonConfigSchema, serverConfigSchema]);
41+
5642
export type ServerlessSecuritySchema = TypeOf<typeof configSchema>;
5743

5844
export const config: PluginConfigDescriptor<ServerlessSecuritySchema> = {
59-
exposeToBrowser: {
60-
enableExperimental: true,
61-
productTypes: true,
62-
},
45+
exposeToBrowser,
6346
schema: configSchema,
6447
deprecations: ({ renameFromRoot }) => [
6548
renameFromRoot(

0 commit comments

Comments
 (0)