Skip to content

Commit

Permalink
[Fleet] Handle unavailable spaces in agent policy space selector (#20…
Browse files Browse the repository at this point in the history
…1251)

## Summary

Closes #193827

This PR improves the space selector in agent policy setting to handle
the case where the user does not have access to all policy spaces.

In this case:
* Space selection is disabled
* The "Create space" link is hidden
* A tooltip is shown to inform the user why the input is disabled
* The inaccessible space badges are given an `Unavailable space` badge

### Screenshots

For a user with access to all policy spaces (no change):
![Screenshot 2024-11-21 at 17 11
00](https://github.com/user-attachments/assets/79c4fcc5-17d5-4273-912c-07eecfd73715)

For a user with access to only a subset of policy spaces:
![Screenshot 2024-11-21 at 17 11
09](https://github.com/user-attachments/assets/19ca162a-7ed6-45fd-8ecc-98e6e085af27)

### 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
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
jillguyonnet and elasticmachine authored Nov 25, 2024
1 parent a76084f commit db49418
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 12 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export * from './message_signing_keys';
export * from './locators';
export * from './secrets';
export * from './uninstall_token';
export * from './space_awareness';

// TODO: This is the default `index.max_result_window` ES setting, which dictates
// the maximum amount of results allowed to be returned from a search. It's possible
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/fleet/common/constants/space_awareness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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.
*/

/**
* The identifier in a saved object's `namespaces` array when it is shared to an unknown space (e.g., one that the end user is not authorized to see).
*/
export const UNKNOWN_SPACE = '?';
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type { AgentPolicy, NewAgentPolicy } from '../../../../../../../common/ty

import { useLicense } from '../../../../../../hooks/use_license';

import { useFleetStatus } from '../../../../hooks';

import type { LicenseService } from '../../../../../../../common/services';
import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/services';

Expand All @@ -26,8 +28,13 @@ import type { ValidationResults } from '../agent_policy_validation';
import { AgentPolicyAdvancedOptionsContent } from '.';

jest.mock('../../../../../../hooks/use_license');
jest.mock('../../../../hooks', () => ({
...jest.requireActual('../../../../hooks'),
useFleetStatus: jest.fn(),
}));

const mockedUseLicence = useLicense as jest.MockedFunction<typeof useLicense>;
const mockedUseFleetStatus = useFleetStatus as jest.MockedFunction<typeof useFleetStatus>;

describe('Agent policy advanced options content', () => {
let testRender: TestRenderer;
Expand All @@ -40,13 +47,18 @@ describe('Agent policy advanced options content', () => {
hasAtLeast: () => true,
isPlatinum: () => true,
} as unknown as LicenseService);
const useSpaceAwareness = () =>
mockedUseFleetStatus.mockReturnValue({
isSpaceAwarenessEnabled: true,
} as any);

const render = ({
isProtected = false,
isManaged = false,
policyId = 'agent-policy-1',
newAgentPolicy = false,
packagePolicy = [createPackagePolicyMock()],
spaceIds = ['default'],
} = {}) => {
if (newAgentPolicy) {
mockAgentPolicy = generateNewAgentPolicyWithDefaults();
Expand All @@ -56,6 +68,7 @@ describe('Agent policy advanced options content', () => {
package_policies: packagePolicy,
id: policyId,
is_managed: isManaged,
space_ids: spaceIds,
};
}

Expand All @@ -72,6 +85,7 @@ describe('Agent policy advanced options content', () => {
};

beforeEach(() => {
mockedUseFleetStatus.mockReturnValue({} as any);
testRender = createFleetTestRendererMock();
});
afterEach(() => {
Expand Down Expand Up @@ -173,4 +187,39 @@ describe('Agent policy advanced options content', () => {
expect(renderResult.queryByText('This policy has no custom fields')).toBeInTheDocument();
});
});

describe('Space selector', () => {
beforeEach(() => {
usePlatinumLicense();
});

describe('when space awareness is disabled', () => {
it('should not be rendered', () => {
render();
expect(renderResult.queryByTestId('spaceSelectorInput')).not.toBeInTheDocument();
});
});

describe('when space awareness is enabled', () => {
beforeEach(() => {
useSpaceAwareness();
});

describe('when the user has access to all policy spaces', () => {
it('should render the space selection input with the Create space link', () => {
render();
expect(renderResult.queryByTestId('spaceSelectorInput')).toBeInTheDocument();
expect(renderResult.queryByTestId('spaceSelectorInputLink')).toBeInTheDocument();
});
});

describe('when the user does not have access to all policy spaces', () => {
it('should render the space selection input without the Create space link', () => {
render({ spaceIds: ['default', '?'] });
expect(renderResult.queryByTestId('spaceSelectorInput')).toBeInTheDocument();
expect(renderResult.queryByTestId('spaceSelectorInputLink')).not.toBeInTheDocument();
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE,
dataTypes,
DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT,
UNKNOWN_SPACE,
} from '../../../../../../../common/constants';
import type { NewAgentPolicy, AgentPolicy } from '../../../../types';
import {
Expand Down Expand Up @@ -127,7 +128,12 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
const isManagedorAgentlessPolicy =
agentPolicy.is_managed === true || agentPolicy?.supports_agentless === true;

const agentPolicyFormContect = useAgentPolicyFormContext();
const userHasAccessToAllPolicySpaces = useMemo(
() => 'space_ids' in agentPolicy && !agentPolicy.space_ids?.includes(UNKNOWN_SPACE),
[agentPolicy]
);

const agentPolicyFormContext = useAgentPolicyFormContext();

const AgentTamperProtectionSectionContent = useMemo(
() => (
Expand Down Expand Up @@ -309,32 +315,45 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent<Props> =
description={
<FormattedMessage
id="xpack.fleet.agentPolicyForm.spaceDescription"
defaultMessage="Select one or more spaces for this policy or create a new one. {link}"
defaultMessage="Select one or more spaces for this policy or create a new one. {link}{tooltip}"
values={{
link: (
link: userHasAccessToAllPolicySpaces && (
<EuiLink
target="_blank"
href={getAbsolutePath('/app/management/kibana/spaces/create')}
external
data-test-subj="spaceSelectorInputLink"
>
<FormattedMessage
id="xpack.fleet.agentPolicyForm.createSpaceLink"
defaultMessage="Create space"
/>
</EuiLink>
),
tooltip: !userHasAccessToAllPolicySpaces && (
<EuiIconTip
type="iInCircle"
color="subdued"
content={i18n.translate('xpack.fleet.agentPolicyForm.spaceTooltip', {
defaultMessage: 'Access to all policy spaces is required for edit.',
})}
/>
),
}}
/>
}
data-test-subj="spaceSelectorInput"
>
<SpaceSelector
isDisabled={disabled || agentPolicy.is_managed === true}
isDisabled={
disabled || agentPolicy.is_managed === true || !userHasAccessToAllPolicySpaces
}
value={
'space_ids' in agentPolicy && agentPolicy.space_ids
? agentPolicy.space_ids
? agentPolicy.space_ids.filter((id) => id !== UNKNOWN_SPACE)
: [spaceId || 'default']
}
setInvalidSpaceError={agentPolicyFormContect?.setInvalidSpaceError}
setInvalidSpaceError={agentPolicyFormContext?.setInvalidSpaceError}
onChange={(newValue) => {
if (newValue.length === 0) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ import {
import { DevtoolsRequestFlyoutButton } from '../../../../../components';
import { ExperimentalFeaturesService } from '../../../../../services';
import { generateUpdateAgentPolicyDevToolsRequest } from '../../../services';
import { UNKNOWN_SPACE } from '../../../../../../../../common/constants';

const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) =>
pick(agentPolicy, [
const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) => {
const partialPolicy = pick(agentPolicy, [
'name',
'description',
'namespace',
'space_ids',
'monitoring_enabled',
'unenroll_timeout',
'inactivity_timeout',
Expand All @@ -61,6 +61,13 @@ const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) =>
'monitoring_http',
'monitoring_diagnostics',
]);
return {
...partialPolicy,
...(!agentPolicy.space_ids?.includes(UNKNOWN_SPACE) && {
space_ids: agentPolicy.space_ids,
}),
};
};

const FormWrapper = styled.div`
max-width: 1200px;
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -20488,7 +20488,6 @@
"xpack.fleet.agentPolicyForm.newAgentPolicyFieldLabel": "Nouveau nom de la stratégie d'agent",
"xpack.fleet.agentPolicyForm.outputOptionDisabledTypeNotSupportedText": "La sortie {outputType} pour l'intégration des agents n'est pas prise en charge pour Fleet Server, Synthetics ou APM.",
"xpack.fleet.agentPolicyForm.outputOptionDisableOutputTypeText": "La sortie {outputType} pour l'intégration des agents n'est pas prise en charge pour Fleet Server, Synthetics ou APM.",
"xpack.fleet.agentPolicyForm.spaceDescription": "Sélectionnez un ou plusieurs espaces pour cette politique ou créez un nouvel espace. {link}",
"xpack.fleet.agentPolicyForm.spaceFieldLabel": "Espaces",
"xpack.fleet.agentPolicyForm.systemMonitoringText": "Collecte des logs et des mesures du système",
"xpack.fleet.agentPolicyForm.systemMonitoringTooltipText": "Cela ajoutera également une intégration {system} pour collecter les logs et les indicateurs du système.",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -20457,7 +20457,6 @@
"xpack.fleet.agentPolicyForm.newAgentPolicyFieldLabel": "新しいエージェントポリシー名",
"xpack.fleet.agentPolicyForm.outputOptionDisabledTypeNotSupportedText": "Fleet Server、Synthetics、APMではエージェント統合の{outputType}出力はサポートされていません。",
"xpack.fleet.agentPolicyForm.outputOptionDisableOutputTypeText": "Fleet Server、Synthetics、APMではエージェント統合の{outputType}出力はサポートされていません。",
"xpack.fleet.agentPolicyForm.spaceDescription": "このポリシーに1つ以上のスペースを選択するか、新しいスペースを作成します。{link}",
"xpack.fleet.agentPolicyForm.spaceFieldLabel": "スペース",
"xpack.fleet.agentPolicyForm.systemMonitoringText": "システムログとメトリックの収集",
"xpack.fleet.agentPolicyForm.systemMonitoringTooltipText": "これにより、{system}統合も追加され、システムログとメトリックを収集します。",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -20112,7 +20112,6 @@
"xpack.fleet.agentPolicyForm.newAgentPolicyFieldLabel": "新代理策略名称",
"xpack.fleet.agentPolicyForm.outputOptionDisabledTypeNotSupportedText": "Fleet 服务器、Synthetics 或 APM 不支持代理集成的 {outputType} 输出。",
"xpack.fleet.agentPolicyForm.outputOptionDisableOutputTypeText": "Fleet 服务器、Synthetics 或 APM 不支持代理集成的 {outputType} 输出。",
"xpack.fleet.agentPolicyForm.spaceDescription": "为此策略选择一个或多个工作区,或创建新工作区。{link}",
"xpack.fleet.agentPolicyForm.spaceFieldLabel": "工作区",
"xpack.fleet.agentPolicyForm.systemMonitoringText": "收集系统日志和指标",
"xpack.fleet.agentPolicyForm.systemMonitoringTooltipText": "这还会添加 {system} 集成以收集系统日志和指标。",
Expand Down

0 comments on commit db49418

Please sign in to comment.