Skip to content

Commit

Permalink
[Security Solution][Endpoint] Assign and remove Event Filters by poli…
Browse files Browse the repository at this point in the history
…cy based on user license (#122434)

* use licence info to show assign to policy button

fixes elastic/security-team/issues/2032

* use license info to show remove event filter from policy button

fixes elastic/security-team/issues/2032

* use license info to show assign event filter button on empty state

fixes elastic/security-team/issues/2032

* Use license info to show the assign event filter to policy flyout

fixes elastic/security-team/issues/2032

* minor typo

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
ashokaditya and kibanamachine committed Jan 11, 2022
1 parent f9c6259 commit 33af3fc
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -22,15 +21,14 @@ export const PolicyEventFiltersEmptyUnassigned = memo<CommonProps>(({ 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 (
<EuiPageTemplate template="centeredContent">
<EuiEmptyPrompt
Expand All @@ -54,18 +52,17 @@ export const PolicyEventFiltersEmptyUnassigned = memo<CommonProps>(({ policyId,
actions={[
...(canCreateArtifactsByPolicy
? [
// TODO: Uncomment this when assign functionality through the flyout is done
// <EuiButton
// color="primary"
// fill
// onClick={onClickPrimaryButtonHandler}
// data-test-subj="assign-ta-button"
// >
// <FormattedMessage
// id="xpack.securitySolution.endpoint.policy.eventFilters.empty.unassigned.primaryAction"
// defaultMessage="Assign event filters"
// />
// </EuiButton>,
<EuiButton
color="primary"
fill
onClick={onClickPrimaryButtonHandler}
data-test-subj="assign-event-filter-button"
>
<FormattedMessage
id="xpack.securitySolution.endpoint.policy.eventFilters.empty.unassigned.primaryAction"
defaultMessage="Assign event filters"
/>
</EuiButton>,
]
: []),
// eslint-disable-next-line @elastic/eui/href-or-on-click
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppContextTestRender['render']>;
let mockedContext: AppContextTestRender;
Expand All @@ -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)
);
Expand All @@ -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) => {
Expand All @@ -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)
);
Expand All @@ -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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -40,6 +41,7 @@ export const PolicyEventFiltersLayout = React.memo<PolicyEventFiltersLayoutProps
({ policyItem }) => {
const { getAppUrl } = useAppUrl();
const navigateCallback = usePolicyDetailsEventFiltersNavigateCallback();
const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges;
const urlParams = usePolicyDetailsSelector(getCurrentArtifactsLocation);

const { data: allAssigned, isLoading: isLoadingAllAssigned } = useGetAllAssignedEventFilters(
Expand All @@ -56,6 +58,25 @@ export const PolicyEventFiltersLayout = React.memo<PolicyEventFiltersLayoutProps
navigateCallback({ show: undefined });
}, [navigateCallback]);

const assignToPolicyButton = useMemo(
() => (
<EuiButton
fill
iconType="plusInCircle"
data-test-subj="eventFilters-assign-button"
onClick={handleOnClickAssignButton}
>
{i18n.translate(
'xpack.securitySolution.endpoint.policy.eventFilters.layout.assignToPolicy',
{
defaultMessage: 'Assign event filters to policy',
}
)}
</EuiButton>
),
[handleOnClickAssignButton]
);

const aboutInfo = useMemo(() => {
const link = (
<EuiLink
Expand Down Expand Up @@ -122,22 +143,10 @@ export const PolicyEventFiltersLayout = React.memo<PolicyEventFiltersLayoutProps
</EuiText>
</EuiPageHeaderSection>
<EuiPageHeaderSection>
<EuiButton
fill
iconType="plusInCircle"
data-test-subj="eventFilters-assign-button"
onClick={handleOnClickAssignButton}
>
{i18n.translate(
'xpack.securitySolution.endpoint.policy.eventFilters.layout.assignToPolicy',
{
defaultMessage: 'Assign event filters to policy',
}
)}
</EuiButton>
{canCreateArtifactsByPolicy && assignToPolicyButton}
</EuiPageHeaderSection>
</EuiPageHeader>
{urlParams.show === 'list' && (
{canCreateArtifactsByPolicy && urlParams.show === 'list' && (
<PolicyEventFiltersFlyout policyItem={policyItem} onClose={handleOnCloseFlyout} />
)}
<EuiSpacer size="l" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '') => ({
Expand Down Expand Up @@ -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();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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<PolicyData>;
}
export const PolicyEventFiltersList = React.memo<PolicyEventFiltersListProps>(({ policy }) => {
const { getAppUrl } = useAppUrl();
const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges;
const policiesRequest = useGetEndpointSpecificPolicies();
const navigateCallback = usePolicyDetailsEventFiltersNavigateCallback();
const urlParams = usePolicyDetailsSelector(getCurrentArtifactsLocation);
Expand Down Expand Up @@ -142,7 +144,7 @@ export const PolicyEventFiltersList = React.memo<PolicyEventFiltersListProps>(({
};
return {
expanded: expandedItemsMap.get(item.id) || false,
actions: [fullDetailsAction, deleteAction],
actions: canCreateArtifactsByPolicy ? [fullDetailsAction, deleteAction] : [fullDetailsAction],
policies: artifactCardPolicies,
};
};
Expand Down

0 comments on commit 33af3fc

Please sign in to comment.