mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution][Endpoint] Remove checks for superuser
role and instead look at fleet kibana privileges (#120027) (#120380)
* Change endpoint privileges to use fleet authz instead of checking for superuser * split user privileges react context component from hook in order to better support mocking * remove `isPlatinumPlus` from endpoint privileges and refactor to use `useUserPrivileges()` hook instead * add `endpointAuthz` to the Server API route handler context * moved fleet's `createFleetAuthzMock` to `fleet/common` # Conflicts: # x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx
This commit is contained in:
parent
a7b09bd991
commit
f89346697b
37 changed files with 496 additions and 297 deletions
|
@ -13,3 +13,4 @@ export * from './services';
|
|||
export * from './types';
|
||||
export type { FleetAuthz } from './authz';
|
||||
export { calculateAuthz } from './authz';
|
||||
export { createFleetAuthzMock } from './mocks';
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { NewPackagePolicy, PackagePolicy, DeletePackagePoliciesResponse } from './types';
|
||||
import type { DeletePackagePoliciesResponse, NewPackagePolicy, PackagePolicy } from './types';
|
||||
import type { FleetAuthz } from './authz';
|
||||
|
||||
export const createNewPackagePolicyMock = (): NewPackagePolicy => {
|
||||
return {
|
||||
|
@ -56,3 +57,27 @@ export const deletePackagePolicyMock = (): DeletePackagePoliciesResponse => {
|
|||
},
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates mock `authz` object
|
||||
*/
|
||||
export const createFleetAuthzMock = (): FleetAuthz => {
|
||||
return {
|
||||
fleet: {
|
||||
all: true,
|
||||
setup: true,
|
||||
readEnrollmentTokens: true,
|
||||
},
|
||||
integrations: {
|
||||
readPackageInfo: true,
|
||||
readInstalledPackages: true,
|
||||
installPackages: true,
|
||||
upgradePackages: true,
|
||||
removePackages: true,
|
||||
readPackageSettings: true,
|
||||
writePackageSettings: true,
|
||||
readIntegrationPolicies: true,
|
||||
writeIntegrationPolicies: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
import { of } from 'rxjs';
|
||||
|
||||
import {
|
||||
coreMock,
|
||||
elasticsearchServiceMock,
|
||||
loggingSystemMock,
|
||||
savedObjectsServiceMock,
|
||||
coreMock,
|
||||
savedObjectsClientMock,
|
||||
savedObjectsServiceMock,
|
||||
} from '../../../../../src/core/server/mocks';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/server/mocks';
|
||||
import { licensingMock } from '../../../../plugins/licensing/server/mocks';
|
||||
|
@ -21,7 +21,7 @@ import type { PackagePolicyServiceInterface } from '../services/package_policy';
|
|||
import type { AgentPolicyServiceInterface, PackageService } from '../services';
|
||||
import type { FleetAppContext } from '../plugin';
|
||||
import { createMockTelemetryEventsSender } from '../telemetry/__mocks__';
|
||||
import type { FleetAuthz } from '../../common';
|
||||
import { createFleetAuthzMock } from '../../common';
|
||||
import { agentServiceMock } from '../services/agents/agent_service.mock';
|
||||
import type { FleetRequestHandlerContext } from '../types';
|
||||
|
||||
|
@ -145,27 +145,3 @@ export const createMockPackageService = (): PackageService => {
|
|||
ensureInstalledPackage: jest.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates mock `authz` object
|
||||
*/
|
||||
export const createFleetAuthzMock = (): FleetAuthz => {
|
||||
return {
|
||||
fleet: {
|
||||
all: true,
|
||||
setup: true,
|
||||
readEnrollmentTokens: true,
|
||||
},
|
||||
integrations: {
|
||||
readPackageInfo: true,
|
||||
readInstalledPackages: true,
|
||||
installPackages: true,
|
||||
upgradePackages: true,
|
||||
removePackages: true,
|
||||
readPackageSettings: true,
|
||||
writePackageSettings: true,
|
||||
readIntegrationPolicies: true,
|
||||
writeIntegrationPolicies: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,12 +9,14 @@ import { httpServerMock, savedObjectsClientMock } from 'src/core/server/mocks';
|
|||
|
||||
import type { PostFleetSetupResponse } from '../../../common';
|
||||
import { RegistryError } from '../../errors';
|
||||
import { createAppContextStartContractMock, xpackMocks, createFleetAuthzMock } from '../../mocks';
|
||||
import { createAppContextStartContractMock, xpackMocks } from '../../mocks';
|
||||
import { agentServiceMock } from '../../services/agents/agent_service.mock';
|
||||
import { appContextService } from '../../services/app_context';
|
||||
import { setupFleet } from '../../services/setup';
|
||||
import type { FleetRequestHandlerContext } from '../../types';
|
||||
|
||||
import { createFleetAuthzMock } from '../../../common';
|
||||
|
||||
import { fleetSetupHandler } from './handlers';
|
||||
|
||||
jest.mock('../../services/setup', () => {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { calculateEndpointAuthz, getEndpointAuthzInitialState } from './authz';
|
||||
import { createFleetAuthzMock, FleetAuthz } from '../../../../../fleet/common';
|
||||
import { createLicenseServiceMock } from '../../../license/mocks';
|
||||
import type { EndpointAuthz } from '../../types/authz';
|
||||
|
||||
describe('Endpoint Authz service', () => {
|
||||
let licenseService: ReturnType<typeof createLicenseServiceMock>;
|
||||
let fleetAuthz: FleetAuthz;
|
||||
|
||||
beforeEach(() => {
|
||||
licenseService = createLicenseServiceMock();
|
||||
fleetAuthz = createFleetAuthzMock();
|
||||
});
|
||||
|
||||
describe('calculateEndpointAuthz()', () => {
|
||||
describe('and `fleet.all` access is true', () => {
|
||||
it.each<Array<keyof EndpointAuthz>>([
|
||||
['canAccessFleet'],
|
||||
['canAccessEndpointManagement'],
|
||||
['canIsolateHost'],
|
||||
])('should set `%s` to `true`', (authProperty) => {
|
||||
expect(calculateEndpointAuthz(licenseService, fleetAuthz)[authProperty]).toBe(true);
|
||||
});
|
||||
|
||||
it('should set `canIsolateHost` to false if not proper license', () => {
|
||||
licenseService.isPlatinumPlus.mockReturnValue(false);
|
||||
|
||||
expect(calculateEndpointAuthz(licenseService, fleetAuthz).canIsolateHost).toBe(false);
|
||||
});
|
||||
|
||||
it('should set `canUnIsolateHost` to true even if not proper license', () => {
|
||||
licenseService.isPlatinumPlus.mockReturnValue(false);
|
||||
|
||||
expect(calculateEndpointAuthz(licenseService, fleetAuthz).canUnIsolateHost).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and `fleet.all` access is false', () => {
|
||||
beforeEach(() => (fleetAuthz.fleet.all = false));
|
||||
|
||||
it.each<Array<keyof EndpointAuthz>>([
|
||||
['canAccessFleet'],
|
||||
['canAccessEndpointManagement'],
|
||||
['canIsolateHost'],
|
||||
])('should set `%s` to `false`', (authProperty) => {
|
||||
expect(calculateEndpointAuthz(licenseService, fleetAuthz)[authProperty]).toBe(false);
|
||||
});
|
||||
|
||||
it('should set `canUnIsolateHost` to true even if not proper license', () => {
|
||||
licenseService.isPlatinumPlus.mockReturnValue(false);
|
||||
|
||||
expect(calculateEndpointAuthz(licenseService, fleetAuthz).canUnIsolateHost).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEndpointAuthzInitialState()', () => {
|
||||
it('returns expected initial state', () => {
|
||||
expect(getEndpointAuthzInitialState()).toEqual({
|
||||
canAccessFleet: false,
|
||||
canAccessEndpointManagement: false,
|
||||
canIsolateHost: false,
|
||||
canUnIsolateHost: true,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LicenseService } from '../../../license';
|
||||
import { FleetAuthz } from '../../../../../fleet/common';
|
||||
import { EndpointAuthz } from '../../types/authz';
|
||||
|
||||
/**
|
||||
* Used by both the server and the UI to generate the Authorization for access to Endpoint related
|
||||
* functionality
|
||||
*
|
||||
* @param licenseService
|
||||
* @param fleetAuthz
|
||||
*/
|
||||
export const calculateEndpointAuthz = (
|
||||
licenseService: LicenseService,
|
||||
fleetAuthz: FleetAuthz
|
||||
): EndpointAuthz => {
|
||||
const isPlatinumPlusLicense = licenseService.isPlatinumPlus();
|
||||
const hasAllAccessToFleet = fleetAuthz.fleet.all;
|
||||
|
||||
return {
|
||||
canAccessFleet: hasAllAccessToFleet,
|
||||
canAccessEndpointManagement: hasAllAccessToFleet,
|
||||
canCreateArtifactsByPolicy: isPlatinumPlusLicense,
|
||||
canIsolateHost: isPlatinumPlusLicense && hasAllAccessToFleet,
|
||||
canUnIsolateHost: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const getEndpointAuthzInitialState = (): EndpointAuthz => {
|
||||
return {
|
||||
canAccessFleet: false,
|
||||
canAccessEndpointManagement: false,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
canIsolateHost: false,
|
||||
canUnIsolateHost: true,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { getEndpointAuthzInitialState, calculateEndpointAuthz } from './authz';
|
||||
export { getEndpointAuthzInitialStateMock } from './mocks';
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EndpointAuthz } from '../../types/authz';
|
||||
import { getEndpointAuthzInitialState } from './authz';
|
||||
|
||||
export const getEndpointAuthzInitialStateMock = (
|
||||
overrides: Partial<EndpointAuthz> = {}
|
||||
): EndpointAuthz => {
|
||||
const authz: EndpointAuthz = {
|
||||
...(
|
||||
Object.entries(getEndpointAuthzInitialState()) as Array<[keyof EndpointAuthz, boolean]>
|
||||
).reduce((mockPrivileges, [key, value]) => {
|
||||
// Invert the initial values (from `false` to `true`) so that everything is authorized
|
||||
mockPrivileges[key] = !value;
|
||||
|
||||
return mockPrivileges;
|
||||
}, {} as EndpointAuthz),
|
||||
// this one is currently treated special in that everyone can un-isolate
|
||||
canUnIsolateHost: true,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
return authz;
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set of Endpoint Specific privileges that control application authorization. This interface is
|
||||
* used both on the client and server for consistency
|
||||
*/
|
||||
export interface EndpointAuthz {
|
||||
/** If user has permissions to access Fleet */
|
||||
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 create Artifacts by Policy */
|
||||
canCreateArtifactsByPolicy: boolean;
|
||||
/** If user has permissions to isolate hosts */
|
||||
canIsolateHost: boolean;
|
||||
/** If user has permissions to un-isolate (release) hosts */
|
||||
canUnIsolateHost: boolean;
|
||||
}
|
||||
|
||||
export interface EndpointPrivileges extends EndpointAuthz {
|
||||
loading: boolean;
|
||||
}
|
|
@ -1246,3 +1246,5 @@ interface BaseListResponse<D = unknown> {
|
|||
* Returned by the server via GET /api/endpoint/metadata
|
||||
*/
|
||||
export type MetadataListResponse = BaseListResponse<HostInfo>;
|
||||
|
||||
export type { EndpointPrivileges } from './authz';
|
||||
|
|
|
@ -25,7 +25,7 @@ import { State } from '../common/store';
|
|||
import { StartServices } from '../types';
|
||||
import { PageRouter } from './routes';
|
||||
import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
|
||||
import { UserPrivilegesProvider } from '../common/components/user_privileges';
|
||||
import { UserPrivilegesProvider } from '../common/components/user_privileges/user_privileges_context';
|
||||
|
||||
interface StartAppComponent {
|
||||
children: React.ReactNode;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { initialUserPrivilegesState, UserPrivilegesState } from '../user_privileges_context';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../endpoint/mocks';
|
||||
|
||||
export const useUserPrivileges = jest.fn(() => {
|
||||
const mockedPrivileges: UserPrivilegesState = {
|
||||
...initialUserPrivilegesState(),
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
|
||||
};
|
||||
|
||||
return mockedPrivileges;
|
||||
});
|
|
@ -5,5 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './use_endpoint_privileges';
|
||||
export { useEndpointPrivileges } from './use_endpoint_privileges';
|
||||
export { getEndpointPrivilegesInitialState } from './utils';
|
||||
|
|
|
@ -5,24 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { EndpointPrivileges } from './use_endpoint_privileges';
|
||||
import { getEndpointPrivilegesInitialState } from './utils';
|
||||
import { EndpointPrivileges } from '../../../../../common/endpoint/types';
|
||||
import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz/mocks';
|
||||
|
||||
export const getEndpointPrivilegesInitialStateMock = (
|
||||
overrides: Partial<EndpointPrivileges> = {}
|
||||
): EndpointPrivileges => {
|
||||
// Get the initial state and set all permissions to `true` (enabled) for testing
|
||||
export const getEndpointPrivilegesInitialStateMock = ({
|
||||
loading = false,
|
||||
...overrides
|
||||
}: Partial<EndpointPrivileges> = {}): EndpointPrivileges => {
|
||||
const endpointPrivilegesMock: EndpointPrivileges = {
|
||||
...(
|
||||
Object.entries(getEndpointPrivilegesInitialState()) as Array<
|
||||
[keyof EndpointPrivileges, boolean]
|
||||
>
|
||||
).reduce((mockPrivileges, [key, value]) => {
|
||||
mockPrivileges[key] = !value;
|
||||
|
||||
return mockPrivileges;
|
||||
}, {} as EndpointPrivileges),
|
||||
...overrides,
|
||||
...getEndpointAuthzInitialStateMock(overrides),
|
||||
loading,
|
||||
};
|
||||
|
||||
return endpointPrivilegesMock;
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
*/
|
||||
|
||||
import { act, renderHook, RenderHookResult, RenderResult } from '@testing-library/react-hooks';
|
||||
import { useHttp, useCurrentUser } from '../../../lib/kibana';
|
||||
import { EndpointPrivileges, useEndpointPrivileges } from './use_endpoint_privileges';
|
||||
import { useCurrentUser, useKibana } from '../../../lib/kibana';
|
||||
import { useEndpointPrivileges } from './use_endpoint_privileges';
|
||||
import { securityMock } from '../../../../../../security/public/mocks';
|
||||
import { appRoutesService } from '../../../../../../fleet/common';
|
||||
import { AuthenticatedUser } from '../../../../../../security/common';
|
||||
import { licenseService } from '../../../hooks/use_license';
|
||||
import { fleetGetCheckPermissionsHttpMock } from '../../../../management/pages/mocks';
|
||||
import { getEndpointPrivilegesInitialStateMock } from './mocks';
|
||||
import { EndpointPrivileges } from '../../../../../common/endpoint/types';
|
||||
import { getEndpointPrivilegesInitialState } from './utils';
|
||||
|
||||
jest.mock('../../../lib/kibana');
|
||||
jest.mock('../../../hooks/use_license', () => {
|
||||
|
@ -32,10 +32,9 @@ const licenseServiceMock = licenseService as jest.Mocked<typeof licenseService>;
|
|||
|
||||
describe('When using useEndpointPrivileges hook', () => {
|
||||
let authenticatedUser: AuthenticatedUser;
|
||||
let fleetApiMock: ReturnType<typeof fleetGetCheckPermissionsHttpMock>;
|
||||
let result: RenderResult<EndpointPrivileges>;
|
||||
let unmount: ReturnType<typeof renderHook>['unmount'];
|
||||
let waitForNextUpdate: ReturnType<typeof renderHook>['waitForNextUpdate'];
|
||||
let releaseFleetAuthz: () => void;
|
||||
let render: () => RenderHookResult<void, EndpointPrivileges>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -45,14 +44,19 @@ describe('When using useEndpointPrivileges hook', () => {
|
|||
|
||||
(useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser);
|
||||
|
||||
fleetApiMock = fleetGetCheckPermissionsHttpMock(
|
||||
useHttp() as Parameters<typeof fleetGetCheckPermissionsHttpMock>[0]
|
||||
);
|
||||
licenseServiceMock.isPlatinumPlus.mockReturnValue(true);
|
||||
|
||||
// Add a daly to fleet service that provides authz information
|
||||
const fleetAuthz = useKibana().services.fleet!.authz;
|
||||
|
||||
// Add a delay to the fleet Authz promise to test out the `loading` property
|
||||
useKibana().services.fleet!.authz = new Promise((resolve) => {
|
||||
releaseFleetAuthz = () => resolve(fleetAuthz);
|
||||
});
|
||||
|
||||
render = () => {
|
||||
const hookRenderResponse = renderHook(() => useEndpointPrivileges());
|
||||
({ result, unmount, waitForNextUpdate } = hookRenderResponse);
|
||||
({ result, unmount } = hookRenderResponse);
|
||||
return hookRenderResponse;
|
||||
};
|
||||
});
|
||||
|
@ -62,88 +66,22 @@ describe('When using useEndpointPrivileges hook', () => {
|
|||
});
|
||||
|
||||
it('should return `loading: true` while retrieving privileges', async () => {
|
||||
// Add a daly to the API response that we can control from the test
|
||||
let releaseApiResponse: () => void;
|
||||
fleetApiMock.responseProvider.checkPermissions.mockDelay.mockReturnValue(
|
||||
new Promise<void>((resolve) => {
|
||||
releaseApiResponse = () => resolve();
|
||||
})
|
||||
);
|
||||
(useCurrentUser as jest.Mock).mockReturnValue(null);
|
||||
|
||||
const { rerender } = render();
|
||||
expect(result.current).toEqual(
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
canAccessEndpointManagement: false,
|
||||
canAccessFleet: false,
|
||||
loading: true,
|
||||
})
|
||||
);
|
||||
expect(result.current).toEqual(getEndpointPrivilegesInitialState());
|
||||
|
||||
// Make user service available
|
||||
(useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser);
|
||||
rerender();
|
||||
expect(result.current).toEqual(
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
canAccessEndpointManagement: false,
|
||||
canAccessFleet: false,
|
||||
loading: true,
|
||||
})
|
||||
);
|
||||
expect(result.current).toEqual(getEndpointPrivilegesInitialState());
|
||||
|
||||
// Release the API response
|
||||
await act(async () => {
|
||||
fleetApiMock.waitForApi();
|
||||
releaseApiResponse!();
|
||||
releaseFleetAuthz();
|
||||
await useKibana().services.fleet!.authz;
|
||||
});
|
||||
|
||||
expect(result.current).toEqual(getEndpointPrivilegesInitialStateMock());
|
||||
});
|
||||
|
||||
it('should call Fleet permissions api to determine user privilege to fleet', async () => {
|
||||
render();
|
||||
await waitForNextUpdate();
|
||||
await fleetApiMock.waitForApi();
|
||||
expect(useHttp().get as jest.Mock).toHaveBeenCalledWith(
|
||||
appRoutesService.getCheckPermissionsPath()
|
||||
);
|
||||
});
|
||||
|
||||
it('should set privileges to false if user does not have superuser role', async () => {
|
||||
authenticatedUser.roles = [];
|
||||
render();
|
||||
await waitForNextUpdate();
|
||||
await fleetApiMock.waitForApi();
|
||||
expect(result.current).toEqual(
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
canAccessEndpointManagement: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should set privileges to false if fleet api check returns failure', async () => {
|
||||
fleetApiMock.responseProvider.checkPermissions.mockReturnValue({
|
||||
error: 'MISSING_SECURITY',
|
||||
success: false,
|
||||
});
|
||||
|
||||
render();
|
||||
await waitForNextUpdate();
|
||||
await fleetApiMock.waitForApi();
|
||||
expect(result.current).toEqual(
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
canAccessEndpointManagement: false,
|
||||
canAccessFleet: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it.each([['canIsolateHost'], ['canCreateArtifactsByPolicy']])(
|
||||
'should set %s to false if license is not PlatinumPlus',
|
||||
async (privilege) => {
|
||||
licenseServiceMock.isPlatinumPlus.mockReturnValue(false);
|
||||
render();
|
||||
await waitForNextUpdate();
|
||||
expect(result.current).toEqual(expect.objectContaining({ [privilege]: false }));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -6,24 +6,14 @@
|
|||
*/
|
||||
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCurrentUser, useHttp } from '../../../lib/kibana';
|
||||
import { appRoutesService, CheckPermissionsResponse } from '../../../../../../fleet/common';
|
||||
import { useCurrentUser, useKibana } from '../../../lib/kibana';
|
||||
import { useLicense } from '../../../hooks/use_license';
|
||||
import { Immutable } from '../../../../../common/endpoint/types';
|
||||
|
||||
export interface EndpointPrivileges {
|
||||
loading: boolean;
|
||||
/** If user has permissions to access Fleet */
|
||||
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 create Artifacts by Policy */
|
||||
canCreateArtifactsByPolicy: boolean;
|
||||
/** If user has permissions to use the Host isolation feature */
|
||||
canIsolateHost: boolean;
|
||||
/** @deprecated do not use. instead, use one of the other privileges defined */
|
||||
isPlatinumPlus: boolean;
|
||||
}
|
||||
import { EndpointPrivileges, Immutable } from '../../../../../common/endpoint/types';
|
||||
import {
|
||||
calculateEndpointAuthz,
|
||||
getEndpointAuthzInitialState,
|
||||
} from '../../../../../common/endpoint/service/authz';
|
||||
import { FleetAuthz } from '../../../../../../fleet/common';
|
||||
|
||||
/**
|
||||
* Retrieve the endpoint privileges for the current user.
|
||||
|
@ -32,23 +22,39 @@ export interface EndpointPrivileges {
|
|||
* to keep API calls to a minimum.
|
||||
*/
|
||||
export const useEndpointPrivileges = (): Immutable<EndpointPrivileges> => {
|
||||
const http = useHttp();
|
||||
const user = useCurrentUser();
|
||||
const fleetServices = useKibana().services.fleet;
|
||||
const isMounted = useRef<boolean>(true);
|
||||
const isPlatinumPlusLicense = useLicense().isPlatinumPlus();
|
||||
const [canAccessFleet, setCanAccessFleet] = useState<boolean>(false);
|
||||
const licenseService = useLicense();
|
||||
const [fleetCheckDone, setFleetCheckDone] = useState<boolean>(false);
|
||||
const [fleetAuthz, setFleetAuthz] = useState<FleetAuthz | null>(null);
|
||||
|
||||
const privileges = useMemo(() => {
|
||||
const privilegeList: EndpointPrivileges = Object.freeze({
|
||||
loading: !fleetCheckDone || !user,
|
||||
...(fleetAuthz
|
||||
? calculateEndpointAuthz(licenseService, fleetAuthz)
|
||||
: getEndpointAuthzInitialState()),
|
||||
});
|
||||
|
||||
return privilegeList;
|
||||
}, [fleetCheckDone, user, fleetAuthz, licenseService]);
|
||||
|
||||
// Check if user can access fleet
|
||||
useEffect(() => {
|
||||
if (!fleetServices) {
|
||||
setFleetCheckDone(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setFleetCheckDone(false);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const fleetPermissionsResponse = await http.get<CheckPermissionsResponse>(
|
||||
appRoutesService.getCheckPermissionsPath()
|
||||
);
|
||||
const fleetAuthzForCurrentUser = await fleetServices.authz;
|
||||
|
||||
if (isMounted.current) {
|
||||
setCanAccessFleet(fleetPermissionsResponse.success);
|
||||
setFleetAuthz(fleetAuthzForCurrentUser);
|
||||
}
|
||||
} finally {
|
||||
if (isMounted.current) {
|
||||
|
@ -56,30 +62,7 @@ export const useEndpointPrivileges = (): Immutable<EndpointPrivileges> => {
|
|||
}
|
||||
}
|
||||
})();
|
||||
}, [http]);
|
||||
|
||||
// Check if user has `superuser` role
|
||||
const isSuperUser = useMemo(() => {
|
||||
if (user?.roles) {
|
||||
return user.roles.includes('superuser');
|
||||
}
|
||||
return false;
|
||||
}, [user?.roles]);
|
||||
|
||||
const privileges = useMemo(() => {
|
||||
const privilegeList: EndpointPrivileges = Object.freeze({
|
||||
loading: !fleetCheckDone || !user,
|
||||
canAccessFleet,
|
||||
canAccessEndpointManagement: canAccessFleet && isSuperUser,
|
||||
canCreateArtifactsByPolicy: isPlatinumPlusLicense,
|
||||
canIsolateHost: isPlatinumPlusLicense,
|
||||
// FIXME: Remove usages of the property below
|
||||
/** @deprecated */
|
||||
isPlatinumPlus: isPlatinumPlusLicense,
|
||||
});
|
||||
|
||||
return privilegeList;
|
||||
}, [canAccessFleet, fleetCheckDone, isSuperUser, user, isPlatinumPlusLicense]);
|
||||
}, [fleetServices]);
|
||||
|
||||
// Capture if component is unmounted
|
||||
useEffect(
|
||||
|
|
|
@ -5,15 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EndpointPrivileges } from './use_endpoint_privileges';
|
||||
import { EndpointPrivileges } from '../../../../../common/endpoint/types';
|
||||
import { getEndpointAuthzInitialState } from '../../../../../common/endpoint/service/authz';
|
||||
|
||||
export const getEndpointPrivilegesInitialState = (): EndpointPrivileges => {
|
||||
return {
|
||||
loading: true,
|
||||
canAccessFleet: false,
|
||||
canAccessEndpointManagement: false,
|
||||
canIsolateHost: false,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
isPlatinumPlus: false,
|
||||
...getEndpointAuthzInitialState(),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
import { DeepReadonly } from 'utility-types';
|
||||
import { UserPrivilegesContext, UserPrivilegesState } from './user_privileges_context';
|
||||
|
||||
export const useUserPrivileges = (): DeepReadonly<UserPrivilegesState> =>
|
||||
useContext(UserPrivilegesContext);
|
|
@ -5,16 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { DeepReadonly } from 'utility-types';
|
||||
|
||||
import { Capabilities } from '../../../../../../../src/core/public';
|
||||
import { useFetchDetectionEnginePrivileges } from '../../../detections/components/user_privileges/use_fetch_detection_engine_privileges';
|
||||
import { useFetchListPrivileges } from '../../../detections/components/user_privileges/use_fetch_list_privileges';
|
||||
import { EndpointPrivileges, useEndpointPrivileges } from './endpoint';
|
||||
|
||||
import React, { createContext, useEffect, useState } from 'react';
|
||||
import { Capabilities } from '../../../../../../../src/core/types';
|
||||
import { SERVER_APP_ID } from '../../../../common/constants';
|
||||
import { getEndpointPrivilegesInitialState } from './endpoint/utils';
|
||||
import { useFetchListPrivileges } from '../../../detections/components/user_privileges/use_fetch_list_privileges';
|
||||
import { useFetchDetectionEnginePrivileges } from '../../../detections/components/user_privileges/use_fetch_detection_engine_privileges';
|
||||
import { getEndpointPrivilegesInitialState, useEndpointPrivileges } from './endpoint';
|
||||
import { EndpointPrivileges } from '../../../../common/endpoint/types';
|
||||
|
||||
export interface UserPrivilegesState {
|
||||
listPrivileges: ReturnType<typeof useFetchListPrivileges>;
|
||||
detectionEnginePrivileges: ReturnType<typeof useFetchDetectionEnginePrivileges>;
|
||||
|
@ -28,8 +26,9 @@ export const initialUserPrivilegesState = (): UserPrivilegesState => ({
|
|||
endpointPrivileges: getEndpointPrivilegesInitialState(),
|
||||
kibanaSecuritySolutionsPrivileges: { crud: false, read: false },
|
||||
});
|
||||
|
||||
const UserPrivilegesContext = createContext<UserPrivilegesState>(initialUserPrivilegesState());
|
||||
export const UserPrivilegesContext = createContext<UserPrivilegesState>(
|
||||
initialUserPrivilegesState()
|
||||
);
|
||||
|
||||
interface UserPrivilegesProviderProps {
|
||||
kibanaCapabilities: Capabilities;
|
||||
|
@ -73,6 +72,3 @@ export const UserPrivilegesProvider = ({
|
|||
</UserPrivilegesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useUserPrivileges = (): DeepReadonly<UserPrivilegesState> =>
|
||||
useContext(UserPrivilegesContext);
|
|
@ -25,8 +25,8 @@ import {
|
|||
import { FieldHook } from '../../shared_imports';
|
||||
import { SUB_PLUGINS_REDUCER } from './utils';
|
||||
import { createSecuritySolutionStorageMock, localStorageMock } from './mock_local_storage';
|
||||
import { UserPrivilegesProvider } from '../components/user_privileges';
|
||||
import { CASES_FEATURE_ID } from '../../../common/constants';
|
||||
import { UserPrivilegesProvider } from '../components/user_privileges/user_privileges_context';
|
||||
|
||||
const state: State = mockGlobalState;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Capabilities } from 'src/core/public';
|
|||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import * as api from '../../containers/detection_engine/alerts/api';
|
||||
import { TestProviders } from '../../../common/mock/test_providers';
|
||||
import { UserPrivilegesProvider } from '../../../common/components/user_privileges';
|
||||
import { UserPrivilegesProvider } from '../../../common/components/user_privileges/user_privileges_context';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
jest.mock('../../containers/detection_engine/alerts/api');
|
||||
|
|
|
@ -8,18 +8,21 @@
|
|||
import React from 'react';
|
||||
import { act, fireEvent } from '@testing-library/react';
|
||||
import { AppContextTestRender, createAppRootMockRenderer } from '../../../common/mock/endpoint';
|
||||
import {
|
||||
EndpointPrivileges,
|
||||
useEndpointPrivileges,
|
||||
} from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
|
||||
import { SearchExceptions, SearchExceptionsProps } from '.';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../common/components/user_privileges/endpoint/mocks';
|
||||
jest.mock('../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
import {
|
||||
initialUserPrivilegesState,
|
||||
UserPrivilegesState,
|
||||
} from '../../../common/components/user_privileges/user_privileges_context';
|
||||
import { EndpointPrivileges } from '../../../../common/endpoint/types';
|
||||
|
||||
jest.mock('../../../common/components/user_privileges');
|
||||
|
||||
let onSearchMock: jest.Mock;
|
||||
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
|
||||
const mockUseUserPrivileges = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('Search exceptions', () => {
|
||||
let appTestContext: AppContextTestRender;
|
||||
|
@ -28,13 +31,16 @@ describe('Search exceptions', () => {
|
|||
props?: Partial<SearchExceptionsProps>
|
||||
) => ReturnType<AppContextTestRender['render']>;
|
||||
|
||||
const loadedUserEndpointPrivilegesState = (
|
||||
const loadedUserPrivilegesState = (
|
||||
endpointOverrides: Partial<EndpointPrivileges> = {}
|
||||
): EndpointPrivileges =>
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
isPlatinumPlus: false,
|
||||
...endpointOverrides,
|
||||
});
|
||||
): UserPrivilegesState => {
|
||||
return {
|
||||
...initialUserPrivilegesState(),
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock({
|
||||
...endpointOverrides,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onSearchMock = jest.fn();
|
||||
|
@ -51,11 +57,11 @@ describe('Search exceptions', () => {
|
|||
return renderResult;
|
||||
};
|
||||
|
||||
mockUseEndpointPrivileges.mockReturnValue(loadedUserEndpointPrivilegesState());
|
||||
mockUseUserPrivileges.mockReturnValue(loadedUserPrivilegesState());
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mockUseEndpointPrivileges.mockReset();
|
||||
mockUseUserPrivileges.mockReset();
|
||||
});
|
||||
|
||||
it('should have a default value', () => {
|
||||
|
@ -102,8 +108,8 @@ describe('Search exceptions', () => {
|
|||
it('should hide policies selector when no license', () => {
|
||||
const generator = new EndpointDocGenerator('policy-list');
|
||||
const policy = generator.generatePolicyPackagePolicy();
|
||||
mockUseEndpointPrivileges.mockReturnValue(
|
||||
loadedUserEndpointPrivilegesState({ isPlatinumPlus: false })
|
||||
mockUseUserPrivileges.mockReturnValue(
|
||||
loadedUserPrivilegesState({ canCreateArtifactsByPolicy: false })
|
||||
);
|
||||
const element = render({ policyList: [policy], hasPolicyFilter: true });
|
||||
|
||||
|
@ -113,8 +119,8 @@ describe('Search exceptions', () => {
|
|||
it('should display policies selector when right license', () => {
|
||||
const generator = new EndpointDocGenerator('policy-list');
|
||||
const policy = generator.generatePolicyPackagePolicy();
|
||||
mockUseEndpointPrivileges.mockReturnValue(
|
||||
loadedUserEndpointPrivilegesState({ isPlatinumPlus: true })
|
||||
mockUseUserPrivileges.mockReturnValue(
|
||||
loadedUserPrivilegesState({ canCreateArtifactsByPolicy: true })
|
||||
);
|
||||
const element = render({ policyList: [policy], hasPolicyFilter: true });
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/e
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { PolicySelectionItem, PoliciesSelector } from '../policies_selector';
|
||||
import { ImmutableArray, PolicyData } from '../../../../common/endpoint/types';
|
||||
import { useEndpointPrivileges } from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
||||
|
||||
export interface SearchExceptionsProps {
|
||||
defaultValue?: string;
|
||||
|
@ -34,7 +34,7 @@ export const SearchExceptions = memo<SearchExceptionsProps>(
|
|||
defaultExcludedPolicies,
|
||||
hideRefreshButton = false,
|
||||
}) => {
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges;
|
||||
const [query, setQuery] = useState<string>(defaultValue);
|
||||
const [includedPolicies, setIncludedPolicies] = useState<string>(defaultIncludedPolicies || '');
|
||||
const [excludedPolicies, setExcludedPolicies] = useState<string>(defaultExcludedPolicies || '');
|
||||
|
@ -92,7 +92,7 @@ export const SearchExceptions = memo<SearchExceptionsProps>(
|
|||
data-test-subj="searchField"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{isPlatinumPlus && hasPolicyFilter && policyList ? (
|
||||
{canCreateArtifactsByPolicy && hasPolicyFilter && policyList ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<PoliciesSelector
|
||||
policies={policyList}
|
||||
|
|
|
@ -13,11 +13,12 @@ import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'
|
|||
import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint';
|
||||
import { getHostIsolationExceptionItems } from '../service';
|
||||
import { HostIsolationExceptionsList } from './host_isolation_exceptions_list';
|
||||
import { useEndpointPrivileges } from '../../../../common/components/user_privileges/endpoint';
|
||||
import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
import { EndpointPrivileges } from '../../../../../common/endpoint/types';
|
||||
|
||||
jest.mock('../service');
|
||||
jest.mock('../../../../common/hooks/use_license');
|
||||
jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
jest.mock('../../../../common/components/user_privileges');
|
||||
|
||||
const getHostIsolationExceptionItemsMock = getHostIsolationExceptionItems as jest.Mock;
|
||||
|
||||
|
@ -27,7 +28,20 @@ describe('When on the host isolation exceptions page', () => {
|
|||
let history: AppContextTestRender['history'];
|
||||
let mockedContext: AppContextTestRender;
|
||||
|
||||
const useEndpointPrivilegesMock = useEndpointPrivileges as jest.Mock;
|
||||
const useUserPrivilegesMock = _useUserPrivileges as jest.Mock;
|
||||
|
||||
const setEndpointPrivileges = (overrides: Partial<EndpointPrivileges> = {}) => {
|
||||
const newPrivileges = _useUserPrivileges();
|
||||
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
...newPrivileges,
|
||||
endpointPrivileges: {
|
||||
...newPrivileges.endpointPrivileges,
|
||||
...overrides,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const waitForApiCall = () => {
|
||||
return waitFor(() => expect(getHostIsolationExceptionItemsMock).toHaveBeenCalled());
|
||||
};
|
||||
|
@ -162,7 +176,7 @@ describe('When on the host isolation exceptions page', () => {
|
|||
|
||||
describe('has canIsolateHost privileges', () => {
|
||||
beforeEach(async () => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({ canIsolateHost: true });
|
||||
setEndpointPrivileges({ canIsolateHost: true });
|
||||
getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock);
|
||||
});
|
||||
|
||||
|
@ -185,7 +199,7 @@ describe('When on the host isolation exceptions page', () => {
|
|||
|
||||
describe('does not have canIsolateHost privileges', () => {
|
||||
beforeEach(() => {
|
||||
useEndpointPrivilegesMock.mockReturnValue({ canIsolateHost: false });
|
||||
setEndpointPrivileges({ canIsolateHost: false });
|
||||
});
|
||||
|
||||
it('should not show the create flyout if the user navigates to the create url', () => {
|
||||
|
|
|
@ -31,11 +31,11 @@ import {
|
|||
EDIT_HOST_ISOLATION_EXCEPTION_LABEL,
|
||||
} from './components/translations';
|
||||
import { getEndpointListPath } from '../../../common/routing';
|
||||
import { useEndpointPrivileges } from '../../../../common/components/user_privileges/endpoint';
|
||||
import {
|
||||
MANAGEMENT_DEFAULT_PAGE_SIZE,
|
||||
MANAGEMENT_PAGE_SIZE_OPTIONS,
|
||||
} from '../../../common/constants';
|
||||
import { useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
|
||||
type HostIsolationExceptionPaginatedContent = PaginatedContentProps<
|
||||
Immutable<ExceptionListItemSchema>,
|
||||
|
@ -44,7 +44,7 @@ type HostIsolationExceptionPaginatedContent = PaginatedContentProps<
|
|||
|
||||
export const HostIsolationExceptionsList = () => {
|
||||
const history = useHistory();
|
||||
const privileges = useEndpointPrivileges();
|
||||
const privileges = useUserPrivileges().endpointPrivileges;
|
||||
|
||||
const location = useHostIsolationExceptionsSelector(getCurrentLocation);
|
||||
const navigateCallback = useHostIsolationExceptionsNavigateCallback();
|
||||
|
|
|
@ -10,7 +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/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
|
||||
interface CommonProps {
|
||||
policyId: string;
|
||||
|
@ -18,7 +18,7 @@ interface CommonProps {
|
|||
}
|
||||
|
||||
export const PolicyTrustedAppsEmptyUnassigned = memo<CommonProps>(({ policyId, policyName }) => {
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges;
|
||||
const navigateCallback = usePolicyDetailsNavigateCallback();
|
||||
const { onClickHandler, toRouteUrl } = useGetLinkTo(policyId, policyName);
|
||||
const onClickPrimaryButtonHandler = useCallback(
|
||||
|
@ -49,7 +49,7 @@ export const PolicyTrustedAppsEmptyUnassigned = memo<CommonProps>(({ policyId, p
|
|||
/>
|
||||
}
|
||||
actions={[
|
||||
...(isPlatinumPlus
|
||||
...(canCreateArtifactsByPolicy
|
||||
? [
|
||||
<EuiButton
|
||||
color="primary"
|
||||
|
|
|
@ -128,7 +128,7 @@ describe('Policy trusted apps layout', () => {
|
|||
it('should hide assign button on empty state with unassigned policies when downgraded to a gold or below license', async () => {
|
||||
mockUseEndpointPrivileges.mockReturnValue(
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
isPlatinumPlus: false,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
})
|
||||
);
|
||||
const component = render();
|
||||
|
@ -146,7 +146,7 @@ describe('Policy trusted apps layout', () => {
|
|||
it('should hide the `Assign trusted applications` button when there is data and the license is downgraded to gold or below', async () => {
|
||||
mockUseEndpointPrivileges.mockReturnValue(
|
||||
getEndpointPrivilegesInitialStateMock({
|
||||
isPlatinumPlus: false,
|
||||
canCreateArtifactsByPolicy: false,
|
||||
})
|
||||
);
|
||||
const component = render();
|
||||
|
|
|
@ -30,10 +30,10 @@ 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/endpoint/use_endpoint_privileges';
|
||||
import { useAppUrl } from '../../../../../../common/lib/kibana';
|
||||
import { APP_UI_ID } from '../../../../../../../common/constants';
|
||||
import { getTrustedAppsListPath } from '../../../../../common/routing';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
|
||||
export const PolicyTrustedAppsLayout = React.memo(() => {
|
||||
const { getAppUrl } = useAppUrl();
|
||||
|
@ -43,7 +43,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
const policyItem = usePolicyDetailsSelector(policyDetails);
|
||||
const navigateCallback = usePolicyDetailsNavigateCallback();
|
||||
const hasAssignedTrustedApps = usePolicyDetailsSelector(doesPolicyHaveTrustedApps);
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges;
|
||||
const totalAssignedCount = usePolicyDetailsSelector(
|
||||
getPolicyTrustedAppsListPagination
|
||||
).totalItemCount;
|
||||
|
@ -139,7 +139,9 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
</EuiText>
|
||||
</EuiPageHeaderSection>
|
||||
|
||||
<EuiPageHeaderSection>{isPlatinumPlus && assignTrustedAppButton}</EuiPageHeaderSection>
|
||||
<EuiPageHeaderSection>
|
||||
{canCreateArtifactsByPolicy && assignTrustedAppButton}
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
@ -168,7 +170,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
|||
<PolicyTrustedAppsList hideTotalShowingLabel={true} />
|
||||
)}
|
||||
</EuiPageContent>
|
||||
{isPlatinumPlus && showListFlyout ? <PolicyTrustedAppsFlyout /> : null}
|
||||
{canCreateArtifactsByPolicy && showListFlyout ? <PolicyTrustedAppsFlyout /> : null}
|
||||
</div>
|
||||
) : null;
|
||||
});
|
||||
|
|
|
@ -20,14 +20,12 @@ import {
|
|||
} from '../../../../../state';
|
||||
import { fireEvent, within, act, waitFor } from '@testing-library/react';
|
||||
import { APP_UI_ID } from '../../../../../../../common/constants';
|
||||
import {
|
||||
EndpointPrivileges,
|
||||
useEndpointPrivileges,
|
||||
} from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { EndpointPrivileges } from '../../../../../../../common/endpoint/types';
|
||||
|
||||
jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges');
|
||||
const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock;
|
||||
jest.mock('../../../../../../common/components/user_privileges');
|
||||
const mockUseUserPrivileges = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('when rendering the PolicyTrustedAppsList', () => {
|
||||
// The index (zero based) of the card created by the generator that is policy specific
|
||||
|
@ -82,11 +80,14 @@ describe('when rendering the PolicyTrustedAppsList', () => {
|
|||
};
|
||||
|
||||
afterAll(() => {
|
||||
mockUseEndpointPrivileges.mockReset();
|
||||
mockUseUserPrivileges.mockReset();
|
||||
});
|
||||
beforeEach(() => {
|
||||
appTestContext = createAppRootMockRenderer();
|
||||
mockUseEndpointPrivileges.mockReturnValue(loadedUserEndpointPrivilegesState());
|
||||
mockUseUserPrivileges.mockReturnValue({
|
||||
...mockUseUserPrivileges(),
|
||||
endpointPrivileges: loadedUserEndpointPrivilegesState(),
|
||||
});
|
||||
|
||||
mockedApis = policyDetailsPageAllApiHttpMocks(appTestContext.coreStart.http);
|
||||
appTestContext.setExperimentalFlag({ trustedAppsByPolicyEnabled: true });
|
||||
|
@ -324,11 +325,12 @@ describe('when rendering the PolicyTrustedAppsList', () => {
|
|||
});
|
||||
|
||||
it('does not show remove option in actions menu if license is downgraded to gold or below', async () => {
|
||||
mockUseEndpointPrivileges.mockReturnValue(
|
||||
loadedUserEndpointPrivilegesState({
|
||||
isPlatinumPlus: false,
|
||||
})
|
||||
);
|
||||
mockUseUserPrivileges.mockReturnValue({
|
||||
...mockUseUserPrivileges(),
|
||||
endpointPrivileges: loadedUserEndpointPrivilegesState({
|
||||
canCreateArtifactsByPolicy: false,
|
||||
}),
|
||||
});
|
||||
await render();
|
||||
await toggleCardActionMenu(POLICY_SPECIFIC_CARD_INDEX);
|
||||
|
||||
|
|
|
@ -38,7 +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/endpoint/use_endpoint_privileges';
|
||||
import { useUserPrivileges } from '../../../../../../common/components/user_privileges';
|
||||
|
||||
const DATA_TEST_SUBJ = 'policyTrustedAppsGrid';
|
||||
|
||||
|
@ -52,7 +52,7 @@ export const PolicyTrustedAppsList = memo<PolicyTrustedAppsListProps>(
|
|||
const toasts = useToasts();
|
||||
const history = useHistory();
|
||||
const { getAppUrl } = useAppUrl();
|
||||
const { isPlatinumPlus } = useEndpointPrivileges();
|
||||
const { canCreateArtifactsByPolicy } = useUserPrivileges().endpointPrivileges;
|
||||
const policyId = usePolicyDetailsSelector(policyIdFromParams);
|
||||
const hasTrustedApps = usePolicyDetailsSelector(doesPolicyHaveTrustedApps);
|
||||
const isLoading = usePolicyDetailsSelector(isPolicyTrustedAppListLoading);
|
||||
|
@ -158,7 +158,7 @@ export const PolicyTrustedAppsList = memo<PolicyTrustedAppsListProps>(
|
|||
];
|
||||
const thisTrustedAppCardProps: ArtifactCardGridCardComponentProps = {
|
||||
expanded: Boolean(isCardExpanded[trustedApp.id]),
|
||||
actions: isPlatinumPlus
|
||||
actions: canCreateArtifactsByPolicy
|
||||
? [
|
||||
...fullDetailsAction,
|
||||
{
|
||||
|
@ -194,7 +194,14 @@ export const PolicyTrustedAppsList = memo<PolicyTrustedAppsListProps>(
|
|||
}
|
||||
|
||||
return newCardProps;
|
||||
}, [allPoliciesById, getAppUrl, getTestId, isCardExpanded, trustedAppItems, isPlatinumPlus]);
|
||||
}, [
|
||||
allPoliciesById,
|
||||
getAppUrl,
|
||||
getTestId,
|
||||
isCardExpanded,
|
||||
trustedAppItems,
|
||||
canCreateArtifactsByPolicy,
|
||||
]);
|
||||
|
||||
const provideCardProps = useCallback<Required<ArtifactCardGridProps>['cardComponentProps']>(
|
||||
(item) => {
|
||||
|
|
|
@ -17,10 +17,7 @@ import {
|
|||
UseMessagesStorage,
|
||||
} from '../../common/containers/local_storage/use_messages_storage';
|
||||
import { Overview } from './index';
|
||||
import {
|
||||
initialUserPrivilegesState,
|
||||
useUserPrivileges,
|
||||
} from '../../common/components/user_privileges';
|
||||
import { useUserPrivileges } from '../../common/components/user_privileges';
|
||||
import { useSourcererDataView } from '../../common/containers/sourcerer';
|
||||
import { useFetchIndex } from '../../common/containers/source';
|
||||
import { useIsThreatIntelModuleEnabled } from '../containers/overview_cti_links/use_is_threat_intel_module_enabled';
|
||||
|
@ -30,9 +27,10 @@ import {
|
|||
mockCtiLinksResponse,
|
||||
} from '../components/overview_cti_links/mock';
|
||||
import { useCtiDashboardLinks } from '../containers/overview_cti_links';
|
||||
import { EndpointPrivileges } from '../../common/components/user_privileges/endpoint/use_endpoint_privileges';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
|
||||
import { useHostsRiskScore } from '../containers/overview_risky_host_links/use_hosts_risk_score';
|
||||
import { initialUserPrivilegesState } from '../../common/components/user_privileges/user_privileges_context';
|
||||
import { EndpointPrivileges } from '../../../common/endpoint/types';
|
||||
|
||||
jest.mock('../../common/lib/kibana');
|
||||
jest.mock('../../common/containers/source');
|
||||
|
|
|
@ -17,9 +17,8 @@ import {
|
|||
createMockAgentPolicyService,
|
||||
createMockAgentService,
|
||||
createArtifactsClientMock,
|
||||
createFleetAuthzMock,
|
||||
} from '../../../fleet/server/mocks';
|
||||
import { createMockConfig } from '../lib/detection_engine/routes/__mocks__';
|
||||
import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__';
|
||||
import {
|
||||
EndpointAppContextService,
|
||||
EndpointAppContextServiceSetupContract,
|
||||
|
@ -40,6 +39,7 @@ import { parseExperimentalConfigValue } from '../../common/experimental_features
|
|||
import { createCasesClientMock } from '../../../cases/server/client/mocks';
|
||||
import { requestContextFactoryMock } from '../request_context_factory.mock';
|
||||
import { EndpointMetadataService } from './services/metadata';
|
||||
import { createFleetAuthzMock } from '../../../fleet/common';
|
||||
|
||||
/**
|
||||
* Creates a mocked EndpointAppContext.
|
||||
|
@ -183,8 +183,7 @@ export function createRouteHandlerContext(
|
|||
dataClient: jest.Mocked<IScopedClusterClient>,
|
||||
savedObjectsClient: jest.Mocked<SavedObjectsClientContract>
|
||||
) {
|
||||
const context =
|
||||
xpackMocks.createRequestHandlerContext() as unknown as jest.Mocked<SecuritySolutionRequestHandlerContext>;
|
||||
const context = requestContextMock.create() as jest.Mocked<SecuritySolutionRequestHandlerContext>;
|
||||
context.core.elasticsearch.client = dataClient;
|
||||
context.core.savedObjects.client = savedObjectsClient;
|
||||
return context;
|
||||
|
|
|
@ -48,6 +48,7 @@ import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'
|
|||
import { legacyMetadataSearchResponseMock } from '../metadata/support/test_support';
|
||||
import { AGENT_ACTIONS_INDEX, ElasticsearchAssetType } from '../../../../../fleet/common';
|
||||
import { CasesClientMock } from '../../../../../cases/server/client/mocks';
|
||||
import { EndpointAuthz } from '../../../../common/endpoint/types/authz';
|
||||
|
||||
interface CallRouteInterface {
|
||||
body?: HostIsolationRequestBody;
|
||||
|
@ -55,6 +56,7 @@ interface CallRouteInterface {
|
|||
searchResponse?: HostMetadata;
|
||||
mockUser?: any;
|
||||
license?: License;
|
||||
authz?: Partial<EndpointAuthz>;
|
||||
}
|
||||
|
||||
const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } });
|
||||
|
@ -182,7 +184,7 @@ describe('Host Isolation', () => {
|
|||
// it returns the requestContext mock used in the call, to assert internal calls (e.g. the indexed document)
|
||||
callRoute = async (
|
||||
routePrefix: string,
|
||||
{ body, idxResponse, searchResponse, mockUser, license }: CallRouteInterface,
|
||||
{ body, idxResponse, searchResponse, mockUser, license, authz = {} }: CallRouteInterface,
|
||||
indexExists?: { endpointDsExists: boolean }
|
||||
): Promise<jest.Mocked<SecuritySolutionRequestHandlerContext>> => {
|
||||
const asUser = mockUser ? mockUser : superUser;
|
||||
|
@ -191,6 +193,12 @@ describe('Host Isolation', () => {
|
|||
);
|
||||
|
||||
const ctx = createRouteHandlerContext(mockScopedClient, mockSavedObjectClient);
|
||||
|
||||
ctx.securitySolution.endpointAuthz = {
|
||||
...ctx.securitySolution.endpointAuthz,
|
||||
...authz,
|
||||
};
|
||||
|
||||
// mock _index_template
|
||||
ctx.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = jest
|
||||
.fn()
|
||||
|
@ -206,6 +214,7 @@ describe('Host Isolation', () => {
|
|||
statusCode: 404,
|
||||
});
|
||||
});
|
||||
|
||||
const withIdxResp = idxResponse ? idxResponse : { statusCode: 201 };
|
||||
const mockIndexResponse = jest.fn().mockImplementation(() => Promise.resolve(withIdxResp));
|
||||
const mockSearchResponse = jest
|
||||
|
@ -213,19 +222,25 @@ describe('Host Isolation', () => {
|
|||
.mockImplementation(() =>
|
||||
Promise.resolve({ body: legacyMetadataSearchResponseMock(searchResponse) })
|
||||
);
|
||||
|
||||
if (indexExists) {
|
||||
ctx.core.elasticsearch.client.asInternalUser.index = mockIndexResponse;
|
||||
}
|
||||
|
||||
ctx.core.elasticsearch.client.asCurrentUser.index = mockIndexResponse;
|
||||
ctx.core.elasticsearch.client.asCurrentUser.search = mockSearchResponse;
|
||||
|
||||
const withLicense = license ? license : Platinum;
|
||||
licenseEmitter.next(withLicense);
|
||||
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ body });
|
||||
const [, routeHandler]: [
|
||||
RouteConfig<any, any, any, any>,
|
||||
RequestHandler<any, any, any, any>
|
||||
] = routerMock.post.mock.calls.find(([{ path }]) => path.startsWith(routePrefix))!;
|
||||
|
||||
await routeHandler(ctx, mockRequest, mockResponse);
|
||||
|
||||
return ctx as unknown as jest.Mocked<SecuritySolutionRequestHandlerContext>;
|
||||
};
|
||||
});
|
||||
|
@ -424,14 +439,17 @@ describe('Host Isolation', () => {
|
|||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
it('prohibits license levels less than platinum from isolating hosts', async () => {
|
||||
licenseEmitter.next(Gold);
|
||||
|
||||
it('prohibits isolating hosts if no authz for it', async () => {
|
||||
await callRoute(ISOLATE_HOST_ROUTE, {
|
||||
body: { endpoint_ids: ['XYZ'] },
|
||||
authz: { canIsolateHost: false },
|
||||
license: Gold,
|
||||
});
|
||||
|
||||
expect(mockResponse.forbidden).toBeCalled();
|
||||
});
|
||||
|
||||
it('allows any license level to unisolate', async () => {
|
||||
licenseEmitter.next(Gold);
|
||||
await callRoute(UNISOLATE_HOST_ROUTE, {
|
||||
|
@ -442,37 +460,33 @@ describe('Host Isolation', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('User Level', () => {
|
||||
it('allows superuser to perform isolation', async () => {
|
||||
const superU = { username: 'foo', roles: ['superuser'] };
|
||||
describe('User Authorization Level', () => {
|
||||
it('allows user to perform isolation when canIsolateHost is true', async () => {
|
||||
await callRoute(ISOLATE_HOST_ROUTE, {
|
||||
body: { endpoint_ids: ['XYZ'] },
|
||||
mockUser: superU,
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
it('allows superuser to perform unisolation', async () => {
|
||||
const superU = { username: 'foo', roles: ['superuser'] };
|
||||
await callRoute(UNISOLATE_HOST_ROUTE, {
|
||||
body: { endpoint_ids: ['XYZ'] },
|
||||
mockUser: superU,
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
|
||||
it('prohibits non-admin user from performing isolation', async () => {
|
||||
const superU = { username: 'foo', roles: ['user'] };
|
||||
it('allows user to perform unisolation when canUnIsolateHost is true', async () => {
|
||||
await callRoute(UNISOLATE_HOST_ROUTE, {
|
||||
body: { endpoint_ids: ['XYZ'] },
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
});
|
||||
|
||||
it('prohibits user from performing isolation if canIsolateHost is false', async () => {
|
||||
await callRoute(ISOLATE_HOST_ROUTE, {
|
||||
body: { endpoint_ids: ['XYZ'] },
|
||||
mockUser: superU,
|
||||
authz: { canIsolateHost: false },
|
||||
});
|
||||
expect(mockResponse.forbidden).toBeCalled();
|
||||
});
|
||||
it('prohibits non-admin user from performing unisolation', async () => {
|
||||
const superU = { username: 'foo', roles: ['user'] };
|
||||
|
||||
it('prohibits user from performing un-isolation if canUnIsolateHost is false', async () => {
|
||||
await callRoute(UNISOLATE_HOST_ROUTE, {
|
||||
body: { endpoint_ids: ['XYZ'] },
|
||||
mockUser: superU,
|
||||
authz: { canUnIsolateHost: false },
|
||||
});
|
||||
expect(mockResponse.forbidden).toBeCalled();
|
||||
});
|
||||
|
|
|
@ -33,7 +33,6 @@ import {
|
|||
import { getMetadataForEndpoints } from '../../services';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
import { APP_ID } from '../../../../common/constants';
|
||||
import { userCanIsolate } from '../../../../common/endpoint/actions';
|
||||
import { doLogsEndpointActionDsExists } from '../../utils';
|
||||
|
||||
/**
|
||||
|
@ -100,24 +99,19 @@ export const isolationRequestHandler = function (
|
|||
SecuritySolutionRequestHandlerContext
|
||||
> {
|
||||
return async (context, req, res) => {
|
||||
// only allow admin users
|
||||
const user = endpointContext.service.security?.authc.getCurrentUser(req);
|
||||
if (!userCanIsolate(user?.roles)) {
|
||||
const { canIsolateHost, canUnIsolateHost } = context.securitySolution.endpointAuthz;
|
||||
|
||||
// Ensure user has authorization to use this api
|
||||
if ((!canIsolateHost && isolate) || (!canUnIsolateHost && !isolate)) {
|
||||
return res.forbidden({
|
||||
body: {
|
||||
message: 'You do not have permission to perform this action',
|
||||
message:
|
||||
'You do not have permission to perform this action or license level does not allow for this action',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// isolation requires plat+
|
||||
if (isolate && !endpointContext.service.getLicenseService()?.isPlatinumPlus()) {
|
||||
return res.forbidden({
|
||||
body: {
|
||||
message: 'Your license level does not allow for this action',
|
||||
},
|
||||
});
|
||||
}
|
||||
const user = endpointContext.service.security?.authc.getCurrentUser(req);
|
||||
|
||||
// fetch the Agent IDs to send the commands to
|
||||
const endpointIDs = [...new Set(req.body.endpoint_ids)]; // dedupe
|
||||
|
|
|
@ -30,6 +30,7 @@ import type {
|
|||
SecuritySolutionApiRequestHandlerContext,
|
||||
SecuritySolutionRequestHandlerContext,
|
||||
} from '../../../../types';
|
||||
import { getEndpointAuthzInitialStateMock } from '../../../../../common/endpoint/service/authz';
|
||||
|
||||
const createMockClients = () => {
|
||||
const core = coreMock.createRequestHandlerContext();
|
||||
|
@ -93,6 +94,7 @@ const createSecuritySolutionRequestContextMock = (
|
|||
|
||||
return {
|
||||
core,
|
||||
endpointAuthz: getEndpointAuthzInitialStateMock(),
|
||||
getConfig: jest.fn(() => clients.config),
|
||||
getFrameworkRequest: jest.fn(() => {
|
||||
return {
|
||||
|
|
|
@ -17,7 +17,18 @@ import {
|
|||
SecuritySolutionPluginCoreSetupDependencies,
|
||||
SecuritySolutionPluginSetupDependencies,
|
||||
} from './plugin_contract';
|
||||
import { SecuritySolutionApiRequestHandlerContext } from './types';
|
||||
import {
|
||||
SecuritySolutionApiRequestHandlerContext,
|
||||
SecuritySolutionRequestHandlerContext,
|
||||
} from './types';
|
||||
import { Immutable } from '../common/endpoint/types';
|
||||
import { EndpointAuthz } from '../common/endpoint/types/authz';
|
||||
import {
|
||||
calculateEndpointAuthz,
|
||||
getEndpointAuthzInitialState,
|
||||
} from '../common/endpoint/service/authz';
|
||||
import { licenseService } from './lib/license';
|
||||
import { FleetAuthz } from '../../fleet/common';
|
||||
|
||||
export interface IRequestContextFactory {
|
||||
create(
|
||||
|
@ -41,7 +52,7 @@ export class RequestContextFactory implements IRequestContextFactory {
|
|||
}
|
||||
|
||||
public async create(
|
||||
context: RequestHandlerContext,
|
||||
context: Omit<SecuritySolutionRequestHandlerContext, 'securitySolution'>,
|
||||
request: KibanaRequest
|
||||
): Promise<SecuritySolutionApiRequestHandlerContext> {
|
||||
const { options, appClientFactory } = this;
|
||||
|
@ -55,9 +66,31 @@ export class RequestContextFactory implements IRequestContextFactory {
|
|||
config,
|
||||
});
|
||||
|
||||
let endpointAuthz: Immutable<EndpointAuthz>;
|
||||
let fleetAuthz: FleetAuthz;
|
||||
|
||||
// If Fleet is enabled, then get its Authz
|
||||
if (startPlugins.fleet) {
|
||||
fleetAuthz = context.fleet?.authz ?? (await startPlugins.fleet?.authz.fromRequest(request));
|
||||
}
|
||||
|
||||
return {
|
||||
core: context.core,
|
||||
|
||||
get endpointAuthz(): Immutable<EndpointAuthz> {
|
||||
// Lazy getter of endpoint Authz. No point in defining it if it is never used.
|
||||
if (!endpointAuthz) {
|
||||
// If no fleet (fleet plugin is optional in the configuration), then just turn off all permissions
|
||||
if (!startPlugins.fleet) {
|
||||
endpointAuthz = getEndpointAuthzInitialState();
|
||||
} else {
|
||||
endpointAuthz = calculateEndpointAuthz(licenseService, fleetAuthz);
|
||||
}
|
||||
}
|
||||
|
||||
return endpointAuthz;
|
||||
},
|
||||
|
||||
getConfig: () => config,
|
||||
|
||||
getFrameworkRequest: () => frameworkRequest,
|
||||
|
|
|
@ -17,10 +17,12 @@ import { AppClient } from './client';
|
|||
import { ConfigType } from './config';
|
||||
import { IRuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/types';
|
||||
import { FrameworkRequest } from './lib/framework';
|
||||
import { EndpointAuthz } from '../common/endpoint/types/authz';
|
||||
|
||||
export { AppClient };
|
||||
|
||||
export interface SecuritySolutionApiRequestHandlerContext extends RequestHandlerContext {
|
||||
endpointAuthz: EndpointAuthz;
|
||||
getConfig: () => ConfigType;
|
||||
getFrameworkRequest: () => FrameworkRequest;
|
||||
getAppClient: () => AppClient;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue