From 94b19672c7ee391c4479d7e6f6da992b76bc982a Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 3 Jul 2019 09:53:48 -0400 Subject: [PATCH 1/3] use ES builtin privileges API for role management --- .../common/model/builtin_es_privileges.ts | 10 +++ .../plugins/security/common/model/index.ts | 1 + x-pack/legacy/plugins/security/index.js | 2 + .../public/services/role_privileges.js | 57 ------------- .../components/edit_role_page.test.tsx | 79 +++++++++++++------ .../edit_role/components/edit_role_page.tsx | 13 ++- .../cluster_privileges.test.tsx.snap | 65 +-------------- .../elasticsearch_privileges.test.tsx.snap | 15 ++++ .../index_privilege_form.test.tsx.snap | 35 +------- .../privileges/es/cluster_privileges.test.tsx | 8 +- .../privileges/es/cluster_privileges.tsx | 7 +- .../es/elasticsearch_privileges.test.tsx | 12 +++ .../es/elasticsearch_privileges.tsx | 11 ++- .../es/index_privilege_form.test.tsx | 4 + .../privileges/es/index_privilege_form.tsx | 5 +- .../privileges/es/index_privileges.test.tsx | 2 + .../privileges/es/index_privileges.tsx | 9 ++- .../views/management/edit_role/index.js | 11 ++- .../routes/api/v1/builtin_privileges.ts | 25 ++++++ .../legacy/server/lib/esjs_shield_plugin.js | 9 +++ .../apis/security/builtin_es_privileges.ts | 40 ++++++++++ .../api_integration/apis/security/index.js | 1 + 22 files changed, 224 insertions(+), 197 deletions(-) create mode 100644 x-pack/legacy/plugins/security/common/model/builtin_es_privileges.ts delete mode 100644 x-pack/legacy/plugins/security/public/services/role_privileges.js create mode 100644 x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts create mode 100644 x-pack/test/api_integration/apis/security/builtin_es_privileges.ts diff --git a/x-pack/legacy/plugins/security/common/model/builtin_es_privileges.ts b/x-pack/legacy/plugins/security/common/model/builtin_es_privileges.ts new file mode 100644 index 0000000000000..373d5df31bcec --- /dev/null +++ b/x-pack/legacy/plugins/security/common/model/builtin_es_privileges.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface BuiltinESPrivileges { + cluster: string[]; + index: string[]; +} diff --git a/x-pack/legacy/plugins/security/common/model/index.ts b/x-pack/legacy/plugins/security/common/model/index.ts index 83648cfb59a67..6557db90a6207 100644 --- a/x-pack/legacy/plugins/security/common/model/index.ts +++ b/x-pack/legacy/plugins/security/common/model/index.ts @@ -10,3 +10,4 @@ export { RawKibanaPrivileges, RawKibanaFeaturePrivileges } from './raw_kibana_pr export { KibanaPrivileges } from './kibana_privileges'; export { User, EditUser, getUserDisplayName } from './user'; export { AuthenticatedUser, canUserChangePassword } from './authenticated_user'; +export { BuiltinESPrivileges } from './builtin_es_privileges'; diff --git a/x-pack/legacy/plugins/security/index.js b/x-pack/legacy/plugins/security/index.js index 250c8729b5466..196cedfa8dc36 100644 --- a/x-pack/legacy/plugins/security/index.js +++ b/x-pack/legacy/plugins/security/index.js @@ -11,6 +11,7 @@ import { initUsersApi } from './server/routes/api/v1/users'; import { initExternalRolesApi } from './server/routes/api/external/roles'; import { initPrivilegesApi } from './server/routes/api/external/privileges'; import { initIndicesApi } from './server/routes/api/v1/indices'; +import { initGetBuiltinPrivilegesApi } from './server/routes/api/v1/builtin_privileges'; import { initOverwrittenSessionView } from './server/routes/views/overwritten_session'; import { initLoginView } from './server/routes/views/login'; import { initLogoutView } from './server/routes/views/logout'; @@ -212,6 +213,7 @@ export const security = (kibana) => new kibana.Plugin({ initExternalRolesApi(server); initIndicesApi(server); initPrivilegesApi(server); + initGetBuiltinPrivilegesApi(server); initLoginView(server, xpackMainPlugin); initLogoutView(server); initLoggedOutView(server); diff --git a/x-pack/legacy/plugins/security/public/services/role_privileges.js b/x-pack/legacy/plugins/security/public/services/role_privileges.js deleted file mode 100644 index e9e3dbf729a2c..0000000000000 --- a/x-pack/legacy/plugins/security/public/services/role_privileges.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -const clusterPrivileges = [ - 'all', - 'monitor', - 'manage', - 'manage_security', - 'manage_index_templates', - 'manage_pipeline', - 'manage_ingest_pipelines', - 'transport_client', - 'manage_ml', - 'monitor_ml', - 'manage_data_frame_transforms', - 'monitor_data_frame_transforms', - 'manage_watcher', - 'monitor_watcher', - 'read_ccr', - 'manage_ccr', - 'manage_ilm', - 'read_ilm', - 'monitor_rollup', - 'manage_rollup', - 'manage_token', - 'manage_saml', - 'create_snapshot', - 'manage_oidc', -]; -const indexPrivileges = [ - 'all', - 'manage', - 'monitor', - 'read', - 'index', - 'create', - 'delete', - 'write', - 'delete_index', - 'create_index', - 'view_index_metadata', - 'read_cross_cluster', - 'manage_follow_index', - 'manage_ilm', - 'manage_leader_index', -]; - -export function getClusterPrivileges() { - return [...clusterPrivileges]; -} - -export function getIndexPrivileges() { - return [...indexPrivileges]; -} diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx index 94206c99b5954..80c8c079b9001 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.test.tsx @@ -65,6 +65,13 @@ const buildRawKibanaPrivileges = () => { return privilegesFactory(actions, xpackMainPlugin as any).get(); }; +const buildBuiltinESPrivileges = () => { + return { + cluster: ['all', 'manage', 'monitor'], + index: ['all', 'read', 'write', 'index'], + }; +}; + const buildUICapabilities = (canManageSpaces = true) => { return { catalogue: {}, @@ -132,7 +139,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const spaces: Space[] = buildSpaces(); const uiCapabilities: UICapabilities = buildUICapabilities(); @@ -146,7 +154,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={spaces} spacesEnabled={true} uiCapabilities={uiCapabilities} @@ -180,7 +189,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const spaces: Space[] = buildSpaces(); const uiCapabilities: UICapabilities = buildUICapabilities(); @@ -194,7 +204,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={spaces} spacesEnabled={true} uiCapabilities={uiCapabilities} @@ -222,7 +233,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const spaces: Space[] = buildSpaces(); const uiCapabilities: UICapabilities = buildUICapabilities(); @@ -236,7 +248,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={spaces} spacesEnabled={true} uiCapabilities={uiCapabilities} @@ -280,7 +293,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const spaces: Space[] = buildSpaces(); const uiCapabilities: UICapabilities = buildUICapabilities(); @@ -294,7 +308,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={spaces} spacesEnabled={true} uiCapabilities={uiCapabilities} @@ -327,7 +342,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const spaces: Space[] = buildSpaces(); const uiCapabilities: UICapabilities = buildUICapabilities(false); @@ -341,7 +357,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={spaces} spacesEnabled={true} uiCapabilities={uiCapabilities} @@ -374,7 +391,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const spaces: Space[] = buildSpaces(); const uiCapabilities: UICapabilities = buildUICapabilities(false); @@ -388,7 +406,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={spaces} spacesEnabled={true} uiCapabilities={uiCapabilities} @@ -424,7 +443,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const uiCapabilities: UICapabilities = buildUICapabilities(); const wrapper = mountWithIntl( @@ -437,7 +457,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={[]} spacesEnabled={false} uiCapabilities={uiCapabilities} @@ -471,7 +492,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const uiCapabilities: UICapabilities = buildUICapabilities(); const wrapper = mountWithIntl( @@ -484,7 +506,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={[]} spacesEnabled={false} uiCapabilities={uiCapabilities} @@ -512,7 +535,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const uiCapabilities: UICapabilities = buildUICapabilities(); const wrapper = mountWithIntl( @@ -525,7 +549,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={[]} spacesEnabled={false} uiCapabilities={uiCapabilities} @@ -568,7 +593,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const uiCapabilities: UICapabilities = buildUICapabilities(); const wrapper = mountWithIntl( @@ -581,7 +607,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={[]} spacesEnabled={false} uiCapabilities={uiCapabilities} @@ -613,7 +640,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const uiCapabilities: UICapabilities = buildUICapabilities(false); const wrapper = mountWithIntl( @@ -626,7 +654,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={[]} spacesEnabled={false} uiCapabilities={uiCapabilities} @@ -659,7 +688,8 @@ describe('', () => { const features: Feature[] = buildFeatures(); const mockHttpClient = jest.fn(); const indexPatterns: string[] = ['foo*', 'bar*']; - const privileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const kibanaPrivileges: RawKibanaPrivileges = buildRawKibanaPrivileges(); + const builtinESPrivileges = buildBuiltinESPrivileges(); const uiCapabilities: UICapabilities = buildUICapabilities(false); const wrapper = mountWithIntl( @@ -672,7 +702,8 @@ describe('', () => { features={features} httpClient={mockHttpClient} indexPatterns={indexPatterns} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} spaces={[]} spacesEnabled={false} uiCapabilities={uiCapabilities} diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx index 2ff3aba2cae5b..9996427f59b24 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/edit_role_page.tsx @@ -24,7 +24,12 @@ import { UICapabilities } from 'ui/capabilities'; import { toastNotifications } from 'ui/notify'; import { Space } from '../../../../../../spaces/common/model/space'; import { Feature } from '../../../../../../xpack_main/types'; -import { KibanaPrivileges, RawKibanaPrivileges, Role } from '../../../../../common/model'; +import { + KibanaPrivileges, + RawKibanaPrivileges, + Role, + BuiltinESPrivileges, +} from '../../../../../common/model'; import { isReadOnlyRole, isReservedRole, @@ -46,7 +51,8 @@ interface Props { httpClient: any; allowDocumentLevelSecurity: boolean; allowFieldLevelSecurity: boolean; - privileges: RawKibanaPrivileges; + kibanaPrivileges: RawKibanaPrivileges; + builtinESPrivileges: BuiltinESPrivileges; spaces?: Space[]; spacesEnabled: boolean; intl: InjectedIntl; @@ -246,6 +252,7 @@ class EditRolePageUI extends Component { runAsUsers={this.props.runAsUsers} validator={this.validator} indexPatterns={this.props.indexPatterns} + builtinESPrivileges={this.props.builtinESPrivileges} allowDocumentLevelSecurity={this.props.allowDocumentLevelSecurity} allowFieldLevelSecurity={this.props.allowFieldLevelSecurity} /> @@ -264,7 +271,7 @@ class EditRolePageUI extends Component {
{ kibana: [], }; - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/cluster_privileges.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/cluster_privileges.tsx index 7901fb9ff7fb4..b6bd1930a1d27 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/cluster_privileges.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/cluster_privileges.tsx @@ -8,19 +8,16 @@ import { EuiComboBox, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { Component } from 'react'; import { Role } from '../../../../../../../common/model'; import { isReadOnlyRole } from '../../../../../../lib/role_utils'; -// @ts-ignore -import { getClusterPrivileges } from '../../../../../../services/role_privileges'; interface Props { role: Role; + availableClusterPrivileges: string[]; onChange: (privs: string[]) => void; } export class ClusterPrivileges extends Component { public render() { - const clusterPrivileges = getClusterPrivileges(); - - return {this.buildComboBox(clusterPrivileges)}; + return {this.buildComboBox(this.props.availableClusterPrivileges)}; } public buildComboBox = (items: string[]) => { diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx index ccdbf5a9f3555..5ba3d1daf61ac 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.test.tsx @@ -30,6 +30,10 @@ test('it renders without crashing', () => { allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, validator: new RoleValidator(), + builtinESPrivileges: { + cluster: ['all', 'manage', 'monitor'], + index: ['all', 'read', 'write', 'index'], + }, }; const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); @@ -54,6 +58,10 @@ test('it renders ClusterPrivileges', () => { allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, validator: new RoleValidator(), + builtinESPrivileges: { + cluster: ['all', 'manage', 'monitor'], + index: ['all', 'read', 'write', 'index'], + }, }; const wrapper = mountWithIntl(); expect(wrapper.find(ClusterPrivileges)).toHaveLength(1); @@ -78,6 +86,10 @@ test('it renders IndexPrivileges', () => { allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, validator: new RoleValidator(), + builtinESPrivileges: { + cluster: ['all', 'manage', 'monitor'], + index: ['all', 'read', 'write', 'index'], + }, }; const wrapper = mountWithIntl(); expect(wrapper.find(IndexPrivileges)).toHaveLength(1); diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx index 1d450bd868b11..7540cf2cedab7 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/elasticsearch_privileges.tsx @@ -19,7 +19,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; -import { Role } from '../../../../../../../common/model'; +import { Role, BuiltinESPrivileges } from '../../../../../../../common/model'; // @ts-ignore import { documentationLinks } from '../../../../../../documentation_links'; import { RoleValidator } from '../../../lib/validate_role'; @@ -35,6 +35,7 @@ interface Props { onChange: (role: Role) => void; runAsUsers: string[]; validator: RoleValidator; + builtinESPrivileges: BuiltinESPrivileges; indexPatterns: string[]; allowDocumentLevelSecurity: boolean; allowFieldLevelSecurity: boolean; @@ -59,6 +60,7 @@ export class ElasticsearchPrivileges extends Component { indexPatterns, allowDocumentLevelSecurity, allowFieldLevelSecurity, + builtinESPrivileges, } = this.props; const indexProps = { @@ -69,6 +71,7 @@ export class ElasticsearchPrivileges extends Component { allowDocumentLevelSecurity, allowFieldLevelSecurity, onChange, + availableIndexPrivileges: builtinESPrivileges.index, }; return ( @@ -93,7 +96,11 @@ export class ElasticsearchPrivileges extends Component { } > - + diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx index 136222e920362..6d386fd78a11b 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.test.tsx @@ -22,6 +22,7 @@ test('it renders without crashing', () => { formIndex: 0, indexPatterns: [], availableFields: [], + availableIndexPrivileges: ['all', 'read', 'write', 'index'], isReadOnlyRole: false, allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, @@ -48,6 +49,7 @@ describe('delete button', () => { formIndex: 0, indexPatterns: [], availableFields: [], + availableIndexPrivileges: ['all', 'read', 'write', 'index'], isReadOnlyRole: false, allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, @@ -99,6 +101,7 @@ describe(`document level security`, () => { formIndex: 0, indexPatterns: [], availableFields: [], + availableIndexPrivileges: ['all', 'read', 'write', 'index'], isReadOnlyRole: false, allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, @@ -157,6 +160,7 @@ describe('field level security', () => { formIndex: 0, indexPatterns: [], availableFields: [], + availableIndexPrivileges: ['all', 'read', 'write', 'index'], isReadOnlyRole: false, allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx index 2a64227bba0e8..bafc56dc167ea 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privilege_form.tsx @@ -20,8 +20,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { ChangeEvent, Component, Fragment } from 'react'; import { RoleIndexPrivilege } from '../../../../../../../common/model'; -// @ts-ignore -import { getIndexPrivileges } from '../../../../../../services/role_privileges'; import { RoleValidator } from '../../../lib/validate_role'; const fromOption = (option: any) => option.label; @@ -31,6 +29,7 @@ interface Props { formIndex: number; indexPrivilege: RoleIndexPrivilege; indexPatterns: string[]; + availableIndexPrivileges: string[]; availableFields: string[]; onChange: (indexPrivilege: RoleIndexPrivilege) => void; onDelete: () => void; @@ -126,7 +125,7 @@ export class IndexPrivilegeForm extends Component { > { allowFieldLevelSecurity: true, editable: true, validator: new RoleValidator(), + availableIndexPrivileges: ['all', 'read', 'write', 'index'], }; const wrapper = shallowWithIntl(); expect(wrapper).toMatchSnapshot(); @@ -60,6 +61,7 @@ test('it renders a IndexPrivilegeForm for each privilege on the role', () => { allowDocumentLevelSecurity: true, allowFieldLevelSecurity: true, validator: new RoleValidator(), + availableIndexPrivileges: ['all', 'read', 'write', 'index'], }; const wrapper = mountWithIntl(); expect(wrapper.find(IndexPrivilegeForm)).toHaveLength(1); diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx index 7b7934fb3413a..1f54a5aacf948 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/components/privileges/es/index_privileges.tsx @@ -14,6 +14,7 @@ import { IndexPrivilegeForm } from './index_privilege_form'; interface Props { role: Role; indexPatterns: string[]; + availableIndexPrivileges: string[]; allowDocumentLevelSecurity: boolean; allowFieldLevelSecurity: boolean; httpClient: any; @@ -42,7 +43,12 @@ export class IndexPrivileges extends Component { public render() { const { indices = [] } = this.props.role.elasticsearch; - const { indexPatterns, allowDocumentLevelSecurity, allowFieldLevelSecurity } = this.props; + const { + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity, + availableIndexPrivileges, + } = this.props; const props = { indexPatterns, @@ -60,6 +66,7 @@ export class IndexPrivileges extends Component { {...props} formIndex={idx} validator={this.props.validator} + availableIndexPrivileges={availableIndexPrivileges} indexPrivilege={indexPrivilege} availableFields={this.state.availableFields[indexPrivilege.names.join(',')]} onChange={this.onIndexPrivilegeChange(idx)} diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js index aa5bf65742895..282d18d7191d4 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js @@ -87,9 +87,12 @@ const routeDefinition = (action) => ({ } return []; }, - privileges() { + kibanaPrivileges() { return kfetch({ method: 'get', pathname: '/api/security/privileges', query: { includeActions: true } }); }, + builtinESPrivileges() { + return kfetch({ method: 'get', pathname: '/api/security/v1/privileges/builtin' }); + }, features() { return kfetch({ method: 'get', pathname: '/api/features/v1' }).catch(e => { // TODO: This check can be removed once all of these `resolve` entries are moved out of Angular and into the React app. @@ -132,7 +135,8 @@ const routeDefinition = (action) => ({ users, indexPatterns, spaces, - privileges, + kibanaPrivileges, + builtinESPrivileges, features, } = $route.current.locals; @@ -153,7 +157,8 @@ const routeDefinition = (action) => ({ spacesEnabled={enableSpaceAwarePrivileges} uiCapabilities={capabilities.get()} features={features} - privileges={privileges} + kibanaPrivileges={kibanaPrivileges} + builtinESPrivileges={builtinESPrivileges} /> , domNode); diff --git a/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts b/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts new file mode 100644 index 0000000000000..92be54b9c5490 --- /dev/null +++ b/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Legacy } from 'kibana'; +import { BuiltinESPrivileges } from '../../../../common/model'; +import { getClient } from '../../../../../../server/lib/get_client_shield'; + +export function initGetBuiltinPrivilegesApi(server: Legacy.Server) { + server.route({ + method: 'GET', + path: '/api/security/v1/privileges/builtin', + async handler(req: Legacy.Request) { + const callWithRequest = getClient(server).callWithRequest; + const privileges = await callWithRequest( + req, + 'shield.getBuiltinPrivileges' + ); + + return privileges; + }, + }); +} diff --git a/x-pack/legacy/server/lib/esjs_shield_plugin.js b/x-pack/legacy/server/lib/esjs_shield_plugin.js index 51b22abdbfbd6..bdfc00d0230ae 100644 --- a/x-pack/legacy/server/lib/esjs_shield_plugin.js +++ b/x-pack/legacy/server/lib/esjs_shield_plugin.js @@ -488,5 +488,14 @@ fmt: '/_security/user/_has_privileges' } }); + + shield.getBuiltinPrivileges = ca({ + params: {}, + urls: [ + { + fmt: '/_security/privilege/_builtin' + } + ] + }); }; })); diff --git a/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts b/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts new file mode 100644 index 0000000000000..25c26c07b3dac --- /dev/null +++ b/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect/expect.js'; +import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; + +// eslint-disable-next-line import/no-default-export +export default function({ getService }: KibanaFunctionalTestDefaultProviders) { + const supertest = getService('supertest'); + + describe('Builtin ES Privileges', () => { + describe('GET /api/security/v1/privileges/builtin', () => { + it('should return a list of available builtin privileges', async () => { + await supertest + .get('/api/security/v1/privileges/builtin') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200) + .then((response: Record) => { + const sampleOfExpectedClusterPrivileges = ['all', 'manage', 'monitor']; + const sampleOfExpectedIndexPrivileges = ['create', 'index', 'delete']; + + const payload = response.body; + expect(Object.keys(payload).sort()).to.eql(['cluster', 'index']); + + sampleOfExpectedClusterPrivileges.forEach(privilege => + expect(payload.cluster).to.contain(privilege) + ); + + sampleOfExpectedIndexPrivileges.forEach(privilege => + expect(payload.index).to.contain(privilege) + ); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security/index.js b/x-pack/test/api_integration/apis/security/index.js index c2c39cb22f562..b272af4a35460 100644 --- a/x-pack/test/api_integration/apis/security/index.js +++ b/x-pack/test/api_integration/apis/security/index.js @@ -9,6 +9,7 @@ export default function ({ loadTestFile }) { this.tags('ciGroup6'); loadTestFile(require.resolve('./basic_login')); + loadTestFile(require.resolve('./builtin_es_privileges')); loadTestFile(require.resolve('./index_fields')); loadTestFile(require.resolve('./roles')); loadTestFile(require.resolve('./privileges')); From 7c5f601370b96af9efec2f430466d7ee092d7fa4 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 3 Jul 2019 10:42:40 -0400 Subject: [PATCH 2/3] Exclude 'none' from privilege lists --- .../security/server/routes/api/v1/builtin_privileges.ts | 4 ++++ .../api_integration/apis/security/builtin_es_privileges.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts b/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts index 92be54b9c5490..7133d62c3fb99 100644 --- a/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts +++ b/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts @@ -19,6 +19,10 @@ export function initGetBuiltinPrivilegesApi(server: Legacy.Server) { 'shield.getBuiltinPrivileges' ); + // Exclude the `none` privilege, as it doesn't make sense as an option within the Kibana UI + privileges.cluster = privileges.cluster.filter(privilege => privilege !== 'none'); + privileges.index = privileges.index.filter(privilege => privilege !== 'none'); + return privileges; }, }); diff --git a/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts b/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts index 25c26c07b3dac..4a06cf9a4dc1d 100644 --- a/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts +++ b/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts @@ -33,6 +33,9 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { sampleOfExpectedIndexPrivileges.forEach(privilege => expect(payload.index).to.contain(privilege) ); + + expect(payload.cluster).not.to.contain('none'); + expect(payload.index).not.to.contain('none'); }); }); }); From 4dc67211ce33ec3c8c030bba698e61561841a512 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 8 Jul 2019 06:13:59 -0400 Subject: [PATCH 3/3] additional cleanup --- .../public/services/application_privilege.js | 18 ------------------ .../public/views/management/edit_role/index.js | 4 +--- .../server/routes/api/v1/builtin_privileges.ts | 2 +- .../apis/security/builtin_es_privileges.ts | 4 ++-- 4 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 x-pack/legacy/plugins/security/public/services/application_privilege.js diff --git a/x-pack/legacy/plugins/security/public/services/application_privilege.js b/x-pack/legacy/plugins/security/public/services/application_privilege.js deleted file mode 100644 index 00db6cccfb13e..0000000000000 --- a/x-pack/legacy/plugins/security/public/services/application_privilege.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'angular-resource'; -import { uiModules } from 'ui/modules'; - -const module = uiModules.get('security', ['ngResource']); -module.service('ApplicationPrivileges', ($resource, chrome) => { - const baseUrl = chrome.addBasePath('/api/security/v1/privileges'); - return $resource(baseUrl, null, { - query: { - isArray: false, - } - }); -}); diff --git a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js index 282d18d7191d4..952b86b9c8c29 100644 --- a/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js +++ b/x-pack/legacy/plugins/security/public/views/management/edit_role/index.js @@ -10,8 +10,6 @@ import { capabilities } from 'ui/capabilities'; import { kfetch } from 'ui/kfetch'; import { fatalError, toastNotifications } from 'ui/notify'; import template from 'plugins/security/views/management/edit_role/edit_role.html'; -import 'ui/angular_ui_select'; -import 'plugins/security/services/application_privilege'; import 'plugins/security/services/shield_user'; import 'plugins/security/services/shield_role'; import 'plugins/security/services/shield_indices'; @@ -91,7 +89,7 @@ const routeDefinition = (action) => ({ return kfetch({ method: 'get', pathname: '/api/security/privileges', query: { includeActions: true } }); }, builtinESPrivileges() { - return kfetch({ method: 'get', pathname: '/api/security/v1/privileges/builtin' }); + return kfetch({ method: 'get', pathname: '/api/security/v1/esPrivileges/builtin' }); }, features() { return kfetch({ method: 'get', pathname: '/api/features/v1' }).catch(e => { diff --git a/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts b/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts index 7133d62c3fb99..991b57b11a8f8 100644 --- a/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts +++ b/x-pack/legacy/plugins/security/server/routes/api/v1/builtin_privileges.ts @@ -11,7 +11,7 @@ import { getClient } from '../../../../../../server/lib/get_client_shield'; export function initGetBuiltinPrivilegesApi(server: Legacy.Server) { server.route({ method: 'GET', - path: '/api/security/v1/privileges/builtin', + path: '/api/security/v1/esPrivileges/builtin', async handler(req: Legacy.Request) { const callWithRequest = getClient(server).callWithRequest; const privileges = await callWithRequest( diff --git a/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts b/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts index 4a06cf9a4dc1d..2b0c8c5bc5996 100644 --- a/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts +++ b/x-pack/test/api_integration/apis/security/builtin_es_privileges.ts @@ -12,10 +12,10 @@ export default function({ getService }: KibanaFunctionalTestDefaultProviders) { const supertest = getService('supertest'); describe('Builtin ES Privileges', () => { - describe('GET /api/security/v1/privileges/builtin', () => { + describe('GET /api/security/v1/esPrivileges/builtin', () => { it('should return a list of available builtin privileges', async () => { await supertest - .get('/api/security/v1/privileges/builtin') + .get('/api/security/v1/esPrivileges/builtin') .set('kbn-xsrf', 'xxx') .send() .expect(200)