Skip to content

Commit

Permalink
[Security Solutions] Add PLI authorisation for Cases Connector (#161343)
Browse files Browse the repository at this point in the history
## Summary

* Create a new capability called `cases_connectors` which will control
the access to the cases connector feature. Note that for users to have
access to this feature they also need to be authorized for cases feature
and actions feature.
* Create a new API tag `casesGetConnectorsConfigure` to restrict access
to the Get Connectors APIs.

## Authorization

For the authorization of users we use a) a new UI capability b) a new
API access tag and c) the existing Cases RBAC. The Cases feature
privilege in Security solution is constructed based on the configuration
provided by the security serverless plugin. The UI capability, the API
tag, and the cases operations will be added/removed depending on the
configuration.

### UI capability

We include the `CASES_CONNECTORS_CAPABILITY` which will be used by the
UI to show/hide various UI components responsible for the case
connectors feature.

### APIs

There are two APIs that use connectors in Cases. The [Get Connectors
API](https://www.elastic.co/guide/en/kibana/current/case-apis.html#findCaseConnectors)
which returns all supported connectors by Cases and the [Push Case
API](https://www.elastic.co/guide/en/kibana/current/case-apis.html#pushCaseDefaultSpace)
that push a case to an external service.

#### Get Connectors API

The Get Connectors API does not interact with any of the cases' saved
objects. It uses the `actionsClient`, provided by the actions plugin, to
get all connectors and filter out the ones supported by cases. For that
reason, an API tag called `GET_CONNECTORS_CONFIGURE_API_TAG` is added to
the API to control access. If the user has access to any of the Cases
kibana privilege features (Security, Observability, or Stack) it will
have access to the API. This is an expected behavior and in the Security
serverless project, only one Case feature will be available.

#### Push Case API

The Push Case API already authorizes users by using the Cases RBAC. The
user should have the `push` operation set in the Cases Kibana feature
privilege to be able to use the API.

## Permissions

<meta charset="utf-8"><b style="font-weight:normal;"
id="docs-internal-guid-d1fea174-7fff-4f03-ed2e-9fc3ad3ed789"><div
dir="ltr" style="margin-left:0pt;" align="left">

Cases | Actions | Case Connectors | Outcome
-- | -- | -- | --
read | all | all | See the connector but cannot edit (current behavior)
read | all | none | Hide the connectors in Cases
read | read | all | See the connector but cannot edit (current behavior)
read | read | none | Hide the connectors in Cases
all | all | all | Full access
all | all | none | Hide the connectors in Cases
all | read | all | See the connector but cannot edit (current behavior)
all | read | none | Hide the connectors in Cases

</div><br /></b>

When the Actions is set to `none` all connector features are hidden

### How to test it?
#### ESS
* Run ESS and check if it still works as expected for all combinations
of cases and actions permissions.

#### Serverless
* Run Serverless with security essentials (serverless.security.yml) and
check if it works as expected for all combinations of cases and actions
permissions.

```
xpack.serverless.security.productTypes:
  [
    { product_line: 'security', product_tier: 'essentials' }
  ]


```
* Run Serverless with security complete (config/serverless.security.yml)
and check if it works as expected for all combinations of cases and
actions permissions.
```
xpack.serverless.security.productTypes:
  [
    { product_line: 'security', product_tier: 'complete' },
  ]
 
 ```



### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Christos Nasikas <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2023
1 parent 527c2d5 commit aa42bcc
Show file tree
Hide file tree
Showing 41 changed files with 478 additions and 96 deletions.
6 changes: 6 additions & 0 deletions x-pack/plugins/cases/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export const READ_CASES_CAPABILITY = 'read_cases' as const;
export const UPDATE_CASES_CAPABILITY = 'update_cases' as const;
export const DELETE_CASES_CAPABILITY = 'delete_cases' as const;
export const PUSH_CASES_CAPABILITY = 'push_cases' as const;
export const CASES_CONNECTORS_CAPABILITY = 'cases_connectors' as const;

/**
* Cases API Tags
Expand All @@ -173,6 +174,11 @@ export const SUGGEST_USER_PROFILES_API_TAG = 'casesSuggestUserProfiles';
*/
export const BULK_GET_USER_PROFILES_API_TAG = 'bulkGetUserProfiles';

/**
* This tag is registered for the connectors (configure) get API
*/
export const GET_CONNECTORS_CONFIGURE_API_TAG = 'casesGetConnectorsConfigure';

/**
* User profiles
*/
Expand Down
5 changes: 4 additions & 1 deletion x-pack/plugins/cases/common/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import type {
READ_CASES_CAPABILITY,
UPDATE_CASES_CAPABILITY,
} from '..';
import type { PUSH_CASES_CAPABILITY } from '../constants';
import type { CaseMetricsFeature, CasesMetricsResponse, SingleCaseMetricsResponse } from '../api';
import type { CASES_CONNECTORS_CAPABILITY, PUSH_CASES_CAPABILITY } from '../constants';
import type { SnakeToCamelCase } from '../types';
import type {
CaseSeverity,
Expand Down Expand Up @@ -285,6 +286,7 @@ export interface CasesPermissions {
update: boolean;
delete: boolean;
push: boolean;
connectors: boolean;
}

export interface CasesCapabilities {
Expand All @@ -293,4 +295,5 @@ export interface CasesCapabilities {
[UPDATE_CASES_CAPABILITY]: boolean;
[DELETE_CASES_CAPABILITY]: boolean;
[PUSH_CASES_CAPABILITY]: boolean;
[CASES_CONNECTORS_CAPABILITY]: boolean;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 18 additions & 3 deletions x-pack/plugins/cases/common/utils/api_tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
* 2.0.
*/

import { BULK_GET_USER_PROFILES_API_TAG, SUGGEST_USER_PROFILES_API_TAG } from '../constants';
import {
BULK_GET_USER_PROFILES_API_TAG,
GET_CONNECTORS_CONFIGURE_API_TAG,
SUGGEST_USER_PROFILES_API_TAG,
} from '../constants';
import { HttpApiTagOperation } from '../constants/types';
import type { Owner } from '../constants/types';
import { constructFilesHttpOperationTag } from '../files';
Expand All @@ -16,8 +20,19 @@ export const getApiTags = (owner: Owner) => {
const read = constructFilesHttpOperationTag(owner, HttpApiTagOperation.Read);

return {
all: [SUGGEST_USER_PROFILES_API_TAG, BULK_GET_USER_PROFILES_API_TAG, create, read] as const,
read: [SUGGEST_USER_PROFILES_API_TAG, BULK_GET_USER_PROFILES_API_TAG, read] as const,
all: [
SUGGEST_USER_PROFILES_API_TAG,
BULK_GET_USER_PROFILES_API_TAG,
GET_CONNECTORS_CONFIGURE_API_TAG,
create,
read,
] as const,
read: [
SUGGEST_USER_PROFILES_API_TAG,
BULK_GET_USER_PROFILES_API_TAG,
GET_CONNECTORS_CONFIGURE_API_TAG,
read,
] as const,
delete: [deleteTag] as const,
};
};
4 changes: 3 additions & 1 deletion x-pack/plugins/cases/common/utils/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import {
CASES_CONNECTORS_CAPABILITY,
CREATE_CASES_CAPABILITY,
DELETE_CASES_CAPABILITY,
PUSH_CASES_CAPABILITY,
Expand All @@ -23,7 +24,8 @@ export const createUICapabilities = () => ({
READ_CASES_CAPABILITY,
UPDATE_CASES_CAPABILITY,
PUSH_CASES_CAPABILITY,
CASES_CONNECTORS_CAPABILITY,
] as const,
read: [READ_CASES_CAPABILITY] as const,
read: [READ_CASES_CAPABILITY, CASES_CONNECTORS_CAPABILITY] as const,
delete: [DELETE_CASES_CAPABILITY] as const,
});
16 changes: 15 additions & 1 deletion x-pack/plugins/cases/public/client/helpers/can_use_cases.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
allCasesPermissions,
noCasesCapabilities,
noCasesPermissions,
readCasesCapabilities,
readCasesPermissions,
readCasesCapabilities,
writeCasesCapabilities,
writeCasesPermissions,
} from '../../common/mock';
Expand Down Expand Up @@ -77,6 +77,12 @@ const hasSecurityWriteAndObservabilityRead: CasesCapabilities = {
generalCases: noCasesCapabilities(),
};

const hasSecurityConnectors: CasesCapabilities = {
securitySolutionCases: readCasesCapabilities(),
observabilityCases: noCasesCapabilities(),
generalCases: noCasesCapabilities(),
};

describe('canUseCases', () => {
it.each([hasAll, hasSecurity, hasObservability, hasSecurityWriteAndObservabilityRead])(
'returns true for all permissions, if a user has access to both on any solution',
Expand Down Expand Up @@ -109,4 +115,12 @@ describe('canUseCases', () => {
expect(permissions).toStrictEqual(noCasesPermissions());
}
);

it.each([hasSecurityConnectors])(
'returns true for only connectors, if a user has access to only connectors on any solution',
(capability) => {
const permissions = canUseCases(capability)();
expect(permissions).toStrictEqual(readCasesPermissions());
}
);
});
5 changes: 4 additions & 1 deletion x-pack/plugins/cases/public/client/helpers/can_use_cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ export const canUseCases =
acc.update = acc.update || userCapabilitiesForOwner.update;
acc.delete = acc.delete || userCapabilitiesForOwner.delete;
acc.push = acc.push || userCapabilitiesForOwner.push;
const allFromAcc = acc.create && acc.read && acc.update && acc.delete && acc.push;
const allFromAcc =
acc.create && acc.read && acc.update && acc.delete && acc.push && acc.connectors;
acc.all = acc.all || userCapabilitiesForOwner.all || allFromAcc;
acc.connectors = acc.connectors || userCapabilitiesForOwner.connectors;

return acc;
},
Expand All @@ -52,6 +54,7 @@ export const canUseCases =
update: false,
delete: false,
push: false,
connectors: false,
}
);

Expand Down
31 changes: 31 additions & 0 deletions x-pack/plugins/cases/public/client/helpers/capabilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('getUICapabilities', () => {
expect(getUICapabilities(undefined)).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": false,
"create": false,
"delete": false,
"push": false,
Expand All @@ -25,6 +26,7 @@ describe('getUICapabilities', () => {
expect(getUICapabilities()).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": false,
"create": false,
"delete": false,
"push": false,
Expand All @@ -38,6 +40,7 @@ describe('getUICapabilities', () => {
expect(getUICapabilities({ create_cases: true })).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": false,
"create": true,
"delete": false,
"push": false,
Expand All @@ -55,10 +58,12 @@ describe('getUICapabilities', () => {
update_cases: false,
delete_cases: false,
push_cases: false,
cases_connectors: false,
})
).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": false,
"create": false,
"delete": false,
"push": false,
Expand All @@ -72,6 +77,7 @@ describe('getUICapabilities', () => {
expect(getUICapabilities({})).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": false,
"create": false,
"delete": false,
"push": false,
Expand All @@ -89,10 +95,35 @@ describe('getUICapabilities', () => {
update_cases: true,
delete_cases: true,
push_cases: true,
cases_connectors: true,
})
).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": true,
"create": false,
"delete": true,
"push": true,
"read": true,
"update": true,
}
`);
});

it('returns false for the all field when cases_connectors is false', () => {
expect(
getUICapabilities({
create_cases: false,
read_cases: true,
update_cases: true,
delete_cases: true,
push_cases: true,
cases_connectors: false,
})
).toMatchInlineSnapshot(`
Object {
"all": false,
"connectors": false,
"create": false,
"delete": true,
"push": true,
Expand Down
5 changes: 4 additions & 1 deletion x-pack/plugins/cases/public/client/helpers/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { CasesPermissions } from '../../../common';
import {
CASES_CONNECTORS_CAPABILITY,
CREATE_CASES_CAPABILITY,
DELETE_CASES_CAPABILITY,
PUSH_CASES_CAPABILITY,
Expand All @@ -22,7 +23,8 @@ export const getUICapabilities = (
const update = !!featureCapabilities?.[UPDATE_CASES_CAPABILITY];
const deletePriv = !!featureCapabilities?.[DELETE_CASES_CAPABILITY];
const push = !!featureCapabilities?.[PUSH_CASES_CAPABILITY];
const all = create && read && update && deletePriv && push;
const connectors = !!featureCapabilities?.[CASES_CONNECTORS_CAPABILITY];
const all = create && read && update && deletePriv && push && connectors;

return {
all,
Expand All @@ -31,5 +33,6 @@ export const getUICapabilities = (
update,
delete: deletePriv,
push,
connectors,
};
};
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/public/common/lib/kibana/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => {
update: permissions.update,
delete: permissions.delete,
push: permissions.push,
connectors: permissions.connectors,
},
visualize: { crud: !!capabilities.visualize?.save, read: !!capabilities.visualize?.show },
dashboard: {
Expand All @@ -213,6 +214,7 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => {
permissions.update,
permissions.delete,
permissions.push,
permissions.connectors,
]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const createStartServicesMock = ({ license }: StartServiceArgs = {}): Sta
update_cases: true,
delete_cases: true,
push_cases: true,
cases_connectors: true,
},
visualize: { save: true, show: true },
dashboard: { show: true, createNew: true },
Expand Down
23 changes: 21 additions & 2 deletions x-pack/plugins/cases/public/common/mock/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,39 @@ import type { CasesCapabilities, CasesPermissions } from '../../containers/types

export const allCasesPermissions = () => buildCasesPermissions();
export const noCasesPermissions = () =>
buildCasesPermissions({ read: false, create: false, update: false, delete: false, push: false });
buildCasesPermissions({
read: false,
create: false,
update: false,
delete: false,
push: false,
connectors: false,
});
export const readCasesPermissions = () =>
buildCasesPermissions({ read: true, create: false, update: false, delete: false, push: false });
buildCasesPermissions({
read: true,
create: false,
update: false,
delete: false,
push: false,
connectors: true,
});
export const noCreateCasesPermissions = () => buildCasesPermissions({ create: false });
export const noUpdateCasesPermissions = () => buildCasesPermissions({ update: false });
export const noPushCasesPermissions = () => buildCasesPermissions({ push: false });
export const noDeleteCasesPermissions = () => buildCasesPermissions({ delete: false });
export const writeCasesPermissions = () => buildCasesPermissions({ read: false });
export const onlyDeleteCasesPermission = () =>
buildCasesPermissions({ read: false, create: false, update: false, delete: true, push: false });
export const noConnectorsCasePermission = () => buildCasesPermissions({ connectors: false });

export const buildCasesPermissions = (overrides: Partial<Omit<CasesPermissions, 'all'>> = {}) => {
const create = overrides.create ?? true;
const read = overrides.read ?? true;
const update = overrides.update ?? true;
const deletePermissions = overrides.delete ?? true;
const push = overrides.push ?? true;
const connectors = overrides.connectors ?? true;
const all = create && read && update && deletePermissions && push;

return {
Expand All @@ -35,6 +51,7 @@ export const buildCasesPermissions = (overrides: Partial<Omit<CasesPermissions,
update,
delete: deletePermissions,
push,
connectors,
};
};

Expand All @@ -46,6 +63,7 @@ export const noCasesCapabilities = () =>
update_cases: false,
delete_cases: false,
push_cases: false,
cases_connectors: false,
});
export const readCasesCapabilities = () =>
buildCasesCapabilities({
Expand All @@ -67,5 +85,6 @@ export const buildCasesCapabilities = (overrides?: Partial<CasesCapabilities>) =
update_cases: overrides?.update_cases ?? true,
delete_cases: overrides?.delete_cases ?? true,
push_cases: overrides?.push_cases ?? true,
cases_connectors: overrides?.cases_connectors ?? true,
};
};
Loading

0 comments on commit aa42bcc

Please sign in to comment.