diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/empty/policy_event_filters_empty_unassigned.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/empty/policy_event_filters_empty_unassigned.tsx index 2c6f4a573d7ab..4d53682d2d669 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/empty/policy_event_filters_empty_unassigned.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/empty/policy_event_filters_empty_unassigned.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import React, { memo } from 'react'; -import { EuiEmptyPrompt, EuiPageTemplate, EuiLink } from '@elastic/eui'; +import React, { memo, useCallback } from 'react'; +import { EuiButton, EuiEmptyPrompt, EuiPageTemplate, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -// TODO: Uncomment this when assign functionality through the flyout is done -// import { usePolicyDetailsNavigateCallback } from '../../policy_hooks'; +import { usePolicyDetailsNavigateCallback } from '../../policy_hooks'; import { useGetLinkTo } from './use_policy_event_filters_empty_hooks'; import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; @@ -22,15 +21,14 @@ export const PolicyEventFiltersEmptyUnassigned = memo(({ policyId, const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const { onClickHandler, toRouteUrl } = useGetLinkTo(policyId, policyName); - // TODO: Uncomment this when assign functionality through the flyout is done - // const navigateCallback = usePolicyDetailsNavigateCallback(); - // const onClickPrimaryButtonHandler = useCallback( - // () => - // navigateCallback({ - // show: 'list', - // }), - // [navigateCallback] - // ); + const navigateCallback = usePolicyDetailsNavigateCallback(); + const onClickPrimaryButtonHandler = useCallback( + () => + navigateCallback({ + show: 'list', + }), + [navigateCallback] + ); return ( (({ policyId, actions={[ ...(canCreateArtifactsByPolicy ? [ - // TODO: Uncomment this when assign functionality through the flyout is done - // - // - // , + + + , ] : []), // eslint-disable-next-line @elastic/eui/href-or-on-click diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.test.tsx index f1aedf3e2d045..fe0668e63774f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.test.tsx @@ -11,12 +11,14 @@ import { AppContextTestRender, createAppRootMockRenderer, } from '../../../../../../common/mock/endpoint'; +import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing'; import { EndpointDocGenerator } from '../../../../../../../common/endpoint/generate_data'; import { ImmutableObject, PolicyData } from '../../../../../../../common/endpoint/types'; import { parsePoliciesAndFilterToKql } from '../../../../../common/utils'; import { eventFiltersListQueryHttpMock } from '../../../../event_filters/test_utils'; import { getFoundExceptionListItemSchemaMock } from '../../../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; +import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks'; let render: () => ReturnType; let mockedContext: AppContextTestRender; @@ -34,12 +36,12 @@ describe('Policy event filters layout', () => { afterEach(() => reactTestingLibrary.cleanup()); - it('should renders layout with a loader', async () => { + it('should render layout with a loader', async () => { const component = render(); expect(component.getByTestId('policy-event-filters-loading-spinner')).toBeTruthy(); }); - it('should renders layout with no assigned event filters data when there are not event filters', async () => { + it('should render layout with no assigned event filters data when there are not event filters', async () => { mockedApi.responseProvider.eventFiltersList.mockReturnValue( getFoundExceptionListItemSchemaMock(0) ); @@ -48,7 +50,7 @@ describe('Policy event filters layout', () => { expect(await component.findByTestId('policy-event-filters-empty-unexisting')).not.toBeNull(); }); - it('should renders layout with no assigned event filters data when there are event filters', async () => { + it('should render layout with no assigned event filters data when there are event filters', async () => { mockedApi.responseProvider.eventFiltersList.mockImplementation( // @ts-expect-error (args) => { @@ -69,7 +71,7 @@ describe('Policy event filters layout', () => { expect(await component.findByTestId('policy-event-filters-empty-unassigned')).not.toBeNull(); }); - it('should renders layout with data', async () => { + it('should render layout with data', async () => { mockedApi.responseProvider.eventFiltersList.mockReturnValue( getFoundExceptionListItemSchemaMock(3) ); @@ -80,4 +82,47 @@ describe('Policy event filters layout', () => { '3 event filters' ); }); + + it('should hide `Assign event filters to policy` on empty state with unassigned policies when downgraded to a gold or below license', () => { + getEndpointPrivilegesInitialStateMock({ + canCreateArtifactsByPolicy: false, + }); + mockedApi.responseProvider.eventFiltersList.mockReturnValue( + getFoundExceptionListItemSchemaMock(0) + ); + + const component = render(); + mockedContext.history.push(getPolicyDetailsArtifactsListPath(policyItem.id)); + expect(component.queryByTestId('assign-event-filter-button')).toBeNull(); + }); + + it('should hide the `Assign event filters to policy` button license is downgraded to gold or below', () => { + getEndpointPrivilegesInitialStateMock({ + canCreateArtifactsByPolicy: false, + }); + mockedApi.responseProvider.eventFiltersList.mockReturnValue( + getFoundExceptionListItemSchemaMock(5) + ); + + const component = render(); + mockedContext.history.push(getPolicyDetailsArtifactsListPath(policyItem.id)); + + expect(component.queryByTestId('eventFilters-assign-button')).toBeNull(); + }); + + it('should hide the `Assign event filters` flyout when license is downgraded to gold or below', () => { + getEndpointPrivilegesInitialStateMock({ + canCreateArtifactsByPolicy: false, + }); + mockedApi.responseProvider.eventFiltersList.mockReturnValue( + getFoundExceptionListItemSchemaMock(2) + ); + + const component = render(); + mockedContext.history.push( + `${getPolicyDetailsArtifactsListPath(policyItem.id)}/eventFilters?show=list` + ); + + expect(component.queryByTestId('eventFilters-assign-flyout')).toBeNull(); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.tsx index a7091f6d92229..5b4138480fdf3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/layout/policy_event_filters_layout.tsx @@ -24,6 +24,7 @@ import { ImmutableObject, PolicyData } from '../../../../../../../common/endpoin import { getEventFiltersListPath } from '../../../../../common/routing'; import { useGetAllAssignedEventFilters, useGetAllEventFilters } from '../hooks'; import { ManagementPageLoader } from '../../../../../components/management_page_loader'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; import { PolicyEventFiltersEmptyUnassigned, PolicyEventFiltersEmptyUnexisting } from '../empty'; import { usePolicyDetailsSelector, @@ -40,6 +41,7 @@ export const PolicyEventFiltersLayout = React.memo { const { getAppUrl } = useAppUrl(); const navigateCallback = usePolicyDetailsEventFiltersNavigateCallback(); + const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const urlParams = usePolicyDetailsSelector(getCurrentArtifactsLocation); const { data: allAssigned, isLoading: isLoadingAllAssigned } = useGetAllAssignedEventFilters( @@ -56,6 +58,25 @@ export const PolicyEventFiltersLayout = React.memo ( + + {i18n.translate( + 'xpack.securitySolution.endpoint.policy.eventFilters.layout.assignToPolicy', + { + defaultMessage: 'Assign event filters to policy', + } + )} + + ), + [handleOnClickAssignButton] + ); + const aboutInfo = useMemo(() => { const link = ( - - {i18n.translate( - 'xpack.securitySolution.endpoint.policy.eventFilters.layout.assignToPolicy', - { - defaultMessage: 'Assign event filters to policy', - } - )} - + {canCreateArtifactsByPolicy && assignToPolicyButton} - {urlParams.show === 'list' && ( + {canCreateArtifactsByPolicy && urlParams.show === 'list' && ( )} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.test.tsx index 1479eca21c1c8..dc78c3edb87fa 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.test.tsx @@ -20,6 +20,7 @@ import { eventFiltersListQueryHttpMock } from '../../../../event_filters/test_ut import { PolicyEventFiltersList } from './policy_event_filters_list'; import { parseQueryFilterToKQL, parsePoliciesAndFilterToKql } from '../../../../../common/utils'; import { SEARCHABLE_FIELDS } from '../../../../event_filters/constants'; +import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks'; const endpointGenerator = new EndpointDocGenerator('seed'); const getDefaultQueryParameters = (customFilter: string | undefined = '') => ({ @@ -133,4 +134,19 @@ describe('Policy details event filters list', () => { ); expect(renderResult.queryByTestId('view-full-details-action')).toBeTruthy(); }); + + it('does not show remove option in actions menu if license is downgraded to gold or below', async () => { + getEndpointPrivilegesInitialStateMock({ + canCreateArtifactsByPolicy: false, + }); + mockedApi.responseProvider.eventFiltersList.mockReturnValue( + getFoundExceptionListItemSchemaMock() + ); + await render(); + userEvent.click( + renderResult.getByTestId('eventFilters-collapsed-list-card-header-actions-button') + ); + + expect(renderResult.queryByTestId('remove-from-policy-action')).toBeNull(); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.tsx index 4153eb45200ed..225497ade8edd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/event_filters/list/policy_event_filters_list.tsx @@ -32,12 +32,14 @@ import { ImmutableObject, PolicyData } from '../../../../../../../common/endpoin import { PolicyEventFiltersDeleteModal } from '../delete_modal'; import { isGlobalPolicyEffected } from '../../../../../components/effected_policy_select/utils'; import { getEventFiltersListPath } from '../../../../../common/routing'; +import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; interface PolicyEventFiltersListProps { policy: ImmutableObject; } export const PolicyEventFiltersList = React.memo(({ policy }) => { const { getAppUrl } = useAppUrl(); + const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges; const policiesRequest = useGetEndpointSpecificPolicies(); const navigateCallback = usePolicyDetailsEventFiltersNavigateCallback(); const urlParams = usePolicyDetailsSelector(getCurrentArtifactsLocation); @@ -142,7 +144,7 @@ export const PolicyEventFiltersList = React.memo(({ }; return { expanded: expandedItemsMap.get(item.id) || false, - actions: [fullDetailsAction, deleteAction], + actions: canCreateArtifactsByPolicy ? [fullDetailsAction, deleteAction] : [fullDetailsAction], policies: artifactCardPolicies, }; };