mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution][Endpoint][Response Actions] Show Actions history on Endpoint Details for platinum users (#145837)
## Summary Fixes a bug where response actions history was not shown for platinum users with Actions Log RBAC privileges. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
80af40e300
commit
7e5c361e38
11 changed files with 54 additions and 19 deletions
|
@ -140,6 +140,7 @@ describe('Endpoint Authz service', () => {
|
|||
['canReadPolicyManagement', 'readPolicyManagement'],
|
||||
['canWriteActionsLogManagement', 'writeActionsLogManagement'],
|
||||
['canReadActionsLogManagement', 'readActionsLogManagement'],
|
||||
['canAccessEndpointActionsLogManagement', 'readActionsLogManagement'],
|
||||
['canIsolateHost', 'writeHostIsolation'],
|
||||
['canUnIsolateHost', 'writeHostIsolation'],
|
||||
['canKillProcess', 'writeProcessOperations'],
|
||||
|
@ -166,6 +167,10 @@ describe('Endpoint Authz service', () => {
|
|||
['canReadPolicyManagement', ['writePolicyManagement', 'readPolicyManagement']],
|
||||
['canWriteActionsLogManagement', ['writeActionsLogManagement']],
|
||||
['canReadActionsLogManagement', ['writeActionsLogManagement', 'readActionsLogManagement']],
|
||||
[
|
||||
'canAccessEndpointActionsLogManagement',
|
||||
['writeActionsLogManagement', 'readActionsLogManagement'],
|
||||
],
|
||||
['canIsolateHost', ['writeHostIsolation']],
|
||||
['canUnIsolateHost', ['writeHostIsolation']],
|
||||
['canKillProcess', ['writeProcessOperations']],
|
||||
|
@ -218,6 +223,7 @@ describe('Endpoint Authz service', () => {
|
|||
canWriteSecuritySolution: false,
|
||||
canReadSecuritySolution: false,
|
||||
canAccessFleet: false,
|
||||
canAccessEndpointActionsLogManagement: false,
|
||||
canAccessEndpointManagement: false,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
canDeleteHostIsolationExceptions: false,
|
||||
|
|
|
@ -63,6 +63,8 @@ export function hasKibanaPrivilege(
|
|||
* @param hasHostIsolationExceptionsItems if set to `true`, then Host Isolation Exceptions related authz properties
|
||||
* may be adjusted to account for a license downgrade scenario
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
export const calculateEndpointAuthz = (
|
||||
licenseService: LicenseService,
|
||||
fleetAuthz: FleetAuthz,
|
||||
|
@ -223,6 +225,7 @@ export const calculateEndpointAuthz = (
|
|||
canReadPolicyManagement,
|
||||
canWriteActionsLogManagement,
|
||||
canReadActionsLogManagement: canReadActionsLogManagement && isEnterpriseLicense,
|
||||
canAccessEndpointActionsLogManagement: canReadActionsLogManagement && isPlatinumPlusLicense,
|
||||
// Response Actions
|
||||
canIsolateHost: canIsolateHost && isPlatinumPlusLicense,
|
||||
canUnIsolateHost: canIsolateHost,
|
||||
|
@ -250,6 +253,7 @@ export const getEndpointAuthzInitialState = (): EndpointAuthz => {
|
|||
return {
|
||||
...defaultEndpointPermissions(),
|
||||
canAccessFleet: false,
|
||||
canAccessEndpointActionsLogManagement: false,
|
||||
canAccessEndpointManagement: false,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
canWriteEndpointList: false,
|
||||
|
|
|
@ -24,6 +24,8 @@ export interface EndpointAuthz extends EndpointPermissions {
|
|||
canAccessFleet: boolean;
|
||||
/** If user has permissions to access Endpoint management (includes check to ensure they also have access to fleet) */
|
||||
canAccessEndpointManagement: boolean;
|
||||
/** If user has permissions to access Actions Log management and also has a platinum license (used for endpoint details flyout) */
|
||||
canAccessEndpointActionsLogManagement: boolean;
|
||||
/** if user has permissions to create Artifacts by Policy */
|
||||
canCreateArtifactsByPolicy: boolean;
|
||||
/** if user has write permissions to endpoint list */
|
||||
|
|
|
@ -143,6 +143,7 @@ describe('When using useEndpointPrivileges hook', () => {
|
|||
getEndpointPrivilegesInitialStateMock({
|
||||
canCreateArtifactsByPolicy: false,
|
||||
canIsolateHost: false,
|
||||
canAccessEndpointActionsLogManagement: false,
|
||||
canWriteHostIsolationExceptions: false,
|
||||
canReadHostIsolationExceptions: hasHIE,
|
||||
canDeleteHostIsolationExceptions: hasHIE,
|
||||
|
|
|
@ -27,6 +27,7 @@ import { RESPONSE_ACTION_API_COMMANDS_NAMES } from '../../../../common/endpoint/
|
|||
import { useUserPrivileges as _useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { getUserPrivilegesMockDefaultValue } from '../../../common/components/user_privileges/__mocks__';
|
||||
|
||||
let mockUseGetEndpointActionList: {
|
||||
isFetched?: boolean;
|
||||
|
@ -138,8 +139,7 @@ jest.mock('../../hooks/response_actions/use_get_file_info', () => {
|
|||
|
||||
const mockUseGetEndpointsList = useGetEndpointsList as jest.Mock;
|
||||
|
||||
// FLAKY https://github.com/elastic/kibana/issues/145635
|
||||
describe.skip('Response actions history', () => {
|
||||
describe('Response actions history', () => {
|
||||
const useUserPrivilegesMock = _useUserPrivileges as jest.Mock<
|
||||
ReturnType<typeof _useUserPrivileges>
|
||||
>;
|
||||
|
@ -195,6 +195,7 @@ describe.skip('Response actions history', () => {
|
|||
...baseMockedActionList,
|
||||
};
|
||||
jest.clearAllMocks();
|
||||
useUserPrivilegesMock.mockImplementation(getUserPrivilegesMockDefaultValue);
|
||||
});
|
||||
|
||||
describe('When index does not exist yet', () => {
|
||||
|
|
|
@ -43,7 +43,7 @@ export const EndpointDetails = memo(() => {
|
|||
const policyInfo = useEndpointSelector(policyVersionInfo);
|
||||
const hostStatus = useEndpointSelector(hostStatusInfo);
|
||||
const show = useEndpointSelector(showView);
|
||||
const { canReadActionsLogManagement } = useUserPrivileges().endpointPrivileges;
|
||||
const { canAccessEndpointActionsLogManagement } = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const ContentLoadingMarkup = useMemo(
|
||||
() => (
|
||||
|
@ -82,7 +82,7 @@ export const EndpointDetails = memo(() => {
|
|||
|
||||
// show the response actions history tab
|
||||
// only when the user has the required permission
|
||||
if (canReadActionsLogManagement) {
|
||||
if (canAccessEndpointActionsLogManagement) {
|
||||
tabs.push({
|
||||
id: EndpointDetailsTabsTypes.activityLog,
|
||||
name: i18.ACTIVITY_LOG.tabTitle,
|
||||
|
@ -97,7 +97,7 @@ export const EndpointDetails = memo(() => {
|
|||
return tabs;
|
||||
},
|
||||
[
|
||||
canReadActionsLogManagement,
|
||||
canAccessEndpointActionsLogManagement,
|
||||
ContentLoadingMarkup,
|
||||
hostDetails,
|
||||
policyInfo,
|
||||
|
@ -142,7 +142,7 @@ export const EndpointDetails = memo(() => {
|
|||
hostname={hostDetails.host.hostname}
|
||||
// show overview tab if forcing response actions history
|
||||
// tab via URL without permission
|
||||
show={!canReadActionsLogManagement ? 'details' : show}
|
||||
show={!canAccessEndpointActionsLogManagement ? 'details' : show}
|
||||
tabs={getTabs(hostDetails.agent.id)}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -44,7 +44,7 @@ export const useEndpointActionItems = (
|
|||
canAccessResponseConsole,
|
||||
canIsolateHost,
|
||||
canUnIsolateHost,
|
||||
canReadActionsLogManagement,
|
||||
canAccessEndpointActionsLogManagement,
|
||||
} = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
return useMemo<ContextMenuItemNavByRouterProps[]>(() => {
|
||||
|
@ -141,7 +141,7 @@ export const useEndpointActionItems = (
|
|||
},
|
||||
]
|
||||
: []),
|
||||
...(options?.isEndpointList && canReadActionsLogManagement
|
||||
...(options?.isEndpointList && canAccessEndpointActionsLogManagement
|
||||
? [
|
||||
{
|
||||
'data-test-subj': 'actionsLink',
|
||||
|
@ -253,7 +253,7 @@ export const useEndpointActionItems = (
|
|||
}, [
|
||||
allCurrentUrlParams,
|
||||
canAccessResponseConsole,
|
||||
canReadActionsLogManagement,
|
||||
canAccessEndpointActionsLogManagement,
|
||||
endpointMetadata,
|
||||
fleetAgentPolicies,
|
||||
getAppUrl,
|
||||
|
|
|
@ -13,6 +13,7 @@ import type { AppContextTestRender } from '../../../common/mock/endpoint';
|
|||
import { createAppRootMockRenderer } from '../../../common/mock/endpoint';
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
import { endpointPageHttpMock } from '../endpoint_hosts/mocks';
|
||||
import { getUserPrivilegesMockDefaultValue } from '../../../common/components/user_privileges/__mocks__';
|
||||
|
||||
jest.mock('../../../common/components/user_privileges');
|
||||
|
||||
|
@ -29,7 +30,7 @@ describe('when in the Administration tab', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
useUserPrivilegesMock.mockReset();
|
||||
useUserPrivilegesMock.mockImplementation(getUserPrivilegesMockDefaultValue);
|
||||
});
|
||||
|
||||
describe('when the user has no permissions', () => {
|
||||
|
@ -96,8 +97,7 @@ describe('when in the Administration tab', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/145204
|
||||
describe.skip('when the user has permissions', () => {
|
||||
describe('when the user has permissions', () => {
|
||||
it('should display the Management view if user has privileges', async () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
endpointPrivileges: { loading: false, canReadEndpointList: true },
|
||||
|
|
|
@ -40,7 +40,13 @@ interface CallApiRouteInterface {
|
|||
authz?: Partial<EndpointAuthz>;
|
||||
}
|
||||
|
||||
const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } });
|
||||
const Enterprise = licenseMock.createLicense({
|
||||
license: { type: 'enterprise', mode: 'enterprise' },
|
||||
});
|
||||
|
||||
const Platinum = licenseMock.createLicense({
|
||||
license: { type: 'platinum', mode: 'platinum' },
|
||||
});
|
||||
const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' } });
|
||||
|
||||
describe('Action List Route', () => {
|
||||
|
@ -94,7 +100,7 @@ describe('Action List Route', () => {
|
|||
|
||||
const ctx = createRouteHandlerContext(mockScopedClient, mockSavedObjectClient);
|
||||
|
||||
const withLicense = license ? license : Platinum;
|
||||
const withLicense = license ? license : Enterprise;
|
||||
licenseEmitter.next(withLicense);
|
||||
|
||||
ctx.securitySolution.getEndpointAuthz.mockResolvedValue({
|
||||
|
@ -102,7 +108,8 @@ describe('Action List Route', () => {
|
|||
// mimicking the behavior of the EndpointAuthz class
|
||||
// just so we can test the license check here
|
||||
// since getEndpointAuthzInitialStateMock sets all keys to true
|
||||
canReadActionsLogManagement: licenseService.isPlatinumPlus(),
|
||||
canReadActionsLogManagement: licenseService.isEnterprise(),
|
||||
canAccessEndpointActionsLogManagement: licenseService.isPlatinumPlus(),
|
||||
}),
|
||||
...authz,
|
||||
});
|
||||
|
@ -135,13 +142,27 @@ describe('Action List Route', () => {
|
|||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
|
||||
it('does not allow user without `canReadActionsLogManagement` access for API requests', async () => {
|
||||
it('allows user with `canAccessEndpointActionsLogManagement` access for API requests', async () => {
|
||||
await callApiRoute(ENDPOINTS_ACTION_LIST_ROUTE, {
|
||||
authz: { canReadActionsLogManagement: false },
|
||||
authz: { canAccessEndpointActionsLogManagement: true },
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
|
||||
it('does not allow user without `canReadActionsLogManagement` or `canAccessEndpointActionsLogManagement` access for API requests', async () => {
|
||||
await callApiRoute(ENDPOINTS_ACTION_LIST_ROUTE, {
|
||||
authz: { canReadActionsLogManagement: false, canAccessEndpointActionsLogManagement: false },
|
||||
});
|
||||
expect(mockResponse.forbidden).toBeCalled();
|
||||
});
|
||||
|
||||
it('does allow user access to API requests if license is at least platinum', async () => {
|
||||
await callApiRoute(ENDPOINTS_ACTION_LIST_ROUTE, {
|
||||
license: Platinum,
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
|
||||
it('does not allow user access to API requests if license is below platinum', async () => {
|
||||
await callApiRoute(ENDPOINTS_ACTION_LIST_ROUTE, {
|
||||
license: Gold,
|
||||
|
|
|
@ -33,7 +33,7 @@ export function registerActionListRoutes(
|
|||
options: { authRequired: true, tags: ['access:securitySolution'] },
|
||||
},
|
||||
withEndpointAuthz(
|
||||
{ all: ['canReadActionsLogManagement'] },
|
||||
{ any: ['canReadActionsLogManagement', 'canAccessEndpointActionsLogManagement'] },
|
||||
endpointContext.logFactory.get('endpointActionList'),
|
||||
actionListHandler(endpointContext)
|
||||
)
|
||||
|
|
|
@ -38,7 +38,7 @@ jest.mock('../../services');
|
|||
const mockGetActionList = getActionList as jest.Mock;
|
||||
const mockGetActionListByStatus = getActionListByStatus as jest.Mock;
|
||||
|
||||
describe(' Action List Handler', () => {
|
||||
describe('Action List Handler', () => {
|
||||
let endpointAppContextService: EndpointAppContextService;
|
||||
let mockResponse: jest.Mocked<KibanaResponseFactory>;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue