[Fleet] Validate namespace restriction per space client side (#188424)

This commit is contained in:
Nicolas Chaulet 2024-07-18 14:13:28 -04:00 committed by GitHub
parent 4960bb38ac
commit 492e29a943
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 281 additions and 49 deletions

View file

@ -61,4 +61,8 @@ describe('getInheritedNamespace', () => {
it('should return default namespace when there are no agent policies', () => {
expect(getInheritedNamespace([])).toEqual('default');
});
it('should allow to override default namespace when there are no agent policies', () => {
expect(getInheritedNamespace([], 'test')).toEqual('test');
});
});

View file

@ -45,9 +45,9 @@ function policyHasIntegration(agentPolicy: AgentPolicy, packageName: string) {
return agentPolicy.package_policies?.some((p) => p.package?.name === packageName);
}
export function getInheritedNamespace(agentPolicies: AgentPolicy[]): string {
export function getInheritedNamespace(agentPolicies: AgentPolicy[], defaultValue?: string): string {
if (agentPolicies.length === 1) {
return agentPolicies[0].namespace;
}
return 'default';
return defaultValue ?? 'default';
}

View file

@ -14,6 +14,10 @@ describe('Fleet - isValidNamespace', () => {
expect(isValidNamespace('testlength😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀').valid).toBe(
true
);
expect(isValidNamespace('', true).valid).toBe(true);
expect(isValidNamespace('', true, ['test']).valid).toBe(true);
expect(isValidNamespace('test', false, ['test']).valid).toBe(true);
expect(isValidNamespace('test_dev', false, ['test']).valid).toBe(true);
});
it('returns false for invalid namespaces', () => {
@ -36,5 +40,6 @@ describe('Fleet - isValidNamespace', () => {
'testlength😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀'
).valid
).toBe(false);
expect(isValidNamespace('default', false, ['test']).valid).toBe(false);
});
});

View file

@ -12,9 +12,43 @@ import { i18n } from '@kbn/i18n';
// and implements a limit based on https://github.com/elastic/kibana/issues/75846
export function isValidNamespace(
namespace: string,
allowBlankNamespace?: boolean
allowBlankNamespace?: boolean,
allowedNamespacePrefixes?: string[]
): { valid: boolean; error?: string } {
return isValidEntity(namespace, 'Namespace', allowBlankNamespace);
if (!namespace.trim() && allowBlankNamespace) {
return { valid: true };
}
const { valid, error } = isValidEntity(namespace, 'Namespace', allowBlankNamespace);
if (!valid) {
return { valid, error };
}
for (const prefix of allowedNamespacePrefixes || []) {
if (!namespace.trim().startsWith(prefix)) {
return allowedNamespacePrefixes?.length === 1
? {
valid: false,
error: i18n.translate('xpack.fleet.namespaceValidation.notAllowedPrefixError', {
defaultMessage: 'Namespace should start with {allowedNamespacePrefixes}',
values: {
allowedNamespacePrefixes: allowedNamespacePrefixes?.[0],
},
}),
}
: {
valid: false,
error: i18n.translate('xpack.fleet.namespaceValidation.notAllowedPrefixesError', {
defaultMessage:
'Namespace should start with one of these prefixes {allowedNamespacePrefixes}',
values: {
allowedNamespacePrefixes: allowedNamespacePrefixes?.join(', ') ?? '',
},
}),
};
}
}
return { valid: true };
}
export function isValidDataset(

View file

@ -287,6 +287,7 @@ export const settingsRoutesService = {
getInfoPath: () => SETTINGS_API_ROUTES.INFO_PATTERN,
getUpdatePath: () => SETTINGS_API_ROUTES.UPDATE_PATTERN,
getEnrollmentInfoPath: () => SETTINGS_API_ROUTES.ENROLLMENT_INFO_PATTERN,
getSpaceInfoPath: () => SETTINGS_API_ROUTES.SPACE_INFO_PATTERN,
};
export const appRoutesService = {

View file

@ -56,7 +56,8 @@ export type PackagePolicyValidationResults = {
export const validatePackagePolicy = (
packagePolicy: NewPackagePolicy,
packageInfo: PackageInfo,
safeLoadYaml: (yaml: string) => any
safeLoadYaml: (yaml: string) => any,
spaceSettings?: { allowedNamespacePrefixes?: string[] }
): PackagePolicyValidationResults => {
const hasIntegrations = doesPackageHaveIntegrations(packageInfo);
const validationResults: PackagePolicyValidationResults = {
@ -75,7 +76,11 @@ export const validatePackagePolicy = (
}
if (packagePolicy?.namespace) {
const namespaceValidation = isValidNamespace(packagePolicy?.namespace, true);
const namespaceValidation = isValidNamespace(
packagePolicy?.namespace,
true,
spaceSettings?.allowedNamespacePrefixes
);
if (!namespaceValidation.valid && namespaceValidation.error) {
validationResults.namespace = [namespaceValidation.error];
}

View file

@ -26,6 +26,7 @@ import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import type { FleetConfigType, FleetStartServices } from '../../plugin';
import { PackageInstallProvider } from '../integrations/hooks';
import { SpaceSettingsContextProvider } from '../../hooks/use_space_settings_context';
import { type FleetStatusProviderProps, useAuthz, useFleetStatus, useFlyoutContext } from './hooks';
@ -213,11 +214,13 @@ export const FleetAppContext: React.FC<{
<ReactQueryDevtools initialIsOpen={false} />
<UIExtensionsContext.Provider value={extensions}>
<FleetStatusProvider defaultFleetStatus={fleetStatus}>
<Router history={history}>
<PackageInstallProvider startServices={startServices}>
<FlyoutContextProvider>{children}</FlyoutContextProvider>
</PackageInstallProvider>
</Router>
<SpaceSettingsContextProvider>
<Router history={history}>
<PackageInstallProvider startServices={startServices}>
<FlyoutContextProvider>{children}</FlyoutContextProvider>
</PackageInstallProvider>
</Router>
</SpaceSettingsContextProvider>
</FleetStatusProvider>
</UIExtensionsContext.Provider>
</QueryClientProvider>

View file

@ -63,6 +63,7 @@ import { CustomFields } from './custom_fields';
interface Props {
agentPolicy: Partial<NewAgentPolicy | AgentPolicy>;
allowedNamespacePrefixes?: string[];
updateAgentPolicy: (u: Partial<NewAgentPolicy | AgentPolicy>) => void;
validation: ValidationResults;
disabled?: boolean;

View file

@ -22,14 +22,12 @@ import { FormattedMessage } from '@kbn/i18n-react';
import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { useSpaceSettingsContext } from '../../../../../hooks/use_space_settings_context';
import type { AgentPolicy, NewAgentPolicy } from '../../../types';
import { sendCreateAgentPolicy, useStartServices, useAuthz } from '../../../hooks';
import { generateNewAgentPolicyWithDefaults } from '../../../../../../common/services/generate_new_agent_policy';
import { agentPolicyFormValidation } from '.';
import { AgentPolicyAdvancedOptionsContent } from './agent_policy_advanced_fields';
import { AgentPolicyFormSystemMonitoringCheckbox } from './agent_policy_system_monitoring_field';
@ -58,11 +56,13 @@ export const AgentPolicyCreateInlineForm: React.FunctionComponent<Props> = ({
const [isLoading, setIsLoading] = useState(false);
const isDisabled = !authz.fleet.allAgentPolicies || isLoading;
const spaceSettings = useSpaceSettingsContext();
const [newAgentPolicy, setNewAgentPolicy] = useState<NewAgentPolicy>(
generateNewAgentPolicyWithDefaults({
name: agentPolicyName,
has_fleet_server: isFleetServerPolicy,
namespace: spaceSettings.defaultNamespace,
})
);
@ -76,7 +76,9 @@ export const AgentPolicyCreateInlineForm: React.FunctionComponent<Props> = ({
[setNewAgentPolicy, newAgentPolicy]
);
const validation = agentPolicyFormValidation(newAgentPolicy);
const validation = agentPolicyFormValidation(newAgentPolicy, {
allowedNamespacePrefixes: spaceSettings.allowedNamespacePrefixes,
});
const createAgentPolicy = useCallback(async () => {
try {

View file

@ -15,10 +15,15 @@ export interface ValidationResults {
}
export const agentPolicyFormValidation = (
agentPolicy: Partial<NewAgentPolicy | AgentPolicy>
agentPolicy: Partial<NewAgentPolicy | AgentPolicy>,
options?: { allowedNamespacePrefixes?: string[] }
): ValidationResults => {
const errors: ValidationResults = {};
const namespaceValidation = isValidNamespace(agentPolicy.namespace || '');
const namespaceValidation = isValidNamespace(
agentPolicy.namespace || '',
false,
options?.allowedNamespacePrefixes
);
if (!agentPolicy.name?.trim()) {
errors.name = [

View file

@ -11,6 +11,7 @@ import { safeLoad } from 'js-yaml';
import { isEqual } from 'lodash';
import { useSpaceSettingsContext } from '../../../../../../../hooks/use_space_settings_context';
import type {
AgentPolicy,
NewPackagePolicy,
@ -152,6 +153,7 @@ export function useOnSubmit({
}) {
const { notifications } = useStartServices();
const confirmForceInstall = useConfirmForceInstall();
const spaceSettings = useSpaceSettingsContext();
// only used to store the resulting package policy once saved
const [savedPackagePolicy, setSavedPackagePolicy] = useState<PackagePolicy>();
// Form state
@ -204,7 +206,8 @@ export function useOnSubmit({
const newValidationResult = validatePackagePolicy(
newPackagePolicy || packagePolicy,
packageInfo,
safeLoad
safeLoad,
spaceSettings
);
setValidationResults(newValidationResult);
// eslint-disable-next-line no-console
@ -213,7 +216,7 @@ export function useOnSubmit({
return newValidationResult;
}
},
[packagePolicy, packageInfo]
[packagePolicy, packageInfo, spaceSettings]
);
// Update package policy method
const updatePackagePolicy = useCallback(

View file

@ -25,6 +25,7 @@ import {
} from '@elastic/eui';
import type { EuiStepProps } from '@elastic/eui/src/components/steps/step';
import { useSpaceSettingsContext } from '../../../../../../hooks/use_space_settings_context';
import { SECRETS_MINIMUM_FLEET_SERVER_VERSION } from '../../../../../../../common/constants';
import {
@ -111,12 +112,18 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
const { params } = useRouteMatch<AddToPolicyParams>();
const fleetStatus = useFleetStatus();
const { docLinks } = useStartServices();
const spaceSettings = useSpaceSettingsContext();
const [newAgentPolicy, setNewAgentPolicy] = useState<NewAgentPolicy>(
generateNewAgentPolicyWithDefaults({ name: 'Agent policy 1' })
generateNewAgentPolicyWithDefaults({
name: 'Agent policy 1',
namespace: spaceSettings.defaultNamespace,
})
);
const [withSysMonitoring, setWithSysMonitoring] = useState<boolean>(true);
const validation = agentPolicyFormValidation(newAgentPolicy);
const validation = agentPolicyFormValidation(newAgentPolicy, {
allowedNamespacePrefixes: spaceSettings.allowedNamespacePrefixes,
});
const [selectedPolicyTab, setSelectedPolicyTab] = useState<SelectedPolicyTab>(
queryParamsPolicyId ? SelectedPolicyTab.EXISTING : SelectedPolicyTab.NEW
@ -379,7 +386,10 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
) : packageInfo ? (
<>
<StepDefinePackagePolicy
namespacePlaceholder={getInheritedNamespace(agentPolicies)}
namespacePlaceholder={getInheritedNamespace(
agentPolicies,
spaceSettings?.allowedNamespacePrefixes?.[0]
)}
packageInfo={packageInfo}
packagePolicy={packagePolicy}
updatePackagePolicy={updatePackagePolicy}
@ -424,6 +434,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
integrationInfo?.name,
extensionView,
handleExtensionViewOnChange,
spaceSettings?.allowedNamespacePrefixes,
]
);

View file

@ -19,6 +19,8 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useSpaceSettingsContext } from '../../../../../../../hooks/use_space_settings_context';
import type { AgentPolicy } from '../../../../../types';
import {
useStartServices,
@ -74,11 +76,15 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
const [agentPolicy, setAgentPolicy] = useState<AgentPolicy>({
...originalAgentPolicy,
});
const spaceSettings = useSpaceSettingsContext();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [hasChanges, setHasChanges] = useState<boolean>(false);
const [agentCount, setAgentCount] = useState<number>(0);
const [withSysMonitoring, setWithSysMonitoring] = useState<boolean>(true);
const validation = agentPolicyFormValidation(agentPolicy);
const validation = agentPolicyFormValidation(agentPolicy, {
allowedNamespacePrefixes: spaceSettings?.allowedNamespacePrefixes,
});
const [hasAdvancedSettingsErrors, setHasAdvancedSettingsErrors] = useState<boolean>(false);
const updateAgentPolicy = (updatedFields: Partial<AgentPolicy>) => {

View file

@ -10,6 +10,7 @@ import { isEqual } from 'lodash';
import { i18n } from '@kbn/i18n';
import type { EuiStepProps } from '@elastic/eui';
import { useSpaceSettingsContext } from '../../../../../../hooks/use_space_settings_context';
import type { AgentPolicy, NewAgentPolicy, NewPackagePolicy } from '../../../../../../../common';
import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/services';
import { SelectedPolicyTab, StepSelectHosts } from '../../create_package_policy_page/components';
@ -49,8 +50,12 @@ export function usePackagePolicySteps({
packagePolicyId,
setNewAgentPolicyName,
}: Params) {
const spaceSettings = useSpaceSettingsContext();
const [newAgentPolicy, setNewAgentPolicy] = useState<NewAgentPolicy>(
generateNewAgentPolicyWithDefaults({ name: 'Agent policy 1' })
generateNewAgentPolicyWithDefaults({
name: 'Agent policy 1',
namespace: spaceSettings.defaultNamespace,
})
);
const [withSysMonitoring, setWithSysMonitoring] = useState<boolean>(true);

View file

@ -24,6 +24,7 @@ import {
EuiSpacer,
} from '@elastic/eui';
import { useSpaceSettingsContext } from '../../../../../../hooks/use_space_settings_context';
import type { NewAgentPolicy, AgentPolicy } from '../../../../types';
import { MAX_FLYOUT_WIDTH } from '../../../../constants';
import { useAuthz, useStartServices, sendCreateAgentPolicy } from '../../../../hooks';
@ -48,12 +49,17 @@ export const CreateAgentPolicyFlyout: React.FunctionComponent<Props> = ({
}) => {
const { notifications } = useStartServices();
const hasFleetAllAgentPoliciesPrivileges = useAuthz().fleet.allAgentPolicies;
const spaceSettings = useSpaceSettingsContext();
const [agentPolicy, setAgentPolicy] = useState<NewAgentPolicy>(
generateNewAgentPolicyWithDefaults()
generateNewAgentPolicyWithDefaults({
namespace: spaceSettings.defaultNamespace,
})
);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [withSysMonitoring, setWithSysMonitoring] = useState<boolean>(true);
const validation = agentPolicyFormValidation(agentPolicy);
const validation = agentPolicyFormValidation(agentPolicy, {
allowedNamespacePrefixes: spaceSettings?.allowedNamespacePrefixes,
});
const [hasAdvancedSettingsErrors, setHasAdvancedSettingsErrors] = useState<boolean>(false);
const updateAgentPolicy = (updatedFields: Partial<NewAgentPolicy>) => {

View file

@ -51,6 +51,7 @@ describe('AgentDetailsActionMenu', () => {
readAgents: true,
allAgents: true,
},
integrations: {},
} as any);
mockedUseAgentVersion.mockReturnValue('8.10.2');
});
@ -133,6 +134,7 @@ describe('AgentDetailsActionMenu', () => {
fleet: {
readAgents: false,
},
integrations: {},
} as any);
const res = renderAndGetDiagnosticsButton({
agent: {

View file

@ -61,6 +61,7 @@ describe('TableRowActions', () => {
readAgents: true,
allAgents: true,
},
integrations: {},
} as any);
mockedUseAgentVersion.mockReturnValue('8.10.2');
});
@ -97,6 +98,7 @@ describe('TableRowActions', () => {
fleet: {
allAgents: false,
},
integrations: {},
} as any);
const res = renderAndGetDiagnosticsButton({
agent: {
@ -192,6 +194,7 @@ describe('TableRowActions', () => {
fleet: {
readAgents: false,
},
integrations: {},
} as any);
const res = renderAndGetRestartUpgradeButton({
agent: {

View file

@ -12,6 +12,7 @@ import { useFleetStatus } from '../../../../hooks/use_fleet_status';
import { useAuthz } from '../../../../hooks/use_authz';
import { AgentsApp } from '.';
import { useGetSpaceSettings } from '../../hooks';
jest.mock('../../../../hooks/use_fleet_status', () => ({
...jest.requireActual('../../../../hooks/use_fleet_status'),
@ -49,7 +50,9 @@ describe('AgentApp', () => {
readAgents: true,
allAgents: true,
},
integrations: {},
} as any);
jest.mocked(useGetSpaceSettings).mockReturnValue({} as any);
});
it('should render the loading component if the status is loading', async () => {

View file

@ -28,6 +28,7 @@ import {
KibanaVersionContext,
useFleetStatus,
} from '../../hooks';
import { SpaceSettingsContextProvider } from '../../hooks/use_space_settings_context';
import { FleetServerFlyout } from '../fleet/components';
@ -104,24 +105,26 @@ export const IntegrationsAppContext: React.FC<{
<ReactQueryDevtools initialIsOpen={false} />
<UIExtensionsContext.Provider value={extensions}>
<FleetStatusProvider defaultFleetStatus={fleetStatus}>
<startServices.customIntegrations.ContextProvider>
<CloudContext>
<Router history={history}>
<ReadOnlyContextProvider>
<AgentPolicyContextProvider>
<PackageInstallProvider startServices={startServices}>
<FlyoutContextProvider>
<IntegrationsHeader
{...{ setHeaderActionMenu, startServices }}
/>
{children}
</FlyoutContextProvider>
</PackageInstallProvider>
</AgentPolicyContextProvider>
</ReadOnlyContextProvider>
</Router>
</CloudContext>
</startServices.customIntegrations.ContextProvider>
<SpaceSettingsContextProvider>
<startServices.customIntegrations.ContextProvider>
<CloudContext>
<Router history={history}>
<ReadOnlyContextProvider>
<AgentPolicyContextProvider>
<PackageInstallProvider startServices={startServices}>
<FlyoutContextProvider>
<IntegrationsHeader
{...{ setHeaderActionMenu, startServices }}
/>
{children}
</FlyoutContextProvider>
</PackageInstallProvider>
</AgentPolicyContextProvider>
</ReadOnlyContextProvider>
</Router>
</CloudContext>
</startServices.customIntegrations.ContextProvider>
</SpaceSettingsContextProvider>
</FleetStatusProvider>
</UIExtensionsContext.Provider>
</QueryClientProvider>

View file

@ -21,6 +21,7 @@ jest.mock('../../hooks', () => {
addAgents: true,
addFleetServers: true,
},
integrations: {},
}),
useFleetStatus: jest.fn().mockReturnValue({ isReady: true }),
};
@ -41,6 +42,7 @@ jest.mock('../../hooks/use_request', () => {
sendGetOneAgentPolicy: jest.fn().mockResolvedValue({
data: { item: { package_policies: [] } },
}),
useGetSpaceSettings: jest.fn().mockReturnValue({}),
useGetAgentPolicies: jest.fn(),
useGetEnrollmentSettings: jest.fn().mockReturnValue({
isLoading: false,

View file

@ -22,8 +22,6 @@ import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agent
import type { FlyOutProps } from './types';
import { AgentEnrollmentFlyout } from '.';
jest.mock('../../hooks/use_authz');
const render = (props?: Partial<FlyOutProps>) => {
cleanup();
const renderer = createFleetTestRendererMock();
@ -53,6 +51,7 @@ describe('<AgentEnrollmentFlyout />', () => {
fleet: {
readAgentPolicies: true,
},
integrations: {},
} as any);
jest.mocked(useFleetServerStandalone).mockReturnValue({ isFleetServerStandalone: false });

View file

@ -14,6 +14,7 @@ import type {
GetSettingsResponse,
GetEnrollmentSettingsRequest,
GetEnrollmentSettingsResponse,
GetSpaceSettingsResponse,
} from '../../types';
import { API_VERSIONS } from '../../../common/constants';
@ -42,6 +43,19 @@ export function useGetSettings() {
});
}
export function useGetSpaceSettings({ enabled }: { enabled?: boolean }) {
return useQuery<GetSpaceSettingsResponse, RequestError>({
queryKey: ['space_settings'],
enabled,
queryFn: () =>
sendRequestForRq<GetSpaceSettingsResponse>({
method: 'get',
path: settingsRoutesService.getSpaceInfoPath(),
version: API_VERSIONS.public.v1,
}),
});
}
export function sendGetSettings() {
return sendRequest<GetSettingsResponse>({
method: 'get',

View file

@ -0,0 +1,54 @@
/*
* 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 React from 'react';
import { createFleetTestRendererMock } from '../mock';
import { ExperimentalFeaturesService } from '../services';
import { useGetSpaceSettings } from './use_request';
import {
SpaceSettingsContextProvider,
useSpaceSettingsContext,
} from './use_space_settings_context';
jest.mock('./use_request');
jest.mock('../services');
describe('useSpaceSettingsContext', () => {
function renderHook() {
return createFleetTestRendererMock().renderHook(
() => useSpaceSettingsContext(),
({ children }: { children: any }) => (
<SpaceSettingsContextProvider>{children}</SpaceSettingsContextProvider>
)
);
}
beforeEach(() => {
jest.mocked(ExperimentalFeaturesService.get).mockReturnValue({
useSpaceAwareness: true,
} as any);
jest.mocked(useGetSpaceSettings).mockReturnValue({} as any);
});
it('should return default defaultNamespace if no restrictions', () => {
const res = renderHook();
expect(res.result.current.defaultNamespace).toBe('default');
});
it('should return restricted defaultNamespace if there is namespace prefix restrictions', () => {
jest.mocked(useGetSpaceSettings).mockReturnValue({
isInitialLoading: false,
data: {
item: {
allowed_namespace_prefixes: ['test'],
},
},
} as any);
const res = renderHook();
expect(res.result.current.defaultNamespace).toBe('test');
});
});

View file

@ -0,0 +1,51 @@
/*
* 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 React, { createContext, useContext } from 'react';
import { ExperimentalFeaturesService } from '../services';
import { useAuthz } from './use_authz';
import { useGetSpaceSettings } from './use_request';
const spaceSettingsContext = createContext<{
isInitialLoading?: boolean;
allowedNamespacePrefixes: string[];
defaultNamespace: string;
}>({
allowedNamespacePrefixes: [],
defaultNamespace: 'default',
});
export const SpaceSettingsContextProvider: React.FC<{
enabled?: boolean;
children?: React.ReactNode;
}> = ({ enabled = true, children }) => {
const useSpaceAwareness = ExperimentalFeaturesService.get()?.useSpaceAwareness ?? false;
const authz = useAuthz();
const isAllowed =
authz.fleet.allAgentPolicies ||
authz.fleet.allSettings ||
authz.integrations.writeIntegrationPolicies;
const spaceSettingsReq = useGetSpaceSettings({
enabled: useSpaceAwareness && enabled && isAllowed,
});
const settings = React.useMemo(() => {
return {
isInitialLoading: spaceSettingsReq.isInitialLoading,
allowedNamespacePrefixes: spaceSettingsReq.data?.item.allowed_namespace_prefixes ?? [],
defaultNamespace: spaceSettingsReq.data?.item.allowed_namespace_prefixes?.[0] ?? 'default',
};
}, [spaceSettingsReq.isInitialLoading, spaceSettingsReq.data]);
return <spaceSettingsContext.Provider value={settings}>{children}</spaceSettingsContext.Provider>;
};
export function useSpaceSettingsContext() {
return useContext(spaceSettingsContext);
}

View file

@ -112,9 +112,17 @@ export const createFleetTestRendererMock = (): TestRenderer => {
);
}),
HookWrapper,
renderHook: (callback) => {
renderHook: (
callback,
ExtraWrapper: WrapperComponent<any> = memo(({ children }) => <>{children}</>)
) => {
const wrapper: WrapperComponent<any> = ({ children }) => (
<testRendererMocks.HookWrapper>
<ExtraWrapper>{children}</ExtraWrapper>
</testRendererMocks.HookWrapper>
);
return renderHook(callback, {
wrapper: testRendererMocks.HookWrapper,
wrapper,
});
},
render: (ui, options) => {
@ -135,6 +143,7 @@ export const createFleetTestRendererMock = (): TestRenderer => {
export const createIntegrationsTestRendererMock = (): TestRenderer => {
const basePath = '/mock';
const extensions: UIExtensionsStorage = {};
ExperimentalFeaturesService.init(allowedExperimentalValues);
const startServices = createStartServices(basePath);
const HookWrapper = memo(({ children }: { children?: React.ReactNode }) => {
return (

View file

@ -144,6 +144,7 @@ export type {
EnrollmentSettingsFleetServerPolicy,
GetEnrollmentSettingsRequest,
GetEnrollmentSettingsResponse,
GetSpaceSettingsResponse,
} from '../../common/types';
export {
entries,

View file

@ -42,7 +42,7 @@ export const putSpaceSettingsHandler: FleetRequestHandler<
},
spaceId: soClient.getCurrentNamespace(),
});
const settings = await settingsService.getSettings(soClient);
const settings = await getSpaceSettings(soClient.getCurrentNamespace());
const body = {
item: settings,
};