Skip to content

Commit

Permalink
[Spaces] Show callout on spaces permission tab when security is disab…
Browse files Browse the repository at this point in the history
…led (elastic#210961)

## Summary

This PR updates the behavior of `Permissions` tab in `Space Management`
when `xpack.security.enabled` is set to `false` to show a callout with a
meaningful explanation.
![Screenshot 2025-02-17 at 12 42
58](https://github.com/user-attachments/assets/881d0bbe-e30b-41b4-8c0a-5b8e127786ab)


Closes: elastic#210241

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
kowalczyk-krzysztof and kibanamachine authored Feb 18, 2025
1 parent 4cd9ef3 commit 46132d8
Show file tree
Hide file tree
Showing 15 changed files with 108 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
mappingRolesFieldRules: `${ELASTICSEARCH_DOCS}role-mapping-resources.html#mapping-roles-rule-field`,
runAsPrivilege: `${ELASTICSEARCH_DOCS}security-privileges.html#_run_as_privilege`,
deprecatedV1Endpoints: `${KIBANA_DOCS}breaking-changes-summary.html#breaking-199656`,
enableElasticSearchSecurityFeatures: `${ELASTICSEARCH_DOCS}security-minimal-setup.html#_enable_elasticsearch_security_features`,
},
spaces: {
kibanaLegacyUrlAliases: `${KIBANA_DOCS}legacy-url-aliases.html`,
Expand Down
1 change: 1 addition & 0 deletions src/platform/packages/shared/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ export interface DocLinks {
mappingRolesFieldRules: string;
runAsPrivilege: string;
deprecatedV1Endpoints: string;
enableElasticSearchSecurityFeatures: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export const EditSpace: FC<PageProps> = ({
logger,
notifications,
isRoleManagementEnabled,
license,
enableSecurityLink,
} = useEditSpaceServices();
const [space, setSpace] = useState<Space | null>(null);
const [userActiveSpace, setUserActiveSpace] = useState<Space | null>(null);
Expand All @@ -83,6 +85,7 @@ export const EditSpace: FC<PageProps> = ({
const [isLoadingFeatures, setIsLoadingFeatures] = useState(true);
const [isLoadingRoles, setIsLoadingRoles] = useState(true);
const selectedTabId = getSelectedTabId(Boolean(capabilities?.roles?.view), _selectedTabId);
const isSecurityEnabled = Boolean(license?.isEnabled());
const [tabs, selectedTabContent] = useTabs({
space,
features,
Expand All @@ -91,6 +94,8 @@ export const EditSpace: FC<PageProps> = ({
capabilities,
history,
currentSelectedTabId: selectedTabId,
isSecurityEnabled,
enableSecurityLink,
...props,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const TestComponent: React.FC<React.PropsWithChildren> = ({ children }) => {
userProfile={userProfile}
i18n={i18n}
logger={logger}
enableSecurityLink=""
>
{children}
</EditSpaceProviderRoot>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ describe('EditSpaceSettings', () => {
theme={theme}
i18n={i18n}
logger={logger}
enableSecurityLink=""
>
{children}
</EditSpaceProviderRoot>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ describe('EditSpaceAssignedRolesTab', () => {
theme={theme}
i18n={i18n}
logger={logger}
enableSecurityLink=""
>
{children}
</EditSpaceProviderRoot>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { render } from '@testing-library/react';
import React from 'react';

import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
import { scopedHistoryMock } from '@kbn/core-application-browser-mocks';
import { KibanaFeature } from '@kbn/features-plugin/common';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';

import type { GetTabsProps } from './edit_space_tabs';
import { getTabs } from './edit_space_tabs';
Expand Down Expand Up @@ -45,6 +46,7 @@ const getCapabilities = (
describe('Edit Space Tabs: getTabs', () => {
it('can include a Permissions tab', () => {
const isRoleManagementEnabled = true;
const isSecurityEnabled = true;
const capabilities = getCapabilities();

expect(
Expand All @@ -56,6 +58,8 @@ describe('Edit Space Tabs: getTabs', () => {
history,
allowFeatureVisibility,
allowSolutionVisibility,
isSecurityEnabled,
enableSecurityLink: '',
}).map(({ id, name }) => ({ name, id }))
).toEqual([
{ id: 'general', name: 'General settings' },
Expand All @@ -66,6 +70,7 @@ describe('Edit Space Tabs: getTabs', () => {

it('can include count of roles as a badge for Permissions tab', () => {
const isRoleManagementEnabled = true;
const isSecurityEnabled = true;
const capabilities = getCapabilities();

const rolesTab = getTabs({
Expand All @@ -77,6 +82,8 @@ describe('Edit Space Tabs: getTabs', () => {
history,
allowFeatureVisibility,
allowSolutionVisibility,
isSecurityEnabled,
enableSecurityLink: '',
}).find((tab) => tab.id === 'roles');

if (!rolesTab?.append) {
Expand All @@ -87,6 +94,32 @@ describe('Edit Space Tabs: getTabs', () => {
expect(getByText('42')).toBeInTheDocument();
});

it('should show a warning callout when security is disabled', () => {
const isRoleManagementEnabled = true;
const isSecurityEnabled = false;
const capabilities = getCapabilities();

const rolesTab = getTabs({
rolesCount: 0,
isRoleManagementEnabled,
capabilities,
space,
features,
history,
allowFeatureVisibility,
allowSolutionVisibility,
isSecurityEnabled,
enableSecurityLink: '',
}).find((tab) => tab.id === 'roles');

if (!rolesTab?.content) {
throw new Error('roles tab did not exist!');
}
const { getByTestId } = render(<IntlProvider locale="en">{rolesTab.content}</IntlProvider>);

expect(getByTestId('securityDisabledCallout')).toBeInTheDocument();
});

it('hides Permissions tab when role management is not enabled', () => {
expect(
getTabs({
Expand All @@ -97,6 +130,8 @@ describe('Edit Space Tabs: getTabs', () => {
history,
allowFeatureVisibility,
allowSolutionVisibility,
isSecurityEnabled: true,
enableSecurityLink: '',
}).map(({ id, name }) => ({ name, id }))
).toEqual([
{ id: 'general', name: 'General settings' },
Expand All @@ -114,6 +149,8 @@ describe('Edit Space Tabs: getTabs', () => {
history,
allowFeatureVisibility,
allowSolutionVisibility,
isSecurityEnabled: true,
enableSecurityLink: '',
}).map(({ id, name }) => ({ name, id }))
).toEqual([
{ id: 'general', name: 'General settings' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { i18n } from '@kbn/i18n';
import { withSuspense } from '@kbn/shared-ux-utility';

import { TAB_ID_CONTENT, TAB_ID_GENERAL, TAB_ID_ROLES } from './constants';
import { SecurityDisabledCallout } from './security_disabled_callout';
import type { Space } from '../../../common';

export interface EditSpaceTab {
Expand All @@ -35,6 +36,8 @@ export interface GetTabsProps {
};
allowFeatureVisibility: boolean;
allowSolutionVisibility: boolean;
isSecurityEnabled: boolean;
enableSecurityLink: string;
}

const SuspenseEditSpaceSettingsTab = withSuspense(
Expand Down Expand Up @@ -68,6 +71,8 @@ export const getTabs = ({
capabilities,
rolesCount,
isRoleManagementEnabled,
isSecurityEnabled,
enableSecurityLink,
...props
}: GetTabsProps): EditSpaceTab[] => {
const reloadWindow = () => {
Expand Down Expand Up @@ -105,12 +110,14 @@ export const getTabs = ({
{rolesCount ?? 0}
</EuiNotificationBadge>
),
content: (
content: isSecurityEnabled ? (
<SuspenseEditSpaceAssignedRolesTab
space={space}
features={features}
isReadOnly={!canUserModifyRoles}
/>
) : (
<SecurityDisabledCallout enableSecurityLink={enableSecurityLink} />
),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type UseTabsProps = Pick<GetTabsProps, 'capabilities' | 'rolesCount'> & {
history: ScopedHistory;
allowFeatureVisibility: boolean;
allowSolutionVisibility: boolean;
isSecurityEnabled: boolean;
enableSecurityLink: string;
};

export const useTabs = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const SUTProvider = ({
getSecurityLicense: getSecurityLicenseMock,
navigateToUrl: jest.fn(),
capabilities,
enableSecurityLink: '',
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface EditSpaceProviderRootProps
getRolesAPIClient: () => Promise<RolesAPIClient>;
getPrivilegesAPIClient: () => Promise<PrivilegesAPIClientPublicContract>;
getSecurityLicense: () => Promise<SecurityLicense>;
enableSecurityLink: string;
}

interface EditSpaceClients {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const renderPrivilegeRolesForm = ({
fetchRolesError: false,
},
invokeClient: spacesClientsInvocatorMock,
enableSecurityLink: '',
}}
>
<PrivilegesRolesForm
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 { EuiCallOut, EuiLink } from '@elastic/eui';
import React from 'react';

import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';

interface Props {
enableSecurityLink: string;
}

export const SecurityDisabledCallout = ({ enableSecurityLink }: Props) => (
<EuiCallOut
color="warning"
data-test-subj="securityDisabledCallout"
title={i18n.translate(
'xpack.spaces.management.spaceDetails.contentTabs.securityDisabledTitle',
{
defaultMessage: 'Security features are disabled',
}
)}
>
<FormattedMessage
id="xpack.spaces.management.spaceDetails.contentTabs.securityDisabledDescription"
defaultMessage="To manage space permissions, you must enable security features first. {learnMoreLink}"
values={{
learnMoreLink: (
<EuiLink href={enableSecurityLink} target="_blank" external={true}>
<FormattedMessage
id="xpack.spaces.management.spaceDetails.contentTabs.securityDisabledLearnMore"
defaultMessage="Learn more"
/>
</EuiLink>
),
}}
/>
</EuiCallOut>
);
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ describe('spacesManagementApp', () => {
css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)."
data-test-subj="kbnRedirectAppLink"
>
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"serverBasePath":"","http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"overlays":{"banners":{}},"notifications":{"toasts":{}},"userProfile":{},"theme":{"theme$":{}},"i18n":{},"logger":{"context":[]},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"allowSolutionVisibility":true}
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"serverBasePath":"","http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}},"overlays":{"banners":{}},"notifications":{"toasts":{}},"userProfile":{},"theme":{"theme$":{}},"i18n":{},"logger":{"context":[]},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"allowSolutionVisibility":true,"enableSecurityLink":"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-minimal-setup.html#_enable_elasticsearch_security_features"}
</div>
</div>
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export const spacesManagementApp = Object.freeze({
text: title,
href: `/`,
};
const { notifications, application, chrome, http, overlays } = coreStart;
const { notifications, application, chrome, http, overlays, docLinks } = coreStart;
const enableSecurityLink = docLinks.links.security.enableElasticSearchSecurityFeatures;

chrome.docTitle.change(title);

Expand Down Expand Up @@ -174,6 +175,7 @@ export const spacesManagementApp = Object.freeze({
allowFeatureVisibility={config.allowFeatureVisibility}
allowSolutionVisibility={config.allowSolutionVisibility}
getPrivilegesAPIClient={getPrivilegesAPIClient}
enableSecurityLink={enableSecurityLink}
/>
);
};
Expand Down

0 comments on commit 46132d8

Please sign in to comment.