Skip to content

Commit 8b0b09a

Browse files
legregojportnerkobelbelasticmachine
authored
Security - Role Mappings UI (#53620) (#54534)
* Initial role mappings UI * apply design edits * address PR feedback * fix type cast for number field * Update x-pack/legacy/plugins/security/public/views/management/role_mappings/edit_role_mapping/components/mapping_info_panel/mapping_info_panel.tsx Co-Authored-By: Joe Portner <5295965+jportner@users.noreply.github.com> * Cleanup FTR configuration, and handle role mapping 404 errors properly * align naming of role mappings feature check * Apply suggestions from code review Co-Authored-By: Brandon Kobel <brandon.kobel@gmail.com> * add missing test assertions * inlining feature check logic * switch to using snapshot * use href instead of onClick * adding delete unit test * consolidate href building * unify page load error handling * simplify initial loading state * documenting unconditional catch blocks * use nodes.info instead of transport.request * Apply suggestions from code review Co-Authored-By: Brandon Kobel <brandon.kobel@gmail.com> * move model out of LP into NP * convert except_field_rule to except_any_rule * docs, take 1 * update gif Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> Co-authored-by: Brandon Kobel <brandon.kobel@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> Co-authored-by: Brandon Kobel <brandon.kobel@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 96b3b23 commit 8b0b09a

File tree

116 files changed

+9179
-16
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+9179
-16
lines changed

docs/user/security/index.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ cause Kibana's authorization to behave unexpectedly.
3737
include::authorization/index.asciidoc[]
3838
include::authorization/kibana-privileges.asciidoc[]
3939
include::api-keys/index.asciidoc[]
40+
include::role-mappings/index.asciidoc[]
4041
include::rbac_tutorial.asciidoc[]
379 KB
Loading
2.1 MB
Loading
339 KB
Loading
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[role="xpack"]
2+
[[role-mappings]]
3+
=== Role mappings
4+
5+
Role mappings allow you to describe which roles to assign to your users
6+
using a set of rules. Role mappings are required when authenticating via
7+
an external identity provider, such as Active Directory, Kerberos, PKI, OIDC,
8+
or SAML.
9+
10+
Role mappings have no effect for users inside the `native` or `file` realms.
11+
12+
To manage your role mappings, use *Management > Security > Role Mappings*.
13+
14+
With *Role mappings*, you can:
15+
16+
* View your configured role mappings
17+
* Create/Edit/Delete role mappings
18+
19+
[role="screenshot"]
20+
image:user/security/role-mappings/images/role-mappings-grid.png["Role mappings"]
21+
22+
23+
[float]
24+
=== Create a role mapping
25+
26+
To create a role mapping, navigate to *Management > Security > Role Mappings*, and click **Create role mapping**.
27+
Give your role mapping a unique name, and choose which roles you wish to assign to your users.
28+
If you need more flexibility, you can use {ref}/security-api-put-role-mapping.html#_role_templates[role templates] instead.
29+
30+
Next, define the rules describing which users should receive the roles you defined. Rules can optionally grouped and nested, allowing for sophisticated logic to suite complex requirements.
31+
View the {ref}/role-mapping-resources.html[role mapping resources for an overview of the allowed rule types].
32+
33+
34+
[float]
35+
=== Example
36+
37+
Let's create a `sales-users` role mapping, which assigns a `sales` role to users whose username
38+
starts with `sls_`, *or* belongs to the `executive` group.
39+
40+
First, we give the role mapping a name, and assign the `sales` role:
41+
42+
[role="screenshot"]
43+
image:user/security/role-mappings/images/role-mappings-create-step-1.png["Create role mapping, step 1"]
44+
45+
Next, we define the two rules, making sure to set the group to *Any are true*:
46+
47+
[role="screenshot"]
48+
image:user/security/role-mappings/images/role-mappings-create-step-2.gif["Create role mapping, step 2"]
49+
50+
Click *Save role mapping* once you're finished.
51+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import axios, { AxiosInstance } from 'axios';
21+
import util from 'util';
22+
import { ToolingLog } from '@kbn/dev-utils';
23+
24+
export class RoleMappings {
25+
private log: ToolingLog;
26+
private axios: AxiosInstance;
27+
28+
constructor(url: string, log: ToolingLog) {
29+
this.log = log;
30+
this.axios = axios.create({
31+
headers: { 'kbn-xsrf': 'x-pack/ftr/services/security/role_mappings' },
32+
baseURL: url,
33+
maxRedirects: 0,
34+
validateStatus: () => true, // we do our own validation below and throw better error messages
35+
});
36+
}
37+
38+
public async create(name: string, roleMapping: Record<string, any>) {
39+
this.log.debug(`creating role mapping ${name}`);
40+
const { data, status, statusText } = await this.axios.post(
41+
`/internal/security/role_mapping/${name}`,
42+
roleMapping
43+
);
44+
if (status !== 200) {
45+
throw new Error(
46+
`Expected status code of 200, received ${status} ${statusText}: ${util.inspect(data)}`
47+
);
48+
}
49+
this.log.debug(`created role mapping ${name}`);
50+
}
51+
52+
public async delete(name: string) {
53+
this.log.debug(`deleting role mapping ${name}`);
54+
const { data, status, statusText } = await this.axios.delete(
55+
`/internal/security/role_mapping/${name}`
56+
);
57+
if (status !== 200 && status !== 404) {
58+
throw new Error(
59+
`Expected status code of 200 or 404, received ${status} ${statusText}: ${util.inspect(
60+
data
61+
)}`
62+
);
63+
}
64+
this.log.debug(`deleted role mapping ${name}`);
65+
}
66+
}

test/common/services/security/security.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { format as formatUrl } from 'url';
2121

2222
import { Role } from './role';
2323
import { User } from './user';
24+
import { RoleMappings } from './role_mappings';
2425
import { FtrProviderContext } from '../../ftr_provider_context';
2526

2627
export function SecurityServiceProvider({ getService }: FtrProviderContext) {
@@ -30,6 +31,7 @@ export function SecurityServiceProvider({ getService }: FtrProviderContext) {
3031

3132
return new (class SecurityService {
3233
role = new Role(url, log);
34+
roleMappings = new RoleMappings(url, log);
3335
user = new User(url, log);
3436
})();
3537
}

x-pack/dev-tools/jest/create_jest_config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) {
2828
'\\.(css|less|scss)$': `${kibanaDirectory}/src/dev/jest/mocks/style_mock.js`,
2929
'^test_utils/enzyme_helpers': `${xPackKibanaDirectory}/test_utils/enzyme_helpers.tsx`,
3030
'^test_utils/find_test_subject': `${xPackKibanaDirectory}/test_utils/find_test_subject.ts`,
31+
'^test_utils/stub_web_worker': `${xPackKibanaDirectory}/test_utils/stub_web_worker.ts`,
3132
},
3233
coverageDirectory: '<rootDir>/../target/kibana-coverage/jest',
3334
coverageReporters: ['html'],

x-pack/legacy/plugins/security/common/model.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@ export {
1111
BuiltinESPrivileges,
1212
EditUser,
1313
FeaturesPrivileges,
14+
InlineRoleTemplate,
15+
InvalidRoleTemplate,
1416
KibanaPrivileges,
1517
RawKibanaFeaturePrivileges,
1618
RawKibanaPrivileges,
1719
Role,
1820
RoleIndexPrivilege,
1921
RoleKibanaPrivilege,
22+
RoleMapping,
23+
RoleTemplate,
24+
StoredRoleTemplate,
2025
User,
2126
canUserChangePassword,
2227
getUserDisplayName,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { CoreSetup } from 'src/core/public';
8+
import { RoleMapping } from '../../common/model';
9+
10+
interface CheckRoleMappingFeaturesResponse {
11+
canManageRoleMappings: boolean;
12+
canUseInlineScripts: boolean;
13+
canUseStoredScripts: boolean;
14+
hasCompatibleRealms: boolean;
15+
}
16+
17+
type DeleteRoleMappingsResponse = Array<{
18+
name: string;
19+
success: boolean;
20+
error?: Error;
21+
}>;
22+
23+
export class RoleMappingsAPI {
24+
constructor(private readonly http: CoreSetup['http']) {}
25+
26+
public async checkRoleMappingFeatures(): Promise<CheckRoleMappingFeaturesResponse> {
27+
return this.http.get(`/internal/security/_check_role_mapping_features`);
28+
}
29+
30+
public async getRoleMappings(): Promise<RoleMapping[]> {
31+
return this.http.get(`/internal/security/role_mapping`);
32+
}
33+
34+
public async getRoleMapping(name: string): Promise<RoleMapping> {
35+
return this.http.get(`/internal/security/role_mapping/${encodeURIComponent(name)}`);
36+
}
37+
38+
public async saveRoleMapping(roleMapping: RoleMapping) {
39+
const payload = { ...roleMapping };
40+
delete payload.name;
41+
42+
return this.http.post(
43+
`/internal/security/role_mapping/${encodeURIComponent(roleMapping.name)}`,
44+
{ body: JSON.stringify(payload) }
45+
);
46+
}
47+
48+
public async deleteRoleMappings(names: string[]): Promise<DeleteRoleMappingsResponse> {
49+
return Promise.all(
50+
names.map(name =>
51+
this.http
52+
.delete(`/internal/security/role_mapping/${encodeURIComponent(name)}`)
53+
.then(() => ({ success: true, name }))
54+
.catch(error => ({ success: false, name, error }))
55+
)
56+
);
57+
}
58+
}

0 commit comments

Comments
 (0)