-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[SECURITY] Deprecated sub feature cases in security solutions #112695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
558089b
d36f13c
9c2f036
398b5a9
58ac232
6bf8264
f578bb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * 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. | ||
| */ | ||
|
|
||
| // eslint-disable-next-line @kbn/eslint/no-restricted-paths | ||
| import type { DeprecationsDetails, GetDeprecationsContext } from '../../../../../src/core/server'; | ||
| import type { Role } from './role'; | ||
|
|
||
| export interface PrivilegeDeprecationsRolesByFeatureIdResponse { | ||
| roles?: Role[]; | ||
| errors?: DeprecationsDetails[]; | ||
| } | ||
|
|
||
| export interface PrivilegeDeprecationsRolesByFeatureIdRequest { | ||
| context: GetDeprecationsContext; | ||
| featureId: string; | ||
| } | ||
| export interface PrivilegeDeprecationsServices { | ||
| getKibanaRolesByFeatureId: ( | ||
| args: PrivilegeDeprecationsRolesByFeatureIdRequest | ||
| ) => Promise<PrivilegeDeprecationsRolesByFeatureIdResponse>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /* | ||
| * 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 { transformElasticsearchRoleToRole, ElasticsearchRole } from './elasticsearch_role'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| /* | ||
| * 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. | ||
| */ | ||
|
|
||
| /** | ||
| * getKibanaRolesByFeature | ||
| */ | ||
|
|
||
| export { getPrivilegeDeprecationsServices } from './privilege_deprecations'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| /* | ||
| * 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 type { SecurityLicense } from '../../common/licensing'; | ||
| import type { | ||
| PrivilegeDeprecationsRolesByFeatureIdRequest, | ||
| PrivilegeDeprecationsRolesByFeatureIdResponse, | ||
| } from '../../common/model'; | ||
| import { transformElasticsearchRoleToRole } from '../authorization'; | ||
| import type { AuthorizationServiceSetupInternal, ElasticsearchRole } from '../authorization'; | ||
| import { getDetailedErrorMessage, getErrorStatusCode } from '../errors'; | ||
|
|
||
| export const getPrivilegeDeprecationsServices = ( | ||
| authz: AuthorizationServiceSetupInternal, | ||
| license: SecurityLicense | ||
| ) => { | ||
| const getKibanaRolesByFeatureId = async ({ | ||
| context, | ||
| featureId, | ||
| }: PrivilegeDeprecationsRolesByFeatureIdRequest): Promise<PrivilegeDeprecationsRolesByFeatureIdResponse> => { | ||
| // Nothing to do if security is disabled | ||
| if (!license.isEnabled()) { | ||
| return { | ||
| roles: [], | ||
| }; | ||
| } | ||
| let kibanaRoles; | ||
| try { | ||
| const { body: elasticsearchRoles } = await context.esClient.asCurrentUser.security.getRole< | ||
| Record<string, ElasticsearchRole> | ||
| >(); | ||
|
|
||
| kibanaRoles = Object.entries(elasticsearchRoles).map(([roleName, elasticsearchRole]) => | ||
| transformElasticsearchRoleToRole( | ||
| // @ts-expect-error @elastic/elasticsearch `XPackRole` type doesn't define `applications` and `transient_metadata`. | ||
| elasticsearchRole, | ||
| roleName, | ||
| authz.applicationName | ||
| ) | ||
| ); | ||
| } catch (e) { | ||
| const statusCode = getErrorStatusCode(e); | ||
| const isUnauthorized = statusCode === 403; | ||
| const message = isUnauthorized | ||
| ? `You must have the 'manage_security' cluster privilege to fix role deprecations.` | ||
| : `Error retrieving roles for privilege deprecations: ${getDetailedErrorMessage(e)}`; | ||
|
|
||
| return { | ||
| errors: [ | ||
| { | ||
| title: 'title', | ||
| level: 'fetch_error', | ||
| message, | ||
| correctiveActions: { | ||
| manualSteps: [ | ||
| 'A user with the "manage_security" cluster privilege is required to perform this check.', | ||
| ], | ||
| }, | ||
| }, | ||
| ], | ||
| }; | ||
| } | ||
| return { | ||
| roles: kibanaRoles.filter((role) => | ||
| role.kibana.find((privilege) => Object.hasOwnProperty.call(privilege.feature, featureId)) | ||
| ), | ||
| }; | ||
| }; | ||
| return Object.freeze({ | ||
| getKibanaRolesByFeatureId, | ||
| }); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| /* | ||
| * 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 { updateSecuritySolutionPrivileges } from '.'; | ||
|
|
||
| describe('deprecations', () => { | ||
| describe('create cases privileges from siem privileges without cases sub-feature', () => { | ||
| test('should be empty if siem privileges is an empty array', () => { | ||
| expect(updateSecuritySolutionPrivileges([])).toMatchInlineSnapshot(`Object {}`); | ||
| }); | ||
|
|
||
| test('siem privileges === ["all"]', () => { | ||
XavierM marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| expect(updateSecuritySolutionPrivileges(['all'])).toMatchInlineSnapshot(` | ||
| Object { | ||
| "securitySolutionCases": Array [ | ||
| "all", | ||
| ], | ||
| "siem": Array [ | ||
| "all", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
|
|
||
| test('siem privileges === ["read"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['read'])).toMatchInlineSnapshot(` | ||
| Object { | ||
| "securitySolutionCases": Array [ | ||
| "read", | ||
| ], | ||
| "siem": Array [ | ||
| "read", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
| }); | ||
|
|
||
| describe('create cases privileges from siem privileges with cases sub-feature', () => { | ||
| test('should be empty if siem privileges is an empty array', () => { | ||
| expect(updateSecuritySolutionPrivileges([])).toMatchInlineSnapshot(`Object {}`); | ||
XavierM marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }); | ||
|
|
||
| test('siem privileges === ["minimal_all"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['minimal_all'])).toMatchInlineSnapshot(` | ||
| Object { | ||
| "siem": Array [ | ||
| "all", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
|
|
||
| test('siem privileges === ["minimal_all", "cases_read"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['minimal_all', 'cases_read'])) | ||
| .toMatchInlineSnapshot(` | ||
| Object { | ||
| "securitySolutionCases": Array [ | ||
| "read", | ||
| ], | ||
| "siem": Array [ | ||
| "all", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
|
|
||
| test('siem privileges === ["minimal_all", "cases_all"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['minimal_all', 'cases_all'])).toMatchInlineSnapshot(` | ||
| Object { | ||
| "securitySolutionCases": Array [ | ||
| "all", | ||
| ], | ||
| "siem": Array [ | ||
| "all", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
|
|
||
| test('siem privileges === ["minimal_read"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['minimal_read'])).toMatchInlineSnapshot(` | ||
| Object { | ||
| "siem": Array [ | ||
| "read", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
|
|
||
| test('siem privileges === ["minimal_read", "cases_read"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['minimal_read', 'cases_read'])) | ||
| .toMatchInlineSnapshot(` | ||
| Object { | ||
| "securitySolutionCases": Array [ | ||
| "read", | ||
| ], | ||
| "siem": Array [ | ||
| "read", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
|
|
||
| test('siem privileges === ["minimal_read", "cases_all"]', () => { | ||
| expect(updateSecuritySolutionPrivileges(['minimal_read', 'cases_all'])) | ||
| .toMatchInlineSnapshot(` | ||
| Object { | ||
| "securitySolutionCases": Array [ | ||
| "all", | ||
| ], | ||
| "siem": Array [ | ||
| "read", | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
| }); | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also imagine we can have
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will add them just in case |
||


Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to convert that to i18n and the other ones