diff --git a/x-pack/plugins/fleet/common/services/agent_policies_helper.test.ts b/x-pack/plugins/fleet/common/services/agent_policies_helper.test.ts
index ded9ae843ad6..2ddac31675e3 100644
--- a/x-pack/plugins/fleet/common/services/agent_policies_helper.test.ts
+++ b/x-pack/plugins/fleet/common/services/agent_policies_helper.test.ts
@@ -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');
+ });
});
diff --git a/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts b/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts
index 7260e389e14f..8a1e26861468 100644
--- a/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts
+++ b/x-pack/plugins/fleet/common/services/agent_policies_helpers.ts
@@ -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';
}
diff --git a/x-pack/plugins/fleet/common/services/is_valid_namespace.test.ts b/x-pack/plugins/fleet/common/services/is_valid_namespace.test.ts
index ebd6b98a732b..f35fb4af2f14 100644
--- a/x-pack/plugins/fleet/common/services/is_valid_namespace.test.ts
+++ b/x-pack/plugins/fleet/common/services/is_valid_namespace.test.ts
@@ -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);
});
});
diff --git a/x-pack/plugins/fleet/common/services/is_valid_namespace.ts b/x-pack/plugins/fleet/common/services/is_valid_namespace.ts
index dcc1a2b9b5cb..bb7d6bb2f49c 100644
--- a/x-pack/plugins/fleet/common/services/is_valid_namespace.ts
+++ b/x-pack/plugins/fleet/common/services/is_valid_namespace.ts
@@ -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(
diff --git a/x-pack/plugins/fleet/common/services/routes.ts b/x-pack/plugins/fleet/common/services/routes.ts
index decef8fe628d..1b8551d89b83 100644
--- a/x-pack/plugins/fleet/common/services/routes.ts
+++ b/x-pack/plugins/fleet/common/services/routes.ts
@@ -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 = {
diff --git a/x-pack/plugins/fleet/common/services/validate_package_policy.ts b/x-pack/plugins/fleet/common/services/validate_package_policy.ts
index 1de38822cbc8..69a26e674777 100644
--- a/x-pack/plugins/fleet/common/services/validate_package_policy.ts
+++ b/x-pack/plugins/fleet/common/services/validate_package_policy.ts
@@ -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];
}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx
index 5d3dee34339c..65a57cc81523 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx
@@ -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<{
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
index ef5dc9b8e3c4..c17e3345bfd1 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
@@ -63,6 +63,7 @@ import { CustomFields } from './custom_fields';
interface Props {
agentPolicy: Partial;
+ allowedNamespacePrefixes?: string[];
updateAgentPolicy: (u: Partial) => void;
validation: ValidationResults;
disabled?: boolean;
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx
index 4ca70267c7ed..96e1056f736c 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_create_inline.tsx
@@ -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 = ({
const [isLoading, setIsLoading] = useState(false);
const isDisabled = !authz.fleet.allAgentPolicies || isLoading;
+ const spaceSettings = useSpaceSettingsContext();
const [newAgentPolicy, setNewAgentPolicy] = useState(
generateNewAgentPolicyWithDefaults({
name: agentPolicyName,
has_fleet_server: isFleetServerPolicy,
+ namespace: spaceSettings.defaultNamespace,
})
);
@@ -76,7 +76,9 @@ export const AgentPolicyCreateInlineForm: React.FunctionComponent = ({
[setNewAgentPolicy, newAgentPolicy]
);
- const validation = agentPolicyFormValidation(newAgentPolicy);
+ const validation = agentPolicyFormValidation(newAgentPolicy, {
+ allowedNamespacePrefixes: spaceSettings.allowedNamespacePrefixes,
+ });
const createAgentPolicy = useCallback(async () => {
try {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx
index 4ba769c31152..bb4e39b265f0 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx
@@ -15,10 +15,15 @@ export interface ValidationResults {
}
export const agentPolicyFormValidation = (
- agentPolicy: Partial
+ agentPolicy: Partial,
+ 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 = [
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx
index 02e364f3e3d3..4f8d616b2ff6 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx
@@ -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();
// 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(
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx
index 886761404563..67275a3cf403 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx
@@ -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();
const fleetStatus = useFleetStatus();
const { docLinks } = useStartServices();
+ const spaceSettings = useSpaceSettingsContext();
const [newAgentPolicy, setNewAgentPolicy] = useState(
- generateNewAgentPolicyWithDefaults({ name: 'Agent policy 1' })
+ generateNewAgentPolicyWithDefaults({
+ name: 'Agent policy 1',
+ namespace: spaceSettings.defaultNamespace,
+ })
);
const [withSysMonitoring, setWithSysMonitoring] = useState(true);
- const validation = agentPolicyFormValidation(newAgentPolicy);
+ const validation = agentPolicyFormValidation(newAgentPolicy, {
+ allowedNamespacePrefixes: spaceSettings.allowedNamespacePrefixes,
+ });
const [selectedPolicyTab, setSelectedPolicyTab] = useState(
queryParamsPolicyId ? SelectedPolicyTab.EXISTING : SelectedPolicyTab.NEW
@@ -379,7 +386,10 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
) : packageInfo ? (
<>
(
const [agentPolicy, setAgentPolicy] = useState({
...originalAgentPolicy,
});
+ const spaceSettings = useSpaceSettingsContext();
+
const [isLoading, setIsLoading] = useState(false);
const [hasChanges, setHasChanges] = useState(false);
const [agentCount, setAgentCount] = useState(0);
const [withSysMonitoring, setWithSysMonitoring] = useState(true);
- const validation = agentPolicyFormValidation(agentPolicy);
+ const validation = agentPolicyFormValidation(agentPolicy, {
+ allowedNamespacePrefixes: spaceSettings?.allowedNamespacePrefixes,
+ });
const [hasAdvancedSettingsErrors, setHasAdvancedSettingsErrors] = useState(false);
const updateAgentPolicy = (updatedFields: Partial) => {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx
index 9ce4d8b81157..9ade778c74f3 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/hooks/use_package_policy_steps.tsx
@@ -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(
- generateNewAgentPolicyWithDefaults({ name: 'Agent policy 1' })
+ generateNewAgentPolicyWithDefaults({
+ name: 'Agent policy 1',
+ namespace: spaceSettings.defaultNamespace,
+ })
);
const [withSysMonitoring, setWithSysMonitoring] = useState(true);
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx
index 39b30b601714..f147f7e112ea 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/list_page/components/create_agent_policy.tsx
@@ -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 = ({
}) => {
const { notifications } = useStartServices();
const hasFleetAllAgentPoliciesPrivileges = useAuthz().fleet.allAgentPolicies;
+ const spaceSettings = useSpaceSettingsContext();
const [agentPolicy, setAgentPolicy] = useState(
- generateNewAgentPolicyWithDefaults()
+ generateNewAgentPolicyWithDefaults({
+ namespace: spaceSettings.defaultNamespace,
+ })
);
const [isLoading, setIsLoading] = useState(false);
const [withSysMonitoring, setWithSysMonitoring] = useState(true);
- const validation = agentPolicyFormValidation(agentPolicy);
+ const validation = agentPolicyFormValidation(agentPolicy, {
+ allowedNamespacePrefixes: spaceSettings?.allowedNamespacePrefixes,
+ });
const [hasAdvancedSettingsErrors, setHasAdvancedSettingsErrors] = useState(false);
const updateAgentPolicy = (updatedFields: Partial) => {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx
index 5927bb4140e4..fc697c33af04 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx
@@ -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: {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx
index b13135516d18..10848d3a13c4 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx
@@ -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: {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx
index f85e7d4a8751..7720361b3346 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx
@@ -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 () => {
diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx
index 21e2ba249b69..992ecff3e49a 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx
@@ -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<{
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx
index 0cdf9f2eed9c..6658eb5a8e0b 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx
@@ -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,
diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx
index f8f185491150..19709c55665f 100644
--- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx
+++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx
@@ -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) => {
cleanup();
const renderer = createFleetTestRendererMock();
@@ -53,6 +51,7 @@ describe('', () => {
fleet: {
readAgentPolicies: true,
},
+ integrations: {},
} as any);
jest.mocked(useFleetServerStandalone).mockReturnValue({ isFleetServerStandalone: false });
diff --git a/x-pack/plugins/fleet/public/hooks/use_request/settings.ts b/x-pack/plugins/fleet/public/hooks/use_request/settings.ts
index 671f7af64471..f9fc3028e1d6 100644
--- a/x-pack/plugins/fleet/public/hooks/use_request/settings.ts
+++ b/x-pack/plugins/fleet/public/hooks/use_request/settings.ts
@@ -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({
+ queryKey: ['space_settings'],
+ enabled,
+ queryFn: () =>
+ sendRequestForRq({
+ method: 'get',
+ path: settingsRoutesService.getSpaceInfoPath(),
+ version: API_VERSIONS.public.v1,
+ }),
+ });
+}
+
export function sendGetSettings() {
return sendRequest({
method: 'get',
diff --git a/x-pack/plugins/fleet/public/hooks/use_space_settings_context.test.tsx b/x-pack/plugins/fleet/public/hooks/use_space_settings_context.test.tsx
new file mode 100644
index 000000000000..6f3d286db370
--- /dev/null
+++ b/x-pack/plugins/fleet/public/hooks/use_space_settings_context.test.tsx
@@ -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 }) => (
+ {children}
+ )
+ );
+ }
+ 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');
+ });
+});
diff --git a/x-pack/plugins/fleet/public/hooks/use_space_settings_context.tsx b/x-pack/plugins/fleet/public/hooks/use_space_settings_context.tsx
new file mode 100644
index 000000000000..665ceca8c3dd
--- /dev/null
+++ b/x-pack/plugins/fleet/public/hooks/use_space_settings_context.tsx
@@ -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 {children};
+};
+
+export function useSpaceSettingsContext() {
+ return useContext(spaceSettingsContext);
+}
diff --git a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx
index ded8351892e2..3a69f5fdc52e 100644
--- a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx
+++ b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx
@@ -112,9 +112,17 @@ export const createFleetTestRendererMock = (): TestRenderer => {
);
}),
HookWrapper,
- renderHook: (callback) => {
+ renderHook: (
+ callback,
+ ExtraWrapper: WrapperComponent = memo(({ children }) => <>{children}>)
+ ) => {
+ const wrapper: WrapperComponent = ({ children }) => (
+
+ {children}
+
+ );
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 (
diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts
index aeb6d302adaa..20d94e6d44fa 100644
--- a/x-pack/plugins/fleet/public/types/index.ts
+++ b/x-pack/plugins/fleet/public/types/index.ts
@@ -144,6 +144,7 @@ export type {
EnrollmentSettingsFleetServerPolicy,
GetEnrollmentSettingsRequest,
GetEnrollmentSettingsResponse,
+ GetSpaceSettingsResponse,
} from '../../common/types';
export {
entries,
diff --git a/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts b/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts
index c959638d0fc6..4123c2ea37e6 100644
--- a/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts
+++ b/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts
@@ -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,
};