Skip to content

Commit

Permalink
Initial role mappings UI
Browse files Browse the repository at this point in the history
  • Loading branch information
legrego committed Dec 19, 2019
1 parent 665c962 commit 17d77c9
Show file tree
Hide file tree
Showing 115 changed files with 9,222 additions and 16 deletions.
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);
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';
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> {
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,10 +11,12 @@ 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 '../../services/shield_user';
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 { i18n } from '@kbn/i18n';
Expand All @@ -38,11 +40,23 @@ routes
resolve: {
securityManagementSection: function(ShieldUser) {
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,25 @@ 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();
}
// getCurrent will reject if there is no authenticated user, so we prevent them from seeing the security
// management screens
//
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`;
Loading

0 comments on commit 17d77c9

Please sign in to comment.