mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solution][Endpoint] Fix Policy form being displayed as Read Only when displayed in Fleet pages (#147212)
## Summary - Fix Policy form being displayed as Read Only when displayed in Fleet pages Tested: With Role that DOES NOT have access to Security Solution - Fleet - Agent Policies - Edit agent Policy - Edit endpoint package policy edit form: 👍 - Integrations Endpoint Page - Policies - edit form: 👍 With Role that does have `policy management` privilege to Security Solution - Policies - Edit policy form: 👍
This commit is contained in:
parent
412fa4285b
commit
ac6f2fb782
23 changed files with 194 additions and 201 deletions
|
@ -35,7 +35,8 @@ export const useEndpointPrivileges = (): Immutable<EndpointPrivileges> => {
|
|||
const http = useHttp();
|
||||
const user = useCurrentUser();
|
||||
|
||||
const fleetServicesFromUseKibana = useKibana().services.fleet;
|
||||
const kibanaServices = useKibana().services;
|
||||
const fleetServicesFromUseKibana = kibanaServices.fleet;
|
||||
// The `fleetServicesFromPluginStart` will be defined when this hooks called from a component
|
||||
// that is being rendered under the Fleet context (UI extensions). The `fleetServicesFromUseKibana`
|
||||
// above will be `undefined` in this case.
|
||||
|
@ -56,8 +57,9 @@ export const useEndpointPrivileges = (): Immutable<EndpointPrivileges> => {
|
|||
const [hasHostIsolationExceptionsItems, setHasHostIsolationExceptionsItems] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const securitySolutionPermissions = calculatePermissionsFromCapabilities(
|
||||
useKibana().services.application.capabilities
|
||||
const securitySolutionPermissions = useMemo(
|
||||
() => calculatePermissionsFromCapabilities(kibanaServices.application.capabilities),
|
||||
[kibanaServices.application.capabilities]
|
||||
);
|
||||
|
||||
const privileges = useMemo(() => {
|
||||
|
|
|
@ -154,6 +154,11 @@ export interface AppContextTestRender {
|
|||
* @param flags
|
||||
*/
|
||||
setExperimentalFlag: (flags: Partial<ExperimentalFeatures>) => void;
|
||||
|
||||
/**
|
||||
* The React Query client (setup to support jest testing)
|
||||
*/
|
||||
queryClient: QueryClient;
|
||||
}
|
||||
|
||||
// Defined a private custom reducer that reacts to an action that enables us to update the
|
||||
|
@ -310,6 +315,7 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
|
|||
renderHook,
|
||||
renderReactQueryHook,
|
||||
setExperimentalFlag,
|
||||
queryClient,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -11,9 +11,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiSpacer, EuiSwitch, EuiText } from '@elastic/eui';
|
||||
|
||||
import { OperatingSystem } from '@kbn/securitysolution-utils';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { isAntivirusRegistrationEnabled } from '../../../store/policy_details/selectors';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { ConfigForm } from '../config_form';
|
||||
|
||||
const TRANSLATIONS: Readonly<{ [K in 'title' | 'description' | 'label']: string }> = {
|
||||
|
@ -42,7 +41,7 @@ const TRANSLATIONS: Readonly<{ [K in 'title' | 'description' | 'label']: string
|
|||
export const AntivirusRegistrationForm = memo(() => {
|
||||
const antivirusRegistrationEnabled = usePolicyDetailsSelector(isAntivirusRegistrationEnabled);
|
||||
const dispatch = useDispatch();
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
|
||||
const handleSwitchChange = useCallback(
|
||||
(event) =>
|
||||
|
@ -70,7 +69,7 @@ export const AntivirusRegistrationForm = memo(() => {
|
|||
label={TRANSLATIONS.label}
|
||||
checked={antivirusRegistrationEnabled}
|
||||
onChange={handleSwitchChange}
|
||||
disabled={!canWritePolicyManagement}
|
||||
disabled={!showEditableFormFields}
|
||||
/>
|
||||
</ConfigForm>
|
||||
);
|
||||
|
|
|
@ -11,9 +11,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiSwitch } from '@elastic/eui';
|
||||
|
||||
import { OperatingSystem } from '@kbn/securitysolution-utils';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { isCredentialHardeningEnabled } from '../../../store/policy_details/selectors';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { ConfigForm } from '../config_form';
|
||||
|
||||
const TRANSLATIONS: Readonly<{ [K in 'title' | 'label']: string }> = {
|
||||
|
@ -34,7 +33,7 @@ const TRANSLATIONS: Readonly<{ [K in 'title' | 'label']: string }> = {
|
|||
export const AttackSurfaceReductionForm = memo(() => {
|
||||
const credentialHardeningEnabled = usePolicyDetailsSelector(isCredentialHardeningEnabled);
|
||||
const dispatch = useDispatch();
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
|
||||
const handleSwitchChange = useCallback(
|
||||
(event) =>
|
||||
|
@ -53,7 +52,7 @@ export const AttackSurfaceReductionForm = memo(() => {
|
|||
label={TRANSLATIONS.label}
|
||||
checked={credentialHardeningEnabled}
|
||||
onChange={handleSwitchChange}
|
||||
disabled={!canWritePolicyManagement}
|
||||
disabled={!showEditableFormFields}
|
||||
/>
|
||||
</ConfigForm>
|
||||
);
|
||||
|
|
|
@ -19,12 +19,11 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { OperatingSystem } from '@kbn/securitysolution-utils';
|
||||
import { ThemeContext } from 'styled-components';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import type {
|
||||
PolicyOperatingSystem,
|
||||
UIPolicyConfig,
|
||||
} from '../../../../../../../common/endpoint/types';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
||||
import { ConfigForm, ConfigFormHeading } from '../config_form';
|
||||
|
||||
|
@ -76,7 +75,7 @@ const InnerEventsForm = <T extends OperatingSystem>({
|
|||
onValueSelection,
|
||||
supplementalOptions,
|
||||
}: EventsFormProps<T>) => {
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
|
||||
const theme = useContext(ThemeContext);
|
||||
const countSelected = useCallback(() => {
|
||||
|
@ -124,7 +123,7 @@ const InnerEventsForm = <T extends OperatingSystem>({
|
|||
data-test-subj={`policy${OPERATING_SYSTEM_TO_TEST_SUBJ[os]}Event_${protectionField}`}
|
||||
checked={selection[protectionField]}
|
||||
onChange={(event) => onValueSelection(protectionField, event.target.checked)}
|
||||
disabled={!canWritePolicyManagement}
|
||||
disabled={!showEditableFormFields}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@ -169,7 +168,7 @@ const InnerEventsForm = <T extends OperatingSystem>({
|
|||
checked={selection[protectionField]}
|
||||
onChange={(event) => onValueSelection(protectionField, event.target.checked)}
|
||||
disabled={
|
||||
!canWritePolicyManagement ||
|
||||
!showEditableFormFields ||
|
||||
(isDisabled ? isDisabled(policyDetailsConfig) : false)
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -9,11 +9,13 @@ import type { PropsWithChildren } from 'react';
|
|||
import React, { memo } from 'react';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
import type { Store } from 'redux';
|
||||
import { UserPrivilegesProvider } from '../../../../../../../common/components/user_privileges/user_privileges_context';
|
||||
import type { SecuritySolutionQueryClient } from '../../../../../../../common/containers/query_client/query_client_provider';
|
||||
import { ReactQueryClientProvider } from '../../../../../../../common/containers/query_client/query_client_provider';
|
||||
import { SecuritySolutionStartDependenciesContext } from '../../../../../../../common/components/user_privileges/endpoint/security_solution_start_dependencies';
|
||||
import { CurrentLicense } from '../../../../../../../common/components/current_license';
|
||||
import type { StartPlugins } from '../../../../../../../types';
|
||||
import { useKibana } from '../../../../../../../common/lib/kibana';
|
||||
|
||||
export type RenderContextProvidersProps = PropsWithChildren<{
|
||||
store: Store;
|
||||
|
@ -23,11 +25,16 @@ export type RenderContextProvidersProps = PropsWithChildren<{
|
|||
|
||||
export const RenderContextProviders = memo<RenderContextProvidersProps>(
|
||||
({ store, depsStart, queryClient, children }) => {
|
||||
const {
|
||||
application: { capabilities },
|
||||
} = useKibana().services;
|
||||
return (
|
||||
<ReduxStoreProvider store={store}>
|
||||
<ReactQueryClientProvider queryClient={queryClient}>
|
||||
<SecuritySolutionStartDependenciesContext.Provider value={depsStart}>
|
||||
<CurrentLicense>{children}</CurrentLicense>
|
||||
<UserPrivilegesProvider kibanaCapabilities={capabilities}>
|
||||
<CurrentLicense>{children}</CurrentLicense>
|
||||
</UserPrivilegesProvider>
|
||||
</SecuritySolutionStartDependenciesContext.Provider>
|
||||
</ReactQueryClientProvider>
|
||||
</ReduxStoreProvider>
|
||||
|
|
|
@ -9,18 +9,16 @@ import React from 'react';
|
|||
import type { AppContextTestRender } from '../../../../../../common/mock/endpoint';
|
||||
import { createFleetContextRendererMock, generateFleetPackageInfo } from '../mocks';
|
||||
import { EndpointPackageCustomExtension } from './endpoint_package_custom_extension';
|
||||
import { useEndpointPrivileges as _useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { exceptionsListAllHttpMocks } from '../../../../../mocks/exceptions_list_http_mocks';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { useUserPrivileges as _useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { getUserPrivilegesMockDefaultValue } from '../../../../../../common/components/user_privileges/__mocks__';
|
||||
|
||||
jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
const useEndpointPrivilegesMock = _useEndpointPrivileges as jest.Mock;
|
||||
jest.mock('../../../../../../common/components/user_privileges');
|
||||
const useUserPrivilegesMock = _useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('When displaying the EndpointPackageCustomExtension fleet UI extension', () => {
|
||||
let render: () => ReturnType<AppContextTestRender['render']>;
|
||||
let renderResult: ReturnType<AppContextTestRender['render']>;
|
||||
let http: AppContextTestRender['coreStart']['http'];
|
||||
const artifactCards = Object.freeze([
|
||||
'trustedApps-fleetCard',
|
||||
'eventFilters-fleetCard',
|
||||
|
@ -30,7 +28,6 @@ describe('When displaying the EndpointPackageCustomExtension fleet UI extension'
|
|||
|
||||
beforeEach(() => {
|
||||
const mockedTestContext = createFleetContextRendererMock();
|
||||
http = mockedTestContext.coreStart.http;
|
||||
render = () => {
|
||||
renderResult = mockedTestContext.render(
|
||||
<EndpointPackageCustomExtension
|
||||
|
@ -44,69 +41,42 @@ describe('When displaying the EndpointPackageCustomExtension fleet UI extension'
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
useEndpointPrivilegesMock.mockImplementation(getEndpointPrivilegesInitialStateMock);
|
||||
useUserPrivilegesMock.mockImplementation(getUserPrivilegesMockDefaultValue);
|
||||
});
|
||||
|
||||
it('should show artifact cards', async () => {
|
||||
it.each([...artifactCards])('should show artifact card: `%s`', (artifactCardtestId) => {
|
||||
render();
|
||||
|
||||
await waitFor(() => {
|
||||
artifactCards.forEach((artifactCard) => {
|
||||
expect(renderResult.getByTestId(artifactCard)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
expect(renderResult.getByTestId(artifactCardtestId)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should NOT show artifact cards if no endpoint management authz', async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock({
|
||||
canReadBlocklist: false,
|
||||
canReadEventFilters: false,
|
||||
canReadHostIsolationExceptions: false,
|
||||
canReadTrustedApplications: false,
|
||||
canIsolateHost: false,
|
||||
}),
|
||||
});
|
||||
render();
|
||||
|
||||
await waitFor(() => {
|
||||
artifactCards.forEach((artifactCard) => {
|
||||
expect(renderResult.queryByTestId(artifactCard)).toBeNull();
|
||||
it.each([...artifactCards])(
|
||||
'should NOT show artifact card if no endpoint management authz: %s',
|
||||
(artifactCardTestId) => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
...getUserPrivilegesMockDefaultValue(),
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock({
|
||||
canReadBlocklist: false,
|
||||
canReadEventFilters: false,
|
||||
canReadHostIsolationExceptions: false,
|
||||
canDeleteHostIsolationExceptions: false,
|
||||
canReadTrustedApplications: false,
|
||||
}),
|
||||
});
|
||||
|
||||
render();
|
||||
|
||||
expect(renderResult.queryByTestId(artifactCardTestId)).toBeNull();
|
||||
expect(renderResult.queryByTestId('noPrivilegesPage')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show Host Isolations Exceptions if user has no authz but entries exist', async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock(),
|
||||
canIsolateHost: false,
|
||||
});
|
||||
// Mock APIs
|
||||
exceptionsListAllHttpMocks(http);
|
||||
render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(renderResult.getByTestId('hostIsolationExceptions-fleetCard')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT show Host Isolation Exceptions if user has no authz and no entries exist', async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock({ canReadHostIsolationExceptions: false }),
|
||||
});
|
||||
render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(renderResult.queryByTestId('hostIsolationExceptions-fleetCard')).toBeNull();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('should only show loading spinner if loading', () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock(),
|
||||
loading: true,
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
...getUserPrivilegesMockDefaultValue(),
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock({ loading: true }),
|
||||
});
|
||||
|
||||
render();
|
||||
|
||||
expect(renderResult.getByTestId('endpointExtensionLoadingSpinner')).toBeInTheDocument();
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { ReactElement } from 'react';
|
|||
import React, { memo, useMemo } from 'react';
|
||||
import { EuiSpacer, EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import type { PackageCustomExtensionComponentProps } from '@kbn/fleet-plugin/public';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { NoPrivileges } from '../../../../../../common/components/no_privileges';
|
||||
import { useCanAccessSomeArtifacts } from '../hooks/use_can_access_some_artifacts';
|
||||
import { useHttp } from '../../../../../../common/lib/kibana';
|
||||
|
@ -29,7 +30,6 @@ import {
|
|||
HOST_ISOLATION_EXCEPTIONS_LABELS,
|
||||
TRUSTED_APPS_LABELS,
|
||||
} from './translations';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint';
|
||||
|
||||
const TrustedAppsArtifactCard = memo<PackageCustomExtensionComponentProps>((props) => {
|
||||
const http = useHttp();
|
||||
|
@ -115,7 +115,7 @@ export const EndpointPackageCustomExtension = memo<PackageCustomExtensionCompone
|
|||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
} = useEndpointPrivileges();
|
||||
} = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const userCanAccessContent = useCanAccessSomeArtifacts();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiLoadingContent, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { useUserPrivileges } from '../../../../../../../common/components/user_privileges';
|
||||
import {
|
||||
BLOCKLISTS_LABELS,
|
||||
EVENT_FILTERS_LABELS,
|
||||
|
@ -36,7 +37,6 @@ import { SEARCHABLE_FIELDS as EVENT_FILTERS_SEARCHABLE_FIELDS } from '../../../.
|
|||
import { SEARCHABLE_FIELDS as HOST_ISOLATION_EXCEPTIONS_SEARCHABLE_FIELDS } from '../../../../../host_isolation_exceptions/constants';
|
||||
import { SEARCHABLE_FIELDS as BLOCKLIST_SEARCHABLE_FIELDS } from '../../../../../blocklist/constants';
|
||||
import { useHttp } from '../../../../../../../common/lib/kibana';
|
||||
import { useEndpointPrivileges } from '../../../../../../../common/components/user_privileges/endpoint';
|
||||
|
||||
interface PolicyArtifactCardProps {
|
||||
policyId: string;
|
||||
|
@ -48,7 +48,7 @@ const TrustedAppsPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
|||
() => TrustedAppsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
const { canReadPolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
|
@ -78,7 +78,7 @@ const EventFiltersPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
|||
() => EventFiltersApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
const { canReadPolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
|
@ -108,7 +108,7 @@ const HostIsolationExceptionsPolicyCard = memo<PolicyArtifactCardProps>(({ polic
|
|||
() => HostIsolationExceptionsApiClient.getInstance(http),
|
||||
[http]
|
||||
);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
const { canReadPolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
|
@ -135,7 +135,7 @@ HostIsolationExceptionsPolicyCard.displayName = 'HostIsolationExceptionsPolicyCa
|
|||
const BlocklistPolicyCard = memo<PolicyArtifactCardProps>(({ policyId }) => {
|
||||
const http = useHttp();
|
||||
const blocklistsApiClientInstance = useMemo(() => BlocklistsApiClient.getInstance(http), [http]);
|
||||
const { canReadPolicyManagement } = useEndpointPrivileges();
|
||||
const { canReadPolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const getArtifactPathHandler: FleetIntegrationArtifactCardProps['getArtifactsPath'] =
|
||||
useCallback(() => {
|
||||
|
@ -174,7 +174,7 @@ export const EndpointPolicyArtifactCards = memo<EndpointPolicyArtifactCardsProps
|
|||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
} = useEndpointPrivileges();
|
||||
} = useUserPrivileges().endpointPrivileges;
|
||||
const canAccessArtifactContent = useCanAccessSomeArtifacts();
|
||||
|
||||
if (loading) {
|
||||
|
|
|
@ -72,7 +72,9 @@ export const FleetIntegrationArtifactsCard = memo<FleetIntegrationArtifactCardPr
|
|||
{ policies: [policyId, 'all'] },
|
||||
searchableFields,
|
||||
{
|
||||
onError: (error) => toasts.addDanger(labels.artifactsSummaryApiError(error.message)),
|
||||
onError: (error) => {
|
||||
toasts.addDanger(labels.artifactsSummaryApiError(error.message));
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -6,24 +6,16 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import type { PackagePolicy, NewPackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { composeHttpHandlerMocks } from '../../../../../../common/mock/endpoint/http_handler_mock_factory';
|
||||
import type { AppContextTestRender } from '../../../../../../common/mock/endpoint';
|
||||
import {
|
||||
fleetGetAgentStatusHttpMock,
|
||||
fleetGetEndpointPackagePolicyHttpMock,
|
||||
} from '../../../../../mocks';
|
||||
import { EndpointPolicyEditExtension } from './endpoint_policy_edit_extension';
|
||||
import { createFleetContextRendererMock } from '../mocks';
|
||||
import { getUserPrivilegesMockDefaultValue } from '../../../../../../common/components/user_privileges/__mocks__';
|
||||
|
||||
jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
jest.mock('../../../../../../common/components/user_privileges');
|
||||
const useEndpointPrivilegesMock = useEndpointPrivileges as jest.Mock;
|
||||
const useUserPrivilegesMock = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('When displaying the EndpointPolicyEditExtension fleet UI extension', () => {
|
||||
|
@ -36,12 +28,7 @@ describe('When displaying the EndpointPolicyEditExtension fleet UI extension', (
|
|||
]);
|
||||
|
||||
beforeEach(() => {
|
||||
useEndpointPrivilegesMock.mockReturnValue(getEndpointPrivilegesInitialStateMock());
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
|
||||
});
|
||||
const mockedTestContext = createFleetContextRendererMock();
|
||||
composeHttpHandlerMocks([fleetGetEndpointPackagePolicyHttpMock, fleetGetAgentStatusHttpMock]);
|
||||
|
||||
render = () =>
|
||||
mockedTestContext.render(
|
||||
|
@ -53,34 +40,33 @@ describe('When displaying the EndpointPolicyEditExtension fleet UI extension', (
|
|||
);
|
||||
});
|
||||
|
||||
it('should show artifact cards', async () => {
|
||||
const renderResult = render();
|
||||
|
||||
await waitFor(() => {
|
||||
artifactCards.forEach((artifactCard) => {
|
||||
expect(renderResult.getByTestId(artifactCard)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
useUserPrivilegesMock.mockReturnValue(getUserPrivilegesMockDefaultValue());
|
||||
});
|
||||
|
||||
it('should NOT show artifact cards if no endpoint management authz', async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock({
|
||||
canReadTrustedApplications: false,
|
||||
canReadEventFilters: false,
|
||||
canReadBlocklist: false,
|
||||
canReadHostIsolationExceptions: false,
|
||||
}),
|
||||
});
|
||||
it.each([...artifactCards])('should show artifact card `%s`', (artifactCardTestId) => {
|
||||
const renderResult = render();
|
||||
|
||||
await waitFor(() => {
|
||||
artifactCards.forEach((artifactCard) => {
|
||||
expect(renderResult.queryByTestId(artifactCard)).toBeNull();
|
||||
});
|
||||
});
|
||||
expect(renderResult.getByTestId(artifactCardTestId)).toBeTruthy();
|
||||
});
|
||||
|
||||
it.each([...artifactCards])(
|
||||
'should NOT show artifact cards if no endpoint management authz: %s',
|
||||
(artifactCardTestId) => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock({
|
||||
canReadTrustedApplications: false,
|
||||
canReadEventFilters: false,
|
||||
canReadBlocklist: false,
|
||||
canReadHostIsolationExceptions: false,
|
||||
}),
|
||||
});
|
||||
const renderResult = render();
|
||||
|
||||
expect(renderResult.queryByTestId(artifactCardTestId)).toBeNull();
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
['trustedApps', 'trusted_apps'],
|
||||
['eventFilters', 'event_filters'],
|
||||
|
@ -88,20 +74,18 @@ describe('When displaying the EndpointPolicyEditExtension fleet UI extension', (
|
|||
['blocklists', 'blocklist'],
|
||||
])(
|
||||
'should link to the %s list page if no Authz for policy management',
|
||||
async (artifactTestIdPrefix, pageUrlName) => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({
|
||||
...getEndpointPrivilegesInitialStateMock({
|
||||
(artifactTestIdPrefix, pageUrlName) => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock({
|
||||
canReadPolicyManagement: false,
|
||||
}),
|
||||
});
|
||||
|
||||
const { getByTestId } = render();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
getByTestId(`${artifactTestIdPrefix}-link-to-exceptions`).getAttribute('href')
|
||||
).toEqual(`/app/security/administration/${pageUrlName}?includedPolicies=someid%2Cglobal`);
|
||||
});
|
||||
expect(
|
||||
getByTestId(`${artifactTestIdPrefix}-link-to-exceptions`).getAttribute('href')
|
||||
).toEqual(`/app/security/administration/${pageUrlName}?includedPolicies=someid%2Cglobal`);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -103,39 +103,37 @@ const WrappedPolicyDetailsForm = memo<{
|
|||
|
||||
return (
|
||||
<div data-test-subj="endpointIntegrationPolicyForm">
|
||||
<>
|
||||
<EndpointPolicyArtifactCards policyId={policyId} />
|
||||
<div>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<EndpointPolicyArtifactCards policyId={policyId} />
|
||||
<div>
|
||||
<EuiText>
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.settings.title"
|
||||
defaultMessage="Policy settings"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{endpointDetailsLoadingError ? (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.settings.title"
|
||||
defaultMessage="Policy settings"
|
||||
id="xpack.securitySolution.endpoint.policyDetails.loadError"
|
||||
defaultMessage="Failed to load endpoint policy settings"
|
||||
/>
|
||||
</h5>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{endpointDetailsLoadingError ? (
|
||||
<EuiCallOut
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policyDetails.loadError"
|
||||
defaultMessage="Failed to load endpoint policy settings"
|
||||
/>
|
||||
}
|
||||
iconType="alert"
|
||||
color="warning"
|
||||
data-test-subj="endpiontPolicySettingsLoadingError"
|
||||
>
|
||||
{endpointDetailsLoadingError.message}
|
||||
</EuiCallOut>
|
||||
) : !endpointPolicyDetails ? (
|
||||
<EuiLoadingSpinner size="l" className="essentialAnimation" />
|
||||
) : (
|
||||
<PolicyDetailsForm />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
iconType="alert"
|
||||
color="warning"
|
||||
data-test-subj="endpiontPolicySettingsLoadingError"
|
||||
>
|
||||
{endpointDetailsLoadingError.message}
|
||||
</EuiCallOut>
|
||||
) : !endpointPolicyDetails ? (
|
||||
<EuiLoadingSpinner size="l" className="essentialAnimation" />
|
||||
) : (
|
||||
<PolicyDetailsForm />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
|
||||
/**
|
||||
* Checks to see if the current user can access at least one artifact page.
|
||||
|
@ -18,7 +18,7 @@ export const useCanAccessSomeArtifacts = (): boolean => {
|
|||
canReadEventFilters,
|
||||
canReadTrustedApplications,
|
||||
canReadHostIsolationExceptions,
|
||||
} = useEndpointPrivileges();
|
||||
} = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
return useMemo(() => {
|
||||
return (
|
||||
|
|
|
@ -14,7 +14,7 @@ import { I18nProvider } from '@kbn/i18n-react';
|
|||
import type { PackageInfo } from '@kbn/fleet-plugin/common/types';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { SecuritySolutionQueryClient } from '../../../../../common/containers/query_client/query_client_provider';
|
||||
import { deepFreeze } from '@kbn/std';
|
||||
import type { AppContextTestRender, UiRender } from '../../../../../common/mock/endpoint';
|
||||
import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint';
|
||||
import { createFleetContextReduxStore } from './components/with_security_context/store';
|
||||
|
@ -61,9 +61,10 @@ export const createFleetContextRendererMock = (): AppContextTestRender => {
|
|||
});
|
||||
|
||||
const mockedContext = createAppRootMockRenderer();
|
||||
const { coreStart, depsStart, queryClient, startServices } = mockedContext;
|
||||
const store = createFleetContextReduxStore({
|
||||
coreStart: mockedContext.coreStart,
|
||||
depsStart: mockedContext.depsStart,
|
||||
coreStart,
|
||||
depsStart,
|
||||
reducersObject: {
|
||||
management: managementReducer,
|
||||
app: (state, action: AppAction | UpdateExperimentalFeaturesTestAction) => {
|
||||
|
@ -81,8 +82,6 @@ export const createFleetContextRendererMock = (): AppContextTestRender => {
|
|||
additionalMiddleware: [mockedContext.middlewareSpy.actionSpyMiddleware],
|
||||
});
|
||||
|
||||
const queryClient = new SecuritySolutionQueryClient();
|
||||
|
||||
const Wrapper: RenderOptions['wrapper'] = ({ children }) => {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
@ -94,15 +93,16 @@ export const createFleetContextRendererMock = (): AppContextTestRender => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
startServices.application.capabilities = deepFreeze({
|
||||
...startServices.application.capabilities,
|
||||
siem: { show: true, crud: true },
|
||||
});
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<EuiThemeProvider>
|
||||
<KibanaContextProvider services={mockedContext.startServices}>
|
||||
<RenderContextProviders
|
||||
store={store}
|
||||
depsStart={mockedContext.depsStart}
|
||||
queryClient={queryClient}
|
||||
>
|
||||
<KibanaContextProvider services={startServices}>
|
||||
<RenderContextProviders store={store} depsStart={depsStart} queryClient={queryClient}>
|
||||
{children}
|
||||
</RenderContextProviders>
|
||||
</KibanaContextProvider>
|
||||
|
|
|
@ -21,9 +21,8 @@ import {
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
import { policyConfig } from '../store/policy_details/selectors';
|
||||
import { usePolicyDetailsSelector } from './policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from './policy_hooks';
|
||||
import { AdvancedPolicySchema } from '../models/advanced_policy_schema';
|
||||
|
||||
function setValue(obj: Record<string, unknown>, value: string, path: string[]) {
|
||||
|
@ -146,7 +145,7 @@ const PolicyAdvanced = React.memo(
|
|||
lastSupportedVersion?: string;
|
||||
documentation: string;
|
||||
}) => {
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
const dispatch = useDispatch();
|
||||
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
|
||||
const onChange = useCallback(
|
||||
|
@ -198,7 +197,7 @@ const PolicyAdvanced = React.memo(
|
|||
fullWidth
|
||||
value={value as string}
|
||||
onChange={onChange}
|
||||
disabled={!canWritePolicyManagement}
|
||||
disabled={!showEditableFormFields}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</>
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiButtonEmpty, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import { EuiButtonEmpty, EuiLoadingContent, EuiSpacer, EuiText } from '@elastic/eui';
|
||||
import React, { memo, useCallback, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
import { MalwareProtections } from './policy_forms/protections/malware';
|
||||
import { MemoryProtection } from './policy_forms/protections/memory';
|
||||
import { BehaviorProtection } from './policy_forms/protections/behavior';
|
||||
|
@ -54,6 +55,11 @@ export const PolicyDetailsForm = memo(() => {
|
|||
setShowAdvancedPolicy(!showAdvancedPolicy);
|
||||
}, [showAdvancedPolicy]);
|
||||
const isPlatinumPlus = useLicense().isPlatinumPlus();
|
||||
const { loading: authzLoading } = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
if (authzLoading) {
|
||||
return <EuiLoadingContent lines={5} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -343,6 +343,7 @@ describe('Policy Form Layout', () => {
|
|||
beforeEach(() => {
|
||||
const mockedPrivileges = getUserPrivilegesMockDefaultValue();
|
||||
mockedPrivileges.endpointPrivileges.canWritePolicyManagement = false;
|
||||
mockedPrivileges.endpointPrivileges.canAccessFleet = false;
|
||||
|
||||
useUserPrivilegesMock.mockReturnValue(mockedPrivileges);
|
||||
|
||||
|
|
|
@ -23,8 +23,7 @@ import { useLocation } from 'react-router-dom';
|
|||
import type { ApplicationStart } from '@kbn/core/public';
|
||||
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../../common/hooks/use_experimental_features';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import {
|
||||
policyDetails,
|
||||
agentStatusSummary,
|
||||
|
@ -52,7 +51,7 @@ export const PolicyFormLayout = React.memo(() => {
|
|||
} = useKibana();
|
||||
const toasts = useToasts();
|
||||
const { state: locationRouteState } = useLocation<PolicyDetailsRouteState>();
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
|
||||
// Store values
|
||||
const policyItem = usePolicyDetailsSelector(policyDetails);
|
||||
|
@ -181,7 +180,7 @@ export const PolicyFormLayout = React.memo(() => {
|
|||
</EuiButtonEmpty>
|
||||
</EuiThemeProvider>
|
||||
</EuiFlexItem>
|
||||
{canWritePolicyManagement && (
|
||||
{showEditableFormFields && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
fill={true}
|
||||
|
|
|
@ -9,11 +9,10 @@ import React, { useCallback, useMemo } from 'react';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { htmlIdGenerator, EuiRadio } from '@elastic/eui';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import type { ImmutableArray, UIPolicyConfig } from '../../../../../../../common/endpoint/types';
|
||||
import { ProtectionModes } from '../../../../../../../common/endpoint/types';
|
||||
import type { MacPolicyProtection, LinuxPolicyProtection, PolicyProtection } from '../../../types';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
||||
import type { AppAction } from '../../../../../../common/store/actions';
|
||||
import { useLicense } from '../../../../../../common/hooks/use_license';
|
||||
|
@ -35,7 +34,7 @@ export const ProtectionRadio = React.memo(
|
|||
const radioButtonId = useMemo(() => htmlIdGenerator()(), []);
|
||||
const selected = policyDetailsConfig && policyDetailsConfig.windows[protection].mode;
|
||||
const isPlatinumPlus = useLicense().isPlatinumPlus();
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
|
||||
const handleRadioChange = useCallback(() => {
|
||||
if (policyDetailsConfig) {
|
||||
|
@ -89,7 +88,7 @@ export const ProtectionRadio = React.memo(
|
|||
id={radioButtonId}
|
||||
checked={selected === protectionMode}
|
||||
onChange={handleRadioChange}
|
||||
disabled={!canWritePolicyManagement || selected === ProtectionModes.off}
|
||||
disabled={!showEditableFormFields || selected === ProtectionModes.off}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,10 +10,9 @@ import { useDispatch } from 'react-redux';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiSwitch } from '@elastic/eui';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { useLicense } from '../../../../../../common/hooks/use_license';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import type { AppAction } from '../../../../../../common/store/actions';
|
||||
import type {
|
||||
ImmutableArray,
|
||||
|
@ -41,7 +40,7 @@ export const ProtectionSwitch = React.memo(
|
|||
}) => {
|
||||
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
|
||||
const isPlatinumPlus = useLicense().isPlatinumPlus();
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
const dispatch = useDispatch<(action: AppAction) => void>();
|
||||
const selected = policyDetailsConfig && policyDetailsConfig.windows[protection].mode;
|
||||
|
||||
|
@ -125,7 +124,7 @@ export const ProtectionSwitch = React.memo(
|
|||
})}
|
||||
checked={selected !== ProtectionModes.off}
|
||||
onChange={handleSwitchChange}
|
||||
disabled={!canWritePolicyManagement}
|
||||
disabled={!showEditableFormFields}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,12 +19,11 @@ import {
|
|||
EuiText,
|
||||
EuiTextArea,
|
||||
} from '@elastic/eui';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import type { ImmutableArray, UIPolicyConfig } from '../../../../../../../common/endpoint/types';
|
||||
import { ProtectionModes } from '../../../../../../../common/endpoint/types';
|
||||
import type { PolicyProtection, MacPolicyProtection, LinuxPolicyProtection } from '../../../types';
|
||||
import { ConfigFormHeading } from '../../components/config_form';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
||||
import type { AppAction } from '../../../../../../common/store/actions';
|
||||
import { SupportedVersionNotice } from './supported_version';
|
||||
|
@ -37,7 +36,7 @@ export const UserNotification = React.memo(
|
|||
protection: PolicyProtection;
|
||||
osList: ImmutableArray<Partial<keyof UIPolicyConfig>>;
|
||||
}) => {
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
|
||||
const dispatch = useDispatch<(action: AppAction) => void>();
|
||||
const selected = policyDetailsConfig && policyDetailsConfig.windows[protection].mode;
|
||||
|
@ -141,7 +140,7 @@ export const UserNotification = React.memo(
|
|||
id={`${protection}UserNotificationCheckbox}`}
|
||||
onChange={handleUserNotificationCheckbox}
|
||||
checked={userNotificationSelected}
|
||||
disabled={!canWritePolicyManagement || selected === ProtectionModes.off}
|
||||
disabled={!showEditableFormFields || selected === ProtectionModes.off}
|
||||
label={i18n.translate('xpack.securitySolution.endpoint.policyDetail.notifyUser', {
|
||||
defaultMessage: 'Notify user',
|
||||
})}
|
||||
|
@ -198,7 +197,7 @@ export const UserNotification = React.memo(
|
|||
value={userNotificationMessage}
|
||||
onChange={handleCustomUserNotification}
|
||||
fullWidth={true}
|
||||
disabled={!canWritePolicyManagement}
|
||||
disabled={!showEditableFormFields}
|
||||
data-test-subj={`${protection}UserNotificationCustomMessage`}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -19,7 +19,6 @@ import {
|
|||
import { OperatingSystem } from '@kbn/securitysolution-utils';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { APP_UI_ID } from '../../../../../../../common/constants';
|
||||
import { SecurityPageName } from '../../../../../../app/types';
|
||||
import type {
|
||||
|
@ -36,7 +35,7 @@ import { RadioButtons } from '../components/radio_buttons';
|
|||
import { UserNotification } from '../components/user_notification';
|
||||
import { ProtectionSwitch } from '../components/protection_switch';
|
||||
import { policyConfig } from '../../../store/policy_details/selectors';
|
||||
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { useShowEditableFormFields, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import type { AppAction } from '../../../../../../common/store/actions';
|
||||
|
||||
/** The Malware Protections form for policy details
|
||||
|
@ -60,7 +59,7 @@ export const MalwareProtections = React.memo(() => {
|
|||
defaultMessage: 'Blocklist enabled',
|
||||
}
|
||||
);
|
||||
const { canWritePolicyManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const showEditableFormFields = useShowEditableFormFields();
|
||||
const isPlatinumPlus = useLicense().isPlatinumPlus();
|
||||
const dispatch = useDispatch<(action: AppAction) => void>();
|
||||
const policyDetailsConfig = usePolicyDetailsSelector(policyConfig);
|
||||
|
@ -120,7 +119,7 @@ export const MalwareProtections = React.memo(() => {
|
|||
checked={policyDetailsConfig.windows[protection].blocklist}
|
||||
onChange={handleBlocklistSwitchChange}
|
||||
disabled={
|
||||
!canWritePolicyManagement || policyDetailsConfig.windows[protection].mode === 'off'
|
||||
!showEditableFormFields || policyDetailsConfig.windows[protection].mode === 'off'
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import {
|
||||
|
@ -13,6 +13,8 @@ import {
|
|||
ENDPOINT_EVENT_FILTERS_LIST_ID,
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID,
|
||||
} from '@kbn/securitysolution-list-constants';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import { useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
import type { PolicyDetailsArtifactsPageLocation, PolicyDetailsState } from '../types';
|
||||
import type { State } from '../../../../common/store';
|
||||
import {
|
||||
|
@ -26,7 +28,7 @@ import {
|
|||
getPolicyHostIsolationExceptionsPath,
|
||||
} from '../../../common/routing';
|
||||
import { getCurrentArtifactsLocation, policyIdFromParams } from '../store/policy_details/selectors';
|
||||
import { POLICIES_PATH } from '../../../../../common/constants';
|
||||
import { APP_UI_ID, POLICIES_PATH } from '../../../../../common/constants';
|
||||
|
||||
/**
|
||||
* Narrows global state down to the PolicyDetailsState before calling the provided Policy Details Selector
|
||||
|
@ -88,3 +90,27 @@ export const useIsPolicySettingsBarVisible = () => {
|
|||
window.location.pathname.includes('/settings')
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates if user is granted Write access to Policy Management. This method differs from what
|
||||
* `useUserPrivileges().endpointPrivileges.canWritePolicyManagement` in that it also checks if
|
||||
* user has `canAccessFleet` if form is being displayed outside of Security Solution.
|
||||
* This is to ensure that the Policy Form remains accessible when displayed inside of Fleet
|
||||
* pages if the user does not have privileges to security solution policy management.
|
||||
*/
|
||||
export const useShowEditableFormFields = (): boolean => {
|
||||
const { canWritePolicyManagement, canAccessFleet } = useUserPrivileges().endpointPrivileges;
|
||||
const { getUrlForApp } = useKibana().services.application;
|
||||
|
||||
const securitySolutionUrl = useMemo(() => {
|
||||
return getUrlForApp(APP_UI_ID);
|
||||
}, [getUrlForApp]);
|
||||
|
||||
return useMemo(() => {
|
||||
if (window.location.pathname.startsWith(securitySolutionUrl)) {
|
||||
return canWritePolicyManagement;
|
||||
} else {
|
||||
return canAccessFleet;
|
||||
}
|
||||
}, [canAccessFleet, canWritePolicyManagement, securitySolutionUrl]);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue