mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solution][Endpoint][Admin][TA by Policy] Policy details trusted app tab downgrade experience (#114871)
This commit is contained in:
parent
a8b4379523
commit
c5f3be6979
6 changed files with 140 additions and 46 deletions
|
@ -10,6 +10,7 @@ import { EuiEmptyPrompt, EuiButton, EuiPageTemplate, EuiLink } from '@elastic/eu
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { usePolicyDetailsNavigateCallback } from '../../policy_hooks';
|
||||
import { useGetLinkTo } from './use_policy_trusted_apps_empty_hooks';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
|
||||
|
||||
interface CommonProps {
|
||||
policyId: string;
|
||||
|
@ -17,6 +18,7 @@ interface CommonProps {
|
|||
}
|
||||
|
||||
export const PolicyTrustedAppsEmptyUnassigned = memo<CommonProps>(({ policyId, policyName }) => {
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
const navigateCallback = usePolicyDetailsNavigateCallback();
|
||||
const { onClickHandler, toRouteUrl } = useGetLinkTo(policyId, policyName);
|
||||
const onClickPrimaryButtonHandler = useCallback(
|
||||
|
@ -47,12 +49,21 @@ export const PolicyTrustedAppsEmptyUnassigned = memo<CommonProps>(({ policyId, p
|
|||
/>
|
||||
}
|
||||
actions={[
|
||||
<EuiButton color="primary" fill onClick={onClickPrimaryButtonHandler}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.trustedApps.empty.unassigned.primaryAction"
|
||||
defaultMessage="Assign trusted applications"
|
||||
/>
|
||||
</EuiButton>,
|
||||
...(isPlatinumPlus
|
||||
? [
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
onClick={onClickPrimaryButtonHandler}
|
||||
data-test-subj="assign-ta-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.trustedApps.empty.unassigned.primaryAction"
|
||||
defaultMessage="Assign trusted applications"
|
||||
/>
|
||||
</EuiButton>,
|
||||
]
|
||||
: []),
|
||||
// eslint-disable-next-line @elastic/eui/href-or-on-click
|
||||
<EuiLink onClick={onClickHandler} href={toRouteUrl}>
|
||||
<FormattedMessage
|
||||
|
|
|
@ -219,7 +219,7 @@ export const PolicyTrustedAppsFlyout = React.memo(() => {
|
|||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.noAssignable"
|
||||
defaultMessage="There are no assignable Trused Apps to assign to this policy"
|
||||
defaultMessage="There are no trusted applications that can be assigned to this policy."
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -19,8 +19,20 @@ import { createLoadedResourceState, isLoadedResourceState } from '../../../../..
|
|||
import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing';
|
||||
import { EndpointDocGenerator } from '../../../../../../../common/endpoint/generate_data';
|
||||
import { policyListApiPathHandlers } from '../../../store/test_mock_utils';
|
||||
import { licenseService } from '../../../../../../common/hooks/use_license';
|
||||
|
||||
jest.mock('../../../../trusted_apps/service');
|
||||
jest.mock('../../../../../../common/hooks/use_license', () => {
|
||||
const licenseServiceInstance = {
|
||||
isPlatinumPlus: jest.fn(),
|
||||
};
|
||||
return {
|
||||
licenseService: licenseServiceInstance,
|
||||
useLicense: () => {
|
||||
return licenseServiceInstance;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
let mockedContext: AppContextTestRender;
|
||||
let waitForAction: MiddlewareActionSpyHelper['waitForAction'];
|
||||
|
@ -106,4 +118,31 @@ describe('Policy trusted apps layout', () => {
|
|||
|
||||
expect(component.getByTestId('policyDetailsTrustedAppsCount')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should hide assign button on empty state with unassigned policies when downgraded to a gold or below license', async () => {
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
const component = render();
|
||||
mockedContext.history.push(getPolicyDetailsArtifactsListPath('1234'));
|
||||
|
||||
await waitForAction('assignedTrustedAppsListStateChanged');
|
||||
|
||||
mockedContext.store.dispatch({
|
||||
type: 'policyArtifactsDeosAnyTrustedAppExists',
|
||||
payload: createLoadedResourceState(true),
|
||||
});
|
||||
expect(component.queryByTestId('assign-ta-button')).toBeNull();
|
||||
});
|
||||
it('should hide the `Assign trusted applications` button when there is data and the license is downgraded to gold or below', async () => {
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
TrustedAppsHttpServiceMock.mockImplementation(() => {
|
||||
return {
|
||||
getTrustedAppsList: () => getMockListResponse(),
|
||||
};
|
||||
});
|
||||
const component = render();
|
||||
mockedContext.history.push(getPolicyDetailsArtifactsListPath('1234'));
|
||||
|
||||
await waitForAction('assignedTrustedAppsListStateChanged');
|
||||
expect(component.queryByTestId('assignTrustedAppButton')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
import { usePolicyDetailsNavigateCallback, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||
import { PolicyTrustedAppsFlyout } from '../flyout';
|
||||
import { PolicyTrustedAppsList } from '../list/policy_trusted_apps_list';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
|
||||
|
||||
export const PolicyTrustedAppsLayout = React.memo(() => {
|
||||
const location = usePolicyDetailsSelector(getCurrentArtifactsLocation);
|
||||
|
@ -33,6 +34,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
const policyItem = usePolicyDetailsSelector(policyDetails);
|
||||
const navigateCallback = usePolicyDetailsNavigateCallback();
|
||||
const hasAssignedTrustedApps = usePolicyDetailsSelector(doesPolicyHaveTrustedApps);
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
|
||||
const showListFlyout = location.show === 'list';
|
||||
|
||||
|
@ -41,6 +43,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
<EuiButton
|
||||
fill
|
||||
iconType="plusInCircle"
|
||||
data-test-subj="assignTrustedAppButton"
|
||||
onClick={() =>
|
||||
navigateCallback({
|
||||
show: 'list',
|
||||
|
@ -88,7 +91,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection>{assignTrustedAppButton}</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection>{isPlatinumPlus && assignTrustedAppButton}</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
) : null}
|
||||
<EuiPageContent
|
||||
|
@ -114,7 +117,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
<PolicyTrustedAppsList />
|
||||
)}
|
||||
</EuiPageContent>
|
||||
{showListFlyout ? <PolicyTrustedAppsFlyout /> : null}
|
||||
{isPlatinumPlus && showListFlyout ? <PolicyTrustedAppsFlyout /> : null}
|
||||
</div>
|
||||
) : null;
|
||||
});
|
||||
|
|
|
@ -21,6 +21,13 @@ import {
|
|||
} from '../../../../../state';
|
||||
import { fireEvent, within, act, waitFor } from '@testing-library/react';
|
||||
import { APP_ID } from '../../../../../../../common/constants';
|
||||
import {
|
||||
EndpointPrivileges,
|
||||
useEndpointPrivileges,
|
||||
} from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
|
||||
|
||||
jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges');
|
||||
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
|
||||
|
||||
describe('when rendering the PolicyTrustedAppsList', () => {
|
||||
// The index (zero based) of the card created by the generator that is policy specific
|
||||
|
@ -32,6 +39,16 @@ describe('when rendering the PolicyTrustedAppsList', () => {
|
|||
let mockedApis: ReturnType<typeof policyDetailsPageAllApiHttpMocks>;
|
||||
let waitForAction: AppContextTestRender['middlewareSpy']['waitForAction'];
|
||||
|
||||
const loadedUserEndpointPrivilegesState = (
|
||||
endpointOverrides: Partial<EndpointPrivileges> = {}
|
||||
): EndpointPrivileges => ({
|
||||
loading: false,
|
||||
canAccessFleet: true,
|
||||
canAccessEndpointManagement: true,
|
||||
isPlatinumPlus: true,
|
||||
...endpointOverrides,
|
||||
});
|
||||
|
||||
const getCardByIndexPosition = (cardIndex: number = 0) => {
|
||||
const card = renderResult.getAllByTestId('policyTrustedAppsGrid-card')[cardIndex];
|
||||
|
||||
|
@ -66,8 +83,12 @@ describe('when rendering the PolicyTrustedAppsList', () => {
|
|||
);
|
||||
};
|
||||
|
||||
afterAll(() => {
|
||||
mockUseEndpointPrivileges.mockReset();
|
||||
});
|
||||
beforeEach(() => {
|
||||
appTestContext = createAppRootMockRenderer();
|
||||
mockUseEndpointPrivileges.mockReturnValue(loadedUserEndpointPrivilegesState());
|
||||
|
||||
mockedApis = policyDetailsPageAllApiHttpMocks(appTestContext.coreStart.http);
|
||||
appTestContext.setExperimentalFlag({ trustedAppsByPolicyEnabled: true });
|
||||
|
@ -297,4 +318,16 @@ describe('when rendering the PolicyTrustedAppsList', () => {
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show remove option in actions menu if license is downgraded to gold or below', async () => {
|
||||
await render();
|
||||
mockUseEndpointPrivileges.mockReturnValue(
|
||||
loadedUserEndpointPrivilegesState({
|
||||
isPlatinumPlus: false,
|
||||
})
|
||||
);
|
||||
await toggleCardActionMenu(POLICY_SPECIFIC_CARD_INDEX);
|
||||
|
||||
expect(renderResult.queryByTestId('policyTrustedAppsGrid-removeAction')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ import { ContextMenuItemNavByRouterProps } from '../../../../../components/conte
|
|||
import { ArtifactEntryCollapsibleCardProps } from '../../../../../components/artifact_entry_card';
|
||||
import { useTestIdGenerator } from '../../../../../components/hooks/use_test_id_generator';
|
||||
import { RemoveTrustedAppFromPolicyModal } from './remove_trusted_app_from_policy_modal';
|
||||
import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges';
|
||||
|
||||
const DATA_TEST_SUBJ = 'policyTrustedAppsGrid';
|
||||
|
||||
|
@ -46,6 +47,7 @@ export const PolicyTrustedAppsList = memo(() => {
|
|||
const toasts = useToasts();
|
||||
const history = useHistory();
|
||||
const { getAppUrl } = useAppUrl();
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
const policyId = usePolicyDetailsSelector(policyIdFromParams);
|
||||
const hasTrustedApps = usePolicyDetailsSelector(doesPolicyHaveTrustedApps);
|
||||
const isLoading = usePolicyDetailsSelector(isPolicyTrustedAppListLoading);
|
||||
|
@ -132,44 +134,50 @@ export const PolicyTrustedAppsList = memo(() => {
|
|||
return byIdPolicies;
|
||||
}, {});
|
||||
|
||||
const fullDetailsAction: ArtifactCardGridCardComponentProps['actions'] = [
|
||||
{
|
||||
icon: 'controlsHorizontal',
|
||||
children: i18n.translate(
|
||||
'xpack.securitySolution.endpoint.policy.trustedApps.list.viewAction',
|
||||
{ defaultMessage: 'View full details' }
|
||||
),
|
||||
href: getAppUrl({ appId: APP_ID, path: viewUrlPath }),
|
||||
navigateAppId: APP_ID,
|
||||
navigateOptions: { path: viewUrlPath },
|
||||
'data-test-subj': getTestId('viewFullDetailsAction'),
|
||||
},
|
||||
];
|
||||
const thisTrustedAppCardProps: ArtifactCardGridCardComponentProps = {
|
||||
expanded: Boolean(isCardExpanded[trustedApp.id]),
|
||||
actions: [
|
||||
{
|
||||
icon: 'controlsHorizontal',
|
||||
children: i18n.translate(
|
||||
'xpack.securitySolution.endpoint.policy.trustedApps.list.viewAction',
|
||||
{ defaultMessage: 'View full details' }
|
||||
),
|
||||
href: getAppUrl({ appId: APP_ID, path: viewUrlPath }),
|
||||
navigateAppId: APP_ID,
|
||||
navigateOptions: { path: viewUrlPath },
|
||||
'data-test-subj': getTestId('viewFullDetailsAction'),
|
||||
},
|
||||
{
|
||||
icon: 'trash',
|
||||
children: i18n.translate(
|
||||
'xpack.securitySolution.endpoint.policy.trustedApps.list.removeAction',
|
||||
{ defaultMessage: 'Remove from policy' }
|
||||
),
|
||||
onClick: () => {
|
||||
setTrustedAppsForRemoval([trustedApp]);
|
||||
setShowRemovalModal(true);
|
||||
},
|
||||
disabled: isGlobal,
|
||||
toolTipContent: isGlobal
|
||||
? i18n.translate(
|
||||
'xpack.securitySolution.endpoint.policy.trustedApps.list.removeActionNotAllowed',
|
||||
{
|
||||
defaultMessage:
|
||||
'Globally applied trusted applications cannot be removed from policy.',
|
||||
}
|
||||
)
|
||||
: undefined,
|
||||
toolTipPosition: 'top',
|
||||
'data-test-subj': getTestId('removeAction'),
|
||||
},
|
||||
],
|
||||
actions: isPlatinumPlus
|
||||
? [
|
||||
...fullDetailsAction,
|
||||
{
|
||||
icon: 'trash',
|
||||
children: i18n.translate(
|
||||
'xpack.securitySolution.endpoint.policy.trustedApps.list.removeAction',
|
||||
{ defaultMessage: 'Remove from policy' }
|
||||
),
|
||||
onClick: () => {
|
||||
setTrustedAppsForRemoval([trustedApp]);
|
||||
setShowRemovalModal(true);
|
||||
},
|
||||
disabled: isGlobal,
|
||||
toolTipContent: isGlobal
|
||||
? i18n.translate(
|
||||
'xpack.securitySolution.endpoint.policy.trustedApps.list.removeActionNotAllowed',
|
||||
{
|
||||
defaultMessage:
|
||||
'Globally applied trusted applications cannot be removed from policy.',
|
||||
}
|
||||
)
|
||||
: undefined,
|
||||
toolTipPosition: 'top',
|
||||
'data-test-subj': getTestId('removeAction'),
|
||||
},
|
||||
]
|
||||
: fullDetailsAction,
|
||||
|
||||
policies: assignedPoliciesMenuItems,
|
||||
};
|
||||
|
||||
|
@ -177,7 +185,7 @@ export const PolicyTrustedAppsList = memo(() => {
|
|||
}
|
||||
|
||||
return newCardProps;
|
||||
}, [allPoliciesById, getAppUrl, getTestId, isCardExpanded, trustedAppItems]);
|
||||
}, [allPoliciesById, getAppUrl, getTestId, isCardExpanded, trustedAppItems, isPlatinumPlus]);
|
||||
|
||||
const provideCardProps = useCallback<Required<ArtifactCardGridProps>['cardComponentProps']>(
|
||||
(item) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue