[8.6] [Security Solution][Endpoint][Response Actions] Show Actions history on Endpoint Details for platinum users (#145837) (#145890)

# Backport

This will backport the following commits from `main` to `8.6`:
- [[Security Solution][Endpoint][Response Actions] Show Actions history
on Endpoint Details for platinum users
(#145837)](https://github.com/elastic/kibana/pull/145837)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"Ashokaditya","email":"1849116+ashokaditya@users.noreply.github.com"},"sourceCommit":{"committedDate":"2022-11-21T15:33:03Z","message":"[Security
Solution][Endpoint][Response Actions] Show Actions history on Endpoint
Details for platinum users (#145837)\n\n## Summary\r\n\r\nFixes a bug
where response actions history was not shown for platinum\r\nusers with
Actions Log RBAC privileges.\r\n\r\n### Checklist\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [x] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"7e5c361e3820ca9831bc25b6fb50e75a3010b318","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["backport","release_note:fix","Team:Onboarding
and Lifecycle Mgt","OLM
Sprint","v8.6.0","v8.7.0"],"number":145837,"url":"https://github.com/elastic/kibana/pull/145837","mergeCommit":{"message":"[Security
Solution][Endpoint][Response Actions] Show Actions history on Endpoint
Details for platinum users (#145837)\n\n## Summary\r\n\r\nFixes a bug
where response actions history was not shown for platinum\r\nusers with
Actions Log RBAC privileges.\r\n\r\n### Checklist\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [x] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"7e5c361e3820ca9831bc25b6fb50e75a3010b318"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/145837","number":145837,"mergeCommit":{"message":"[Security
Solution][Endpoint][Response Actions] Show Actions history on Endpoint
Details for platinum users (#145837)\n\n## Summary\r\n\r\nFixes a bug
where response actions history was not shown for platinum\r\nusers with
Actions Log RBAC privileges.\r\n\r\n### Checklist\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n\r\n### For
maintainers\r\n\r\n- [x] This was checked for breaking API changes and
was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"7e5c361e3820ca9831bc25b6fb50e75a3010b318"}}]}]
BACKPORT-->
This commit is contained in:
Ashokaditya 2022-11-21 18:42:39 +01:00 committed by GitHub
parent 6fa7cc475a
commit 87ac6522e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 56 additions and 24 deletions

View file

@ -126,6 +126,7 @@ describe('Endpoint Authz service', () => {
['canReadPolicyManagement', 'readPolicyManagement'],
['canWriteActionsLogManagement', 'writeActionsLogManagement'],
['canReadActionsLogManagement', 'readActionsLogManagement'],
['canAccessEndpointActionsLogManagement', 'readActionsLogManagement'],
['canIsolateHost', 'writeHostIsolation'],
['canUnIsolateHost', 'writeHostIsolation'],
['canKillProcess', 'writeProcessOperations'],
@ -152,6 +153,10 @@ describe('Endpoint Authz service', () => {
['canReadPolicyManagement', ['writePolicyManagement', 'readPolicyManagement']],
['canWriteActionsLogManagement', ['writeActionsLogManagement']],
['canReadActionsLogManagement', ['writeActionsLogManagement', 'readActionsLogManagement']],
[
'canAccessEndpointActionsLogManagement',
['writeActionsLogManagement', 'readActionsLogManagement'],
],
['canIsolateHost', ['writeHostIsolation']],
['canUnIsolateHost', ['writeHostIsolation']],
['canKillProcess', ['writeProcessOperations']],
@ -204,6 +209,7 @@ describe('Endpoint Authz service', () => {
canWriteSecuritySolution: false,
canReadSecuritySolution: false,
canAccessFleet: false,
canAccessEndpointActionsLogManagement: false,
canAccessEndpointManagement: false,
canCreateArtifactsByPolicy: false,
canWriteEndpointList: false,

View file

@ -47,6 +47,8 @@ function hasPermission(
* @param fleetAuthz
* @param userRoles
*/
// eslint-disable-next-line complexity
export const calculateEndpointAuthz = (
licenseService: LicenseService,
fleetAuthz: FleetAuthz,
@ -182,6 +184,7 @@ export const calculateEndpointAuthz = (
canReadPolicyManagement,
canWriteActionsLogManagement,
canReadActionsLogManagement: canReadActionsLogManagement && isEnterpriseLicense,
canAccessEndpointActionsLogManagement: canReadActionsLogManagement && isPlatinumPlusLicense,
// Response Actions
canIsolateHost: canIsolateHost && isPlatinumPlusLicense,
canUnIsolateHost: canIsolateHost,
@ -208,6 +211,7 @@ export const getEndpointAuthzInitialState = (): EndpointAuthz => {
return {
...defaultEndpointPermissions(),
canAccessFleet: false,
canAccessEndpointActionsLogManagement: false,
canAccessEndpointManagement: false,
canCreateArtifactsByPolicy: false,
canWriteEndpointList: false,

View file

@ -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 */

View file

@ -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', () => {

View file

@ -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)}
/>
)}

View file

@ -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,

View file

@ -14,6 +14,7 @@ import { createAppRootMockRenderer } from '../../../common/mock/endpoint';
import { useUserPrivileges } from '../../../common/components/user_privileges';
import { useCanSeeHostIsolationExceptionsMenu } from '../host_isolation_exceptions/view/hooks';
import { endpointPageHttpMock } from '../endpoint_hosts/mocks';
import { getUserPrivilegesMockDefaultValue } from '../../../common/components/user_privileges/__mocks__';
jest.mock('../../../common/components/user_privileges');
jest.mock('../host_isolation_exceptions/view/hooks');
@ -32,7 +33,7 @@ describe('when in the Administration tab', () => {
});
afterEach(() => {
useUserPrivilegesMock.mockReset();
useUserPrivilegesMock.mockImplementation(getUserPrivilegesMockDefaultValue);
useCanSeeHostIsolationExceptionsMenuMock.mockReset();
});
@ -100,8 +101,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 },

View file

@ -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,17 +100,16 @@ describe('Action List Route', () => {
const ctx = createRouteHandlerContext(mockScopedClient, mockSavedObjectClient);
const withLicense = license ? license : Platinum;
const withLicense = license ? license : Enterprise;
licenseEmitter.next(withLicense);
ctx.securitySolution.endpointAuthz = {
...getEndpointAuthzInitialStateMock({
canReadActionsLogManagement:
// mimicking the behavior of the EndpointAuthz class
// just so we can test the license check here
// since getEndpointAuthzInitialStateMock sets all keys to true
ctx.securitySolution.endpointAuthz.canAccessEndpointManagement &&
licenseService.isPlatinumPlus(),
// 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.isEnterprise(),
canAccessEndpointActionsLogManagement: licenseService.isPlatinumPlus(),
}),
...authz,
};
@ -137,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,

View file

@ -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)
)

View file

@ -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>;