mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Validate namespace restriction per space client side (#188424)
This commit is contained in:
parent
4960bb38ac
commit
492e29a943
27 changed files with 281 additions and 49 deletions
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -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>) => {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>) => {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 });
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
}
|
|
@ -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 (
|
||||
|
|
|
@ -144,6 +144,7 @@ export type {
|
|||
EnrollmentSettingsFleetServerPolicy,
|
||||
GetEnrollmentSettingsRequest,
|
||||
GetEnrollmentSettingsResponse,
|
||||
GetSpaceSettingsResponse,
|
||||
} from '../../common/types';
|
||||
export {
|
||||
entries,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue