From b75546f7eb59f49e0d2daefa904736fe7e481f52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yulia=20=C4=8Cech?=
<6585477+yuliacech@users.noreply.github.com>
Date: Wed, 26 Apr 2023 13:33:58 +0200
Subject: [PATCH] [Guided onboarding] Use Kibana features to grant access
(#155065)
## Summary
Fixes https://github.com/elastic/kibana/issues/149132
This PR adds a Kibana feature for the guided onboarding plugin for
better permissions handling. By default `kibana_admin` and `editor`
roles are granted access to guided onboarding. The role `viewer` on the
other hand doesn't have enough permissions to see or use guided
onboarding. For any roles that don't have the correct permissions,
guided onboarding is completely disabled, the same as it's disabled
on-prem.
When creating a new role, the feature "Setup guides" can be enabled or
disabled.
### How to test
1. Add `xpack.cloud.id: 'testID'` to `/config/kibana.dev.yml`
1. Start ES with `yarn es snapshot` and Kibana with `yarn start``
2. Login as elastic and create a test user with the role `viewer`
3. Clear everything from your browser's local storage
4. Login as the test user and check the following
- On the first visit, the "on-prem" welcome message is shown (not the
guided onboarding landing page)
- The url `/app/home#/getting_started` is unknown and redirects back to
the home page
- There is no button "Setup guides" in the header
- There is no link "Setup guides" in the help menu
### Checklist
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../public/components/app.tsx | 60 ++++++++++++-------
.../guided_onboarding/common/constants.ts | 2 +
src/plugins/guided_onboarding/kibana.jsonc | 3 +-
src/plugins/guided_onboarding/public/mocks.ts | 1 +
.../guided_onboarding/public/plugin.tsx | 9 ++-
.../public/services/api.service.ts | 18 +++---
src/plugins/guided_onboarding/public/types.ts | 1 +
.../guided_onboarding/server/feature.ts | 42 +++++++++++++
.../guided_onboarding/server/plugin.ts | 6 +-
src/plugins/guided_onboarding/tsconfig.json | 1 +
.../__snapshots__/home.test.tsx.snap | 35 +++++++++--
.../components/add_data/add_data.tsx | 10 ++--
.../application/components/home.test.tsx | 8 ++-
.../public/application/components/home.tsx | 5 +-
.../public/application/components/home_app.js | 9 ++-
.../cloud_links/kibana.jsonc | 3 +-
.../cloud_links/public/plugin.test.ts | 53 +++++++++++-----
.../cloud_links/public/plugin.tsx | 25 ++++----
.../cloud_links/tsconfig.json | 1 +
.../apis/features/features/features.ts | 1 +
.../apis/security/privileges.ts | 1 +
.../apis/security/privileges_basic.ts | 2 +
.../security_and_spaces/tests/nav_links.ts | 16 ++++-
23 files changed, 230 insertions(+), 82 deletions(-)
create mode 100644 src/plugins/guided_onboarding/server/feature.ts
diff --git a/examples/guided_onboarding_example/public/components/app.tsx b/examples/guided_onboarding_example/public/components/app.tsx
index 1a1083a10f1f..2812697f37c5 100755
--- a/examples/guided_onboarding_example/public/components/app.tsx
+++ b/examples/guided_onboarding_example/public/components/app.tsx
@@ -11,13 +11,7 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
import { Router, Switch } from 'react-router-dom';
import { Route } from '@kbn/shared-ux-router';
-import {
- EuiPage,
- EuiPageBody,
- EuiPageContent_Deprecated as EuiPageContent,
- EuiPageHeader,
- EuiTitle,
-} from '@elastic/eui';
+import { EuiPageTemplate } from '@elastic/eui';
import { CoreStart, ScopedHistory } from '@kbn/core/public';
@@ -39,19 +33,17 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps
return (
-
-
-
-
-
-
-
-
-
-
+
+
+ }
+ />
+ {guidedOnboarding.guidedOnboardingApi?.isEnabled ? (
+
@@ -75,9 +67,31 @@ export const GuidedOnboardingExampleApp = (props: GuidedOnboardingExampleAppDeps
/>
-
-
-
+
+ ) : (
+
+
+
+ }
+ body={
+
+
+
+ }
+ />
+ )}
+
);
};
diff --git a/src/plugins/guided_onboarding/common/constants.ts b/src/plugins/guided_onboarding/common/constants.ts
index 1ab45d3659d7..899666bc757c 100755
--- a/src/plugins/guided_onboarding/common/constants.ts
+++ b/src/plugins/guided_onboarding/common/constants.ts
@@ -9,4 +9,6 @@
export const PLUGIN_ID = 'guidedOnboarding';
export const PLUGIN_NAME = 'guidedOnboarding';
+export const PLUGIN_FEATURE = 'guidedOnboardingFeature';
+
export const API_BASE_PATH = '/internal/guided_onboarding';
diff --git a/src/plugins/guided_onboarding/kibana.jsonc b/src/plugins/guided_onboarding/kibana.jsonc
index e816f0e027fe..300df79253da 100644
--- a/src/plugins/guided_onboarding/kibana.jsonc
+++ b/src/plugins/guided_onboarding/kibana.jsonc
@@ -8,7 +8,8 @@
"server": true,
"browser": true,
"optionalPlugins": [
- "cloud"
+ "cloud",
+ "features"
],
"requiredBundles": [
"kibanaReact"
diff --git a/src/plugins/guided_onboarding/public/mocks.ts b/src/plugins/guided_onboarding/public/mocks.ts
index e190ddbcc219..a6fb3ae40da9 100644
--- a/src/plugins/guided_onboarding/public/mocks.ts
+++ b/src/plugins/guided_onboarding/public/mocks.ts
@@ -28,6 +28,7 @@ const apiServiceMock: jest.Mocked = {
isGuidePanelOpen$: new BehaviorSubject(false),
isLoading$: new BehaviorSubject(false),
getGuideConfig: jest.fn(),
+ isEnabled: true,
},
};
diff --git a/src/plugins/guided_onboarding/public/plugin.tsx b/src/plugins/guided_onboarding/public/plugin.tsx
index 97eac765c5dd..8f37552ae53d 100755
--- a/src/plugins/guided_onboarding/public/plugin.tsx
+++ b/src/plugins/guided_onboarding/public/plugin.tsx
@@ -21,6 +21,8 @@ import {
} from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
+
+import { PLUGIN_FEATURE } from '../common/constants';
import type {
AppPluginStartDependencies,
GuidedOnboardingPluginSetup,
@@ -43,11 +45,12 @@ export class GuidedOnboardingPlugin
): GuidedOnboardingPluginStart {
const { chrome, http, theme, application, notifications, uiSettings } = core;
+ // Guided onboarding UI is only available on cloud and if the access to the Kibana feature is granted
+ const isEnabled = !!(cloud?.isCloudEnabled && application.capabilities[PLUGIN_FEATURE].enabled);
// Initialize services
- apiService.setup(http, !!cloud?.isCloudEnabled);
+ apiService.setup(http, isEnabled);
- // Guided onboarding UI is only available on cloud
- if (cloud?.isCloudEnabled) {
+ if (isEnabled) {
chrome.navControls.registerExtension({
order: 1000,
mount: (target) =>
diff --git a/src/plugins/guided_onboarding/public/services/api.service.ts b/src/plugins/guided_onboarding/public/services/api.service.ts
index d3d20143f600..271a9afa9b56 100644
--- a/src/plugins/guided_onboarding/public/services/api.service.ts
+++ b/src/plugins/guided_onboarding/public/services/api.service.ts
@@ -41,15 +41,15 @@ import {
import { ConfigService } from './config.service';
export class ApiService implements GuidedOnboardingApi {
- private isCloudEnabled: boolean | undefined;
+ private _isEnabled: boolean = false;
private client: HttpSetup | undefined;
private pluginState$!: BehaviorSubject;
public isLoading$ = new BehaviorSubject(false);
public isGuidePanelOpen$: BehaviorSubject = new BehaviorSubject(false);
private configService = new ConfigService();
- public setup(httpClient: HttpSetup, isCloudEnabled: boolean) {
- this.isCloudEnabled = isCloudEnabled;
+ public setup(httpClient: HttpSetup, isEnabled: boolean) {
+ this._isEnabled = isEnabled;
this.client = httpClient;
this.pluginState$ = new BehaviorSubject(undefined);
this.isGuidePanelOpen$ = new BehaviorSubject(false);
@@ -94,7 +94,7 @@ export class ApiService implements GuidedOnboardingApi {
* Subsequently, the observable is updated automatically, when the state changes.
*/
public fetchPluginState$(): Observable {
- if (!this.isCloudEnabled) {
+ if (!this._isEnabled) {
return of(undefined);
}
if (!this.client) {
@@ -118,7 +118,7 @@ export class ApiService implements GuidedOnboardingApi {
* where all guides are displayed with their corresponding status.
*/
public async fetchAllGuidesState(): Promise<{ state: GuideState[] } | undefined> {
- if (!this.isCloudEnabled) {
+ if (!this._isEnabled) {
return undefined;
}
if (!this.client) {
@@ -143,7 +143,7 @@ export class ApiService implements GuidedOnboardingApi {
state: { status?: PluginStatus; guide?: GuideState },
panelState: boolean
): Promise<{ pluginState: PluginState } | undefined> {
- if (!this.isCloudEnabled) {
+ if (!this._isEnabled) {
return undefined;
}
if (!this.client) {
@@ -467,7 +467,7 @@ export class ApiService implements GuidedOnboardingApi {
* @return {Promise} a promise with the guide config or undefined if the config is not found
*/
public async getGuideConfig(guideId: GuideId): Promise {
- if (!this.isCloudEnabled) {
+ if (!this._isEnabled) {
return undefined;
}
if (!this.client) {
@@ -478,6 +478,10 @@ export class ApiService implements GuidedOnboardingApi {
this.isLoading$.next(false);
return config;
}
+
+ public get isEnabled() {
+ return this._isEnabled;
+ }
}
export const apiService = new ApiService();
diff --git a/src/plugins/guided_onboarding/public/types.ts b/src/plugins/guided_onboarding/public/types.ts
index 1103c2ee350d..9f348b991095 100755
--- a/src/plugins/guided_onboarding/public/types.ts
+++ b/src/plugins/guided_onboarding/public/types.ts
@@ -62,4 +62,5 @@ export interface GuidedOnboardingApi {
isGuidePanelOpen$: Observable;
isLoading$: Observable;
getGuideConfig: (guideId: GuideId) => Promise;
+ readonly isEnabled: boolean;
}
diff --git a/src/plugins/guided_onboarding/server/feature.ts b/src/plugins/guided_onboarding/server/feature.ts
new file mode 100644
index 000000000000..fce2cb9033b9
--- /dev/null
+++ b/src/plugins/guided_onboarding/server/feature.ts
@@ -0,0 +1,42 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type { KibanaFeatureConfig } from '@kbn/features-plugin/common';
+import { i18n } from '@kbn/i18n';
+import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
+import { PLUGIN_FEATURE, PLUGIN_ID } from '../common/constants';
+import { guideStateSavedObjectsType, pluginStateSavedObjectsType } from './saved_objects';
+
+export const GUIDED_ONBOARDING_FEATURE: KibanaFeatureConfig = {
+ id: PLUGIN_FEATURE,
+ name: i18n.translate('guidedOnboarding.featureRegistry.featureName', {
+ defaultMessage: 'Setup guides',
+ }),
+ category: DEFAULT_APP_CATEGORIES.management,
+ app: [PLUGIN_ID],
+ privileges: {
+ all: {
+ app: [PLUGIN_ID],
+ savedObject: {
+ all: [guideStateSavedObjectsType, pluginStateSavedObjectsType],
+ read: [],
+ },
+ ui: ['enabled'],
+ },
+ read: {
+ // we haven't implemented "read-only" access yet, so this feature can only be granted
+ // as "all" or "none"
+ disabled: true,
+ savedObject: {
+ all: [],
+ read: [],
+ },
+ ui: [],
+ },
+ },
+};
diff --git a/src/plugins/guided_onboarding/server/plugin.ts b/src/plugins/guided_onboarding/server/plugin.ts
index f264771d780e..3a5138d20133 100755
--- a/src/plugins/guided_onboarding/server/plugin.ts
+++ b/src/plugins/guided_onboarding/server/plugin.ts
@@ -9,6 +9,8 @@
import { PluginInitializerContext, CoreSetup, Plugin, Logger } from '@kbn/core/server';
import type { GuideId, GuideConfig } from '@kbn/guided-onboarding';
+import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
+import { GUIDED_ONBOARDING_FEATURE } from './feature';
import { GuidedOnboardingPluginSetup, GuidedOnboardingPluginStart } from './types';
import { defineRoutes } from './routes';
import { guideStateSavedObjects, pluginStateSavedObjects } from './saved_objects';
@@ -25,7 +27,7 @@ export class GuidedOnboardingPlugin
this.guidesConfig = {} as GuidesConfig;
}
- public setup(core: CoreSetup) {
+ public setup(core: CoreSetup, plugins: { features?: FeaturesPluginSetup }) {
this.logger.debug('guidedOnboarding: Setup');
const router = core.http.createRouter();
@@ -36,6 +38,8 @@ export class GuidedOnboardingPlugin
core.savedObjects.registerType(guideStateSavedObjects);
core.savedObjects.registerType(pluginStateSavedObjects);
+ plugins.features?.registerKibanaFeature(GUIDED_ONBOARDING_FEATURE);
+
return {
registerGuideConfig: (guideId: GuideId, guideConfig: GuideConfig) => {
if (this.guidesConfig[guideId]) {
diff --git a/src/plugins/guided_onboarding/tsconfig.json b/src/plugins/guided_onboarding/tsconfig.json
index 42026215e18f..88569a7a4323 100644
--- a/src/plugins/guided_onboarding/tsconfig.json
+++ b/src/plugins/guided_onboarding/tsconfig.json
@@ -18,6 +18,7 @@
"@kbn/core-http-browser",
"@kbn/core-http-browser-mocks",
"@kbn/config-schema",
+ "@kbn/features-plugin",
],
"exclude": [
"target/**/*",
diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap
index 53df35833013..03dfb3820429 100644
--- a/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap
+++ b/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap
@@ -308,12 +308,19 @@ exports[`home isNewKibanaInstance should safely handle exceptions 1`] = `
Array [
"./home#/getting_started",
],
+ Array [
+ "./home#/getting_started",
+ ],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
],
},
}
@@ -335,12 +342,19 @@ exports[`home isNewKibanaInstance should safely handle exceptions 1`] = `
Array [
"./home#/getting_started",
],
+ Array [
+ "./home#/getting_started",
+ ],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
],
},
}
@@ -389,12 +403,19 @@ exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when t
Array [
"./home#/getting_started",
],
+ Array [
+ "./home#/getting_started",
+ ],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
],
},
}
@@ -416,12 +437,19 @@ exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when t
Array [
"./home#/getting_started",
],
+ Array [
+ "./home#/getting_started",
+ ],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
],
},
}
@@ -437,12 +465,7 @@ exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when t
`;
-exports[`home isNewKibanaInstance should set isNewKibanaInstance to true when there are no index patterns 1`] = `
-
-`;
+exports[`home isNewKibanaInstance should set isNewKibanaInstance to true when there are no index patterns 1`] = `""`;
exports[`home should render home component 1`] = `
<_KibanaPageTemplate
diff --git a/src/plugins/home/public/application/components/add_data/add_data.tsx b/src/plugins/home/public/application/components/add_data/add_data.tsx
index 086e28cbb1f8..5440ad09d2c1 100644
--- a/src/plugins/home/public/application/components/add_data/add_data.tsx
+++ b/src/plugins/home/public/application/components/add_data/add_data.tsx
@@ -35,7 +35,7 @@ interface Props {
}
export const AddData: FC = ({ addBasePath, application, isDarkMode, isCloudEnabled }) => {
- const { trackUiMetric } = getServices();
+ const { trackUiMetric, guidedOnboardingService } = getServices();
const canAccessIntegrations = application.capabilities.navLinks.integrations;
if (canAccessIntegrations) {
return (
@@ -70,7 +70,7 @@ export const AddData: FC = ({ addBasePath, application, isDarkMode, isClo
- {isCloudEnabled && (
+ {guidedOnboardingService?.isEnabled && (
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
= ({ addBasePath, application, isDarkMode, isClo
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
{
diff --git a/src/plugins/home/public/application/components/home.test.tsx b/src/plugins/home/public/application/components/home.test.tsx
index 806ceed7d759..c7240d430907 100644
--- a/src/plugins/home/public/application/components/home.test.tsx
+++ b/src/plugins/home/public/application/components/home.test.tsx
@@ -15,6 +15,8 @@ import { Welcome } from './welcome';
let mockHasIntegrationsPermission = true;
const mockNavigateToUrl = jest.fn();
+let mockIsEnabled = false;
+
jest.mock('../kibana_services', () => ({
getServices: () => ({
getBasePath: () => 'path',
@@ -31,6 +33,9 @@ jest.mock('../kibana_services', () => ({
},
},
},
+ guidedOnboardingService: {
+ isEnabled: mockIsEnabled,
+ },
}),
}));
@@ -234,7 +239,8 @@ describe('home', () => {
expect(component.find(Welcome).exists()).toBe(false);
});
- test('should redirect to guided onboarding on Cloud instead of welcome screen', async () => {
+ test('should redirect to guided onboarding on Cloud instead of welcome screen if guided onboarding is enabled', async () => {
+ mockIsEnabled = true;
const isCloudEnabled = true;
const hasUserDataView = jest.fn(async () => false);
diff --git a/src/plugins/home/public/application/components/home.tsx b/src/plugins/home/public/application/components/home.tsx
index 7a65c48807a3..176b620430a5 100644
--- a/src/plugins/home/public/application/components/home.tsx
+++ b/src/plugins/home/public/application/components/home.tsx
@@ -188,15 +188,14 @@ export class Home extends Component {
public render() {
const { isLoading, isWelcomeEnabled, isNewKibanaInstance } = this.state;
- const { isCloudEnabled } = this.props;
- const { application } = getServices();
+ const { application, guidedOnboardingService } = getServices();
if (isWelcomeEnabled) {
if (isLoading) {
return this.renderLoading();
}
if (isNewKibanaInstance) {
- if (isCloudEnabled) {
+ if (guidedOnboardingService?.isEnabled) {
application.navigateToUrl('./home#/getting_started');
return null;
}
diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js
index 58d9c2a0f3fc..f800a26c787b 100644
--- a/src/plugins/home/public/application/components/home_app.js
+++ b/src/plugins/home/public/application/components/home_app.js
@@ -29,6 +29,7 @@ export function HomeApp({ directories, solutions }) {
addBasePath,
environmentService,
dataViewsService,
+ guidedOnboardingService,
} = getServices();
const environment = environmentService.getEnvironment();
const isCloudEnabled = environment.cloud;
@@ -69,9 +70,11 @@ export function HomeApp({ directories, solutions }) {
-
-
-
+ {guidedOnboardingService.isEnabled && (
+
+
+
+ )}
{
let plugin: CloudLinksPlugin;
@@ -32,26 +33,46 @@ describe('Cloud Links Plugin - public', () => {
});
describe('Onboarding Setup Guide link registration', () => {
- test('registers the Onboarding Setup Guide link when cloud is enabled and it is an authenticated page', () => {
+ describe('guided onboarding is enabled', () => {
+ const guidedOnboarding = guidedOnboardingMock.createStart();
+ test('registers the Onboarding Setup Guide link when cloud and guided onboarding is enabled and it is an authenticated page', () => {
+ const coreStart = coreMock.createStart();
+ coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(false);
+ const cloud = { ...cloudMock.createStart(), isCloudEnabled: true };
+
+ plugin.start(coreStart, { cloud, guidedOnboarding });
+ expect(coreStart.chrome.registerGlobalHelpExtensionMenuLink).toHaveBeenCalledTimes(1);
+ });
+
+ test('does not register the Onboarding Setup Guide link when cloud is enabled but it is an unauthenticated page', () => {
+ const coreStart = coreMock.createStart();
+ coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(true);
+ const cloud = { ...cloudMock.createStart(), isCloudEnabled: true };
+ plugin.start(coreStart, { cloud, guidedOnboarding });
+ expect(coreStart.chrome.registerGlobalHelpExtensionMenuLink).not.toHaveBeenCalled();
+ });
+
+ test('does not register the Onboarding Setup Guide link when cloud is not enabled', () => {
+ const coreStart = coreMock.createStart();
+ const cloud = { ...cloudMock.createStart(), isCloudEnabled: false };
+ plugin.start(coreStart, { cloud, guidedOnboarding });
+ expect(coreStart.chrome.registerGlobalHelpExtensionMenuLink).not.toHaveBeenCalled();
+ });
+ });
+
+ test('do not register the Onboarding Setup Guide link when guided onboarding is disabled', () => {
+ let { guidedOnboardingApi } = guidedOnboardingMock.createStart();
+ guidedOnboardingApi = {
+ ...guidedOnboardingApi!,
+ isEnabled: false,
+ };
+ const guidedOnboarding = { guidedOnboardingApi };
+
const coreStart = coreMock.createStart();
coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(false);
const cloud = { ...cloudMock.createStart(), isCloudEnabled: true };
- plugin.start(coreStart, { cloud });
- expect(coreStart.chrome.registerGlobalHelpExtensionMenuLink).toHaveBeenCalledTimes(1);
- });
- test('does not register the Onboarding Setup Guide link when cloud is enabled but it is an unauthenticated page', () => {
- const coreStart = coreMock.createStart();
- coreStart.http.anonymousPaths.isAnonymous.mockReturnValue(true);
- const cloud = { ...cloudMock.createStart(), isCloudEnabled: true };
- plugin.start(coreStart, { cloud });
- expect(coreStart.chrome.registerGlobalHelpExtensionMenuLink).not.toHaveBeenCalled();
- });
-
- test('does not register the Onboarding Setup Guide link when cloud is not enabled', () => {
- const coreStart = coreMock.createStart();
- const cloud = { ...cloudMock.createStart(), isCloudEnabled: false };
- plugin.start(coreStart, { cloud });
+ plugin.start(coreStart, { cloud, guidedOnboarding });
expect(coreStart.chrome.registerGlobalHelpExtensionMenuLink).not.toHaveBeenCalled();
});
});
diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx
index b5c3c4aeeb55..a2f8af345ac0 100755
--- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx
+++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx
@@ -10,6 +10,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import type { CoreStart, Plugin } from '@kbn/core/public';
import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public';
import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public';
+import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public';
import { maybeAddCloudLinks } from './maybe_add_cloud_links';
interface CloudLinksDepsSetup {
@@ -20,6 +21,7 @@ interface CloudLinksDepsSetup {
interface CloudLinksDepsStart {
cloud?: CloudStart;
security?: SecurityPluginStart;
+ guidedOnboarding?: GuidedOnboardingPluginStart;
}
export class CloudLinksPlugin
@@ -27,18 +29,19 @@ export class CloudLinksPlugin
{
public setup() {}
- public start(core: CoreStart, { cloud, security }: CloudLinksDepsStart) {
+ public start(core: CoreStart, { cloud, security, guidedOnboarding }: CloudLinksDepsStart) {
if (cloud?.isCloudEnabled && !core.http.anonymousPaths.isAnonymous(window.location.pathname)) {
- core.chrome.registerGlobalHelpExtensionMenuLink({
- linkType: 'custom',
- href: core.http.basePath.prepend('/app/home#/getting_started'),
- content: (
-
- ),
- 'data-test-subj': 'cloudOnboardingSetupGuideLink',
- priority: 1000, // We want this link to be at the very top.
- });
-
+ if (guidedOnboarding?.guidedOnboardingApi?.isEnabled) {
+ core.chrome.registerGlobalHelpExtensionMenuLink({
+ linkType: 'custom',
+ href: core.http.basePath.prepend('/app/home#/getting_started'),
+ content: (
+
+ ),
+ 'data-test-subj': 'cloudOnboardingSetupGuideLink',
+ priority: 1000, // We want this link to be at the very top.
+ });
+ }
if (security) {
maybeAddCloudLinks({ security, chrome: core.chrome, cloud });
}
diff --git a/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json b/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json
index 2354df693cb9..ab4f52a34759 100644
--- a/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json
+++ b/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json
@@ -16,6 +16,7 @@
"@kbn/security-plugin",
"@kbn/i18n",
"@kbn/i18n-react",
+ "@kbn/guided-onboarding-plugin",
],
"exclude": [
"target/**/*",
diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts
index 538782b272df..d5038a7c51de 100644
--- a/x-pack/test/api_integration/apis/features/features/features.ts
+++ b/x-pack/test/api_integration/apis/features/features/features.ts
@@ -105,6 +105,7 @@ export default function ({ getService }: FtrProviderContext) {
'advancedSettings',
'indexPatterns',
'graph',
+ 'guidedOnboardingFeature',
'monitoring',
'observabilityCases',
'savedObjectsManagement',
diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts
index d22b46b58f42..c03f9fb91db6 100644
--- a/x-pack/test/api_integration/apis/security/privileges.ts
+++ b/x-pack/test/api_integration/apis/security/privileges.ts
@@ -104,6 +104,7 @@ export default function ({ getService }: FtrProviderContext) {
'readFlappingSettings',
],
maintenanceWindow: ['all', 'read', 'minimal_all', 'minimal_read'],
+ guidedOnboardingFeature: ['all', 'read', 'minimal_all', 'minimal_read'],
},
reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'],
};
diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts
index 3f083b0bd522..201590cdcaf5 100644
--- a/x-pack/test/api_integration/apis/security/privileges_basic.ts
+++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts
@@ -50,6 +50,7 @@ export default function ({ getService }: FtrProviderContext) {
filesSharedImage: ['all', 'read', 'minimal_all', 'minimal_read'],
rulesSettings: ['all', 'read', 'minimal_all', 'minimal_read'],
maintenanceWindow: ['all', 'read', 'minimal_all', 'minimal_read'],
+ guidedOnboardingFeature: ['all', 'read', 'minimal_all', 'minimal_read'],
},
global: ['all', 'read'],
space: ['all', 'read'],
@@ -176,6 +177,7 @@ export default function ({ getService }: FtrProviderContext) {
'readFlappingSettings',
],
maintenanceWindow: ['all', 'read', 'minimal_all', 'minimal_read'],
+ guidedOnboardingFeature: ['all', 'read', 'minimal_all', 'minimal_read'],
},
reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'],
};
diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts
index 5167b611de2b..f72a9a467b26 100644
--- a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts
+++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts
@@ -45,6 +45,19 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
expect(uiCapabilities.value!.navLinks).to.eql(navLinksBuilder.except('monitoring'));
break;
case 'everything_space_all at everything_space':
+ expect(uiCapabilities.success).to.be(true);
+ expect(uiCapabilities.value).to.have.property('navLinks');
+ expect(uiCapabilities.value!.navLinks).to.eql(
+ navLinksBuilder.except(
+ 'monitoring',
+ 'enterpriseSearch',
+ 'enterpriseSearchContent',
+ 'enterpriseSearchAnalytics',
+ 'appSearch',
+ 'workplaceSearch'
+ )
+ );
+ break;
case 'global_read at everything_space':
case 'dual_privileges_read at everything_space':
case 'everything_space_read at everything_space':
@@ -57,7 +70,8 @@ export default function navLinksTests({ getService }: FtrProviderContext) {
'enterpriseSearchContent',
'enterpriseSearchAnalytics',
'appSearch',
- 'workplaceSearch'
+ 'workplaceSearch',
+ 'guidedOnboardingFeature'
)
);
break;