Skip to content
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

Security - Role Mappings UI #53620

Merged
merged 31 commits into from
Jan 11, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
17d77c9
Initial role mappings UI
legrego Dec 19, 2019
cf89a36
apply design edits
legrego Jan 6, 2020
04309cf
Merge branch 'master' of github.com:elastic/kibana into security/role…
legrego Jan 6, 2020
333bfe6
address PR feedback
legrego Jan 7, 2020
ed90d8f
Merge branch 'master' of github.com:elastic/kibana into security/role…
legrego Jan 7, 2020
3c75347
fix type cast for number field
legrego Jan 7, 2020
459a0c3
Update x-pack/legacy/plugins/security/public/views/management/role_ma…
legrego Jan 7, 2020
2319f0d
Cleanup FTR configuration, and handle role mapping 404 errors properly
legrego Jan 8, 2020
8899b1c
Merge branch 'security/role-mappings-ui' of github.com:legrego/kibana…
legrego Jan 8, 2020
f0145af
align naming of role mappings feature check
legrego Jan 9, 2020
6272cec
Apply suggestions from code review
legrego Jan 9, 2020
55b0041
add missing test assertions
legrego Jan 9, 2020
31596d3
inlining feature check logic
legrego Jan 9, 2020
db85d63
switch to using snapshot
legrego Jan 9, 2020
5efd7ab
Merge branch 'security/role-mappings-ui' of github.com:legrego/kibana…
legrego Jan 9, 2020
e55b99d
use href instead of onClick
legrego Jan 9, 2020
357ceee
adding delete unit test
legrego Jan 9, 2020
8e3e795
consolidate href building
legrego Jan 9, 2020
dc50382
unify page load error handling
legrego Jan 9, 2020
85fe0e3
simplify initial loading state
legrego Jan 9, 2020
1f9cbeb
documenting unconditional catch blocks
legrego Jan 9, 2020
3ed9321
use nodes.info instead of transport.request
legrego Jan 9, 2020
47c95ef
Apply suggestions from code review
legrego Jan 9, 2020
8abfda4
Merge branch 'security/role-mappings-ui' of github.com:legrego/kibana…
legrego Jan 9, 2020
36fb2cb
Merge branch 'master' of github.com:elastic/kibana into security/role…
legrego Jan 9, 2020
c8d21b6
move model out of LP into NP
legrego Jan 9, 2020
c4bb9d3
Merge branch 'master' into security/role-mappings-ui
elasticmachine Jan 10, 2020
041099e
convert except_field_rule to except_any_rule
legrego Jan 10, 2020
c8c0ecc
docs, take 1
legrego Jan 10, 2020
a6918e3
Merge branch 'security/role-mappings-ui' of github.com:legrego/kibana…
legrego Jan 10, 2020
ab20b17
update gif
legrego Jan 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions test/common/services/security/role_mappings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import axios, { AxiosInstance } from 'axios';
import util from 'util';
import { ToolingLog } from '@kbn/dev-utils';

export class RoleMappings {
private log: ToolingLog;
private axios: AxiosInstance;

constructor(url: string, log: ToolingLog) {
this.log = log;
this.axios = axios.create({
headers: { 'kbn-xsrf': 'x-pack/ftr/services/security/role_mappings' },
baseURL: url,
maxRedirects: 0,
validateStatus: () => true, // we do our own validation below and throw better error messages
});
}

public async create(name: string, roleMapping: Record<string, any>) {
this.log.debug(`creating role mapping ${name}`);
const { data, status, statusText } = await this.axios.post(
`/internal/security/role_mapping/${name}`,
roleMapping
);
if (status !== 200) {
throw new Error(
`Expected status code of 200, received ${status} ${statusText}: ${util.inspect(data)}`
);
}
this.log.debug(`created role mapping ${name}`);
}

public async delete(name: string) {
this.log.debug(`deleting role mapping ${name}`);
const { data, status, statusText } = await this.axios.delete(
`/internal/security/role_mapping/${name}`
);
if (status !== 200 && status !== 404) {
throw new Error(
`Expected status code of 200 or 404, received ${status} ${statusText}: ${util.inspect(
data
)}`
);
}
this.log.debug(`deleted role mapping ${name}`);
}
}
2 changes: 2 additions & 0 deletions test/common/services/security/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { format as formatUrl } from 'url';

import { Role } from './role';
import { User } from './user';
import { RoleMappings } from './role_mappings';
import { FtrProviderContext } from '../../ftr_provider_context';

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

return new (class SecurityService {
role = new Role(url, log);
roleMappings = new RoleMappings(url, log);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spalger moving the functional testing security service to OSS now requires us to put the RoleMappings service here as well, or introduce a new service... 😒

user = new User(url, log);
})();
}
1 change: 1 addition & 0 deletions x-pack/dev-tools/jest/create_jest_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) {
'\\.(css|less|scss)$': `${kibanaDirectory}/src/dev/jest/mocks/style_mock.js`,
'^test_utils/enzyme_helpers': `${xPackKibanaDirectory}/test_utils/enzyme_helpers.tsx`,
'^test_utils/find_test_subject': `${xPackKibanaDirectory}/test_utils/find_test_subject.ts`,
'^test_utils/stub_web_worker': `${xPackKibanaDirectory}/test_utils/stub_web_worker.ts`,
},
coverageDirectory: '<rootDir>/../target/kibana-coverage/jest',
coverageReporters: ['html'],
Expand Down
2 changes: 2 additions & 0 deletions x-pack/legacy/plugins/security/common/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ export {
canUserChangePassword,
getUserDisplayName,
} from '../../../../plugins/security/common/model';

export * from './role_mapping';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: All of the other models have been moved to the NP, any reason to keep this one in the LP?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, I’ll move these to the NP. I started this so long ago that this wasn’t originally the case

55 changes: 55 additions & 0 deletions x-pack/legacy/plugins/security/common/role_mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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.
*/

interface RoleMappingAnyRule {
any: RoleMappingRule[];
}

interface RoleMappingAllRule {
all: RoleMappingRule[];
}

interface RoleMappingFieldRule {
field: Record<string, any>;
}

interface RoleMappingExceptRule {
except: RoleMappingRule;
}

type RoleMappingRule =
| RoleMappingAnyRule
| RoleMappingAllRule
| RoleMappingFieldRule
| RoleMappingExceptRule;

type RoleTemplateFormat = 'string' | 'json';

export interface InlineRoleTemplate {
template: { source: string };
format?: RoleTemplateFormat;
}

export interface StoredRoleTemplate {
template: { id: string };
format?: RoleTemplateFormat;
}

export interface InvalidRoleTemplate {
template: string;
format?: RoleTemplateFormat;
}

export type RoleTemplate = InlineRoleTemplate | StoredRoleTemplate | InvalidRoleTemplate;

export interface RoleMapping {
name: string;
enabled: boolean;
roles?: string[];
role_templates?: RoleTemplate[];
rules: RoleMappingRule | {};
metadata: Record<string, any>;
}
58 changes: 58 additions & 0 deletions x-pack/legacy/plugins/security/public/lib/role_mappings_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 { CoreSetup } from 'src/core/public';
import { RoleMapping } from '../../common/model';

interface CheckPrivilegesResponse {
canManageRoleMappings: boolean;
canUseInlineScripts: boolean;
canUseStoredScripts: boolean;
hasCompatibleRealms: boolean;
}

type DeleteRoleMappingsResponse = Array<{
name: string;
success: boolean;
error?: Error;
}>;

export class RoleMappingsAPI {
constructor(private readonly http: CoreSetup['http']) {}

public async getRoleMappingFeatures(): Promise<CheckPrivilegesResponse> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We have three rather different namings going on here: CheckPrivilegesResponse, getRoleMappingFeatures and role_mapping_feature_check. Can we pick one and make the others consistent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in f0145af

return this.http.get(`/internal/security/role_mapping_feature_check`);
}

public async getRoleMappings(): Promise<RoleMapping[]> {
return this.http.get(`/internal/security/role_mapping`);
}

public async getRoleMapping(name: string): Promise<RoleMapping> {
return this.http.get(`/internal/security/role_mapping/${encodeURIComponent(name)}`);
}

public async saveRoleMapping(roleMapping: RoleMapping) {
const payload = { ...roleMapping };
delete payload.name;

return this.http.post(
`/internal/security/role_mapping/${encodeURIComponent(roleMapping.name)}`,
{ body: JSON.stringify(payload) }
);
}

public async deleteRoleMappings(names: string[]): Promise<DeleteRoleMappingsResponse> {
return Promise.all(
names.map(name =>
this.http
.delete(`/internal/security/role_mapping/${encodeURIComponent(name)}`)
.then(() => ({ success: true, name }))
.catch(error => ({ success: false, name, error }))
)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import './change_password_form/index';
@import './edit_role/index';
@import './edit_user/index';
@import './edit_user/index';
@import './role_mappings/edit_role_mapping/index';
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,30 @@ export function getApiKeysBreadcrumbs() {
},
];
}

export function getRoleMappingBreadcrumbs() {
return [
MANAGEMENT_BREADCRUMB,
{
text: i18n.translate('xpack.security.roleMapping.breadcrumb', {
defaultMessage: 'Role Mappings',
}),
href: '#/management/security/role_mappings',
},
];
}

export function getEditRoleMappingBreadcrumbs($route: Record<string, any>) {
const { name } = $route.current.params;
return [
...getRoleMappingBreadcrumbs(),
{
text:
name ||
i18n.translate('xpack.security.roleMappings.createBreadcrumb', {
defaultMessage: 'Create',
}),
href: `#/management/security/role_mappings/edit/${name}`,
},
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import 'plugins/security/views/management/roles_grid/roles';
import 'plugins/security/views/management/api_keys_grid/api_keys';
import 'plugins/security/views/management/edit_user/edit_user';
import 'plugins/security/views/management/edit_role/index';
import 'plugins/security/views/management/role_mappings/role_mappings_grid';
import 'plugins/security/views/management/role_mappings/edit_role_mapping';
import routes from 'ui/routes';
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
import { ROLES_PATH, USERS_PATH, API_KEYS_PATH } from './management_urls';
import { ROLES_PATH, USERS_PATH, API_KEYS_PATH, ROLE_MAPPINGS_PATH } from './management_urls';

import { management } from 'ui/management';
import { npSetup } from 'ui/new_platform';
Expand All @@ -38,11 +40,23 @@ routes
resolve: {
securityManagementSection: function() {
const showSecurityLinks = xpackInfo.get('features.security.showLinks');
const showRoleMappingsManagementLink = xpackInfo.get(
'features.security.showRoleMappingsManagement'
);

function deregisterSecurity() {
management.deregister('security');
}

function deregisterRoleMappingsManagement() {
if (management.hasItem('security')) {
const security = management.getSection('security');
if (security.hasItem('roleMappings')) {
security.deregister('roleMappings');
}
}
}

function ensureSecurityRegistered() {
const registerSecurity = () =>
management.register('security', {
Expand Down Expand Up @@ -88,11 +102,26 @@ routes
url: `#${API_KEYS_PATH}`,
});
}

if (showRoleMappingsManagementLink && !security.hasItem('roleMappings')) {
security.register('roleMappings', {
name: 'securityRoleMappingLink',
order: 30,
display: i18n.translate('xpack.security.management.roleMappingsTitle', {
defaultMessage: 'Role Mappings',
}),
url: `#${ROLE_MAPPINGS_PATH}`,
});
}
}

if (!showSecurityLinks) {
deregisterSecurity();
} else {
if (!showRoleMappingsManagementLink) {
deregisterRoleMappingsManagement();
}

// getCurrentUser will reject if there is no authenticated user, so we prevent them from
// seeing the security management screens.
return npSetup.plugins.security.authc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export const CLONE_ROLES_PATH = `${ROLES_PATH}/clone`;
export const USERS_PATH = `${SECURITY_PATH}/users`;
export const EDIT_USERS_PATH = `${USERS_PATH}/edit`;
export const API_KEYS_PATH = `${SECURITY_PATH}/api_keys`;
export const ROLE_MAPPINGS_PATH = `${SECURITY_PATH}/role_mappings`;
export const EDIT_ROLE_MAPPING_PATH = `${SECURITY_PATH}/role_mappings/edit`;
legrego marked this conversation as resolved.
Show resolved Hide resolved
Loading