mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[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:
parent
6fa7cc475a
commit
87ac6522e3
10 changed files with 56 additions and 24 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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