Skip to content

Commit

Permalink
[SECURITY] Add Privilege deprecations services in security plugin (#1…
Browse files Browse the repository at this point in the history
…13151)

* non-working POC for privilege deprecations

* wip to be bale to deprecated sub feature case in security solutions

* finalyze deprecations of cases sub feature in security solutions

* only adding the deprecation servces in security

* add test + translation

* Update x-pack/plugins/security/server/deprecations/privilege_deprecations.ts

Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com>

* joe reviews

* renaming + double check

Co-authored-by: Larry Gregory <larry.gregory@elastic.co>
Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
4 people authored Oct 5, 2021
1 parent 141a88b commit 77675d9
Show file tree
Hide file tree
Showing 13 changed files with 464 additions and 10 deletions.
25 changes: 25 additions & 0 deletions x-pack/plugins/security/common/model/deprecations.ts
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 PrivilegeDeprecationsService {
getKibanaRolesByFeatureId: (
args: PrivilegeDeprecationsRolesByFeatureIdRequest
) => Promise<PrivilegeDeprecationsRolesByFeatureIdResponse>;
}
5 changes: 5 additions & 0 deletions x-pack/plugins/security/common/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ export {
RoleTemplate,
RoleMapping,
} from './role_mapping';
export {
PrivilegeDeprecationsRolesByFeatureIdRequest,
PrivilegeDeprecationsRolesByFeatureIdResponse,
PrivilegeDeprecationsService,
} from './deprecations';
1 change: 1 addition & 0 deletions x-pack/plugins/security/server/authorization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export {
} from './authorization_service';
export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges';
export { CheckPrivilegesPayload } from './types';
export { transformElasticsearchRoleToRole, ElasticsearchRole } from './roles';
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import {
GLOBAL_RESOURCE,
RESERVED_PRIVILEGES_APPLICATION_WILDCARD,
} from '../../../../../common/constants';
import type { Role, RoleKibanaPrivilege } from '../../../../../common/model';
import { PrivilegeSerializer } from '../../../../authorization/privilege_serializer';
import { ResourceSerializer } from '../../../../authorization/resource_serializer';
} from '../../../common/constants';
import type { Role, RoleKibanaPrivilege } from '../../../common/model';
import { PrivilegeSerializer } from '../privilege_serializer';
import { ResourceSerializer } from '../resource_serializer';

export type ElasticsearchRole = Pick<Role, 'name' | 'metadata' | 'transient_metadata'> & {
applications: Array<{
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/security/server/authorization/roles/index.ts
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';
12 changes: 12 additions & 0 deletions x-pack/plugins/security/server/deprecations/index.ts
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 { getPrivilegeDeprecationsService } from './privilege_deprecations';
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
/*
* 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 { GetDeprecationsContext } from 'src/core/server';
import { elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks';

import { getPrivilegeDeprecationsService } from '.';
import { licenseMock } from '../../common/licensing/index.mock';

const kibanaIndexName = '.a-kibana-index';
const application = `kibana-${kibanaIndexName}`;

describe('#getPrivilegeDeprecationsService', () => {
describe('#getKibanaRolesByFeatureId', () => {
const mockAsCurrentUser = elasticsearchServiceMock.createScopedClusterClient();
const mockLicense = licenseMock.create();
const mockLogger = loggingSystemMock.createLogger();
const authz = { applicationName: application };

const { getKibanaRolesByFeatureId } = getPrivilegeDeprecationsService(
authz,
mockLicense,
mockLogger
);

it('happy path to find siem roles with feature_siem privileges', async () => {
mockAsCurrentUser.asCurrentUser.security.getRole.mockResolvedValue(
elasticsearchServiceMock.createSuccessTransportRequestPromise({
first_role: {
cluster: [],
indices: [],
applications: [
{
application,
privileges: ['feature_siem.all', 'feature_siem.cases_read'],
resources: ['space:securitySolutions'],
},
],
run_as: [],
metadata: {
_reserved: true,
},
transient_metadata: {
enabled: true,
},
},
})
);

const mockContext = {
esClient: mockAsCurrentUser,
savedObjectsClient: jest.fn(),
} as unknown as GetDeprecationsContext;

const resp = await getKibanaRolesByFeatureId({ context: mockContext, featureId: 'siem' });
expect(resp).toMatchInlineSnapshot(`
Object {
"roles": Array [
Object {
"_transform_error": Array [],
"_unrecognized_applications": Array [],
"elasticsearch": Object {
"cluster": Array [],
"indices": Array [],
"run_as": Array [],
},
"kibana": Array [
Object {
"base": Array [],
"feature": Object {
"siem": Array [
"all",
"cases_read",
],
},
"spaces": Array [
"securitySolutions",
],
},
],
"metadata": Object {
"_reserved": true,
},
"name": "first_role",
"transient_metadata": Object {
"enabled": true,
},
},
],
}
`);
});

it('happy path to find siem roles with feature_siem and feature_foo and feature_bar privileges', async () => {
mockAsCurrentUser.asCurrentUser.security.getRole.mockResolvedValue(
elasticsearchServiceMock.createSuccessTransportRequestPromise({
first_role: {
cluster: [],
indices: [],
applications: [
{
application,
privileges: [
'feature_foo.foo-privilege-1',
'feature_foo.foo-privilege-2',
'feature_bar.bar-privilege-1',
'feature_siem.all',
'feature_siem.cases_read',
],
resources: ['space:securitySolutions'],
},
],
run_as: [],
metadata: {
_reserved: true,
},
transient_metadata: {
enabled: true,
},
},
})
);

const mockContext = {
esClient: mockAsCurrentUser,
savedObjectsClient: jest.fn(),
} as unknown as GetDeprecationsContext;

const resp = await getKibanaRolesByFeatureId({ context: mockContext, featureId: 'siem' });
expect(resp).toMatchInlineSnapshot(`
Object {
"roles": Array [
Object {
"_transform_error": Array [],
"_unrecognized_applications": Array [],
"elasticsearch": Object {
"cluster": Array [],
"indices": Array [],
"run_as": Array [],
},
"kibana": Array [
Object {
"base": Array [],
"feature": Object {
"bar": Array [
"bar-privilege-1",
],
"foo": Array [
"foo-privilege-1",
"foo-privilege-2",
],
"siem": Array [
"all",
"cases_read",
],
},
"spaces": Array [
"securitySolutions",
],
},
],
"metadata": Object {
"_reserved": true,
},
"name": "first_role",
"transient_metadata": Object {
"enabled": true,
},
},
],
}
`);
});

it('happy path to NOT find siem roles with and feature_foo and feature_bar privileges', async () => {
mockAsCurrentUser.asCurrentUser.security.getRole.mockResolvedValue(
elasticsearchServiceMock.createSuccessTransportRequestPromise({
first_role: {
cluster: [],
indices: [],
applications: [
{
application,
privileges: [
'feature_foo.foo-privilege-1',
'feature_foo.foo-privilege-2',
'feature_bar.bar-privilege-1',
],
resources: ['space:securitySolutions'],
},
],
run_as: [],
metadata: {
_reserved: true,
},
transient_metadata: {
enabled: true,
},
},
})
);

const mockContext = {
esClient: mockAsCurrentUser,
savedObjectsClient: jest.fn(),
} as unknown as GetDeprecationsContext;

const resp = await getKibanaRolesByFeatureId({ context: mockContext, featureId: 'siem' });
expect(resp).toMatchInlineSnapshot(`
Object {
"roles": Array [],
}
`);
});

it('unhappy path with status code 400, we should have the attribute errors', async () => {
mockAsCurrentUser.asCurrentUser.security.getRole.mockResolvedValue(
elasticsearchServiceMock.createErrorTransportRequestPromise({
message: 'Test error',
statusCode: 400,
})
);

const mockContext = {
esClient: mockAsCurrentUser,
savedObjectsClient: jest.fn(),
} as unknown as GetDeprecationsContext;

const resp = await getKibanaRolesByFeatureId({ context: mockContext, featureId: 'siem' });
expect(resp).toMatchInlineSnapshot(`
Object {
"errors": Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"A user with the \\"manage_security\\" cluster privilege is required to perform this check.",
],
},
"level": "fetch_error",
"message": "Error retrieving roles for privilege deprecations: Test error",
"title": "Error in privilege deprecations services",
},
],
}
`);
});

it('unhappy path with status code 403, we should have unauthorized message in the attribute errors', async () => {
mockAsCurrentUser.asCurrentUser.security.getRole.mockResolvedValue(
elasticsearchServiceMock.createErrorTransportRequestPromise({
message: 'Test error',
statusCode: 403,
})
);

const mockContext = {
esClient: mockAsCurrentUser,
savedObjectsClient: jest.fn(),
} as unknown as GetDeprecationsContext;

const resp = await getKibanaRolesByFeatureId({ context: mockContext, featureId: 'siem' });
expect(resp).toMatchInlineSnapshot(`
Object {
"errors": Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"A user with the \\"manage_security\\" cluster privilege is required to perform this check.",
],
},
"level": "fetch_error",
"message": "You must have the 'manage_security' cluster privilege to fix role deprecations.",
"title": "Error in privilege deprecations services",
},
],
}
`);
});
});
});
Loading

0 comments on commit 77675d9

Please sign in to comment.