From 67e05dbcc0158d13f384431ce75bcd0de94faaee Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 11 May 2018 15:26:30 -0400 Subject: [PATCH 01/38] round 1 --- .../plugins/security/public/objects/index.js | 11 + .../security/public/objects/lib/get_fields.js | 14 + .../security/public/objects/lib/get_roles.js | 19 + .../security/public/objects/lib/get_users.js | 14 + .../plugins/security/public/objects/role.js | 13 + .../public/services/role_privileges.js | 42 ++ .../public/views/management/edit_role.html | 186 +-------- .../public/views/management/edit_role.js | 136 ++----- .../components/cluster_privileges.js | 58 +++ .../edit_role/components/edit_role_page.js | 360 ++++++++++++++++++ .../components/index_privilege_form.js | 192 ++++++++++ .../edit_role/components/page_header.js | 43 +++ .../edit_role/lib/is_reserved_role.js | 9 + 13 files changed, 818 insertions(+), 279 deletions(-) create mode 100644 x-pack/plugins/security/public/objects/index.js create mode 100644 x-pack/plugins/security/public/objects/lib/get_fields.js create mode 100644 x-pack/plugins/security/public/objects/lib/get_roles.js create mode 100644 x-pack/plugins/security/public/objects/lib/get_users.js create mode 100644 x-pack/plugins/security/public/objects/role.js create mode 100644 x-pack/plugins/security/public/services/role_privileges.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/page_header.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js diff --git a/x-pack/plugins/security/public/objects/index.js b/x-pack/plugins/security/public/objects/index.js new file mode 100644 index 0000000000000..ce1c8f6aac3df --- /dev/null +++ b/x-pack/plugins/security/public/objects/index.js @@ -0,0 +1,11 @@ +/* + * 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 { getRole } from './lib/get_roles'; + +export { getUsers } from './lib/get_users'; + +export { getFields } from './lib/get_fields'; diff --git a/x-pack/plugins/security/public/objects/lib/get_fields.js b/x-pack/plugins/security/public/objects/lib/get_fields.js new file mode 100644 index 0000000000000..a80f6bfe8eed1 --- /dev/null +++ b/x-pack/plugins/security/public/objects/lib/get_fields.js @@ -0,0 +1,14 @@ +/* + * 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 chrome from 'ui/chrome'; + +const apiBase = chrome.addBasePath(`/api/security/v1/fields`); + +export async function getFields($http, query) { + return await $http + .get(`${apiBase}/${query}`) + .then(response => response.data || []); +} diff --git a/x-pack/plugins/security/public/objects/lib/get_roles.js b/x-pack/plugins/security/public/objects/lib/get_roles.js new file mode 100644 index 0000000000000..784ba57570605 --- /dev/null +++ b/x-pack/plugins/security/public/objects/lib/get_roles.js @@ -0,0 +1,19 @@ +/* + * 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 chrome from 'ui/chrome'; + +const apiBase = chrome.addBasePath(`/api/security/v1/roles`); + +export async function getRole($http, name) { + try { + return await $http.get(`${apiBase}/${name}`); + } catch (response) { + if (response.status === 404) { + return null; + } + throw response; + } +} diff --git a/x-pack/plugins/security/public/objects/lib/get_users.js b/x-pack/plugins/security/public/objects/lib/get_users.js new file mode 100644 index 0000000000000..fd82b62d0f928 --- /dev/null +++ b/x-pack/plugins/security/public/objects/lib/get_users.js @@ -0,0 +1,14 @@ +/* + * 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 chrome from 'ui/chrome'; + +const apiBase = chrome.addBasePath(`/api/security/v1/users`); + +export async function getUsers($http) { + return await $http + .get(apiBase) + .then(response => response.data || []); +} diff --git a/x-pack/plugins/security/public/objects/role.js b/x-pack/plugins/security/public/objects/role.js new file mode 100644 index 0000000000000..98448db207cea --- /dev/null +++ b/x-pack/plugins/security/public/objects/role.js @@ -0,0 +1,13 @@ +/* + * 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 class Role { + name = null; + cluster = []; + indices = []; + run_as = []; //eslint-disable-line camelcase + applications = []; +} diff --git a/x-pack/plugins/security/public/services/role_privileges.js b/x-pack/plugins/security/public/services/role_privileges.js new file mode 100644 index 0000000000000..794a4b30674e5 --- /dev/null +++ b/x-pack/plugins/security/public/services/role_privileges.js @@ -0,0 +1,42 @@ +/* + * 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_watcher', + 'monitor_watcher', +]; +const indexPrivileges = [ + 'all', + 'manage', + 'monitor', + 'read', + 'index', + 'create', + 'delete', + 'write', + 'delete_index', + 'create_index', + 'view_index_metadata', + 'read_cross_cluster', +]; + +export function getClusterPrivileges() { + return [...clusterPrivileges]; +} + +export function getIndexPrivileges() { + return [...indexPrivileges]; +} diff --git a/x-pack/plugins/security/public/views/management/edit_role.html b/x-pack/plugins/security/public/views/management/edit_role.html index 768f50a210758..5539f0521dd79 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.html +++ b/x-pack/plugins/security/public/views/management/edit_role.html @@ -1,185 +1 @@ - - -
- -
-
- -

- - New Role - - - “{{ role.name }}” Role - -

-
- -
- - -
- - Reserved -
-
-
- - -
- -
- - - - -
- Name must begin with a letter or underscore and contain only letters, underscores, and numbers. -
- -
- Name is required. -
-
- -
- -
- -
- -
-
- - -
- - -
- Changes to this section are not supported: this role contains application privileges that do not belong to this instance of Kibana. -
- -
- -
-
- - -
- - - - {{$item}} - - -
-
-
-
- - -
- -
-
- -
- -
- - - - Cancel - -
-
-
-
-
+
diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index 349807d1836c8..33cc6c42a4f40 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -6,9 +6,7 @@ import _ from 'lodash'; import routes from 'ui/routes'; -import { fatalError, toastNotifications } from 'ui/notify'; -import { toggle } from 'plugins/security/lib/util'; -import { isRoleEnabled } from 'plugins/security/lib/role'; +import { fatalError } from 'ui/notify'; import template from 'plugins/security/views/management/edit_role.html'; import 'angular-ui-select'; import 'plugins/security/services/application_privilege'; @@ -21,6 +19,10 @@ import { IndexPatternsProvider } from 'ui/index_patterns/index_patterns'; import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info'; import { checkLicenseError } from 'plugins/security/lib/check_license_error'; import { EDIT_ROLES_PATH, ROLES_PATH } from './management_urls'; +import { EditRolePage } from './edit_role/components/edit_role_page'; + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; routes.when(`${EDIT_ROLES_PATH}/:name?`, { template, @@ -64,104 +66,50 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { } }, controllerAs: 'editRole', - controller($injector, $scope, rbacEnabled) { + controller($injector, $scope, $http, rbacEnabled) { const $route = $injector.get('$route'); - const kbnUrl = $injector.get('kbnUrl'); - const shieldPrivileges = $injector.get('shieldPrivileges'); - const Notifier = $injector.get('Notifier'); const Private = $injector.get('Private'); - const confirmModal = $injector.get('confirmModal'); - const shieldIndices = $injector.get('shieldIndices'); - - $scope.role = $route.current.locals.role; - $scope.users = $route.current.locals.users; - $scope.indexPatterns = $route.current.locals.indexPatterns; - $scope.privileges = shieldPrivileges; - $scope.kibanaPrivileges = $route.current.locals.kibanaPrivileges; - $scope.rbacEnabled = rbacEnabled; - $scope.rolesHref = `#${ROLES_PATH}`; - - this.isNewRole = $route.current.params.name == null; - this.fieldOptions = {}; - - const notifier = new Notifier(); - - $scope.deleteRole = (role) => { - const doDelete = () => { - role.$delete() - .then(() => toastNotifications.addSuccess('Deleted role')) - .then($scope.goToRoleList) - .catch(error => notifier.error(_.get(error, 'data.message'))); - }; - const confirmModalOptions = { - confirmButtonText: 'Delete role', - onConfirm: doDelete - }; - confirmModal('Are you sure you want to delete this role? This action is irreversible!', confirmModalOptions); - }; - - $scope.saveRole = (role) => { - role.indices = role.indices.filter((index) => index.names.length); - role.indices.forEach((index) => index.query || delete index.query); - return role.$save() - .then(() => toastNotifications.addSuccess('Updated role')) - .then($scope.goToRoleList) - .catch(error => notifier.error(_.get(error, 'data.message'))); - }; - - $scope.goToRoleList = () => { - kbnUrl.redirect(ROLES_PATH); - }; - - $scope.addIndex = indices => { - indices.push({ names: [], privileges: [], field_security: { grant: ['*'] } }); - }; - - $scope.areIndicesValid = (indices) => { - return indices - .filter((index) => index.names.length) - .find((index) => index.privileges.length === 0) == null; - }; - - $scope.fetchFieldOptions = (index) => { - const indices = index.names.join(','); - const fieldOptions = this.fieldOptions; - if (indices && fieldOptions[indices] == null) { - shieldIndices.getFields(indices) - .then((fields) => fieldOptions[indices] = fields) - .catch(() => fieldOptions[indices] = []); - } - }; - - $scope.isRoleEnabled = isRoleEnabled; const xpackInfo = Private(XPackInfoProvider); - $scope.allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); - $scope.allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity'); + const allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); + const allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity'); - $scope.$watch('role.indices', (indices) => { - if (!indices.length) $scope.addIndex(indices); - else indices.forEach($scope.fetchFieldOptions); - }, true); + const domNode = document.getElementById('editRoleReactRoot'); - $scope.toggle = toggle; - $scope.includes = _.includes; - $scope.togglePermission = (role, permission) => { - const shouldRemove = $scope.hasPermission(role, permission); - const rolePermissions = role.applications || []; - if (shouldRemove) { - role.applications = rolePermissions.filter(rolePermission => rolePermission.name === permission.name); - } else { - role.applications = rolePermissions.concat([permission]); - } - }; + const { + role, + users, + indexPatterns, + } = $route.current.locals; - $scope.hasPermission = (role, permission) => { - // TODO(legrego): faking until ES is implemented - const rolePermissions = role.applications || []; - return rolePermissions.find(rolePermission => permission.name === rolePermission.name); - }; + this.fieldOptions = {}; + + const roleToEdit = role.toJSON(); + if (roleToEdit.indices.length === 0) { + roleToEdit.indices.push({ + names: [], + privileges: [], + field_security: { + grant: ['*'] + } + }); + } - $scope.union = _.flow(_.union, _.compact); + render(, domNode); + + // unmount react on controller destroy + $scope.$on('$destroy', () => { + unmountComponentAtNode(domNode); + }); } }); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js new file mode 100644 index 0000000000000..8486dc3bf70e1 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js @@ -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 React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { getClusterPrivileges } from '../../../../services/role_privileges'; +import { isReservedRole } from '../lib/is_reserved_role'; +import { + EuiCheckboxGroup +} from '@elastic/eui'; + +export class ClusterPrivileges extends Component { + static propTypes = { + role: PropTypes.object.isRequired, + onChange: PropTypes.func.isRequired + }; + + render() { + const checkboxes = getClusterPrivileges().map(p => ({ + id: p, + label: p + })); + + const { role } = this.props; + + const selectionMap = (role.cluster || []) + .map(k => ({ [k]: true })) + .reduce((acc, o) => ({ ...acc, ...o }), {}); + + return ( + + ); + } + + onClusterPrivilegesChange = (privilege) => { + const { cluster } = this.props.role; + const indexOfExistingPrivilege = cluster.indexOf(privilege); + + const shouldRemove = indexOfExistingPrivilege >= 0; + + const newClusterPrivs = [...cluster]; + if (shouldRemove) { + newClusterPrivs.splice(indexOfExistingPrivilege, 1); + } else { + newClusterPrivs.push(privilege); + } + + this.props.onChange(newClusterPrivs); + } +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js new file mode 100644 index 0000000000000..31c4794bcccbd --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -0,0 +1,360 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { difference } from 'lodash'; +import { + EuiText, + EuiSpacer, + EuiIcon, + EuiAccordion, + EuiComboBox, + EuiPage, + EuiPageContent, + EuiPanel, + EuiForm, + EuiFormRow, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiBadge, +} from '@elastic/eui'; +import { PageHeader } from './page_header'; +import { IndexPrivilegeForm } from './index_privilege_form'; +import { ClusterPrivileges } from './cluster_privileges'; +import { getFields } from '../../../../objects'; +import { isReservedRole } from '../lib/is_reserved_role'; + +export class EditRolePage extends Component { + static propTypes = { + role: PropTypes.object.isRequired, + runAsUsers: PropTypes.array.isRequired, + indexPatterns: PropTypes.array.isRequired, + httpClient: PropTypes.func.isRequired, + rbacEnabled: PropTypes.bool.isRequired, + spacesEnabled: PropTypes.bool.isRequired, + allowDocumentLevelSecurity: PropTypes.bool.isRequired, + allowFieldLevelSecurity: PropTypes.bool.isRequired + }; + + constructor(props) { + super(props); + this.state = { + role: props.role, + availableFields: {} + }; + } + + componentDidMount() { + this.loadAvailableFields(this.state.role.indices); + } + + loadAvailableFields(indices) { + const patterns = indices.map(index => index.names.join(',')); + + const cachedPatterns = Object.keys(this.state.availableFields); + const patternsToFetch = difference(patterns, cachedPatterns); + + const fetchRequests = patternsToFetch.map(this.loadFieldsForPattern); + + Promise.all(fetchRequests) + .then(response => { + + this.setState({ + availableFields: { + ...this.state.availableFields, + ...response.reduce((acc, o) => ({ ...acc, ...o }), {}) + } + }); + }); + } + + loadFieldsForPattern = async (pattern) => { + if (!pattern) return { [pattern]: [] }; + + try { + return { + [pattern]: await getFields(this.props.httpClient, pattern) + }; + + } catch (e) { + return { + [pattern]: [] + }; + } + } + + render() { + return ( + + + + + + +

{this.getTitle()}

+
+ {this.getActionButton()} +
+ + + + + + + + + {isReservedRole(this.props.role) + && Reserved Role} + + + + + {this.getElasticsearchPrivileges()} + + {this.getKibanaPrivileges()} + + + + Save + + + + Cancel + + + +
+
+
+ ); + } + + getTitle = () => { + if (this.editingExistingRole()) { + return `Edit role`; + } + return `New Role`; + }; + + getActionButton = () => { + if (this.editingExistingRole() && !isReservedRole(this.props.role)) { + return ( + + Delete role + + ); + } + + return null; + }; + + onNameChange = (name) => { + this.setState({ + role: { + ...this.state.role, + name + } + }); + } + + getElasticsearchPrivileges = () => { + const { + role + } = this.state; + + return ( + + + Cluster Privileges ({role.cluster.length})
} + > + + + + + + + + Index Privileges ( + {role.indices.filter(i => i.names.length).length}) + + } + > + + +

+ Index Privileges allow you to foo the bar while baring the baz +

+
+ + {this.getIndexPrivileges()} +
+
+
+ + + Run As Privileges ({role.run_as.length})} + > + + ({ id: username, label: username }))} + selectedOptions={this.state.role.run_as.map(u => ({ label: u }))} + onChange={this.onRunAsUserChange} + /> + + + + + ); + }; + + getIndexPrivileges = () => { + const { indices = [] } = this.state.role; + + const { + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity + } = this.props; + + const props = { + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity, + isReservedRole: isReservedRole(this.props.role) + }; + + const forms = indices.map((indexPrivilege, idx) => ( + + )); + + const button = isReservedRole(this.props.role) + ? null + : ( + + + + New Index Privilege + + + + ); + + return ( + + {forms} + {button} + + ); + }; + + addIndexPrivilege = () => { + const { role } = this.state; + + this.setState({ + role: { + ...role, + indices: [...role.indices, { + names: [], + privileges: [], + field_security: { + grant: ['*'] + } + }] + } + }); + }; + + onIndexPrivilegeChange = (index) => { + return (updatedPrivilege) => { + const { role } = this.state; + const { indices } = role; + + const newIndicesState = [...indices]; + newIndicesState[index] = updatedPrivilege; + + this.setState({ + role: { + ...role, + indices: newIndicesState + } + }); + + this.loadAvailableFields(newIndicesState); + }; + }; + + deleteIndexPrivilege = (privilegeIndex) => { + this.setState({ + role: { + ...this.state.role, + indices: this.state.indices.splice(privilegeIndex, 1) + } + }); + } + + onClusterPrivilegesChange = (cluster) => { + this.setState({ + role: { + ...this.state.role, + cluster + } + }); + } + + onRunAsUserChange = (users) => { + this.setState({ + role: { + ...this.state.role, + run_as: users.map(u => u.label) + } + }); + } + + getKibanaPrivileges = () => { + if (!this.props.kibanaPrivilegesEnabled) { + return null; + } + + return ( + + + Cluster Privileges} + > + + { }}/> + + + + ); + }; + + editingExistingRole = () => { + return !!this.props.role.name; + }; +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js new file mode 100644 index 0000000000000..1b0f0a75416f2 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js @@ -0,0 +1,192 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiComboBox, + EuiTextArea, + EuiFormRow, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiSpacer, + EuiHorizontalRule, +} from '@elastic/eui'; +import { getIndexPrivileges } from '../../../../services/role_privileges'; + +const fromOption = (option) => option.label; +const toOption = (value) => ({ label: value }); + +export class IndexPrivilegeForm extends Component { + static propTypes = { + indexPrivilege: PropTypes.object.isRequired, + indexPatterns: PropTypes.array.isRequired, + availableFields: PropTypes.array, + onChange: PropTypes.func.isRequired, + isReservedRole: PropTypes.bool.isRequired, + allowDocumentLevelSecurity: PropTypes.bool.isRequired, + allowFieldLevelSecurity: PropTypes.bool.isRequired, + }; + + render() { + return ( +
+ + + {this.getPrivilegeForm()} + + {( + + + + )} + + +
+ ); + } + + getPrivilegeForm = () => { + return ( + + + + + + + + + + + + + + + {this.getConditionalFeatures()} + + ); + }; + + getConditionalFeatures = () => { + const { + allowDocumentLevelSecurity, + allowFieldLevelSecurity, + indexPrivilege, + availableFields, + } = this.props; + + if (!allowFieldLevelSecurity && !allowDocumentLevelSecurity) { + return null; + } + + const features = []; + if (allowDocumentLevelSecurity) { + features.push(( + + + + + + )); + } + + if (allowFieldLevelSecurity) { + + const { grant = [] } = indexPrivilege.field_security || {}; + + features.push(( + + + + + + )); + } + + return ( + + + + {features} + + + ); + }; + + onCreateIndexPatternOption = (option) => { + const newIndexPatterns = this.props.indexPrivilege.names.concat([option]); + + this.props.onChange({ + ...this.props.indexPrivilege, + names: newIndexPatterns, + }); + }; + + onIndexPatternsChange = (newPatterns) => { + this.props.onChange({ + ...this.props.indexPrivilege, + names: newPatterns.map(fromOption), + }); + }; + + onPrivilegeChange = (newPrivileges) => { + this.props.onChange({ + ...this.props.indexPrivilege, + privileges: newPrivileges.map(fromOption), + }); + }; + + onQueryChange = (e) => { + this.props.onChange({ + ...this.props.indexPrivilege, + query: e.target.value, + }); + }; + + onCreateGrantedField = (grant) => { + const newGrants = this.props.indexPrivilege.field_security.grant.concat([grant]); + + this.props.onChange({ + ...this.props.indexPrivilege, + field_security: { + ...this.props.indexPrivilege.field_security, + grant: newGrants, + }, + }); + }; + + onGrantedFieldsChange = (grantedFields) => { + this.props.onChange({ + ...this.props.indexPrivilege, + field_security: { + ...this.props.indexPrivilege.field_security, + grant: grantedFields.map(fromOption), + }, + }); + }; +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js new file mode 100644 index 0000000000000..956c2809b325f --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js @@ -0,0 +1,43 @@ +/* + * 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 React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { + EuiHeader, + EuiHeaderSection, + EuiHeaderBreadcrumb, + EuiHeaderBreadcrumbs +} from '@elastic/eui'; + +export class PageHeader extends Component { + render() { + return ( + + + + {this.props.breadcrumbs.map(this.buildBreadcrumb)} + + + + ); + } + + buildBreadcrumb = (breadcrumb) => { + return ( + + {breadcrumb.display} + + ); + } +} + + + +PageHeader.propTypes = { + breadcrumbs: PropTypes.array.isRequired +}; diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js b/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js new file mode 100644 index 0000000000000..05a76d7f516c7 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js @@ -0,0 +1,9 @@ +/* + * 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 function isReservedRole(role) { + return role && role.metadata && role.metadata._reserved; +} From 904a6b772f8cb942220ad6a85f4c4788f4c0b52e Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 14 May 2018 09:17:42 -0400 Subject: [PATCH 02/38] validation and cleanup --- .../components/cluster_privileges.js | 31 ++-- .../components/delete_role_button.js | 78 ++++++++++ .../edit_role/components/edit_role_page.js | 145 ++++++++++++------ .../components/index_privilege_form.js | 13 +- .../components/reserved_role_badge.js | 37 +++++ .../management/edit_role/lib/constants.js | 7 + .../management/edit_role/lib/validate_role.js | 84 ++++++++++ 7 files changed, 339 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/constants.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js index 8486dc3bf70e1..8ed97e8aad5c1 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js @@ -4,18 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { getClusterPrivileges } from '../../../../services/role_privileges'; import { isReservedRole } from '../lib/is_reserved_role'; import { - EuiCheckboxGroup + EuiCheckboxGroup, + EuiText, + EuiSpacer, + EuiLink, } from '@elastic/eui'; +import { CLUSTER_PRIVS_DOC_LINK } from '../lib/constants'; export class ClusterPrivileges extends Component { static propTypes = { role: PropTypes.object.isRequired, - onChange: PropTypes.func.isRequired + onChange: PropTypes.func.isRequired, }; render() { @@ -31,12 +35,21 @@ export class ClusterPrivileges extends Component { .reduce((acc, o) => ({ ...acc, ...o }), {}); return ( - + + +

+ Manage the actions this role can perform against your cluster.  + Learn more +

+
+ + +
); } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.js b/x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.js new file mode 100644 index 0000000000000..805dfb232fff4 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.js @@ -0,0 +1,78 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiButton, + EuiOverlayMask, + EuiConfirmModal, +} from '@elastic/eui'; + +export class DeleteRoleButton extends Component { + static propTypes = { + canDelete: PropTypes.bool.isRequired, + onDelete: PropTypes.func.isRequired + } + + state = { + showModal: false + } + + render() { + if (!this.props.canDelete) { + return null; + } + + return ( + + + Delete Role + + {this.maybeShowModal()} + + ); + } + + maybeShowModal = () => { + if (!this.state.showModal) { + return null; + } + return ( + + +

Are you sure you want to delete this role?

+

This action cannot be undone!

+
+
+ ); + } + + closeModal = () => { + this.setState({ + showModal: false + }); + } + + showModal = () => { + this.setState({ + showModal: true + }); + } + + onConfirmDelete = () => { + this.closeModal(); + this.props.onDelete(); + } +} + diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 31c4794bcccbd..c61a2b9b28a7f 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -21,13 +21,17 @@ import { EuiFlexGroup, EuiFlexItem, EuiButton, - EuiBadge, + EuiTitle, } from '@elastic/eui'; import { PageHeader } from './page_header'; import { IndexPrivilegeForm } from './index_privilege_form'; import { ClusterPrivileges } from './cluster_privileges'; import { getFields } from '../../../../objects'; import { isReservedRole } from '../lib/is_reserved_role'; +import { RoleValidator } from '../lib/validate_role'; +import { ReservedRoleBadge } from './reserved_role_badge'; +import { ROLES_PATH } from '../../management_urls'; +import { DeleteRoleButton } from './delete_role_button'; export class EditRolePage extends Component { static propTypes = { @@ -45,8 +49,10 @@ export class EditRolePage extends Component { super(props); this.state = { role: props.role, - availableFields: {} + availableFields: {}, + formError: null }; + this.validator = new RoleValidator({ shouldValidate: false }); } componentDidMount() { @@ -89,11 +95,15 @@ export class EditRolePage extends Component { } render() { + const { + validator + } = this; + return ( - +

{this.getTitle()}

@@ -104,7 +114,7 @@ export class EditRolePage extends Component { - + - {isReservedRole(this.props.role) - && Reserved Role} + @@ -123,12 +132,14 @@ export class EditRolePage extends Component { {this.getKibanaPrivileges()} + + - Save + Save - + Cancel @@ -150,7 +161,7 @@ export class EditRolePage extends Component { if (this.editingExistingRole() && !isReservedRole(this.props.role)) { return ( - Delete role + ); } @@ -158,11 +169,11 @@ export class EditRolePage extends Component { return null; }; - onNameChange = (name) => { + onNameChange = (e) => { this.setState({ role: { ...this.state.role, - name + name: e.target.value } }); } @@ -174,17 +185,23 @@ export class EditRolePage extends Component { return ( - + +

Elasticsearch

+
+ + + + Cluster Privileges ({role.cluster.length})} > - - - + -
- + + + + } > - - -

- Index Privileges allow you to foo the bar while baring the baz -

-
- - {this.getIndexPrivileges()} -
+ +

+ Index Privileges allow you to foo the bar while baring the baz +

+
+ + {this.getIndexPrivileges()}
-
+ + - + Run As Privileges ({role.run_as.length})} > - - ({ id: username, label: username }))} - selectedOptions={this.state.role.run_as.map(u => ({ label: u }))} - onChange={this.onRunAsUserChange} - /> - + +

+ Run As Privileges allow you to foo the bar while baring the baz +

+
+ + ({ id: username, label: username }))} + selectedOptions={this.state.role.run_as.map(u => ({ label: u }))} + onChange={this.onRunAsUserChange} + />
-
+
); }; @@ -245,9 +265,12 @@ export class EditRolePage extends Component { )); @@ -307,13 +330,20 @@ export class EditRolePage extends Component { }; }; - deleteIndexPrivilege = (privilegeIndex) => { - this.setState({ - role: { - ...this.state.role, - indices: this.state.indices.splice(privilegeIndex, 1) - } - }); + onIndexPrivilegeDelete = (privilegeIndex) => { + return () => { + const { role } = this.state; + + const newIndicesState = [...role.indices]; + newIndicesState.splice(privilegeIndex, 1); + + this.setState({ + role: { + ...role, + indices: newIndicesState + } + }); + }; } onClusterPrivilegesChange = (cluster) => { @@ -357,4 +387,31 @@ export class EditRolePage extends Component { editingExistingRole = () => { return !!this.props.role.name; }; + + isPlaceholderPrivilege = (indexPrivilege) => { + return indexPrivilege.names.length === 0; + }; + + saveRole = () => { + this.validator.enableValidation(); + + const result = this.validator.validateForSave(this.state.role); + if (result.isInvalid) { + this.setState({ + formError: result + }); + } else { + this.setState({ + formError: null + }); + } + }; + + deleteRole = () => { + + }; + + backToRoleList = () => { + window.location.hash = ROLES_PATH; + }; } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js index 1b0f0a75416f2..da1df33d69d20 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js @@ -27,8 +27,10 @@ export class IndexPrivilegeForm extends Component { availableFields: PropTypes.array, onChange: PropTypes.func.isRequired, isReservedRole: PropTypes.bool.isRequired, + allowDelete: PropTypes.bool.isRequired, allowDocumentLevelSecurity: PropTypes.bool.isRequired, allowFieldLevelSecurity: PropTypes.bool.isRequired, + validator: PropTypes.object.isRequired, }; render() { @@ -38,9 +40,14 @@ export class IndexPrivilegeForm extends Component { {this.getPrivilegeForm()} - {( + {this.props.allowDelete && ( - + )}
@@ -54,7 +61,7 @@ export class IndexPrivilegeForm extends Component { - + { + const { + role + } = props; + + if (isReservedRole(role)) { + return ( + + + Reserved Role + + + ); + } + return null; +}; + +ReservedRoleBadge.propTypes = { + role: PropTypes.object.isRequired +}; diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/constants.js b/x-pack/plugins/security/public/views/management/edit_role/lib/constants.js new file mode 100644 index 0000000000000..6518df48847e9 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/constants.js @@ -0,0 +1,7 @@ +/* + * 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 const CLUSTER_PRIVS_DOC_LINK = `https://www.elastic.co/guide/en/x-pack/current/security-privileges.html#security-privileges`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js new file mode 100644 index 0000000000000..3c7ffa0b667eb --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js @@ -0,0 +1,84 @@ +/* + * 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 class RoleValidator { + constructor(options = {}) { + this._shouldValidate = options.shouldValidate; + } + + enableValidation() { + this._shouldValidate = true; + } + + disableValidation() { + this._shouldValidate = false; + } + + validateRoleName(role) { + if (!this._shouldValidate) return valid(); + + if (!role.name) { + return invalid(`Please provide a role name`); + } + if (role.name.length > 30) { + return invalid(`Name must not exceed 30 characters`); + } + if (!role.name.match(/^[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*$/)) { + return invalid(`Name must begin with a letter or underscore and contain only letters, underscores, and numbers.`); + } + return valid(); + } + + validateIndexPrivileges(role) { + if (!this._shouldValidate) return valid(); + + if (Array.isArray(role.indices)) { + const areIndicesValid = role.indices + .map(this.validateIndexPrivilege.bind(this)) + .find((result) => result.isInvalid) == null; + + if (areIndicesValid) { + return valid(); + } + return invalid(); + } + } + + validateIndexPrivilege(indexPrivilege) { + if (!this._shouldValidate) return valid(); + + if (indexPrivilege.names.length && !indexPrivilege.privileges.length) { + return invalid(`At least one privilege is required`); + } + return valid(); + } + + validateForSave(role) { + const { isInvalid: isNameInvalid } = this.validateRoleName(role); + const { isInvalid: areIndicesInvalid } = this.validateIndexPrivileges(role); + + if (isNameInvalid || areIndicesInvalid) { + return invalid(); + } + + return valid(); + } + +} + +function invalid(error) { + return { + isInvalid: true, + error + }; +} + +function valid() { + return { + isInvalid: false + }; +} + From 8ae7325c5c5222b5cc7b8d26cb774438fc696d69 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 14 May 2018 09:23:13 -0400 Subject: [PATCH 03/38] Fix react warnings --- .../views/management/edit_role/components/edit_role_page.js | 2 +- .../public/views/management/edit_role/lib/is_reserved_role.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index c61a2b9b28a7f..94ba1482604c8 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -117,7 +117,7 @@ export class EditRolePage extends Component { diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js b/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js index 05a76d7f516c7..84d003e1b6033 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js @@ -5,5 +5,5 @@ */ export function isReservedRole(role) { - return role && role.metadata && role.metadata._reserved; + return (role && role.metadata && role.metadata._reserved) || false; } From 2c70cc162fe3ee99ee99dace164e35618d323fa0 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 14 May 2018 09:59:03 -0400 Subject: [PATCH 04/38] Hook UI actions to API --- .../plugins/security/public/objects/index.js | 2 +- .../objects/lib/{get_roles.js => roles.js} | 8 +++ .../public/views/management/edit_role.js | 2 + .../edit_role/components/edit_role_page.js | 70 ++++++++++++++----- 4 files changed, 63 insertions(+), 19 deletions(-) rename x-pack/plugins/security/public/objects/lib/{get_roles.js => roles.js} (72%) diff --git a/x-pack/plugins/security/public/objects/index.js b/x-pack/plugins/security/public/objects/index.js index ce1c8f6aac3df..88575e4e24677 100644 --- a/x-pack/plugins/security/public/objects/index.js +++ b/x-pack/plugins/security/public/objects/index.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getRole } from './lib/get_roles'; +export { getRole, saveRole, deleteRole } from './lib/roles'; export { getUsers } from './lib/get_users'; diff --git a/x-pack/plugins/security/public/objects/lib/get_roles.js b/x-pack/plugins/security/public/objects/lib/roles.js similarity index 72% rename from x-pack/plugins/security/public/objects/lib/get_roles.js rename to x-pack/plugins/security/public/objects/lib/roles.js index 784ba57570605..c6b68d283569c 100644 --- a/x-pack/plugins/security/public/objects/lib/get_roles.js +++ b/x-pack/plugins/security/public/objects/lib/roles.js @@ -7,6 +7,10 @@ import chrome from 'ui/chrome'; const apiBase = chrome.addBasePath(`/api/security/v1/roles`); +export async function saveRole($http, role) { + return await $http.post(`${apiBase}/${role.name}`, role); +} + export async function getRole($http, name) { try { return await $http.get(`${apiBase}/${name}`); @@ -17,3 +21,7 @@ export async function getRole($http, name) { throw response; } } + +export async function deleteRole($http, name) { + return await $http.delete(`${apiBase}/${name}`); +} diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index 33cc6c42a4f40..3110c0f8aedda 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -69,6 +69,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { controller($injector, $scope, $http, rbacEnabled) { const $route = $injector.get('$route'); const Private = $injector.get('Private'); + const Notifier = $injector.get('Notifier'); const xpackInfo = Private(XPackInfoProvider); const allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); @@ -105,6 +106,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { breadcrumbs={routes.getBreadcrumbs()} allowDocumentLevelSecurity={allowDocumentLevelSecurity} allowFieldLevelSecurity={allowFieldLevelSecurity} + notifier={Notifier} />, domNode); // unmount react on controller destroy diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 94ba1482604c8..9124816a03aa8 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -5,7 +5,8 @@ */ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import { difference } from 'lodash'; +import { difference, get } from 'lodash'; +import { Notifier, toastNotifications } from 'ui/notify'; import { EuiText, EuiSpacer, @@ -26,13 +27,15 @@ import { import { PageHeader } from './page_header'; import { IndexPrivilegeForm } from './index_privilege_form'; import { ClusterPrivileges } from './cluster_privileges'; -import { getFields } from '../../../../objects'; +import { getFields, saveRole, deleteRole } from '../../../../objects'; import { isReservedRole } from '../lib/is_reserved_role'; import { RoleValidator } from '../lib/validate_role'; import { ReservedRoleBadge } from './reserved_role_badge'; import { ROLES_PATH } from '../../management_urls'; import { DeleteRoleButton } from './delete_role_button'; +const notifier = new Notifier(); + export class EditRolePage extends Component { static propTypes = { role: PropTypes.object.isRequired, @@ -42,7 +45,7 @@ export class EditRolePage extends Component { rbacEnabled: PropTypes.bool.isRequired, spacesEnabled: PropTypes.bool.isRequired, allowDocumentLevelSecurity: PropTypes.bool.isRequired, - allowFieldLevelSecurity: PropTypes.bool.isRequired + allowFieldLevelSecurity: PropTypes.bool.isRequired, }; constructor(props) { @@ -101,7 +104,7 @@ export class EditRolePage extends Component { return ( - + @@ -111,7 +114,7 @@ export class EditRolePage extends Component { {this.getActionButton()} - + @@ -119,14 +122,14 @@ export class EditRolePage extends Component { name={'name'} value={this.state.role.name || ''} onChange={this.onNameChange} - readOnly={isReservedRole(this.props.role)} + readOnly={isReservedRole(this.props.role) || this.editingExistingRole()} /> - + {this.getElasticsearchPrivileges()} @@ -161,7 +164,7 @@ export class EditRolePage extends Component { if (this.editingExistingRole() && !isReservedRole(this.props.role)) { return ( - + ); } @@ -194,9 +197,9 @@ export class EditRolePage extends Component { Cluster Privileges ({role.cluster.length})} + buttonContent={
Cluster Privileges ({role.cluster.length})
} > - +
@@ -206,7 +209,7 @@ export class EditRolePage extends Component { id={'indexPrivilegesAccordion'} buttonContent={
- Index Privileges ( + Index Privileges ( {role.indices.filter(i => i.names.length).length})
} @@ -216,7 +219,7 @@ export class EditRolePage extends Component { Index Privileges allow you to foo the bar while baring the baz

- + {this.getIndexPrivileges()} @@ -225,14 +228,14 @@ export class EditRolePage extends Component { Run As Privileges ({role.run_as.length})} + buttonContent={
Run As Privileges ({role.run_as.length})
} >

Run As Privileges allow you to foo the bar while baring the baz

- + ({ id: username, label: username }))} @@ -374,10 +377,10 @@ export class EditRolePage extends Component { Cluster Privileges} + buttonContent={
Cluster Privileges
} > - - { }}/> + + { }} />
@@ -404,11 +407,42 @@ export class EditRolePage extends Component { this.setState({ formError: null }); + + const { + httpClient, + } = this.props; + + const role = { + ...this.state.role + }; + + role.indices = role.indices.filter(i => !this.isPlaceholderPrivilege(i)); + + saveRole(httpClient, role) + .then(() => { + toastNotifications.addSuccess('Saved role'); + this.backToRoleList(); + }) + .catch(error => { + notifier.error(get(error, 'data.message')); + }); } }; - deleteRole = () => { + handleDeleteRole = () => { + const { + httpClient, + role, + } = this.props; + deleteRole(httpClient, role.name) + .then(() => { + toastNotifications.addSuccess('Deleted role'); + this.backToRoleList(); + }) + .catch(error => { + notifier.error(get(error, 'data.message')); + }); }; backToRoleList = () => { From 036c4e438588ed02e9fb5c7432ed38525946320b Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 14 May 2018 10:29:28 -0400 Subject: [PATCH 05/38] adds some testing --- .../components/delete_role_button.test.js | 41 +++++++++ .../components/reserved_role_badge.test.js | 32 +++++++ .../edit_role/lib/validate_role.test.js | 85 +++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.test.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.test.js new file mode 100644 index 0000000000000..477ffbebf8699 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/delete_role_button.test.js @@ -0,0 +1,41 @@ +/* + * 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 React from 'react'; +import { + EuiButton, + EuiConfirmModal, +} from '@elastic/eui'; +import { DeleteRoleButton } from './delete_role_button'; +import { + shallow, + mount +} from 'enzyme'; + +test('it renders without crashing', () => { + const deleteHandler = jest.fn(); + const wrapper = shallow(); + expect(wrapper.find(EuiButton)).toHaveLength(1); + expect(deleteHandler).toHaveBeenCalledTimes(0); +}); + +test('it shows a confirmation dialog when clicked', () => { + const deleteHandler = jest.fn(); + const wrapper = mount(); + + wrapper.find(EuiButton).simulate('click'); + + expect(wrapper.find(EuiConfirmModal)).toHaveLength(1); + + expect(deleteHandler).toHaveBeenCalledTimes(0); +}); + +test('it renders nothing when canDelete is false', () => { + const deleteHandler = jest.fn(); + const wrapper = shallow(); + expect(wrapper.find('*')).toHaveLength(0); + expect(deleteHandler).toHaveBeenCalledTimes(0); +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js new file mode 100644 index 0000000000000..c9227a415f9ff --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js @@ -0,0 +1,32 @@ +/* + * 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 React from 'react'; +import { + EuiBadge +} from '@elastic/eui'; +import { ReservedRoleBadge } from './reserved_role_badge'; +import { + shallow +} from 'enzyme'; + +const reservedRole = { + metadata: { + _reserved: true + } +}; + +const unreservedRole = {}; + +test('it renders without crashing', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiBadge)).toHaveLength(1); +}); + +test('it renders nothing for an unreserved role', () => { + const wrapper = shallow(); + expect(wrapper.find('*')).toHaveLength(0); +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js new file mode 100644 index 0000000000000..eea22c4d75fb0 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js @@ -0,0 +1,85 @@ +/* + * 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 { RoleValidator } from "./validate_role"; + +let validator; + +describe('validateRoleName', () => { + beforeEach(() => { + validator = new RoleValidator({ shouldValidate: true }); + }); + + test('it allows an alphanumeric role name', () => { + const role = { + name: 'This-is-30-character-role-name' + }; + + expect(validator.validateRoleName(role)).toEqual({ isInvalid: false }); + }); + + test('it requires a non-empty value', () => { + const role = { + name: '' + }; + + expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, error: `Please provide a role name` }); + }); + + test('it cannot exceed 30 characters', () => { + const role = { + name: 'This-is-a31-character-role-name' + }; + + expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, error: `Name must not exceed 30 characters` }); + }); + + const charList = `!#%^&*()+=[]{}\|';:"/,<>?`.split(''); + charList.forEach(element => { + test(`it cannot support the "${element}" character`, () => { + const role = { + name: `role-${element}` + }; + + expect(validator.validateRoleName(role)).toEqual( + { isInvalid: true, + error: `Name must begin with a letter or underscore and contain only letters, underscores, and numbers.` + } + ); + }); + }); +}); + +describe('validateIndexPrivileges', () => { + beforeEach(() => { + validator = new RoleValidator({ shouldValidate: true }); + }); + + test('it ignores privilegs with no indices defined', () => { + const role = { + indices: [{ + names: [], + privileges: [] + }] + }; + + expect(validator.validateIndexPrivileges(role)).toEqual({ + isInvalid: false + }); + }); + + test('it requires privilges when an index is defined', () => { + const role = { + indices: [{ + names: ['index-*'], + privileges: [] + }] + }; + + expect(validator.validateIndexPrivileges(role)).toEqual({ + isInvalid: true + }); + }); +}); From 378e78cd65802a2d15ec0884743bce30a0ab21e5 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 14 May 2018 12:56:10 -0400 Subject: [PATCH 06/38] replace spaces with dashes when typing role name --- .../views/management/edit_role/components/edit_role_page.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 9124816a03aa8..ac4668b2b71ab 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -173,10 +173,13 @@ export class EditRolePage extends Component { }; onNameChange = (e) => { + const rawValue = e.target.value; + const name = rawValue.replace(/\s/g, '-'); + this.setState({ role: { ...this.state.role, - name: e.target.value + name } }); } From d888a93bc82c87550f1693e091210af6dff7f38f Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 15 May 2018 08:03:04 -0400 Subject: [PATCH 07/38] use new isDisabled prop for EuiComoBoxes --- .../views/management/edit_role/components/edit_role_page.js | 1 + .../management/edit_role/components/index_privilege_form.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index ac4668b2b71ab..8f87ff58260ae 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -244,6 +244,7 @@ export class EditRolePage extends Component { options={this.props.runAsUsers.map(username => ({ id: username, label: username }))} selectedOptions={this.state.role.run_as.map(u => ({ label: u }))} onChange={this.onRunAsUserChange} + isDisabled={isReservedRole(this.props.role)} /> diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js index da1df33d69d20..138fe723793b3 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/index_privilege_form.js @@ -67,7 +67,7 @@ export class IndexPrivilegeForm extends Component { selectedOptions={this.props.indexPrivilege.names.map(toOption)} onCreateOption={this.onCreateIndexPatternOption} onChange={this.onIndexPatternsChange} - disabled={this.props.isReservedRole} + isDisabled={this.props.isReservedRole} />
@@ -77,7 +77,7 @@ export class IndexPrivilegeForm extends Component { options={getIndexPrivileges().map(toOption)} selectedOptions={this.props.indexPrivilege.privileges.map(toOption)} onChange={this.onPrivilegeChange} - disabled={this.props.isReservedRole} + isDisabled={this.props.isReservedRole} /> @@ -128,7 +128,7 @@ export class IndexPrivilegeForm extends Component { selectedOptions={grant.map(toOption)} onCreateOption={this.onCreateGrantedField} onChange={this.onGrantedFieldsChange} - disabled={this.props.isReservedRole} + isDisabled={this.props.isReservedRole} /> From 63028de17aa58f7a8b7a115277f49664681ef1fa Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 15 May 2018 15:00:59 -0400 Subject: [PATCH 08/38] Update version of EUI to get disable-able ComboBox components --- package.json | 2 +- x-pack/package.json | 2 +- x-pack/yarn.lock | 468 +++----------------------------------------- yarn.lock | 352 ++++++--------------------------- 4 files changed, 82 insertions(+), 742 deletions(-) diff --git a/package.json b/package.json index 9c6acdfd45264..5bdf13c020631 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "url": "https://github.com/elastic/kibana.git" }, "dependencies": { - "@elastic/eui": "v0.0.44", + "@elastic/eui": "v0.0.47", "@elastic/filesaver": "1.1.2", "@elastic/numeral": "2.3.2", "@elastic/ui-ace": "0.2.3", diff --git a/x-pack/package.json b/x-pack/package.json index 95c3ecd15dfb4..bfcb1dd5a9905 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -75,7 +75,7 @@ "yargs": "4.7.1" }, "dependencies": { - "@elastic/eui": "0.0.44", + "@elastic/eui": "0.0.47", "@elastic/node-crypto": "0.1.2", "@elastic/node-phantom-simple": "2.2.4", "@elastic/numeral": "2.3.2", diff --git a/x-pack/yarn.lock b/x-pack/yarn.lock index 9747bb69a2f7c..fb4184fbbb632 100644 --- a/x-pack/yarn.lock +++ b/x-pack/yarn.lock @@ -10,30 +10,25 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@elastic/eui@0.0.44": - version "0.0.44" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.44.tgz#b0b58eb1b10d6f8de017f548bb06c5b5f71e3d61" +"@elastic/eui@0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.47.tgz#5bae27966bb1d68bb3106853610a407509053b44" dependencies: brace "^0.10.0" classnames "^2.2.5" core-js "^2.5.1" - eslint-config-prettier "^2.9.0" - eslint-plugin-prettier "^2.6.0" focus-trap-react "^3.0.4" highlight.js "^9.12.0" html "^1.0.0" - jquery "^3.2.1" keymirror "^0.1.1" lodash "^3.10.1" numeral "^2.0.6" - prettier "^1.11.1" prop-types "^15.6.0" react-ace "^5.5.0" react-color "^2.13.8" react-datepicker v1.4.1 react-input-autosize "^2.2.1" react-virtualized "^9.18.5" - serve "^6.3.1" tabbable "^1.1.0" uuid "^3.1.0" @@ -97,13 +92,6 @@ accept@2.x.x: boom "5.x.x" hoek "4.x.x" -accepts@~1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - acorn-globals@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" @@ -122,10 +110,6 @@ add-event-listener@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/add-event-listener/-/add-event-listener-0.0.1.tgz#a76229ebc64c8aefae204a16273a2f255abea2d0" -address@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" - agentkeepalive@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" @@ -185,12 +169,6 @@ angular-ui-bootstrap@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/angular-ui-bootstrap/-/angular-ui-bootstrap-1.2.5.tgz#b0c1eff0bf3b7a65668984a1b81820a90dc60995" -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - dependencies: - string-width "^2.0.0" - ansi-cyan@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" @@ -276,10 +254,6 @@ aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" -arch@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.0.tgz#3613aa46149064b3c1f0607919bf1d4786e82889" - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -304,15 +278,6 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" -args@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/args/-/args-4.0.0.tgz#5ca24cdba43d4b17111c56616f5f2e9d91933954" - dependencies: - camelcase "5.0.0" - chalk "2.3.2" - leven "2.1.0" - mri "1.1.0" - argv-split@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argv-split/-/argv-split-2.0.1.tgz#be264117790dbd5ccd63ec3f449a1804814ac4c5" @@ -947,12 +912,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" - dependencies: - safe-buffer "5.1.1" - bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -979,7 +938,7 @@ bluebird@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.1.1.tgz#7e2e4318d62ae72a674f6aea6357bb4def1a6e41" -bluebird@3.5.1, bluebird@^3.3.1: +bluebird@^3.3.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -1017,18 +976,6 @@ boom@5.x.x: dependencies: hoek "4.x.x" -boxen@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" - brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" @@ -1144,10 +1091,6 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1180,10 +1123,6 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -1196,7 +1135,7 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1232,22 +1171,6 @@ chai@~1.9.2: assertion-error "1.0.0" deep-eql "0.1.3" -chalk@2.3.2, chalk@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1266,6 +1189,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chance@1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.10.tgz#03500b04ad94e778dd2891b09ec73a6ad87b1996" @@ -1312,10 +1243,6 @@ classnames@2.2.5, classnames@^2.1.2, classnames@^2.2.3, classnames@^2.2.4, class version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" @@ -1334,13 +1261,6 @@ clipboard@^1.6.1: select "^1.1.2" tiny-emitter "^2.0.0" -clipboardy@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" - dependencies: - arch "^2.1.0" - execa "^0.8.0" - cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -1460,24 +1380,6 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" -compressible@~2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" - dependencies: - mime-db ">= 1.33.0 < 2" - -compression@^1.6.2: - version "1.7.2" - resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" - dependencies: - accepts "~1.3.4" - bytes "3.0.0" - compressible "~2.0.13" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.1" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1514,10 +1416,6 @@ content-type-parser@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" -content-type@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - content@3.x.x: version "3.0.6" resolved "https://registry.yarnpkg.com/content/-/content-3.0.6.tgz#9c2e301e9ae515ed65a4b877d78aa5659bb1b809" @@ -1789,10 +1687,6 @@ d3@3.5.6: version "3.5.6" resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.6.tgz#9451c651ca733fb9672c81fb7f2655164a73a42d" -dargs@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1828,7 +1722,7 @@ debug@2.2.0: dependencies: ms "0.7.1" -debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8: +debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1937,22 +1831,10 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - deprecated@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -1971,13 +1853,6 @@ detect-newline@2.X, detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" -detect-port@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.2.2.tgz#57a44533632d8bc74ad255676866ca43f96c7469" - dependencies: - address "^1.0.1" - debug "^2.6.0" - dfa@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.1.0.tgz#d30218bd10d030fa421df3ebbc82285463a31781" @@ -2089,10 +1964,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - elasticsearch@13.0.1: version "13.0.1" resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-13.0.1.tgz#fa58204233052c4cd221e8721e48f3906b385b32" @@ -2104,10 +1975,6 @@ elasticsearch@13.0.1: lodash.isempty "^4.4.0" lodash.trimend "^4.5.1" -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -2200,10 +2067,6 @@ es6-promise@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.0.1.tgz#ccc4963e679f0ca9fb187c777b9e583d3c7573c2" -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - escape-string-regexp@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" @@ -2252,19 +2115,6 @@ escodegen@~1.3.2: optionalDependencies: source-map "~0.1.33" -eslint-config-prettier@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz#5ecd65174d486c22dff389fe036febf502d468a3" - dependencies: - get-stdin "^5.0.1" - -eslint-plugin-prettier@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz#33e4e228bdb06142d03c560ce04ec23f6c767dd7" - dependencies: - fast-diff "^1.1.1" - jest-docblock "^21.0.0" - esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -2301,10 +2151,6 @@ esutils@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - exec-sh@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38" @@ -2335,18 +2181,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -2488,10 +2322,6 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" -fast-diff@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" - fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -2550,10 +2380,6 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" -filesize@3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -2731,22 +2557,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - from@^0.1.3: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" -fs-extra@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-mkdirp-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" @@ -2833,10 +2647,6 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" -get-stdin@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" - get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -3236,7 +3046,7 @@ gulplog@^1.0.0: dependencies: glogg "^1.0.0" -handlebars@4.0.11, handlebars@^4.0.3: +handlebars@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: @@ -3475,15 +3285,6 @@ htmlparser2@^3.9.1: inherits "^2.0.1" readable-stream "^2.0.2" -http-errors@1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" - dependencies: - depd "1.1.1" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - http-errors@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.4.0.tgz#6c0242dea6b3df7afda153c71089b31c6e82aabf" @@ -3491,15 +3292,6 @@ http-errors@~1.4.0: inherits "2.0.1" statuses ">= 1.2.1 < 2" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -3566,7 +3358,7 @@ inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -3611,10 +3403,6 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -ip@1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - iron@4.x.x: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" @@ -3834,7 +3622,7 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-stream@1.1.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3872,10 +3660,6 @@ is-windows@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4065,10 +3849,6 @@ jest-diff@^22.4.3: jest-get-type "^22.4.3" pretty-format "^22.4.3" -jest-docblock@^21.0.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" - jest-docblock@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.4.3.tgz#50886f132b42b280c903c592373bb6e93bb68b19" @@ -4305,7 +4085,7 @@ jquery@^3.1.1: version "3.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787" -jquery@^3.2.1, jquery@^3.3.1: +jquery@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" @@ -4383,12 +4163,6 @@ json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - optionalDependencies: - graceful-fs "^4.1.6" - jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -4476,7 +4250,7 @@ left-pad@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee" -leven@2.1.0, leven@^2.1.0: +leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" @@ -4858,21 +4632,6 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micro-compress@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micro-compress/-/micro-compress-1.0.0.tgz#53f5a80b4ad0320ca165a559b6e3df145d4f704f" - dependencies: - compression "^1.6.2" - -micro@9.1.4: - version "9.1.4" - resolved "https://registry.yarnpkg.com/micro/-/micro-9.1.4.tgz#dbe655f34bb3390509898ddf3fda12348f5cbaa9" - dependencies: - content-type "1.0.4" - is-stream "1.1.0" - mri "1.1.0" - raw-body "2.3.2" - micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -4913,30 +4672,16 @@ mime-db@1.x.x: version "1.32.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414" -"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -mime-types@2.1.18, mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - dependencies: - mime-db "~1.33.0" - mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: mime-db "~1.30.0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -5067,10 +4812,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.13.0, moment@^2.20.1: version "2.20.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" -mri@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" - ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -5141,10 +4882,6 @@ nearley@^2.7.10: railroad-diagrams "^1.0.0" randexp "^0.4.2" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - ngreact@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/ngreact/-/ngreact-0.5.1.tgz#2dcccc1541771796689d13e51bb8d5010af41c57" @@ -5200,10 +4937,6 @@ node-pre-gyp@^0.6.39: tar "^2.2.1" tar-pack "^3.4.0" -node-version@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.1.3.tgz#1081c87cce6d2dbbd61d0e51e28c287782678496" - nomnom@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -5385,16 +5118,6 @@ object.values@^1.0.4: function-bind "^1.1.0" has "^1.0.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5411,16 +5134,6 @@ onetime@^1.0.0: version "1.1.0" resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" -openssl-self-signed-certificate@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz#9d3a4776b1a57e9847350392114ad2f915a83dd4" - -opn@5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" - dependencies: - is-wsl "^1.1.0" - optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -5582,7 +5295,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@1.0.2, path-is-inside@^1.0.1: +path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -5617,12 +5330,6 @@ path-to-regexp@^1.0.0, path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -5685,10 +5392,6 @@ pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -5793,10 +5496,6 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.11.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" - pretty-format@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" @@ -5969,28 +5668,6 @@ randomatic@^1.1.3: is-number "^3.0.0" kind-of "^4.0.0" -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -raw-body@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" - dependencies: - bytes "3.0.0" - http-errors "1.6.2" - iconv-lite "0.4.19" - unpipe "1.0.0" - -rc@^1.0.1, rc@^1.1.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - rc@^1.1.7: version "1.2.3" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.3.tgz#51575a900f8dd68381c710b4712c2154c3e2035b" @@ -6376,19 +6053,6 @@ regexpu-core@^2.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -registry-auth-token@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - dependencies: - rc "^1.0.1" - regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" @@ -6671,7 +6335,7 @@ rxjs@5.3.0: dependencies: symbol-observable "^1.0.1" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -6733,55 +6397,10 @@ semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - sequencify@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" -serve@^6.3.1: - version "6.5.6" - resolved "https://registry.yarnpkg.com/serve/-/serve-6.5.6.tgz#579136688f80f6bf4a618ca8e8cba10dfb4d95e3" - dependencies: - args "4.0.0" - basic-auth "2.0.0" - bluebird "3.5.1" - boxen "1.3.0" - chalk "2.4.0" - clipboardy "1.2.3" - dargs "5.1.0" - detect-port "1.2.2" - filesize "3.6.1" - fs-extra "5.0.0" - handlebars "4.0.11" - ip "1.1.5" - micro "9.1.4" - micro-compress "1.0.0" - mime-types "2.1.18" - node-version "1.1.3" - openssl-self-signed-certificate "1.1.6" - opn "5.3.0" - path-is-inside "1.0.2" - path-type "3.0.0" - send "0.16.2" - update-check "1.3.2" - set-blocking@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-1.0.0.tgz#cd5e5d938048df1ac92dfe92e1f16add656f5ec5" @@ -6818,14 +6437,6 @@ setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - shallow-copy@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" @@ -7074,14 +6685,10 @@ static-module@^1.1.0: static-eval "~0.2.0" through2 "~0.4.1" -"statuses@>= 1.2.1 < 2", statuses@~1.4.0: +"statuses@>= 1.2.1 < 2": version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" -"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -7339,12 +6946,6 @@ temp@^0.8.3: os-tmpdir "^1.0.0" rimraf "~2.2.6" -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - dependencies: - execa "^0.7.0" - test-exclude@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26" @@ -7648,14 +7249,6 @@ unique-stream@^2.0.2: json-stable-stringify "^1.0.0" through2-filter "^2.0.0" -universalify@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" - -unpipe@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -7663,13 +7256,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -update-check@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.3.2.tgz#460f9e9ab24820367f3edbeb4d4142d9936ff171" - dependencies: - registry-auth-token "3.3.2" - registry-url "3.1.0" - urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -7732,10 +7318,6 @@ value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - venn.js@0.2.9: version "0.2.9" resolved "https://registry.yarnpkg.com/venn.js/-/venn.js-0.2.9.tgz#33c29075efa484731d59d884752900cc33033656" @@ -7934,12 +7516,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2" -widest-line@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" - dependencies: - string-width "^2.1.1" - window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" diff --git a/yarn.lock b/yarn.lock index c544a9acbb457..2098dda85233c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,30 +77,25 @@ version "0.0.0" uid "" -"@elastic/eui@0.0.44", "@elastic/eui@v0.0.44": - version "0.0.44" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.44.tgz#b0b58eb1b10d6f8de017f548bb06c5b5f71e3d61" +"@elastic/eui@0.0.47", "@elastic/eui@v0.0.47": + version "0.0.47" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.47.tgz#5bae27966bb1d68bb3106853610a407509053b44" dependencies: brace "^0.10.0" classnames "^2.2.5" core-js "^2.5.1" - eslint-config-prettier "^2.9.0" - eslint-plugin-prettier "^2.6.0" focus-trap-react "^3.0.4" highlight.js "^9.12.0" html "^1.0.0" - jquery "^3.2.1" keymirror "^0.1.1" lodash "^3.10.1" numeral "^2.0.6" - prettier "^1.11.1" prop-types "^15.6.0" react-ace "^5.5.0" react-color "^2.13.8" react-datepicker v1.4.1 react-input-autosize "^2.2.1" react-virtualized "^9.18.5" - serve "^6.3.1" tabbable "^1.1.0" uuid "^3.1.0" @@ -181,14 +176,6 @@ version "9.4.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.7.tgz#57d81cd98719df2c9de118f2d5f3b1120dcd7275" -"@zeit/check-updates@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@zeit/check-updates/-/check-updates-1.1.0.tgz#d0f65026a36f27cd1fd54c647d8294447c1d2d8b" - dependencies: - chalk "2.3.0" - ms "2.1.1" - update-notifier "2.3.0" - JSONStream@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.1.1.tgz#c98bfd88c8f1e1e8694e53c5baa6c8691553e59a" @@ -226,13 +213,6 @@ accepts@1.3.3: mime-types "~2.1.11" negotiator "0.6.1" -accepts@~1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -281,10 +261,6 @@ add-event-listener@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/add-event-listener/-/add-event-listener-0.0.1.tgz#a76229ebc64c8aefae204a16273a2f255abea2d0" -address@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" - adm-zip@0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" @@ -506,10 +482,6 @@ aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" -arch@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.0.tgz#3613aa46149064b3c1f0607919bf1d4786e82889" - are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" @@ -530,16 +502,6 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" -args@3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/args/-/args-3.0.8.tgz#2f425ab639c69d74ff728f3d7c6e93b97b91af7c" - dependencies: - camelcase "4.1.0" - chalk "2.1.0" - mri "1.1.0" - pkginfo "0.4.1" - string-similarity "1.2.0" - arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -1560,12 +1522,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -basic-auth@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" - dependencies: - safe-buffer "5.1.1" - batch-processor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" @@ -1622,14 +1578,14 @@ bluebird@3.4.6: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" -bluebird@3.5.1, bluebird@^3.3.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - bluebird@^2.10.0, bluebird@^2.9.24: version "2.11.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" +bluebird@^3.3.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + bmp-js@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a" @@ -1716,7 +1672,7 @@ boom@5.2.0, boom@5.x.x: dependencies: hoek "4.x.x" -boxen@1.3.0, boxen@^1.2.1, boxen@^1.2.2: +boxen@^1.2.1, boxen@^1.2.2: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" dependencies: @@ -2064,10 +2020,6 @@ camelcase-keys@^3.0.0: camelcase "^3.0.0" map-obj "^1.0.0" -camelcase@4.1.0, camelcase@^4.0.0, camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -2080,6 +2032,10 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" +camelcase@^4.0.0, camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" @@ -2139,14 +2095,6 @@ chai@3.5.0, "chai@>=1.9.2 <4.0.0": deep-eql "^0.1.3" type-detect "^1.0.0" -chalk@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" - dependencies: - ansi-styles "^3.1.0" - escape-string-regexp "^1.0.5" - supports-color "^4.0.0" - chalk@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" @@ -2155,14 +2103,6 @@ chalk@2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" -chalk@2.3.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2173,6 +2113,14 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3, chalk@~1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -2389,13 +2337,6 @@ clipboard@^1.6.1: select "^1.1.2" tiny-emitter "^2.0.0" -clipboardy@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" - dependencies: - arch "^2.1.0" - execa "^0.8.0" - cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -2607,24 +2548,6 @@ component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" -compressible@~2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" - dependencies: - mime-db ">= 1.33.0 < 2" - -compression@^1.6.2: - version "1.7.2" - resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" - dependencies: - accepts "~1.3.4" - bytes "3.0.0" - compressible "~2.0.13" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.1" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2739,7 +2662,7 @@ content-type-parser@^1.0.1, content-type-parser@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" -content-type@1.0.4, content-type@~1.0.1, content-type@~1.0.4: +content-type@~1.0.1, content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -3301,10 +3224,6 @@ d@1: dependencies: es5-ext "^0.10.9" -dargs@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -3352,7 +3271,7 @@ debug@2.6.0: dependencies: ms "0.7.2" -debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -3531,7 +3450,7 @@ depd@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.0.1.tgz#80aec64c9d6d97e65cc2a9caa93c0aa6abf73aaa" -depd@~1.1.0, depd@~1.1.1, depd@~1.1.2: +depd@~1.1.0, depd@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3542,10 +3461,6 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -3560,13 +3475,6 @@ detect-newline@2.X, detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" -detect-port@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.2.2.tgz#57a44533632d8bc74ad255676866ca43f96c7469" - dependencies: - address "^1.0.1" - debug "^2.6.0" - dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -3845,7 +3753,7 @@ encode-uri-query@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/encode-uri-query/-/encode-uri-query-1.0.0.tgz#d632be4aafe8316c6145007ffb2844c5312b194c" -encodeurl@~1.0.1, encodeurl@~1.0.2: +encodeurl@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4364,10 +4272,6 @@ esutils@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - even-better@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/even-better/-/even-better-7.0.2.tgz#d056f429c90ecc20ee9494aca0a751f743504d2e" @@ -4446,18 +4350,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" @@ -4773,10 +4665,6 @@ fileset@^2.0.2: glob "^7.0.3" minimatch "^3.0.3" -filesize@3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa" - fill-keys@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" @@ -4971,10 +4859,6 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - from@^0.1.3, from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" @@ -4997,14 +4881,6 @@ fs-extra@4.0.3, fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" @@ -5630,9 +5506,9 @@ h2o2@5.1.1: joi "9.X.X" wreck "9.X.X" -handlebars@4.0.11, handlebars@^4.0.1, handlebars@^4.0.3: - version "4.0.11" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" +handlebars@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -5640,9 +5516,9 @@ handlebars@4.0.11, handlebars@^4.0.1, handlebars@^4.0.3: optionalDependencies: uglify-js "^2.6" -handlebars@4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" +handlebars@^4.0.1, handlebars@^4.0.3: + version "4.0.11" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -6250,10 +6126,6 @@ ip-regex@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" -ip@1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - iron@4.x.x: version "4.0.5" resolved "https://registry.yarnpkg.com/iron/-/iron-4.0.5.tgz#4f042cceb8b9738f346b59aa734c83a89bc31428" @@ -6600,7 +6472,7 @@ is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-stream@1.1.0, is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -6658,10 +6530,6 @@ is-word-character@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.1.tgz#5a03fa1ea91ace8a6eb0c7cd770eb86d65c8befb" -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -7336,7 +7204,7 @@ jpeg-js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.2.0.tgz#53e448ec9d263e683266467e9442d2c5a2ef5482" -jquery@^3.1.1, jquery@^3.2.1, jquery@^3.3.1: +jquery@^3.1.1, jquery@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" @@ -8421,21 +8289,6 @@ methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micro-compress@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micro-compress/-/micro-compress-1.0.0.tgz#53f5a80b4ad0320ca165a559b6e3df145d4f704f" - dependencies: - compression "^1.6.2" - -micro@9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/micro/-/micro-9.1.0.tgz#f2effba306639076e994c007c327dfc36a5185e9" - dependencies: - content-type "1.0.4" - is-stream "1.1.0" - mri "1.1.0" - raw-body "2.3.2" - micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -8479,11 +8332,11 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.x.x, "mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: +mime-db@1.x.x, mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-types@2.1.18, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: @@ -8493,10 +8346,6 @@ mime@1.3.x: version "1.3.6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -8656,10 +8505,6 @@ moment@2.x.x, "moment@>= 2.9.0", moment@^2.10.6, moment@^2.13.0, moment@^2.20.1: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" -mri@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.0.tgz#5c0a3f29c8ccffbbb1ec941dcec09d71fa32f36a" - ms@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" @@ -8676,7 +8521,7 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -ms@2.1.1, ms@^2.0.0: +ms@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" @@ -8879,10 +8724,6 @@ node-status-codes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" -node-version@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.1.0.tgz#f437d7ba407e65e2c4eaef8887b1718ba523d4f0" - nomnom@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -9106,10 +8947,6 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -9126,16 +8963,6 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -openssl-self-signed-certificate@1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/openssl-self-signed-certificate/-/openssl-self-signed-certificate-1.1.6.tgz#9d3a4776b1a57e9847350392114ad2f915a83dd4" - -opn@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225" - dependencies: - is-wsl "^1.1.0" - optimist@^0.6.1, optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -9387,7 +9214,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1, path-is-absolute@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@1.0.2, path-is-inside@^1.0.1, path-is-inside@^1.0.2: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -9412,12 +9239,6 @@ path-to-regexp@^1.0.0, path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@3.0.0, path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - dependencies: - pify "^3.0.0" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -9432,6 +9253,12 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -9564,10 +9391,6 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" -pkginfo@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" - plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" @@ -9903,10 +9726,6 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettier@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75" - prettier@^1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325" @@ -10181,7 +10000,7 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@^1.2.0, range-parser@~1.2.0: +range-parser@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -11109,7 +10928,7 @@ rxjs@5.4.3: dependencies: symbol-observable "^1.0.1" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -11226,51 +11045,6 @@ semver@~4.3.3: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - -serve@^6.3.1: - version "6.5.3" - resolved "https://registry.yarnpkg.com/serve/-/serve-6.5.3.tgz#39ae7b7ff5934a9ca93ba7235344eb34b726cc48" - dependencies: - "@zeit/check-updates" "1.1.0" - args "3.0.8" - basic-auth "2.0.0" - bluebird "3.5.1" - boxen "1.3.0" - chalk "2.3.2" - clipboardy "1.2.3" - dargs "5.1.0" - detect-port "1.2.2" - filesize "3.6.0" - fs-extra "5.0.0" - handlebars "4.0.11" - ip "1.1.5" - micro "9.1.0" - micro-compress "1.0.0" - mime-types "2.1.18" - node-version "1.1.0" - openssl-self-signed-certificate "1.1.6" - opn "5.2.0" - path-is-inside "1.0.2" - path-type "3.0.0" - send "0.16.2" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -11745,7 +11519,7 @@ static-module@^2.2.0: static-eval "^2.0.0" through2 "~2.0.3" -statuses@1, "statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2", statuses@~1.4.0: +statuses@1, "statuses@>= 1.2.1 < 2", "statuses@>= 1.3.1 < 2": version "1.4.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" @@ -11817,12 +11591,6 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" -string-similarity@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-1.2.0.tgz#d75153cb383846318b7a39a8d9292bb4db4e9c30" - dependencies: - lodash "^4.13.1" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -12720,7 +12488,19 @@ upath@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" -update-notifier@2.3.0, update-notifier@^2.2.0: +update-notifier@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" + dependencies: + chalk "^1.0.0" + configstore "^1.0.0" + is-npm "^1.0.0" + latest-version "^1.0.0" + repeating "^1.1.2" + semver-diff "^2.0.0" + string-length "^1.0.0" + +update-notifier@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" dependencies: @@ -12734,18 +12514,6 @@ update-notifier@2.3.0, update-notifier@^2.2.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" -update-notifier@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" - dependencies: - chalk "^1.0.0" - configstore "^1.0.0" - is-npm "^1.0.0" - latest-version "^1.0.0" - repeating "^1.1.2" - semver-diff "^2.0.0" - string-length "^1.0.0" - urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -12857,10 +12625,6 @@ value-or-function@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - vega-canvas@1: version "1.0.1" resolved "https://registry.yarnpkg.com/vega-canvas/-/vega-canvas-1.0.1.tgz#22cfa510af0cfbd920fc6af8b6111d3de5e63c44" From 3984c2b31a76aed72629fe1f5e525665e6111b7f Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 16 May 2018 13:53:19 -0400 Subject: [PATCH 09/38] working port from rbac-phase-1 --- .../public/views/management/edit_role.js | 27 +----- .../edit_role/components/edit_role_page.js | 40 +++++---- .../{ => privileges}/cluster_privileges.js | 8 +- .../{ => privileges}/index_privilege_form.js | 2 +- .../privileges/kibana_privileges.js | 83 +++++++++++++++++++ .../lib/get_application_privileges.js | 26 ++++++ .../lib/set_application_privileges.js | 20 +++++ 7 files changed, 164 insertions(+), 42 deletions(-) rename x-pack/plugins/security/public/views/management/edit_role/components/{ => privileges}/cluster_privileges.js (88%) rename x-pack/plugins/security/public/views/management/edit_role/components/{ => privileges}/index_privilege_form.js (98%) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/get_application_privileges.js diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index de26a66847dee..9c19ccacaf824 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -26,26 +26,6 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -const getKibanaPrivileges = (kibanaApplicationPrivilege, role, application) => { - const kibanaPrivileges = kibanaApplicationPrivilege.reduce((acc, p) => { - acc[p.name] = false; - return acc; - }, {}); - - if (!role.applications || role.applications.length === 0) { - return kibanaPrivileges; - } - - const applications = role.applications.filter(x => x.application === application); - - const assigned = _.uniq(_.flatten(_.pluck(applications, 'privileges'))); - assigned.forEach(a => { - kibanaPrivileges[a] = true; - }); - - return kibanaPrivileges; -}; - const getOtherApplications = (kibanaPrivileges, role, application) => { if (!role.applications || role.applications.length === 0) { return []; @@ -82,6 +62,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { }, kibanaApplicationPrivilege(ApplicationPrivilege, kbnUrl, Promise, Private) { return ApplicationPrivilege.query().$promise + .then(privileges => privileges.map(p => p.toJSON())) .catch(checkLicenseError(kbnUrl, Promise, Private)); }, users(ShieldUser, kbnUrl, Promise, Private) { @@ -109,7 +90,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { $scope.rbacEnabled = rbacEnabled; const kibanaApplicationPrivilege = $route.current.locals.kibanaApplicationPrivilege; const role = $route.current.locals.role; - $scope.kibanaPrivileges = getKibanaPrivileges(kibanaApplicationPrivilege, role, rbacApplication); + $scope.kibanaPrivileges = {};// getKibanaPrivileges(kibanaApplicationPrivilege, role, rbacApplication); $scope.otherApplications = getOtherApplications(kibanaApplicationPrivilege, role, rbacApplication); $scope.rolesHref = `#${ROLES_PATH}`; @@ -144,10 +125,10 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { render( { - if (!this.props.kibanaPrivilegesEnabled) { + if (!this.props.rbacEnabled) { return null; } return ( - - Cluster Privileges} - > - - { }} /> - - + +

Kibana

+
+ + + +
); }; + onKibanaPrivilegesChange = (applications) => { + this.setState({ + role: { + ...this.state.role, + applications + } + }); + } + editingExistingRole = () => { return !!this.props.role.name; }; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js similarity index 88% rename from x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js rename to x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js index 8ed97e8aad5c1..8193b73e022bf 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/cluster_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js @@ -6,15 +6,15 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import { getClusterPrivileges } from '../../../../services/role_privileges'; -import { isReservedRole } from '../lib/is_reserved_role'; +import { getClusterPrivileges } from '../../../../../services/role_privileges'; +import { isReservedRole } from '../../lib/is_reserved_role'; import { EuiCheckboxGroup, EuiText, EuiSpacer, EuiLink, } from '@elastic/eui'; -import { CLUSTER_PRIVS_DOC_LINK } from '../lib/constants'; +import { CLUSTER_PRIVS_DOC_LINK } from '../../lib/constants'; export class ClusterPrivileges extends Component { static propTypes = { @@ -42,7 +42,7 @@ export class ClusterPrivileges extends Component { Learn more

- + option.label; const toOption = (value) => ({ label: value }); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js new file mode 100644 index 0000000000000..4f26e847ab84c --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js @@ -0,0 +1,83 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { isReservedRole } from '../../lib/is_reserved_role'; +import { getKibanaPrivileges } from '../../lib/get_application_privileges'; +import { togglePrivilege } from '../../lib/set_application_privileges'; +import { + EuiCheckboxGroup, + EuiText, + EuiSpacer, +} from '@elastic/eui'; + + +export class KibanaPrivileges extends Component { + static propTypes = { + role: PropTypes.object.isRequired, + kibanaAppPrivileges: PropTypes.array.isRequired, + onChange: PropTypes.func.isRequired, + }; + + idPrefix = () => `${this.props.rbacApplication}_`; + + privilegeToId = (privilege) => `${this.idPrefix()}${privilege}`; + + idToPrivilege = (id) => id.split(this.idPrefix())[1]; + + render() { + + const { + kibanaAppPrivileges, + role, + rbacApplication + } = this.props; + + const kibanaPrivileges = getKibanaPrivileges(kibanaAppPrivileges, role, rbacApplication); + + const checkboxes = Object.keys(kibanaPrivileges).map(p => ({ + id: this.privilegeToId(p), + label: p + })); + + const selectionMap = Object.keys(kibanaPrivileges).reduce((acc, p) => { + return { + ...acc, + [this.privilegeToId(p)]: kibanaPrivileges[p] + }; + }, {}); + + return ( + + +

+ Manage the actions this role can perform against Kibana.  +

+
+ + +
+ ); + } + + onKibanaPrivilegesChange = (privilege) => { + console.log('kib priv change', privilege); + const role = { + ...this.props.role, + applications: [...this.props.role.applications] + }; + + togglePrivilege(role, this.props.rbacApplication, this.idToPrivilege(privilege)); + + this.props.onChange(role.applications); + } +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/get_application_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/lib/get_application_privileges.js new file mode 100644 index 0000000000000..1ee8d201806b6 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/get_application_privileges.js @@ -0,0 +1,26 @@ +/* + * 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 _ from 'lodash'; + +export function getKibanaPrivileges(kibanaApplicationPrivilege, role, application) { + const kibanaPrivileges = kibanaApplicationPrivilege.reduce((acc, p) => { + acc[p.name] = false; + return acc; + }, {}); + + if (!role.applications || role.applications.length === 0) { + return kibanaPrivileges; + } + + const applications = role.applications.filter(x => x.application === application); + + const assigned = _.uniq(_.flatten(_.pluck(applications, 'privileges'))); + assigned.forEach(a => { + kibanaPrivileges[a] = true; + }); + + return kibanaPrivileges; +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/set_application_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/lib/set_application_privileges.js index e14aad01f6807..e6e133fbf69bd 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/set_application_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/set_application_privileges.js @@ -27,3 +27,23 @@ export function setApplicationPrivileges(kibanaPrivileges, role, application) { }]; } } + +export function togglePrivilege(role, application, permission) { + const appPermissions = role.applications + .find(a => a.application === application && a.resources[0] === DEFAULT_RESOURCE); + + if (!appPermissions) { + role.applications.push({ + application, + privileges: [permission], + resources: [DEFAULT_RESOURCE] + }); + } else { + const indexOfExisting = appPermissions.privileges.indexOf(permission); + if (indexOfExisting >= 0) { + appPermissions.privileges.splice(indexOfExisting, 1); + } else { + appPermissions.privileges.push(permission); + } + } +} From 4f39d2b273223b1b2da2c2dc5956d0a1f2a06e73 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 16 May 2018 15:09:00 -0400 Subject: [PATCH 10/38] refactor permission components --- .../public/views/management/edit_role.html | 197 +--------- .../public/views/management/edit_role.js | 20 - .../edit_role/components/edit_role_page.js | 359 ++++-------------- .../privileges/elasticsearch_privileges.js | 135 +++++++ .../edit_role/components/privileges/index.js | 8 + .../components/privileges/index_privileges.js | 172 +++++++++ .../privileges/kibana_privileges.js | 12 +- 7 files changed, 395 insertions(+), 508 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/index.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js diff --git a/x-pack/plugins/security/public/views/management/edit_role.html b/x-pack/plugins/security/public/views/management/edit_role.html index 198dae71b4485..2e8134b17d29f 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.html +++ b/x-pack/plugins/security/public/views/management/edit_role.html @@ -1,196 +1 @@ -
- - - -
- - -
- -
- -

- - New Role - - - “{{ role.name }}” Role - -

-
- -
- - -
- - Reserved -
-
-
- -
-
-
- - - This role contains application privileges for the {{ otherApplications.join(', ') }} application(s) that can't be edited. - If they are for other instances of Kibana, you must manage those privileges on that Kibana. - -
-
-
- - -
- -
- - - - -
- Name must begin with a letter or underscore and contain only letters, underscores, and numbers. -
- -
- Name is required. -
-
- -
- -
- -
- -
-
- - -
- - -
- -
-
- - -
- - - - {{$item}} - - -
-
-
-
- - -
- -
-
- -
- -
- - - - Cancel - -
-
-
-
-
+
\ No newline at end of file diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index 9c19ccacaf824..27dc0a63141c2 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -25,15 +25,6 @@ import { EditRolePage } from './edit_role/components/edit_role_page'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; - -const getOtherApplications = (kibanaPrivileges, role, application) => { - if (!role.applications || role.applications.length === 0) { - return []; - } - - return role.applications.map(x => x.application).filter(x => x !== application); -}; - routes.when(`${EDIT_ROLES_PATH}/:name?`, { template, resolve: { @@ -83,17 +74,8 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { const Notifier = $injector.get('Notifier'); - $scope.role = $route.current.locals.role; - $scope.users = $route.current.locals.users; - $scope.indexPatterns = $route.current.locals.indexPatterns; - - $scope.rbacEnabled = rbacEnabled; const kibanaApplicationPrivilege = $route.current.locals.kibanaApplicationPrivilege; const role = $route.current.locals.role; - $scope.kibanaPrivileges = {};// getKibanaPrivileges(kibanaApplicationPrivilege, role, rbacApplication); - $scope.otherApplications = getOtherApplications(kibanaApplicationPrivilege, role, rbacApplication); - - $scope.rolesHref = `#${ROLES_PATH}`; this.isNewRole = $route.current.params.name == null; this.fieldOptions = {}; @@ -141,7 +123,5 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { $scope.$on('$destroy', () => { unmountComponentAtNode(domNode); }); - - $scope.union = _.flow(_.union, _.compact); } }); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index a276688eca4f3..cf8b77a77f72f 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -3,38 +3,31 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { difference, get } from 'lodash'; +import { get } from 'lodash'; import { Notifier, toastNotifications } from 'ui/notify'; import { EuiText, EuiSpacer, - EuiIcon, - EuiAccordion, - EuiComboBox, EuiPage, EuiPageContent, - EuiPanel, EuiForm, EuiFormRow, EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiButton, - EuiTitle, } from '@elastic/eui'; import { PageHeader } from './page_header'; -import { IndexPrivilegeForm } from './privileges/index_privilege_form'; -import { ClusterPrivileges } from './privileges/cluster_privileges'; -import { getFields, saveRole, deleteRole } from '../../../../objects'; +import { saveRole, deleteRole } from '../../../../objects'; import { isReservedRole } from '../lib/is_reserved_role'; import { RoleValidator } from '../lib/validate_role'; import { ReservedRoleBadge } from './reserved_role_badge'; import { ROLES_PATH } from '../../management_urls'; import { DeleteRoleButton } from './delete_role_button'; import { setApplicationPrivileges } from '../lib/set_application_privileges'; -import { KibanaPrivileges } from './privileges/kibana_privileges'; +import { ElasticsearchPrivileges, KibanaPrivileges } from './privileges'; const notifier = new Notifier(); @@ -56,112 +49,52 @@ export class EditRolePage extends Component { super(props); this.state = { role: props.role, - availableFields: {}, formError: null }; this.validator = new RoleValidator({ shouldValidate: false }); } - componentDidMount() { - this.loadAvailableFields(this.state.role.indices); - } - - loadAvailableFields(indices) { - const patterns = indices.map(index => index.names.join(',')); - - const cachedPatterns = Object.keys(this.state.availableFields); - const patternsToFetch = difference(patterns, cachedPatterns); - - const fetchRequests = patternsToFetch.map(this.loadFieldsForPattern); - - Promise.all(fetchRequests) - .then(response => { - - this.setState({ - availableFields: { - ...this.state.availableFields, - ...response.reduce((acc, o) => ({ ...acc, ...o }), {}) - } - }); - }); - } - - loadFieldsForPattern = async (pattern) => { - if (!pattern) return { [pattern]: [] }; - - try { - return { - [pattern]: await getFields(this.props.httpClient, pattern) - }; - - } catch (e) { - return { - [pattern]: [] - }; - } - } - render() { - const { - validator - } = this; - return ( - - -

{this.getTitle()}

-
- {this.getActionButton()} -
- - - - - - - - + + {this.getRoleName()} {this.getElasticsearchPrivileges()} + + {this.getKibanaPrivileges()} - - - Save - - - - Cancel - - - + {this.getFormButtons()}
); } - getTitle = () => { - if (this.editingExistingRole()) { - return `Edit role`; - } - return `New Role`; + getFormTitle = () => { + const titleText = this.editingExistingRole() + ? 'Edit Role' + : 'New Role'; + + return ( + + +

{titleText}

+
+ {this.getActionButton()} +
+ ); }; getActionButton = () => { @@ -176,6 +109,24 @@ export class EditRolePage extends Component { return null; }; + getRoleName = () => { + return ( + + + + + + + + + ); + } + onNameChange = (e) => { const rawValue = e.target.value; const name = rawValue.replace(/\s/g, '-'); @@ -188,190 +139,24 @@ export class EditRolePage extends Component { }); } - getElasticsearchPrivileges = () => { - const { - role - } = this.state; - + getElasticsearchPrivileges() { return ( - - -

Elasticsearch

-
- - - - - Cluster Privileges ({role.cluster.length})
} - > - - - - - - - - Index Privileges ( - {role.indices.filter(i => i.names.length).length}) -
- } - > - -

- Index Privileges allow you to foo the bar while baring the baz -

-
- - {this.getIndexPrivileges()} - - - - - - Run As Privileges ({role.run_as.length})} - > - -

- Run As Privileges allow you to foo the bar while baring the baz -

-
- - ({ id: username, label: username }))} - selectedOptions={this.state.role.run_as.map(u => ({ label: u }))} - onChange={this.onRunAsUserChange} - isDisabled={isReservedRole(this.props.role)} - /> -
-
- - ); - }; - - getIndexPrivileges = () => { - const { indices = [] } = this.state.role; - - const { - indexPatterns, - allowDocumentLevelSecurity, - allowFieldLevelSecurity - } = this.props; - - const props = { - indexPatterns, - allowDocumentLevelSecurity, - allowFieldLevelSecurity, - isReservedRole: isReservedRole(this.props.role) - }; - - const forms = indices.map((indexPrivilege, idx) => ( - - )); - - const button = isReservedRole(this.props.role) - ? null - : ( - - - - New Index Privilege - - - - ); - - return ( - - {forms} - {button} - ); - }; - - addIndexPrivilege = () => { - const { role } = this.state; - - this.setState({ - role: { - ...role, - indices: [...role.indices, { - names: [], - privileges: [], - field_security: { - grant: ['*'] - } - }] - } - }); - }; - - onIndexPrivilegeChange = (index) => { - return (updatedPrivilege) => { - const { role } = this.state; - const { indices } = role; - - const newIndicesState = [...indices]; - newIndicesState[index] = updatedPrivilege; - - this.setState({ - role: { - ...role, - indices: newIndicesState - } - }); - - this.loadAvailableFields(newIndicesState); - }; - }; - - onIndexPrivilegeDelete = (privilegeIndex) => { - return () => { - const { role } = this.state; - - const newIndicesState = [...role.indices]; - newIndicesState.splice(privilegeIndex, 1); - - this.setState({ - role: { - ...role, - indices: newIndicesState - } - }); - }; } - onClusterPrivilegesChange = (cluster) => { + onRoleChange = (role) => { this.setState({ - role: { - ...this.state.role, - cluster - } - }); - } - - onRunAsUserChange = (users) => { - this.setState({ - role: { - ...this.state.role, - run_as: users.map(u => u.label) - } + role }); } @@ -381,31 +166,29 @@ export class EditRolePage extends Component { } return ( - - -

Kibana

-
- - - - -
+ ); }; - onKibanaPrivilegesChange = (applications) => { - this.setState({ - role: { - ...this.state.role, - applications - } - }); - } + getFormButtons = () => { + return ( + + + Save + + + + Cancel + + + + ); + }; editingExistingRole = () => { return !!this.props.role.name; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js new file mode 100644 index 0000000000000..ef1c09bf70095 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -0,0 +1,135 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { + EuiText, + EuiSpacer, + EuiIcon, + EuiAccordion, + EuiComboBox, + EuiPanel, + EuiTitle, +} from '@elastic/eui'; +import { isReservedRole } from '../../lib/is_reserved_role'; +import { ClusterPrivileges } from './cluster_privileges'; +import { IndexPrivileges } from './index_privileges'; + +export class ElasticsearchPrivileges extends Component { + static propTypes = { + role: PropTypes.object.isRequired, + httpClient: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + runAsUsers: PropTypes.array.isRequired, + validator: PropTypes.object.isRequired, + }; + + render() { + const { + role, + httpClient, + validator, + onChange, + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity, + } = this.props; + + const indexProps = { + role, + httpClient, + validator, + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity, + onChange, + }; + + return ( + + +

Elasticsearch

+
+ + + + + Cluster Privileges ({role.cluster.length})} + > + + + + + + + + Index Privileges ( + {role.indices.filter(i => i.names.length).length}) + + } + > + +

+ Index Privileges allow you to foo the bar while baring the baz +

+
+ + + + + +
+
+ + + + Run As Privileges ({role.run_as.length})} + > + +

+ Run As Privileges allow you to foo the bar while baring the baz +

+
+ + ({ id: username, label: username }))} + selectedOptions={this.props.role.run_as.map(u => ({ label: u }))} + onChange={this.onRunAsUserChange} + isDisabled={isReservedRole(this.props.role)} + /> +
+
+
+ ); + } + + onClusterPrivilegesChange = (cluster) => { + const role = { + ...this.props.role, + cluster + }; + + this.props.onChange(role); + } + + onRunAsUserChange = (users) => { + const role = { + ...this.props.role, + run_as: users.map(u => u.label) + }; + + this.props.onChange(role); + } +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index.js new file mode 100644 index 0000000000000..d142af394b155 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index.js @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ElasticsearchPrivileges } from './elasticsearch_privileges'; +export { KibanaPrivileges } from './kibana_privileges'; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js new file mode 100644 index 0000000000000..4f52be678e7f0 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -0,0 +1,172 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import _ from 'lodash'; +import { isReservedRole } from '../../lib/is_reserved_role'; +import { IndexPrivilegeForm } from './index_privilege_form'; +import { getFields } from '../../../../../objects'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButton +} from '@elastic/eui'; + +export class IndexPrivileges extends Component { + static propTypes = { + role: PropTypes.object.isRequired, + indexPatterns: PropTypes.array.isRequired, + allowDocumentLevelSecurity: PropTypes.bool.isRequired, + allowFieldLevelSecurity: PropTypes.bool.isRequired, + httpClient: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + validator: PropTypes.object.isRequired, + } + + state = { + availableFields: {} + } + + componentDidMount() { + this.loadAvailableFields(this.props.role.indices); + } + + render() { + const { indices = [] } = this.props.role; + + const { + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity + } = this.props; + + const props = { + indexPatterns, + allowDocumentLevelSecurity, + allowFieldLevelSecurity, + isReservedRole: isReservedRole(this.props.role) + }; + + const forms = indices.map((indexPrivilege, idx) => ( + + )); + + const button = isReservedRole(this.props.role) + ? null + : ( + + + + New Index Privilege + + + + ); + + return ( + + {forms} + {button} + + ); + } + + addIndexPrivilege = () => { + const { role } = this.props; + + const newIndices = [...role.indices, { + names: [], + privileges: [], + field_security: { + grant: ['*'] + } + }]; + + this.props.onChange({ + ...this.props.role, + indices: newIndices + }); + }; + + onIndexPrivilegeChange = (privilegeIndex) => { + return (updatedPrivilege) => { + const { role } = this.props; + const { indices } = role; + + const newIndices = [...indices]; + newIndices[privilegeIndex] = updatedPrivilege; + + this.props.onChange({ + ...this.props.role, + indices: newIndices + }); + + this.loadAvailableFields(newIndices); + }; + }; + + onIndexPrivilegeDelete = (privilegeIndex) => { + return () => { + const { role } = this.state; + + const newIndices = [...role.indices]; + newIndices.splice(privilegeIndex, 1); + + this.props.onChange({ + ...this.props.role, + indices: newIndices + }); + }; + } + + isPlaceholderPrivilege = (indexPrivilege) => { + return indexPrivilege.names.length === 0; + }; + + loadAvailableFields(indices) { + const patterns = indices.map(index => index.names.join(',')); + + const cachedPatterns = Object.keys(this.state.availableFields); + const patternsToFetch = _.difference(patterns, cachedPatterns); + + const fetchRequests = patternsToFetch.map(this.loadFieldsForPattern); + + Promise.all(fetchRequests) + .then(response => { + + this.setState({ + availableFields: { + ...this.state.availableFields, + ...response.reduce((acc, o) => ({ ...acc, ...o }), {}) + } + }); + }); + } + + loadFieldsForPattern = async (pattern) => { + if (!pattern) return { [pattern]: [] }; + + try { + return { + [pattern]: await getFields(this.props.httpClient, pattern) + }; + + } catch (e) { + return { + [pattern]: [] + }; + } + } +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js index 4f26e847ab84c..32005aafa04ce 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js @@ -13,9 +13,8 @@ import { EuiCheckboxGroup, EuiText, EuiSpacer, + EuiTitle, } from '@elastic/eui'; - - export class KibanaPrivileges extends Component { static propTypes = { role: PropTypes.object.isRequired, @@ -53,6 +52,12 @@ export class KibanaPrivileges extends Component { return ( + +

Kibana

+
+ + +

Manage the actions this role can perform against Kibana.  @@ -70,7 +75,6 @@ export class KibanaPrivileges extends Component { } onKibanaPrivilegesChange = (privilege) => { - console.log('kib priv change', privilege); const role = { ...this.props.role, applications: [...this.props.role.applications] @@ -78,6 +82,6 @@ export class KibanaPrivileges extends Component { togglePrivilege(role, this.props.rbacApplication, this.idToPrivilege(privilege)); - this.props.onChange(role.applications); + this.props.onChange(role); } } From 918cca4a6b5367ceaecd2de9c5b556331263ca24 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 16 May 2018 15:44:15 -0400 Subject: [PATCH 11/38] combine role utils into a single file --- x-pack/plugins/security/public/lib/role.js | 11 ++++++++++- .../management/edit_role/components/edit_role_page.js | 2 +- .../components/privileges/cluster_privileges.js | 2 +- .../components/privileges/elasticsearch_privileges.js | 2 +- .../components/privileges/index_privileges.js | 2 +- .../components/privileges/kibana_privileges.js | 2 +- .../edit_role/components/reserved_role_badge.js | 2 +- .../management/edit_role/lib/is_reserved_role.js | 9 --------- 8 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js diff --git a/x-pack/plugins/security/public/lib/role.js b/x-pack/plugins/security/public/lib/role.js index 89eade0f0584e..96057aa55f595 100644 --- a/x-pack/plugins/security/public/lib/role.js +++ b/x-pack/plugins/security/public/lib/role.js @@ -14,4 +14,13 @@ import { get } from 'lodash'; */ export function isRoleEnabled(role) { return get(role, 'transient_metadata.enabled', true); -} \ No newline at end of file +} + +/** + * Returns whether given role is reserved or not. + * + * @param {role} the Role as returned by roles API + */ +export function isReservedRole(role) { + return get(role, 'metadata._reserved', false); +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index cf8b77a77f72f..3564d66221ac0 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { PageHeader } from './page_header'; import { saveRole, deleteRole } from '../../../../objects'; -import { isReservedRole } from '../lib/is_reserved_role'; +import { isReservedRole } from '../../../../lib/role'; import { RoleValidator } from '../lib/validate_role'; import { ReservedRoleBadge } from './reserved_role_badge'; import { ROLES_PATH } from '../../management_urls'; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js index 8193b73e022bf..f0bd225db61e5 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js @@ -7,7 +7,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { getClusterPrivileges } from '../../../../../services/role_privileges'; -import { isReservedRole } from '../../lib/is_reserved_role'; +import { isReservedRole } from '../../../../../lib/role'; import { EuiCheckboxGroup, EuiText, diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js index ef1c09bf70095..39345f8461b2a 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -15,9 +15,9 @@ import { EuiPanel, EuiTitle, } from '@elastic/eui'; -import { isReservedRole } from '../../lib/is_reserved_role'; import { ClusterPrivileges } from './cluster_privileges'; import { IndexPrivileges } from './index_privileges'; +import { isReservedRole } from '../../../../../lib/role'; export class ElasticsearchPrivileges extends Component { static propTypes = { diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js index 4f52be678e7f0..ef9800eb416f4 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -6,7 +6,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; -import { isReservedRole } from '../../lib/is_reserved_role'; +import { isReservedRole } from '../../../../../lib/role'; import { IndexPrivilegeForm } from './index_privilege_form'; import { getFields } from '../../../../../objects'; import { diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js index 32005aafa04ce..b8b81afdf5d34 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js @@ -6,7 +6,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; -import { isReservedRole } from '../../lib/is_reserved_role'; +import { isReservedRole } from '../../../../../lib/role'; import { getKibanaPrivileges } from '../../lib/get_application_privileges'; import { togglePrivilege } from '../../lib/set_application_privileges'; import { diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js index bebc146f602ff..8d0c17bd062ab 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { isReservedRole } from '../lib/is_reserved_role'; +import { isReservedRole } from '../../../../lib/role'; import { EuiBadge, EuiToolTip, diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js b/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js deleted file mode 100644 index 84d003e1b6033..0000000000000 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/is_reserved_role.js +++ /dev/null @@ -1,9 +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. - */ - -export function isReservedRole(role) { - return (role && role.metadata && role.metadata._reserved) || false; -} From fcc74323b2cd518cf455acaeaaf5393cb7726047 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 16 May 2018 15:59:25 -0400 Subject: [PATCH 12/38] add index.js to components directory --- .../plugins/security/public/views/management/edit_role.js | 2 +- .../public/views/management/edit_role/components/index.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/index.js diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index 27dc0a63141c2..8a3423ea8ccae 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -20,7 +20,7 @@ import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info'; import { checkLicenseError } from 'plugins/security/lib/check_license_error'; import { EDIT_ROLES_PATH, ROLES_PATH } from './management_urls'; -import { EditRolePage } from './edit_role/components/edit_role_page'; +import { EditRolePage } from './edit_role/components'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/index.js b/x-pack/plugins/security/public/views/management/edit_role/components/index.js new file mode 100644 index 0000000000000..1a0afb37c4791 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/index.js @@ -0,0 +1,7 @@ +/* + * 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 { EditRolePage } from './edit_role_page'; From 74e1d4a8b9bf67b7dddae8119b322a7d4a239058 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 17 May 2018 08:48:01 -0400 Subject: [PATCH 13/38] Improve form validation --- .../privileges/elasticsearch_privileges.js | 30 +++++++++++++++---- .../privileges/kibana_privileges.js | 3 +- .../management/edit_role/lib/validate_role.js | 4 +-- .../edit_role/lib/validate_role.test.js | 9 +++--- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js index 39345f8461b2a..e8e40ba3fc4ae 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -8,6 +8,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiText, + EuiTextColor, EuiSpacer, EuiIcon, EuiAccordion, @@ -49,10 +50,16 @@ export class ElasticsearchPrivileges extends Component { onChange, }; + const { + isInvalid + } = validator.validateIndexPrivileges(role); + + const indexTextColor = isInvalid ? 'danger' : 'default'; + return ( -

Elasticsearch

+

Elasticsearch

@@ -60,8 +67,13 @@ export class ElasticsearchPrivileges extends Component { Cluster Privileges ({role.cluster.length})} + buttonContent={ +
+ Cluster Privileges ({role.cluster.length}) +
+ } > +
@@ -72,14 +84,18 @@ export class ElasticsearchPrivileges extends Component { id={'indexPrivilegesAccordion'} buttonContent={
- Index Privileges ( - {role.indices.filter(i => i.names.length).length}) + + Index Privileges ( + {role.indices.filter(i => i.names.length).length}) +
} > + +

- Index Privileges allow you to foo the bar while baring the baz + Control access to the data in your cluster.

@@ -96,9 +112,11 @@ export class ElasticsearchPrivileges extends Component { id={'runAsPrivilegesAccordion'} buttonContent={
Run As Privileges ({role.run_as.length})
} > + +

- Run As Privileges allow you to foo the bar while baring the baz + Allow requests to be submitted on behalf of other users.

diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js index b8b81afdf5d34..3c222800108b1 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js @@ -18,6 +18,7 @@ import { export class KibanaPrivileges extends Component { static propTypes = { role: PropTypes.object.isRequired, + spaces: PropTypes.array, kibanaAppPrivileges: PropTypes.array.isRequired, onChange: PropTypes.func.isRequired, }; @@ -53,7 +54,7 @@ export class KibanaPrivileges extends Component { return ( -

Kibana

+

Kibana

diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js index 3c7ffa0b667eb..0179c4bb2d40e 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js @@ -23,8 +23,8 @@ export class RoleValidator { if (!role.name) { return invalid(`Please provide a role name`); } - if (role.name.length > 30) { - return invalid(`Name must not exceed 30 characters`); + if (role.name.length > 1024) { + return invalid(`Name must not exceed 1024 characters`); } if (!role.name.match(/^[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*$/)) { return invalid(`Name must begin with a letter or underscore and contain only letters, underscores, and numbers.`); diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js index eea22c4d75fb0..795e681be5d59 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js @@ -28,12 +28,12 @@ describe('validateRoleName', () => { expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, error: `Please provide a role name` }); }); - test('it cannot exceed 30 characters', () => { + test('it cannot exceed 1024 characters', () => { const role = { - name: 'This-is-a31-character-role-name' + name: new Array(1026).join('A') }; - expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, error: `Name must not exceed 30 characters` }); + expect(validator.validateRoleName(role)).toEqual({ isInvalid: true, error: `Name must not exceed 1024 characters` }); }); const charList = `!#%^&*()+=[]{}\|';:"/,<>?`.split(''); @@ -44,7 +44,8 @@ describe('validateRoleName', () => { }; expect(validator.validateRoleName(role)).toEqual( - { isInvalid: true, + { + isInvalid: true, error: `Name must begin with a letter or underscore and contain only letters, underscores, and numbers.` } ); From 92812e54ded619745b915236ddcf2e50cae1883b Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 18 May 2018 14:48:59 -0400 Subject: [PATCH 14/38] Fixes #18166 : warn when no fields are granted --- .../privileges/index_privilege_form.js | 36 ++++++++++++++----- .../components/privileges/index_privileges.js | 2 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index 3d69531880807..f5059ccef160f 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -6,6 +6,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { + EuiCallOut, EuiComboBox, EuiTextArea, EuiFormRow, @@ -116,20 +117,39 @@ export class IndexPrivilegeForm extends Component { )); } + let grantedFieldsWarning = null; + if (allowFieldLevelSecurity) { const { grant = [] } = indexPrivilege.field_security || {}; + if (grant.length === 0) { + grantedFieldsWarning = ( + + + +

+ If no fields are granted, then users assigned to this role will not be able to + see any data for this index. Is this really what you want? +

+
+
+ ); + } + features.push(( - - + + + + {grantedFieldsWarning} + )); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js index ef9800eb416f4..58a1834974e56 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -119,7 +119,7 @@ export class IndexPrivileges extends Component { onIndexPrivilegeDelete = (privilegeIndex) => { return () => { - const { role } = this.state; + const { role } = this.props; const newIndices = [...role.indices]; newIndices.splice(privilegeIndex, 1); From b6d1b9aa7f91d6240408e4c4f9020f0335f31fd3 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 21 May 2018 13:23:56 -0400 Subject: [PATCH 15/38] Disable certain edits for Reserved Spaces --- x-pack/plugins/spaces/common/index.js | 7 ++++ .../components/manage_space_page.js | 28 ++++++++++---- .../components/reserved_space_badge.js | 37 +++++++++++++++++++ .../components/reserved_space_badge.test.js | 30 +++++++++++++++ .../management/components/url_context.js | 6 ++- 5 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/spaces/common/index.js create mode 100644 x-pack/plugins/spaces/public/views/management/components/reserved_space_badge.js create mode 100644 x-pack/plugins/spaces/public/views/management/components/reserved_space_badge.test.js diff --git a/x-pack/plugins/spaces/common/index.js b/x-pack/plugins/spaces/common/index.js new file mode 100644 index 0000000000000..1cd6907d3d64c --- /dev/null +++ b/x-pack/plugins/spaces/common/index.js @@ -0,0 +1,7 @@ +/* + * 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 { isReservedSpace } from './is_reserved_space'; \ No newline at end of file diff --git a/x-pack/plugins/spaces/public/views/management/components/manage_space_page.js b/x-pack/plugins/spaces/public/views/management/components/manage_space_page.js index 2262e597fb433..2207076c3e695 100644 --- a/x-pack/plugins/spaces/public/views/management/components/manage_space_page.js +++ b/x-pack/plugins/spaces/public/views/management/components/manage_space_page.js @@ -25,6 +25,8 @@ import { DeleteSpacesButton } from './delete_spaces_button'; import { Notifier, toastNotifications } from 'ui/notify'; import { UrlContext } from './url_context'; import { toUrlContext, isValidUrlContext } from '../lib/url_context_utils'; +import { isReservedSpace } from '../../../../common'; +import { ReservedSpaceBadge } from './reserved_space_badge'; export class ManageSpacePage extends React.Component { state = { @@ -71,12 +73,7 @@ export class ManageSpacePage extends React.Component { - - -

{this.getTitle()}

-
- {this.getActionButton()} -
+ {this.getFormHeading()} @@ -108,7 +105,7 @@ export class ManageSpacePage extends React.Component { @@ -128,6 +125,19 @@ export class ManageSpacePage extends React.Component { ); } + getFormHeading = () => { + const isReserved = isReservedSpace(this.state.space); + + return ( + + +

{this.getTitle()}

+
+ {isReserved ? this.getReservedBadge() : this.getActionButton()} +
+ ); + }; + getTitle = () => { if (this.editingExistingSpace()) { return `Edit space`; @@ -135,8 +145,10 @@ export class ManageSpacePage extends React.Component { return `Create a space`; }; + getReservedBadge = () => ; + getActionButton = () => { - if (this.editingExistingSpace()) { + if (this.editingExistingSpace() && !isReservedSpace(this.state.space)) { return ( { + const { + space + } = props; + + if (isReservedSpace(space)) { + return ( + + + Reserved Space + + + ); + } + return null; +}; + +ReservedSpaceBadge.propTypes = { + space: PropTypes.object.isRequired +}; diff --git a/x-pack/plugins/spaces/public/views/management/components/reserved_space_badge.test.js b/x-pack/plugins/spaces/public/views/management/components/reserved_space_badge.test.js new file mode 100644 index 0000000000000..e45926b859824 --- /dev/null +++ b/x-pack/plugins/spaces/public/views/management/components/reserved_space_badge.test.js @@ -0,0 +1,30 @@ +/* + * 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 React from 'react'; +import { + EuiBadge +} from '@elastic/eui'; +import { ReservedSpaceBadge } from './reserved_space_badge'; +import { + shallow +} from 'enzyme'; + +const reservedSpace = { + _reserved: true +}; + +const unreservedSpace = {}; + +test('it renders without crashing', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiBadge)).toHaveLength(1); +}); + +test('it renders nothing for an unreserved space', () => { + const wrapper = shallow(); + expect(wrapper.find('*')).toHaveLength(0); +}); diff --git a/x-pack/plugins/spaces/public/views/management/components/url_context.js b/x-pack/plugins/spaces/public/views/management/components/url_context.js index 418a51b29347e..6253e36d0f9e2 100644 --- a/x-pack/plugins/spaces/public/views/management/components/url_context.js +++ b/x-pack/plugins/spaces/public/views/management/components/url_context.js @@ -35,7 +35,7 @@ export class UrlContext extends Component {
this.textFieldRef = ref} @@ -48,6 +48,10 @@ export class UrlContext extends Component { } getLabel = () => { + if (!this.props.editable) { + return (

URL Context

); + } + const editLinkText = this.state.editing ? `[stop editing]` : `[edit]`; return (

URL Context {editLinkText}

); }; From 36e7a67b5e6822ad9670f3b0c8a56d844bb7e963 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Tue, 22 May 2018 11:05:20 -0400 Subject: [PATCH 16/38] Adding built-in types and alphabetizing (#19306) --- x-pack/plugins/security/server/lib/privileges/privileges.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security/server/lib/privileges/privileges.js b/x-pack/plugins/security/server/lib/privileges/privileges.js index e05be3117f934..129d4b3abfb3e 100644 --- a/x-pack/plugins/security/server/lib/privileges/privileges.js +++ b/x-pack/plugins/security/server/lib/privileges/privileges.js @@ -45,7 +45,7 @@ function buildSavedObjectsReadPrivileges() { } function buildSavedObjectsPrivileges(actions) { - const objectTypes = ['config', 'dashboard', 'index-pattern', 'search', 'visualization', 'graph-workspace']; + const objectTypes = ['config', 'dashboard', 'graph-workspace', 'index-pattern', 'search', 'timelion-sheet', 'url', 'visualization']; return objectTypes .map(type => actions.map(action => `action:saved-objects/${type}/${action}`)) .reduce((acc, types) => [...acc, ...types], []); From 06eb78422cd3c600a15ee691b3be440da3373f38 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Tue, 22 May 2018 14:56:02 -0400 Subject: [PATCH 17/38] Filtering out non-default resource Kibana privileges (#19321) --- x-pack/plugins/security/public/views/management/edit_role.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index 3f0ab02129dbb..ba159bc0c95d9 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -33,7 +33,10 @@ const getKibanaPrivileges = (kibanaApplicationPrivilege, role, application) => { return kibanaPrivileges; } - const applications = role.applications.filter(x => x.application === application); + // we're filtering out privileges for non-default resources as well incase + // the roles were created in a future version + const applications = role.applications + .filter(x => x.application === application && x.application.resources.all(r => r === DEFAULT_RESOURCE)); const assigned = _.uniq(_.flatten(_.pluck(applications, 'privileges'))); assigned.forEach(a => { From 74891fda0e5c23900f5aade0d32b0b6fff205eba Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 25 May 2018 10:28:05 -0400 Subject: [PATCH 18/38] Initial design refactor of edit roles screen --- .../public/views/management/edit_role.js | 1 + .../edit_role/components/edit_role_page.js | 60 +++--- .../privileges/cluster_privileges.js | 44 +++-- .../privileges/elasticsearch_privileges.js | 176 +++++++++++------- .../privileges/elasticsearch_privileges.less | 3 + .../privileges/index_privilege_form.js | 118 +++++++++++- .../components/privileges/index_privileges.js | 26 +-- .../views/management/edit_role/edit_role.less | 8 + .../views/management/manage_spaces.less | 4 +- 9 files changed, 291 insertions(+), 149 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less create mode 100644 x-pack/plugins/security/public/views/management/edit_role/edit_role.less diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index 8a3423ea8ccae..d05e519b4e24d 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -8,6 +8,7 @@ import _ from 'lodash'; import routes from 'ui/routes'; import { fatalError } from 'ui/notify'; import template from 'plugins/security/views/management/edit_role.html'; +import 'plugins/security/views/management/edit_role/edit_role.less'; import 'angular-ui-select'; import 'plugins/security/services/application_privilege'; import 'plugins/security/services/shield_user'; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 3564d66221ac0..332d1d853cfe0 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -8,6 +8,7 @@ import PropTypes from 'prop-types'; import { get } from 'lodash'; import { Notifier, toastNotifications } from 'ui/notify'; import { + EuiPanel, EuiText, EuiSpacer, EuiPage, @@ -56,29 +57,32 @@ export class EditRolePage extends Component { render() { return ( - +
- - + + + + {this.getFormTitle()} - + - {this.getRoleName()} + {this.getRoleName()} - + - {this.getElasticsearchPrivileges()} + {this.getElasticsearchPrivileges()} - + - {this.getKibanaPrivileges()} + {this.getKibanaPrivileges()} - + - {this.getFormButtons()} - - - + {this.getFormButtons()} + + + +
); } @@ -111,19 +115,21 @@ export class EditRolePage extends Component { getRoleName = () => { return ( - - - - - - - - + + + + + + + + + + ); } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js index f0bd225db61e5..e67ddd16a7fb3 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.js @@ -4,17 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; +import _ from 'lodash'; import PropTypes from 'prop-types'; import { getClusterPrivileges } from '../../../../../services/role_privileges'; import { isReservedRole } from '../../../../../lib/role'; import { EuiCheckboxGroup, - EuiText, - EuiSpacer, - EuiLink, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; -import { CLUSTER_PRIVS_DOC_LINK } from '../../lib/constants'; export class ClusterPrivileges extends Component { static propTypes = { @@ -23,35 +22,40 @@ export class ClusterPrivileges extends Component { }; render() { - const checkboxes = getClusterPrivileges().map(p => ({ - id: p, - label: p - })); - const { role } = this.props; + const clusterPrivileges = getClusterPrivileges(); + const privilegeGroups = _.chunk(clusterPrivileges, clusterPrivileges.length / 2); + + return ( + + {privilegeGroups.map(this.buildCheckboxGroup)} + + ); + } + + buildCheckboxGroup = (items, key) => { + const role = this.props.role; + + const checkboxes = items.map(i => ({ + id: i, + label: i + })); const selectionMap = (role.cluster || []) .map(k => ({ [k]: true })) .reduce((acc, o) => ({ ...acc, ...o }), {}); return ( - - -

- Manage the actions this role can perform against your cluster.  - Learn more -

-
- + -
+ ); - } + }; onClusterPrivilegesChange = (privilege) => { const { cluster } = this.props.role; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js index e8e40ba3fc4ae..5e47a82148367 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -8,17 +8,23 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { EuiText, - EuiTextColor, EuiSpacer, EuiIcon, - EuiAccordion, EuiComboBox, EuiPanel, + EuiFormRow, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiDescribedFormGroup, EuiTitle, + EuiHorizontalRule, + EuiLink, } from '@elastic/eui'; import { ClusterPrivileges } from './cluster_privileges'; import { IndexPrivileges } from './index_privileges'; import { isReservedRole } from '../../../../../lib/role'; +import './elasticsearch_privileges.less'; export class ElasticsearchPrivileges extends Component { static propTypes = { @@ -29,7 +35,43 @@ export class ElasticsearchPrivileges extends Component { validator: PropTypes.object.isRequired, }; + state = { + collapsed: false + }; + render() { + + + return ( + + {this.getTitle()} + {this.getForm()} + + ); + } + + getTitle = () => { + return ( + + + +
+ Elasticsearch  +
+
+
+ + {this.state.collapsed ? 'show' : 'hide'} + +
+ ); + }; + + getForm = () => { + if (this.state.collapsed) { + return null; + } + const { role, httpClient, @@ -50,76 +92,33 @@ export class ElasticsearchPrivileges extends Component { onChange, }; - const { - isInvalid - } = validator.validateIndexPrivileges(role); - - const indexTextColor = isInvalid ? 'danger' : 'default'; - return ( - -

Elasticsearch

-
- - - - - Cluster Privileges ({role.cluster.length}) -
- } - > - + Cluster privileges

} + description={ +

+ Manage the actions this role can perform against your cluster. +

+ } + > + - - - + +
- - - - Index Privileges ( - {role.indices.filter(i => i.names.length).length}) - - - } - > - - - -

- Control access to the data in your cluster. -

-
- - - - - -
-
- - Run As Privileges ({role.run_as.length})} - > - - - -

- Allow requests to be submitted on behalf of other users. -

-
- + Run As privileges

} + description={ +

+ Allow requests to be submitted on behalf of other users. +

+ } + > + ({ id: username, label: username }))} @@ -127,12 +126,55 @@ export class ElasticsearchPrivileges extends Component { onChange={this.onRunAsUserChange} isDisabled={isReservedRole(this.props.role)} /> -
-
+ + + + + + +
+ + +

Index privileges

+

Control access to the data in your cluster.

+
+ + Add Index Privilege + +
+ +
+
+ + +
); } + toggleCollapsed = () => { + this.setState({ + collapsed: !this.state.collapsed + }); + } + + addIndexPrivilege = () => { + const { role } = this.props; + + const newIndices = [...role.indices, { + names: [], + privileges: [], + field_security: { + grant: ['*'] + } + }]; + + this.props.onChange({ + ...this.props.role, + indices: newIndices + }); + }; + onClusterPrivilegesChange = (cluster) => { const role = { ...this.props.role, diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less new file mode 100644 index 0000000000000..9bbe1380ea15d --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less @@ -0,0 +1,3 @@ +.manageRoles__esLogo { + margin-right: 10px; +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index f5059ccef160f..e52dec02de291 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -12,9 +12,11 @@ import { EuiFormRow, EuiFlexGroup, EuiFlexItem, - EuiButton, + EuiSwitch, EuiSpacer, EuiHorizontalRule, + EuiLink, + EuiIcon, } from '@elastic/eui'; import { getIndexPrivileges } from '../../../../../services/role_privileges'; @@ -34,21 +36,24 @@ export class IndexPrivilegeForm extends Component { validator: PropTypes.object.isRequired, }; + constructor(props) { + super(props); + this.state = { + queryExanded: !!props.indexPrivilege.query, + documentQuery: props.indexPrivilege.query + }; + } + render() { return (
- + {this.getPrivilegeForm()} {this.props.allowDelete && ( - + )} @@ -82,13 +87,108 @@ export class IndexPrivilegeForm extends Component { /> + {this.getGrantedFieldsControl()} - {this.getConditionalFeatures()} + {this.getGrantedDocumentsControl()} ); }; + getGrantedFieldsControl = () => { + const { + allowFieldLevelSecurity, + availableFields, + indexPrivilege, + } = this.props; + + if (!allowFieldLevelSecurity) { + return null; + } + + const { grant = [] } = indexPrivilege.field_security || {}; + + return ( + + + + + + + + ); + }; + + getGrantedDocumentsControl = () => { + const { + allowDocumentLevelSecurity, + indexPrivilege, + } = this.props; + + if (!allowDocumentLevelSecurity) { + return null; + } + + return ( + + + + + {this.state.queryExpanded && + + + + + + } + + ); + }; + + toggleDocumentQuery = () => { + const willToggleOff = this.state.queryExanded; + const willToggleOn = !willToggleOff; + + // If turning off, then save the current query in state so that we can restore it if the user changes their mind. + this.setState({ + queryExpanded: !this.state.queryExpanded, + documentQuery: willToggleOff ? this.props.indexPrivilege.query : this.state.documentQuery + }); + + // If turning off, then remove the query from the Index Privilege + if (willToggleOff) { + this.props.onChange({ + ...this.props.indexPrivilege, + query: '', + }); + } + + // If turning on, then restore the saved query if available + if (willToggleOn && !this.props.indexPrivilege.query && this.state.documentQuery) { + this.props.onChange({ + ...this.props.indexPrivilege, + query: this.state.documentQuery, + }); + } + }; + getConditionalFeatures = () => { const { allowDocumentLevelSecurity, diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js index 58a1834974e56..2837d1927dc56 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -3,17 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; import { isReservedRole } from '../../../../../lib/role'; import { IndexPrivilegeForm } from './index_privilege_form'; import { getFields } from '../../../../../objects'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiButton -} from '@elastic/eui'; export class IndexPrivileges extends Component { static propTypes = { @@ -63,24 +58,7 @@ export class IndexPrivileges extends Component { /> )); - const button = isReservedRole(this.props.role) - ? null - : ( - - - - New Index Privilege - - - - ); - - return ( - - {forms} - {button} - - ); + return forms; } addIndexPrivilege = () => { diff --git a/x-pack/plugins/security/public/views/management/edit_role/edit_role.less b/x-pack/plugins/security/public/views/management/edit_role/edit_role.less new file mode 100644 index 0000000000000..7b6b150c0444e --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/edit_role.less @@ -0,0 +1,8 @@ +#editRoleReactRoot, .editRolePage, .editRolePage__content { + background: #f5f5f5; +} + +.editRolePage__content { + border: none; + box-shadow: none; +} diff --git a/x-pack/plugins/spaces/public/views/management/manage_spaces.less b/x-pack/plugins/spaces/public/views/management/manage_spaces.less index a9696d0c2f824..ae515cbb829fa 100644 --- a/x-pack/plugins/spaces/public/views/management/manage_spaces.less +++ b/x-pack/plugins/spaces/public/views/management/manage_spaces.less @@ -1,7 +1,7 @@ -.application, .euiPanel { +.manageSpaces__application, .manageSpaces__.euiPanel { background: #f5f5f5 } -.euiPage { +.manageSpaces__euiPage { padding: 0; } From ad0ec049856ba8be2f1c9b9d3c7c243316161c61 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 25 May 2018 10:42:25 -0400 Subject: [PATCH 19/38] improved ui for reserved roles --- .../edit_role/components/edit_role_page.js | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 332d1d853cfe0..34c2178831878 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -87,9 +87,14 @@ export class EditRolePage extends Component { } getFormTitle = () => { - const titleText = this.editingExistingRole() - ? 'Edit Role' - : 'New Role'; + let titleText; + if (isReservedRole(this.props.role)) { + titleText = 'Reserved role'; + } else if (this.editingExistingRole()) { + titleText = 'Edit role'; + } else { + titleText = 'New role'; + } return ( @@ -182,10 +187,24 @@ export class EditRolePage extends Component { }; getFormButtons = () => { + if (isReservedRole(this.props.role)) { + return ( + + + + Return to role list + + + + ); + } + + const saveText = this.editingExistingRole() ? 'Update role' : 'Create role'; + return ( - Save + {saveText} From ebf85d84f6631165af350aa3e3e69e7bc463efef Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 25 May 2018 11:42:30 -0400 Subject: [PATCH 20/38] create CollapsiblePanel component for roles screen --- .../collapsible_panel.test.js.snap | 59 +++++++++++++++ .../edit_role/components/collapsible_panel.js | 74 +++++++++++++++++++ .../components/collapsible_panel.less | 3 + .../components/collapsible_panel.test.js | 57 ++++++++++++++ .../privileges/elasticsearch_privileges.js | 44 +---------- .../privileges/elasticsearch_privileges.less | 3 - .../privileges/kibana_privileges.js | 34 ++++----- 7 files changed, 212 insertions(+), 62 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.test.js delete mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap new file mode 100644 index 0000000000000..d94f2f4c47bab --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it renders without blowing up 1`] = ` + + + + +
+ + + Elasticsearch +
+
+
+ + + hide + + +
+ + +

+ child +

+
+
+`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js new file mode 100644 index 0000000000000..e39b25e8e522b --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js @@ -0,0 +1,74 @@ +/* + * 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 React, { Component, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import './collapsible_panel.less'; +import { + EuiPanel, + EuiLink, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, +} from '@elastic/eui'; + +export class CollapsiblePanel extends Component { + static propTypes = { + iconType: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + } + + state = { + collapsed: false + } + + render() { + return ( + + {this.getTitle()} + {this.getForm()} + + ); + } + + getTitle = () => { + return ( + + + +
+ {this.props.title} +
+
+
+ + {this.state.collapsed ? 'show' : 'hide'} + +
+ ); + }; + + getForm = () => { + if (this.state.collapsed) { + return null; + } + + return ( + + + {this.props.children} + + ); + } + + toggleCollapsed = () => { + this.setState({ + collapsed: !this.state.collapsed + }); + } +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less new file mode 100644 index 0000000000000..cc56943ffcda9 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less @@ -0,0 +1,3 @@ +.collapsiblePanel__logo { + margin-right: 10px; +} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.test.js new file mode 100644 index 0000000000000..ec0182101653a --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.test.js @@ -0,0 +1,57 @@ +/* + * 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 React from 'react'; +import { shallow, mount } from 'enzyme'; +import { CollapsiblePanel } from './collapsible_panel'; +import { EuiLink } from '@elastic/eui'; + +test('it renders without blowing up', () => { + const wrapper = shallow( + +

child

+
+ ); + + expect(wrapper).toMatchSnapshot(); +}); + +test('it renders children by default', () => { + const wrapper = mount( + +

child 1

+

child 2

+
+ ); + + expect(wrapper.find(CollapsiblePanel)).toHaveLength(1); + expect(wrapper.find('.child')).toHaveLength(2); +}); + +test('it hides children when the "hide" link is clicked', () => { + const wrapper = mount( + +

child 1

+

child 2

+
+ ); + + expect(wrapper.find(CollapsiblePanel)).toHaveLength(1); + expect(wrapper.find('.child')).toHaveLength(2); + + wrapper.find(EuiLink).simulate('click'); + + expect(wrapper.find('.child')).toHaveLength(0); +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js index 5e47a82148367..e6a8aa6c60e03 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -9,9 +9,7 @@ import PropTypes from 'prop-types'; import { EuiText, EuiSpacer, - EuiIcon, EuiComboBox, - EuiPanel, EuiFormRow, EuiFlexGroup, EuiFlexItem, @@ -19,12 +17,11 @@ import { EuiDescribedFormGroup, EuiTitle, EuiHorizontalRule, - EuiLink, } from '@elastic/eui'; import { ClusterPrivileges } from './cluster_privileges'; import { IndexPrivileges } from './index_privileges'; import { isReservedRole } from '../../../../../lib/role'; -import './elasticsearch_privileges.less'; +import { CollapsiblePanel } from '../collapsible_panel'; export class ElasticsearchPrivileges extends Component { static propTypes = { @@ -35,43 +32,15 @@ export class ElasticsearchPrivileges extends Component { validator: PropTypes.object.isRequired, }; - state = { - collapsed: false - }; - render() { - - return ( - - {this.getTitle()} + {this.getForm()} - + ); } - getTitle = () => { - return ( - - - -
- Elasticsearch  -
-
-
- - {this.state.collapsed ? 'show' : 'hide'} - -
- ); - }; - getForm = () => { - if (this.state.collapsed) { - return null; - } - const { role, httpClient, @@ -94,7 +63,6 @@ export class ElasticsearchPrivileges extends Component { return ( - Cluster privileges

} description={ @@ -152,12 +120,6 @@ export class ElasticsearchPrivileges extends Component { ); } - toggleCollapsed = () => { - this.setState({ - collapsed: !this.state.collapsed - }); - } - addIndexPrivilege = () => { const { role } = this.props; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less deleted file mode 100644 index 9bbe1380ea15d..0000000000000 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less +++ /dev/null @@ -1,3 +0,0 @@ -.manageRoles__esLogo { - margin-right: 10px; -} diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js index 3c222800108b1..b13002feae003 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { isReservedRole } from '../../../../../lib/role'; import { getKibanaPrivileges } from '../../lib/get_application_privileges'; import { togglePrivilege } from '../../lib/set_application_privileges'; + +import { CollapsiblePanel } from '../collapsible_panel'; import { EuiCheckboxGroup, - EuiText, - EuiSpacer, - EuiTitle, + EuiDescribedFormGroup, } from '@elastic/eui'; export class KibanaPrivileges extends Component { static propTypes = { @@ -30,7 +30,14 @@ export class KibanaPrivileges extends Component { idToPrivilege = (id) => id.split(this.idPrefix())[1]; render() { + return ( + + {this.getForm()} + + ); + } + getForm = () => { const { kibanaAppPrivileges, role, @@ -52,26 +59,17 @@ export class KibanaPrivileges extends Component { }, {}); return ( - - -

Kibana

-
- - - - -

- Manage the actions this role can perform against Kibana.  -

-
- + Application privileges

} + description={

Manage the actions this role can perform within Kibana.

} + > -
+
); } From a826ee5d19c6c9c345c11f07e48b57a553bf22c7 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 29 May 2018 09:09:43 -0400 Subject: [PATCH 21/38] Improve readonly view --- .../edit_role/components/edit_role_page.js | 1 + .../privileges/elasticsearch_privileges.js | 14 ++++++++------ .../privileges/index_privilege_form.js | 18 ++++++++++-------- x-pack/plugins/spaces/index.js | 2 +- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 34c2178831878..da42fdf681332 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -154,6 +154,7 @@ export class EditRolePage extends Component { return ( ({ id: username, label: username }))} selectedOptions={this.props.role.run_as.map(u => ({ label: u }))} onChange={this.onRunAsUserChange} - isDisabled={isReservedRole(this.props.role)} + isDisabled={!this.props.editable} /> @@ -106,9 +106,11 @@ export class ElasticsearchPrivileges extends Component {

Index privileges

Control access to the data in your cluster.

- - Add Index Privilege - + {this.props.editable && ( + + Add Index Privilege + + )}
diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index e52dec02de291..95f013fbb004b 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -137,14 +137,16 @@ export class IndexPrivilegeForm extends Component { return ( - - - + {!this.props.isReservedRole && + + + + } {this.state.queryExpanded && diff --git a/x-pack/plugins/spaces/index.js b/x-pack/plugins/spaces/index.js index 603b8ddb22e11..38d2e84cc992f 100644 --- a/x-pack/plugins/spaces/index.js +++ b/x-pack/plugins/spaces/index.js @@ -52,7 +52,7 @@ export const spaces = (kibana) => new kibana.Plugin({ valid: true, space: await getActiveSpace(request.getSavedObjectsClient(), request.getBasePath()) }; - } catch(e) { + } catch (e) { vars.activeSpace = { valid: false, error: wrapError(e).output.payload From d188dc825ba8370ab7a321ce7c363e94651e71bc Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 29 May 2018 09:17:05 -0400 Subject: [PATCH 22/38] Fix save logic --- .../views/management/edit_role/components/edit_role_page.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index da42fdf681332..2e8e956ef9352 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -27,7 +27,6 @@ import { RoleValidator } from '../lib/validate_role'; import { ReservedRoleBadge } from './reserved_role_badge'; import { ROLES_PATH } from '../../management_urls'; import { DeleteRoleButton } from './delete_role_button'; -import { setApplicationPrivileges } from '../lib/set_application_privileges'; import { ElasticsearchPrivileges, KibanaPrivileges } from './privileges'; const notifier = new Notifier(); @@ -248,8 +247,6 @@ export class EditRolePage extends Component { role.indices = role.indices.filter(i => !this.isPlaceholderPrivilege(i)); role.indices.forEach((index) => index.query || delete index.query); - setApplicationPrivileges(this.props.kibanaPrivileges, role, this.props.rbacApplication); - saveRole(httpClient, role) .then(() => { toastNotifications.addSuccess('Saved role'); From 4464482f9b19800d4d066f72a056804d7cea9e4a Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 29 May 2018 09:33:21 -0400 Subject: [PATCH 23/38] Re-add granted fields warning --- .../privileges/index_privilege_form.js | 126 +++++------------- 1 file changed, 36 insertions(+), 90 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index 95f013fbb004b..b807f41f4b3c8 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -108,22 +108,44 @@ export class IndexPrivilegeForm extends Component { const { grant = [] } = indexPrivilege.field_security || {}; - return ( - - + let grantedFieldsWarning = null; + + if (allowFieldLevelSecurity) { + + if (grant.length === 0) { + grantedFieldsWarning = ( - + + +

+ If no fields are granted, then users assigned to this role will not be able to + see any data for this index. Is this really what you want? +

+
-
-
- ); - }; + ); + } + + return ( + + + + + {grantedFieldsWarning} + + + + ); + } + + return null; + } getGrantedDocumentsControl = () => { const { @@ -191,82 +213,6 @@ export class IndexPrivilegeForm extends Component { } }; - getConditionalFeatures = () => { - const { - allowDocumentLevelSecurity, - allowFieldLevelSecurity, - indexPrivilege, - availableFields, - } = this.props; - - if (!allowFieldLevelSecurity && !allowDocumentLevelSecurity) { - return null; - } - - const features = []; - if (allowDocumentLevelSecurity) { - features.push(( - - - - - - )); - } - - let grantedFieldsWarning = null; - - if (allowFieldLevelSecurity) { - - const { grant = [] } = indexPrivilege.field_security || {}; - - if (grant.length === 0) { - grantedFieldsWarning = ( - - - -

- If no fields are granted, then users assigned to this role will not be able to - see any data for this index. Is this really what you want? -

-
-
- ); - } - - features.push(( - - - - - {grantedFieldsWarning} - - - - )); - } - - return ( - - - - {features} - - - ); - }; - onCreateIndexPatternOption = (option) => { const newIndexPatterns = this.props.indexPrivilege.names.concat([option]); From 767fb277a4b35db977f14c6355e6b3bac8519ee6 Mon Sep 17 00:00:00 2001 From: kobelb Date: Tue, 29 May 2018 10:41:58 -0400 Subject: [PATCH 24/38] Removing unused file --- .../security/server/lib/check_user_permission.js | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 x-pack/plugins/security/server/lib/check_user_permission.js diff --git a/x-pack/plugins/security/server/lib/check_user_permission.js b/x-pack/plugins/security/server/lib/check_user_permission.js deleted file mode 100644 index 6890a6fc79092..0000000000000 --- a/x-pack/plugins/security/server/lib/check_user_permission.js +++ /dev/null @@ -1,14 +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. - */ - -/*! 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 async function checkUserPermission(permission, hasPermission) { - // POC Stub. TODO: Use ES Permissions API once implemented. - return Promise.resolve(hasPermission); -} From ebd09d1159ebf22a11243e6b660dcf72ec8b73a0 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 29 May 2018 15:32:37 -0400 Subject: [PATCH 25/38] additional tests --- .../__snapshots__/page_header.test.js.snap | 11 ++ .../edit_role/components/page_header.js | 4 +- .../edit_role/components/page_header.test.js | 36 ++++ .../cluster_privileges.test.js.snap | 92 ++++++++++ .../elasticsearch_privileges.test.js.snap | 164 ++++++++++++++++++ .../index_privileges.test.js.snap | 3 + .../privileges/cluster_privileges.test.js | 20 +++ .../privileges/elasticsearch_privileges.js | 3 + .../elasticsearch_privileges.test.js | 72 ++++++++ .../privileges/index_privileges.test.js | 54 ++++++ 10 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/cluster_privileges.test.js.snap create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privileges.test.js.snap create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.test.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.test.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.test.js diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap new file mode 100644 index 0000000000000..7dddfa8c5cdef --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it renders without crashing 1`] = ` + + + + + +`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js index 956c2809b325f..ffbab9d0921a7 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { @@ -36,8 +36,6 @@ export class PageHeader extends Component { } } - - PageHeader.propTypes = { breadcrumbs: PropTypes.array.isRequired }; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js new file mode 100644 index 0000000000000..89151673b9911 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js @@ -0,0 +1,36 @@ +/* + * 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 React from 'react'; +import { shallow, mount } from 'enzyme'; +import { PageHeader } from './page_header'; +import { EuiHeaderBreadcrumbs, EuiHeaderBreadcrumb } from '@elastic/eui'; + +test('it renders without crashing', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiHeaderBreadcrumbs)).toHaveLength(1); + expect(wrapper).toMatchSnapshot(); +}); + +test('it renders breadcrumbs', () => { + const breadcrumbs = [{ + id: 'item-1', + href: '#item-1', + current: false + }, { + id: 'item-2', + href: '#item-2', + current: false + }, { + id: 'item-3', + href: '#item-3', + current: true + }]; + + const wrapper = mount(); + expect(wrapper.find(EuiHeaderBreadcrumbs)).toHaveLength(1); + expect(wrapper.find(EuiHeaderBreadcrumb)).toHaveLength(breadcrumbs.length); +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/cluster_privileges.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/cluster_privileges.test.js.snap new file mode 100644 index 0000000000000..5315e277468ed --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/cluster_privileges.test.js.snap @@ -0,0 +1,92 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it renders without crashing 1`] = ` + + + + + + + + +`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap new file mode 100644 index 0000000000000..b01cbf77668e0 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap @@ -0,0 +1,164 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it renders without crashing 1`] = ` + + + + Manage the actions this role can perform against your cluster. +

+ } + fullWidth={false} + gutterSize="l" + title={ +

+ Cluster privileges +

+ } + titleSize="xs" + > + + + +
+ + + Allow requests to be submitted on behalf of other users. +

+ } + fullWidth={false} + gutterSize="l" + title={ +

+ Run As privileges +

+ } + titleSize="xs" + > + + + +
+ + +
+ + + +

+ Index privileges +

+
+ +

+ Control access to the data in your cluster. +

+
+
+ + + Add Index Privilege + + +
+ +
+
+ + + +
+
+`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privileges.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privileges.test.js.snap new file mode 100644 index 0000000000000..189c4766c29f9 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privileges.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it renders without crashing 1`] = `Array []`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.test.js new file mode 100644 index 0000000000000..f75cf0ade71a9 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/cluster_privileges.test.js @@ -0,0 +1,20 @@ +/* + * 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 React from 'react'; +import { shallow, mount } from 'enzyme'; +import { ClusterPrivileges } from './cluster_privileges'; +import { EuiCheckboxGroup } from '@elastic/eui'; + +test('it renders without crashing', () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +test('it renders 2 checkbox groups of privileges', () => { + const wrapper = mount(); + expect(wrapper.find(EuiCheckboxGroup)).toHaveLength(2); +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js index f827523d9ccbf..b473d318b2f45 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -30,6 +30,9 @@ export class ElasticsearchPrivileges extends Component { onChange: PropTypes.func.isRequired, runAsUsers: PropTypes.array.isRequired, validator: PropTypes.object.isRequired, + indexPatterns: PropTypes.array.isRequired, + allowDocumentLevelSecurity: PropTypes.bool.isRequired, + allowFieldLevelSecurity: PropTypes.bool.isRequired, }; render() { diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.test.js new file mode 100644 index 0000000000000..7a2af5723e543 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.test.js @@ -0,0 +1,72 @@ +/* + * 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 React from 'react'; +import { shallow, mount } from 'enzyme'; +import { IndexPrivileges } from './index_privileges'; +import { ClusterPrivileges } from './cluster_privileges'; +import { ElasticsearchPrivileges } from './elasticsearch_privileges'; +import { RoleValidator } from '../../lib/validate_role'; + +test('it renders without crashing', () => { + const props = { + role: { + cluster: [], + indices: [], + run_as: [] + }, + editable: true, + httpClient: jest.fn(), + onChange: jest.fn(), + runAsUsers: [], + indexPatterns: [], + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +test('it renders ClusterPrivileges', () => { + const props = { + role: { + cluster: [], + indices: [], + run_as: [] + }, + editable: true, + httpClient: jest.fn(), + onChange: jest.fn(), + runAsUsers: [], + indexPatterns: [], + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + }; + const wrapper = mount(); + expect(wrapper.find(ClusterPrivileges)).toHaveLength(1); +}); + +test('it renders IndexPrivileges', () => { + const props = { + role: { + cluster: [], + indices: [], + run_as: [] + }, + editable: true, + httpClient: jest.fn(), + onChange: jest.fn(), + runAsUsers: [], + indexPatterns: [], + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + }; + const wrapper = mount(); + expect(wrapper.find(IndexPrivileges)).toHaveLength(1); +}); \ No newline at end of file diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.test.js new file mode 100644 index 0000000000000..221d301ceef0f --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.test.js @@ -0,0 +1,54 @@ +/* + * 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 React from 'react'; +import { shallow, mount } from 'enzyme'; +import { IndexPrivileges } from './index_privileges'; +import { IndexPrivilegeForm } from './index_privilege_form'; +import { RoleValidator } from '../../lib/validate_role'; + +test('it renders without crashing', () => { + const props = { + role: { + cluster: [], + indices: [], + run_as: [] + }, + httpClient: jest.fn(), + onChange: jest.fn(), + indexPatterns: [], + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + }; + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +test('it renders a IndexPrivilegeForm for each privilege on the role', () => { + const props = { + role: { + cluster: [], + indices: [{ + names: ['foo*'], + privileges: ['all'], + query: '*', + field_security: { + grant: ['some_field'] + } + }], + run_as: [] + }, + httpClient: jest.fn(), + onChange: jest.fn(), + indexPatterns: [], + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + }; + const wrapper = mount(); + expect(wrapper.find(IndexPrivilegeForm)).toHaveLength(1); +}); \ No newline at end of file From 642eaae1f4ba2c2cb09a9c18196e7b14922a96c3 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 29 May 2018 15:46:00 -0400 Subject: [PATCH 26/38] port remainder of index privs logic, and remove angular version of that form --- .../components/privileges/index_privileges.js | 9 +- .../index_privileges_form.html | 128 ------------------ .../index_privileges_form.js | 60 -------- .../index_privileges_form.less | 7 - 4 files changed, 6 insertions(+), 198 deletions(-) delete mode 100644 x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.html delete mode 100644 x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.js delete mode 100644 x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.less diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js index 2837d1927dc56..cf04718c3e475 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -6,7 +6,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; -import { isReservedRole } from '../../../../../lib/role'; +import { isReservedRole, isRoleEnabled } from '../../../../../lib/role'; import { IndexPrivilegeForm } from './index_privilege_form'; import { getFields } from '../../../../../objects'; @@ -40,8 +40,11 @@ export class IndexPrivileges extends Component { const props = { indexPatterns, - allowDocumentLevelSecurity, - allowFieldLevelSecurity, + // If editing an existing role while that has been disabled, always show the FLS/DLS fields because currently + // a role is only marked as disabled if it has FLS/DLS setup (usually before the user changed to a license that + // doesn't permit FLS/DLS). + allowDocumentLevelSecurity: allowDocumentLevelSecurity || !isRoleEnabled(this.props.role), + allowFieldLevelSecurity: allowFieldLevelSecurity || !isRoleEnabled(this.props.role), isReservedRole: isReservedRole(this.props.role) }; diff --git a/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.html b/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.html deleted file mode 100644 index 829b0dfaefa92..0000000000000 --- a/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.html +++ /dev/null @@ -1,128 +0,0 @@ -
- - - -
-
- -
- -
- - - {{$item}} - -
-
-
- - -
- - - - {{$item}} - -
-
-
- - -
- Indices must contain at least one privilege. -
- - -
- - - -
-
- - -
- -
- - - - -
- - - - {{$item}} - -
-
-
- -
-
- - - -
-
-
-
-
-
diff --git a/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.js b/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.js deleted file mode 100644 index 1bb058f56d096..0000000000000 --- a/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.js +++ /dev/null @@ -1,60 +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 _ from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './index_privileges_form.html'; - -const module = uiModules.get('security', ['kibana']); -module.directive('kbnIndexPrivilegesForm', function () { - return { - template, - scope: { - isNewRole: '=', - indices: '=', - indexPatterns: '=', - privileges: '=', - fieldOptions: '=', - isReserved: '=', - isEnabled: '=', - allowDocumentLevelSecurity: '=', - allowFieldLevelSecurity: '=', - addIndex: '&', - removeIndex: '&', - }, - restrict: 'E', - replace: true, - controllerAs: 'indexPrivilegesController', - controller: function ($scope) { - this.addIndex = function addIndex() { - $scope.addIndex({ indices: $scope.indices }); - }; - - this.removeIndex = function removeIndex(index) { - $scope.removeIndex({ indices: $scope.indices, index }); - }; - - this.getIndexTitle = function getIndexTitle(index) { - const indices = index.names.length ? index.names.join(', ') : 'No indices'; - const privileges = index.privileges.length ? index.privileges.join(', ') : 'No privileges'; - return `${indices} (${privileges})`; - }; - - this.union = _.flow(_.union, _.compact); - - // If editing an existing role while that has been disabled, always show the FLS/DLS fields because currently - // a role is only marked as disabled if it has FLS/DLS setup (usually before the user changed to a license that - // doesn't permit FLS/DLS). - if (!$scope.isNewRole && !$scope.isEnabled) { - this.showDocumentLevelSecurity = true; - this.showFieldLevelSecurity = true; - } else { - this.showDocumentLevelSecurity = $scope.allowDocumentLevelSecurity; - this.showFieldLevelSecurity = $scope.allowFieldLevelSecurity; - } - }, - }; -}); diff --git a/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.less b/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.less deleted file mode 100644 index edd7a4898f45a..0000000000000 --- a/x-pack/plugins/security/public/views/management/index_privileges_form/index_privileges_form.less +++ /dev/null @@ -1,7 +0,0 @@ -.indexPrivilegesForm { - height: 550px; -} - -.indexPrivilegesList { - flex: 0 0 400px; -} From 009c29fb415ef3c095b38d9ada84ecd8209ccb60 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 30 May 2018 08:23:18 -0400 Subject: [PATCH 27/38] additional tests and a bugfix --- .../security/public/lib/__tests__/role.js | 35 --- .../plugins/security/public/lib/role.test.js | 59 +++++ .../index_privilege_form.test.js.snap | 192 ++++++++++++++++ .../privileges/index_privilege_form.js | 4 +- .../privileges/index_privilege_form.test.js | 206 ++++++++++++++++++ 5 files changed, 459 insertions(+), 37 deletions(-) delete mode 100644 x-pack/plugins/security/public/lib/__tests__/role.js create mode 100644 x-pack/plugins/security/public/lib/role.test.js create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.test.js diff --git a/x-pack/plugins/security/public/lib/__tests__/role.js b/x-pack/plugins/security/public/lib/__tests__/role.js deleted file mode 100644 index efb22152ef7b1..0000000000000 --- a/x-pack/plugins/security/public/lib/__tests__/role.js +++ /dev/null @@ -1,35 +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 expect from 'expect.js'; -import { isRoleEnabled } from '../role'; - -describe('role', () => { - describe('isRoleEnabled', () => { - it('should return false if role is explicitly not enabled', () => { - const testRole = { - transient_metadata: { - enabled: false - } - }; - expect(isRoleEnabled(testRole)).to.be(false); - }); - - it('should return true if role is explicitly enabled', () => { - const testRole = { - transient_metadata: { - enabled: true - } - }; - expect(isRoleEnabled(testRole)).to.be(true); - }); - - it('should return true if role is NOT explicitly enabled or disabled', () => { - const testRole = {}; - expect(isRoleEnabled(testRole)).to.be(true); - }); - }); -}); diff --git a/x-pack/plugins/security/public/lib/role.test.js b/x-pack/plugins/security/public/lib/role.test.js new file mode 100644 index 0000000000000..4c6e9fb896dcf --- /dev/null +++ b/x-pack/plugins/security/public/lib/role.test.js @@ -0,0 +1,59 @@ +/* + * 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 { isRoleEnabled, isReservedRole } from './role'; + +describe('role', () => { + describe('isRoleEnabled', () => { + test('should return false if role is explicitly not enabled', () => { + const testRole = { + transient_metadata: { + enabled: false + } + }; + expect(isRoleEnabled(testRole)).toBe(false); + }); + + test('should return true if role is explicitly enabled', () => { + const testRole = { + transient_metadata: { + enabled: true + } + }; + expect(isRoleEnabled(testRole)).toBe(true); + }); + + test('should return true if role is NOT explicitly enabled or disabled', () => { + const testRole = {}; + expect(isRoleEnabled(testRole)).toBe(true); + }); + }); + + describe('isReservedRole', () => { + test('should return false if role is explicitly not reserved', () => { + const testRole = { + metadata: { + _reserved: false + } + }; + expect(isReservedRole(testRole)).toBe(false); + }); + + test('should return true if role is explicitly reserved', () => { + const testRole = { + metadata: { + _reserved: true + } + }; + expect(isReservedRole(testRole)).toBe(true); + }); + + test('should return false if role is NOT explicitly reserved or not reserved', () => { + const testRole = {}; + expect(isReservedRole(testRole)).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap new file mode 100644 index 0000000000000..440bc8f832bbe --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap @@ -0,0 +1,192 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`it renders without crashing 1`] = ` +
+ + + + + + + + + + + + + + + + + + + + + +

+ If no fields are granted, then users assigned to this role will not be able to see any data for this index. Is this really what you want? +

+
+
+
+
+
+
+ + + + + +
+
+ + + + + +
+ +
+`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index b807f41f4b3c8..615edfa032188 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -39,7 +39,7 @@ export class IndexPrivilegeForm extends Component { constructor(props) { super(props); this.state = { - queryExanded: !!props.indexPrivilege.query, + queryExpanded: !!props.indexPrivilege.query, documentQuery: props.indexPrivilege.query }; } @@ -128,7 +128,7 @@ export class IndexPrivilegeForm extends Component { return ( - + { + const props = { + indexPrivilege: { + names: [], + privileges: [], + query: null, + field_security: { + grant: [] + } + }, + indexPatterns: [], + availableFields: [], + isReservedRole: false, + allowDelete: true, + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + onChange: jest.fn() + }; + + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); +}); + +describe('delete button', () => { + const props = { + indexPrivilege: { + names: [], + privileges: [], + query: null, + field_security: { + grant: [] + } + }, + indexPatterns: [], + availableFields: [], + isReservedRole: false, + allowDelete: true, + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + onChange: jest.fn(), + onDelete: jest.fn() + }; + + test('it is hidden when allowDelete is false', () => { + const testProps = { + ...props, + allowDelete: false + }; + const wrapper = mount(); + expect(wrapper.find(EuiLink)).toHaveLength(0); + }); + + test('it is shown when allowDelete is true', () => { + const testProps = { + ...props, + allowDelete: true + }; + const wrapper = mount(); + expect(wrapper.find(EuiLink)).toHaveLength(1); + }); + + test('it invokes onDelete when clicked', () => { + const testProps = { + ...props, + allowDelete: true + }; + const wrapper = mount(); + wrapper.find(EuiLink).simulate('click'); + expect(testProps.onDelete).toHaveBeenCalledTimes(1); + }); +}); + +describe(`document level security`, () => { + const props = { + indexPrivilege: { + names: [], + privileges: [], + query: "some query", + field_security: { + grant: [] + } + }, + indexPatterns: [], + availableFields: [], + isReservedRole: false, + allowDelete: true, + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + onChange: jest.fn() + }; + + test(`inputs are hidden when DLS is not allowed`, () => { + const testProps = { + ...props, + allowDocumentLevelSecurity: false + }; + + const wrapper = mount(); + expect(wrapper.find(EuiSwitch)).toHaveLength(0); + expect(wrapper.find(EuiTextArea)).toHaveLength(0); + }); + + test('only the switch is shown when allowed, and query is empty', () => { + const testProps = { + ...props, + indexPrivilege: { + ...props.indexPrivilege, + query: null + } + }; + + const wrapper = mount(); + expect(wrapper.find(EuiSwitch)).toHaveLength(1); + expect(wrapper.find(EuiTextArea)).toHaveLength(0); + }); + + test('both inputs are shown when allowed, and query is not empty', () => { + const testProps = { + ...props, + }; + + const wrapper = mount(); + expect(wrapper.find(EuiSwitch)).toHaveLength(1); + expect(wrapper.find(EuiTextArea)).toHaveLength(1); + }); +}); + +describe('field level security', () => { + const props = { + indexPrivilege: { + names: [], + privileges: [], + query: null, + field_security: { + grant: ["foo*"] + } + }, + indexPatterns: [], + availableFields: [], + isReservedRole: false, + allowDelete: true, + allowDocumentLevelSecurity: true, + allowFieldLevelSecurity: true, + validator: new RoleValidator(), + onChange: jest.fn() + }; + + test(`input is hidden when FLS is not allowed`, () => { + const testProps = { + ...props, + allowFieldLevelSecurity: false + }; + + const wrapper = mount(); + expect(wrapper.find(".indexPrivilegeForm__grantedFieldsRow")).toHaveLength(0); + }); + + test('input is shown when allowed', () => { + const testProps = { + ...props, + }; + + const wrapper = mount(); + expect(wrapper.find("div.indexPrivilegeForm__grantedFieldsRow")).toHaveLength(1); + }); + + test('it displays a warning when no fields are granted', () => { + const testProps = { + ...props, + indexPrivilege: { + ...props.indexPrivilege, + field_security: { + grant: [] + } + } + }; + + const wrapper = mount(); + expect(wrapper.find("div.indexPrivilegeForm__grantedFieldsRow")).toHaveLength(1); + expect(wrapper.find(EuiCallOut)).toHaveLength(1); + }); + + test('it does not display a warning when fields are granted', () => { + const testProps = { + ...props + }; + + const wrapper = mount(); + expect(wrapper.find("div.indexPrivilegeForm__grantedFieldsRow")).toHaveLength(1); + expect(wrapper.find(EuiCallOut)).toHaveLength(0); + }); +}); From d0c44e98939162b05fff3fee38d8aa7171986387 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 30 May 2018 08:51:52 -0400 Subject: [PATCH 28/38] remove unused code --- x-pack/plugins/security/public/objects/index.js | 4 +--- .../security/public/objects/lib/get_users.js | 14 -------------- .../plugins/security/public/objects/lib/roles.js | 11 ----------- .../security/public/views/management/edit_role.js | 8 +------- .../edit_role/components/edit_role_page.js | 8 ++++---- .../security/public/views/management/management.js | 1 - .../public/views/management/management.less | 1 - 7 files changed, 6 insertions(+), 41 deletions(-) delete mode 100644 x-pack/plugins/security/public/objects/lib/get_users.js diff --git a/x-pack/plugins/security/public/objects/index.js b/x-pack/plugins/security/public/objects/index.js index 88575e4e24677..a6238ca879901 100644 --- a/x-pack/plugins/security/public/objects/index.js +++ b/x-pack/plugins/security/public/objects/index.js @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getRole, saveRole, deleteRole } from './lib/roles'; - -export { getUsers } from './lib/get_users'; +export { saveRole, deleteRole } from './lib/roles'; export { getFields } from './lib/get_fields'; diff --git a/x-pack/plugins/security/public/objects/lib/get_users.js b/x-pack/plugins/security/public/objects/lib/get_users.js deleted file mode 100644 index fd82b62d0f928..0000000000000 --- a/x-pack/plugins/security/public/objects/lib/get_users.js +++ /dev/null @@ -1,14 +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 chrome from 'ui/chrome'; - -const apiBase = chrome.addBasePath(`/api/security/v1/users`); - -export async function getUsers($http) { - return await $http - .get(apiBase) - .then(response => response.data || []); -} diff --git a/x-pack/plugins/security/public/objects/lib/roles.js b/x-pack/plugins/security/public/objects/lib/roles.js index c6b68d283569c..5fdd95a7ccb64 100644 --- a/x-pack/plugins/security/public/objects/lib/roles.js +++ b/x-pack/plugins/security/public/objects/lib/roles.js @@ -11,17 +11,6 @@ export async function saveRole($http, role) { return await $http.post(`${apiBase}/${role.name}`, role); } -export async function getRole($http, name) { - try { - return await $http.get(`${apiBase}/${name}`); - } catch (response) { - if (response.status === 404) { - return null; - } - throw response; - } -} - export async function deleteRole($http, name) { return await $http.delete(`${apiBase}/${name}`); } diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js index d05e519b4e24d..6c5c88f717cd0 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role.js @@ -78,9 +78,6 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { const kibanaApplicationPrivilege = $route.current.locals.kibanaApplicationPrivilege; const role = $route.current.locals.role; - this.isNewRole = $route.current.params.name == null; - this.fieldOptions = {}; - const xpackInfo = Private(XPackInfoProvider); const allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); const allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity'); @@ -92,8 +89,6 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { indexPatterns, } = $route.current.locals; - this.fieldOptions = {}; - const roleToEdit = role.toJSON(); if (roleToEdit.indices.length === 0) { roleToEdit.indices.push({ @@ -107,12 +102,11 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { render( Date: Wed, 30 May 2018 16:43:52 -0400 Subject: [PATCH 29/38] address PR feedback --- .../security/public/documentation_links.js | 5 +- .../elasticsearch_privileges.test.js.snap | 42 +++++- .../privileges/elasticsearch_privileges.js | 16 ++- .../privileges/elasticsearch_privileges.less | 3 + .../management/{ => edit_role}/edit_role.html | 0 .../views/management/edit_role/index.js | 130 ++++++++++++++++++ .../management/edit_role/lib/constants.js | 7 - .../public/views/management/management.js | 2 +- 8 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less rename x-pack/plugins/security/public/views/management/{ => edit_role}/edit_role.html (100%) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/index.js delete mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/constants.js diff --git a/x-pack/plugins/security/public/documentation_links.js b/x-pack/plugins/security/public/documentation_links.js index 8d9bb3c2256b4..d60092ea945e6 100644 --- a/x-pack/plugins/security/public/documentation_links.js +++ b/x-pack/plugins/security/public/documentation_links.js @@ -7,5 +7,8 @@ import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; export const documentationLinks = { - dashboardViewMode: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-view-modes.html` + dashboardViewMode: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-view-modes.html`, + esClusterPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/current/security-privileges.html#security-privileges`, + esIndicesPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/current/security-privileges.html#privileges-list-indices`, + esRunAsPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/current/security-privileges.html#_run_as_privilege`, }; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap index b01cbf77668e0..f0fe2a5c630f5 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/elasticsearch_privileges.test.js.snap @@ -16,7 +16,19 @@ exports[`it renders without crashing 1`] = ` gutterSize="l" title={

- Cluster privileges + Cluster privileges + + +

} titleSize="xs" @@ -51,7 +63,19 @@ exports[`it renders without crashing 1`] = ` gutterSize="l" title={

- Run As privileges + Run As privileges + + +

} titleSize="xs" @@ -98,7 +122,19 @@ exports[`it renders without crashing 1`] = ` size="xs" >

- Index privileges + Index privileges + + +

Cluster privileges

} + title={

Cluster privileges {this.learnMore(documentationLinks.esClusterPrivileges)}

} description={

Manage the actions this role can perform against your cluster. @@ -82,7 +86,7 @@ export class ElasticsearchPrivileges extends Component { Run As privileges

} + title={

Run As privileges {this.learnMore(documentationLinks.esRunAsPrivileges)}

} description={

Allow requests to be submitted on behalf of other users. @@ -106,7 +110,7 @@ export class ElasticsearchPrivileges extends Component {

-

Index privileges

+

Index privileges {this.learnMore(documentationLinks.esIndicesPrivileges)}

Control access to the data in your cluster.

{this.props.editable && ( @@ -125,6 +129,12 @@ export class ElasticsearchPrivileges extends Component { ); } + learnMore = (href) => ( + + + + ); + addIndexPrivilege = () => { const { role } = this.props; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less new file mode 100644 index 0000000000000..776ef72a4627e --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.less @@ -0,0 +1,3 @@ +.editRole__learnMore { + margin-left: 5px; +} \ No newline at end of file diff --git a/x-pack/plugins/security/public/views/management/edit_role.html b/x-pack/plugins/security/public/views/management/edit_role/edit_role.html similarity index 100% rename from x-pack/plugins/security/public/views/management/edit_role.html rename to x-pack/plugins/security/public/views/management/edit_role/edit_role.html diff --git a/x-pack/plugins/security/public/views/management/edit_role/index.js b/x-pack/plugins/security/public/views/management/edit_role/index.js new file mode 100644 index 0000000000000..9d3e10b75dba4 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/index.js @@ -0,0 +1,130 @@ +/* + * 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 _ from 'lodash'; +import routes from 'ui/routes'; +import { fatalError } from 'ui/notify'; +import template from 'plugins/security/views/management/edit_role/edit_role.html'; +import 'plugins/security/views/management/edit_role/edit_role.less'; +import '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_privileges'; +import 'plugins/security/services/shield_indices'; + +import { IndexPatternsProvider } from 'ui/index_patterns/index_patterns'; +import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info'; +import { checkLicenseError } from 'plugins/security/lib/check_license_error'; +import { EDIT_ROLES_PATH, ROLES_PATH } from '../management_urls'; + +import { EditRolePage } from './components'; + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; + +routes.when(`${EDIT_ROLES_PATH}/:name?`, { + template, + resolve: { + role($route, ShieldRole, kbnUrl, Promise, Notifier) { + const name = $route.current.params.name; + + let role; + + if (name != null) { + role = ShieldRole.get({ name }).$promise + .catch((response) => { + + if (response.status !== 404) { + return fatalError(response); + } + + const notifier = new Notifier(); + notifier.error(`No "${name}" role found.`); + kbnUrl.redirect(ROLES_PATH); + return Promise.halt(); + }); + + } else { + role = Promise.resolve(new ShieldRole({ + cluster: [], + indices: [], + run_as: [], + applications: [] + })); + } + + return role.then(roleToEdit => { + if (roleToEdit.indices.length === 0) { + roleToEdit.indices.push({ + names: [], + privileges: [], + field_security: { + grant: ['*'] + } + }); + } + return roleToEdit; + }); + + }, + kibanaApplicationPrivilege(ApplicationPrivilege, kbnUrl, Promise, Private) { + return ApplicationPrivilege.query().$promise + .then(privileges => privileges.map(p => p.toJSON())) + .catch(checkLicenseError(kbnUrl, Promise, Private)); + }, + users(ShieldUser, kbnUrl, Promise, Private) { + // $promise is used here because the result is an ngResource, not a promise itself + return ShieldUser.query().$promise + .then(users => _.map(users, 'username')) + .catch(checkLicenseError(kbnUrl, Promise, Private)); + }, + indexPatterns(Private) { + const indexPatterns = Private(IndexPatternsProvider); + return indexPatterns.getTitles(); + } + }, + controllerAs: 'editRole', + controller($injector, $scope, $http, rbacEnabled, rbacApplication) { + const $route = $injector.get('$route'); + const Private = $injector.get('Private'); + + const Notifier = $injector.get('Notifier'); + + const kibanaApplicationPrivilege = $route.current.locals.kibanaApplicationPrivilege; + const role = $route.current.locals.role; + + const xpackInfo = Private(XPackInfoProvider); + const allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); + const allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity'); + + const domNode = document.getElementById('editRoleReactRoot'); + + const { + users, + indexPatterns, + } = $route.current.locals; + + render(, domNode); + + // unmount react on controller destroy + $scope.$on('$destroy', () => { + unmountComponentAtNode(domNode); + }); + } +}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/constants.js b/x-pack/plugins/security/public/views/management/edit_role/lib/constants.js deleted file mode 100644 index 6518df48847e9..0000000000000 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/constants.js +++ /dev/null @@ -1,7 +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. - */ - -export const CLUSTER_PRIVS_DOC_LINK = `https://www.elastic.co/guide/en/x-pack/current/security-privileges.html#security-privileges`; diff --git a/x-pack/plugins/security/public/views/management/management.js b/x-pack/plugins/security/public/views/management/management.js index 06866c226ce90..f7000478cbfc7 100644 --- a/x-pack/plugins/security/public/views/management/management.js +++ b/x-pack/plugins/security/public/views/management/management.js @@ -9,7 +9,7 @@ import 'plugins/security/views/management/password_form/password_form'; import 'plugins/security/views/management/users'; import 'plugins/security/views/management/roles'; import 'plugins/security/views/management/edit_user'; -import 'plugins/security/views/management/edit_role'; +import 'plugins/security/views/management/edit_role/index'; import 'plugins/security/views/management/management.less'; import routes from 'ui/routes'; import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info'; From abb55d71452397a95ae45278d9d7572287f27a8c Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Wed, 30 May 2018 16:47:26 -0400 Subject: [PATCH 30/38] improve role validation --- .../__snapshots__/validate_role.test.js.snap | 3 +++ .../management/edit_role/lib/validate_role.js | 20 ++++++++++--------- .../edit_role/lib/validate_role.test.js | 8 ++++++++ 3 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/security/public/views/management/edit_role/lib/__snapshots__/validate_role.test.js.snap diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/__snapshots__/validate_role.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/lib/__snapshots__/validate_role.test.js.snap new file mode 100644 index 0000000000000..20b4280b7f493 --- /dev/null +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/__snapshots__/validate_role.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validateIndexPrivileges it throws when indices is not an array 1`] = `"Expected role.indices to be an array"`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js index 0179c4bb2d40e..3fc638a83782c 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.js @@ -35,16 +35,18 @@ export class RoleValidator { validateIndexPrivileges(role) { if (!this._shouldValidate) return valid(); - if (Array.isArray(role.indices)) { - const areIndicesValid = role.indices - .map(this.validateIndexPrivilege.bind(this)) - .find((result) => result.isInvalid) == null; - - if (areIndicesValid) { - return valid(); - } - return invalid(); + if (!Array.isArray(role.indices)) { + throw new TypeError(`Expected role.indices to be an array`); + } + + const areIndicesValid = role.indices + .map(this.validateIndexPrivilege.bind(this)) + .find((result) => result.isInvalid) == null; + + if (areIndicesValid) { + return valid(); } + return invalid(); } validateIndexPrivilege(indexPrivilege) { diff --git a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js index 795e681be5d59..47d2bfe59a37e 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js +++ b/x-pack/plugins/security/public/views/management/edit_role/lib/validate_role.test.js @@ -83,4 +83,12 @@ describe('validateIndexPrivileges', () => { isInvalid: true }); }); + + test('it throws when indices is not an array', () => { + const role = { + indices: null + }; + + expect(() => validator.validateIndexPrivileges(role)).toThrowErrorMatchingSnapshot(); + }); }); From cb02fe1dec8d1fa8608783943893b1f78d7aec13 Mon Sep 17 00:00:00 2001 From: chorn Date: Thu, 31 May 2018 18:02:34 -0400 Subject: [PATCH 31/38] Design edits - played with spacing - moved some buttons around - moved the reserved lock icon to title - moved links to docs to description and make it a text link - fixed some responsive layouts --- .../kbn-ui-framework/dist/ui_framework.css | 206 ------------------ .../edit_role/components/collapsible_panel.js | 8 +- .../components/collapsible_panel.less | 3 +- .../edit_role/components/edit_role_page.js | 122 +++++------ .../privileges/elasticsearch_privileges.js | 49 ++--- .../privileges/index_privilege_form.js | 68 +++--- .../privileges/kibana_privileges.js | 15 +- .../components/reserved_role_badge.js | 11 +- .../views/management/edit_role/edit_role.less | 10 +- 9 files changed, 143 insertions(+), 349 deletions(-) diff --git a/packages/kbn-ui-framework/dist/ui_framework.css b/packages/kbn-ui-framework/dist/ui_framework.css index 0d013535e3555..101f718149647 100644 --- a/packages/kbn-ui-framework/dist/ui_framework.css +++ b/packages/kbn-ui-framework/dist/ui_framework.css @@ -78,15 +78,12 @@ main { overflow: hidden; } .kuiActionItem { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; } @@ -105,15 +102,12 @@ main { background-color: rgba(0, 0, 0, 0.1); } .kuiBar { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -121,22 +115,18 @@ main { /* 1 */ } .kuiBarSection { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; margin-right: 25px; } .kuiBarSection:not(:first-child):not(:last-child):not(:only-child) { - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -145,12 +135,10 @@ main { margin-left: 0; } .kuiBarSection:last-child { margin-right: 0; - -webkit-box-flex: 0; -webkit-flex: 0 1 auto; -ms-flex: 0 1 auto; flex: 0 1 auto; /* 4 */ - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; @@ -201,12 +189,10 @@ main { * 1. Solves whitespace problems introduced by inline elements. */ .kuiButton__inner { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; /* 1 */ - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -513,11 +499,9 @@ main { background-color: rgba(165, 231, 255, 0.5); } .kuiButtonGroup { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } @@ -545,24 +529,19 @@ main { margin-left: 2px; } .kuiButtonGroup--fullWidth { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } .kuiButtonGroup--fullWidth > .kuiButton { - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; text-align: center; } .kuiCard { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; @@ -572,20 +551,15 @@ main { line-height: 1.5; } .kuiCard__description { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: start; -webkit-justify-content: flex-start; -ms-flex-pack: start; justify-content: flex-start; @@ -613,7 +587,6 @@ main { * 2. Offset the spacing between wrapped cards. */ .kuiCardGroup { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; @@ -628,7 +601,6 @@ main { * 2. Use an even margin all around the card so that the spacing is still even when wrapped. */ } .kuiCardGroup .kuiCard { - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; @@ -636,7 +608,6 @@ main { margin: 15px; /* 2 */ } .kuiCardGroup .kuiCard .kuiCard__description { - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; } @@ -687,20 +658,15 @@ main { right: 0; left: 0; background: rgba(255, 255, 255, 0.7); - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -736,11 +702,9 @@ main { cursor: pointer; } .kuiColorPicker__preview { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } @@ -890,11 +854,9 @@ main { margin-right: 8px; } .kuiContextMenu__itemLayout { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } @@ -1071,13 +1033,11 @@ main { color: #ffffff; } .kuiContextMenuItem__inner { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } .kuiContextMenuItem__text { - -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; } @@ -1096,13 +1056,11 @@ main { text-decoration: none; } .kuiEvent { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } .kuiEventSymbol { - -webkit-box-flex: 0; -webkit-flex: 0 1 auto; -ms-flex: 0 1 auto; flex: 0 1 auto; @@ -1111,7 +1069,6 @@ main { padding-right: 8px; } .kuiEventBody { - -webkit-box-flex: 1; -webkit-flex: 1 1 0%; -ms-flex: 1 1 0%; flex: 1 1 0%; } @@ -1150,16 +1107,13 @@ main { border-bottom: solid 2px #00A69B; } .kuiFlexGroup { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } .kuiFlexGroup .kuiFlexItem { - -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; } @@ -1185,13 +1139,11 @@ main { margin: 20px; } .kuiFlexGroup--justifyContentSpaceEvenly { - -webkit-box-pack: space-evenly; -webkit-justify-content: space-evenly; -ms-flex-pack: space-evenly; justify-content: space-evenly; } .kuiFlexGroup--justifyContentSpaceBetween { - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; } @@ -1202,31 +1154,26 @@ main { justify-content: space-around; } .kuiFlexGroup--justifyContentCenter { - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; } .kuiFlexGroup--justifyContentFlexEnd { - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; } .kuiFlexGroup--alignItemsStart { - -webkit-box-align: start; -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start; } .kuiFlexGroup--alignItemsCenter { - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } .kuiFlexGroup--alignItemsEnd { - -webkit-box-align: end; -webkit-align-items: flex-end; -ms-flex-align: end; align-items: flex-end; } @@ -1243,7 +1190,6 @@ main { flex-wrap: wrap; } } .kuiFlexGrid { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; @@ -1252,12 +1198,10 @@ main { flex-wrap: wrap; margin-bottom: 0; } .kuiFlexGrid > .kuiFlexItem { - -webkit-box-flex: 0; -webkit-flex-grow: 0; -ms-flex-positive: 0; flex-grow: 0; } .kuiFlexGrid > .kuiFlexItem.kuiFlexItem--flexGrowZero { - -webkit-box-flex: 0 !important; -webkit-flex-grow: 0 !important; -ms-flex-positive: 0 !important; flex-grow: 0 !important; @@ -1274,7 +1218,6 @@ main { .kuiFlexGrid--gutterSmall { margin: -4px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1285,7 +1228,6 @@ main { .kuiFlexGrid--gutterSmall { margin: -4px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1296,7 +1238,6 @@ main { .kuiFlexGrid--gutterSmall { margin: -4px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1314,7 +1255,6 @@ main { .kuiFlexGrid--gutterMedium { margin: -8px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1325,7 +1265,6 @@ main { .kuiFlexGrid--gutterMedium { margin: -8px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1336,7 +1275,6 @@ main { .kuiFlexGrid--gutterMedium { margin: -8px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1354,7 +1292,6 @@ main { .kuiFlexGrid--gutterLarge { margin: -12px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1365,7 +1302,6 @@ main { .kuiFlexGrid--gutterLarge { margin: -12px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1376,7 +1312,6 @@ main { .kuiFlexGrid--gutterLarge { margin: -12px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1394,7 +1329,6 @@ main { .kuiFlexGrid--gutterXLarge { margin: -16px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1405,7 +1339,6 @@ main { .kuiFlexGrid--gutterXLarge { margin: -16px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1416,7 +1349,6 @@ main { .kuiFlexGrid--gutterXLarge { margin: -16px; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -1429,13 +1361,10 @@ main { * 1. Allow KuiPanels to expand to fill the item. */ .kuiFlexItem { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; /* 1 */ - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; @@ -1446,7 +1375,6 @@ main { */ } .kuiFlexItem.kuiFlexItem--flexGrowZero { /* 1 */ - -webkit-box-flex: 0; -webkit-flex-grow: 0; -ms-flex-positive: 0; flex-grow: 0; @@ -1456,52 +1384,42 @@ main { flex-basis: auto; /* 2 */ } .kuiFlexItem.kuiFlexItem--flexGrow1 { - -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; } .kuiFlexItem.kuiFlexItem--flexGrow2 { - -webkit-box-flex: 2; -webkit-flex-grow: 2; -ms-flex-positive: 2; flex-grow: 2; } .kuiFlexItem.kuiFlexItem--flexGrow3 { - -webkit-box-flex: 3; -webkit-flex-grow: 3; -ms-flex-positive: 3; flex-grow: 3; } .kuiFlexItem.kuiFlexItem--flexGrow4 { - -webkit-box-flex: 4; -webkit-flex-grow: 4; -ms-flex-positive: 4; flex-grow: 4; } .kuiFlexItem.kuiFlexItem--flexGrow5 { - -webkit-box-flex: 5; -webkit-flex-grow: 5; -ms-flex-positive: 5; flex-grow: 5; } .kuiFlexItem.kuiFlexItem--flexGrow6 { - -webkit-box-flex: 6; -webkit-flex-grow: 6; -ms-flex-positive: 6; flex-grow: 6; } .kuiFlexItem.kuiFlexItem--flexGrow7 { - -webkit-box-flex: 7; -webkit-flex-grow: 7; -ms-flex-positive: 7; flex-grow: 7; } .kuiFlexItem.kuiFlexItem--flexGrow8 { - -webkit-box-flex: 8; -webkit-flex-grow: 8; -ms-flex-positive: 8; flex-grow: 8; } .kuiFlexItem.kuiFlexItem--flexGrow9 { - -webkit-box-flex: 9; -webkit-flex-grow: 9; -ms-flex-positive: 9; flex-grow: 9; } .kuiFlexItem.kuiFlexItem--flexGrow10 { - -webkit-box-flex: 10; -webkit-flex-grow: 10; -ms-flex-positive: 10; flex-grow: 10; } @@ -1592,11 +1510,9 @@ main { background-color: #0079a5; } .kuiCheckBoxLabel { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -1860,18 +1776,15 @@ main { * 1. We may want to put elements in here which have different heights. */ .kuiFieldGroup { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; /* 1 */ } .kuiFieldGroup--alignTop { - -webkit-box-align: start; -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start; } @@ -1882,7 +1795,6 @@ main { margin-left: 10px; } .kuiFieldGroupSection--wide { - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; } @@ -1890,7 +1802,6 @@ main { width: 100%; } .kuiGallery { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; @@ -1899,20 +1810,15 @@ main { flex-wrap: wrap; } .kuiGalleryItem { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -1931,15 +1837,12 @@ main { border-color: #00A6FF; } .kuiGalleryItem__image { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -1970,15 +1873,12 @@ main { color: #666; } .kuiHeaderBar { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -1990,22 +1890,18 @@ main { * 1. Align a single section to the left by default. */ .kuiHeaderBarSection { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; margin-right: 25px; } .kuiHeaderBarSection:not(:first-child):not(:last-child):not(:only-child) { - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -2014,12 +1910,10 @@ main { margin-left: 0; } .kuiHeaderBarSection:last-child { margin-right: 0; - -webkit-box-flex: 0; -webkit-flex: 0 1 auto; -ms-flex: 0 1 auto; flex: 0 1 auto; /* 4 */ - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; @@ -2118,11 +2012,9 @@ main { * 1. Align with first line of title text if it wraps. */ .kuiInfoPanelHeader { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: baseline; -webkit-align-items: baseline; -ms-flex-align: baseline; align-items: baseline; @@ -2179,11 +2071,9 @@ main { * a bit. */ .kuiLocalBreadcrumbs { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -2253,15 +2143,12 @@ main { padding: 0; } .kuiDatePickerNavigation { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -2425,13 +2312,11 @@ main { /* 1 */ } .kuiLocalDropdownPanels { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } .kuiLocalDropdownPanel { - -webkit-box-flex: 1; -webkit-flex: 1 1 0%; -ms-flex: 1 1 0%; flex: 1 1 0%; } @@ -2460,15 +2345,12 @@ main { margin-bottom: 0; } .kuiLocalDropdownHeader { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -2487,7 +2369,6 @@ main { color: #cecece; } .kuiLocalDropdownHeader__actions { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } @@ -2549,21 +2430,17 @@ main { color: #9e9e9e; } .kuiLocalMenu { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } .kuiLocalMenuItem { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -2615,16 +2492,12 @@ main { * dropdown. */ .kuiLocalNav { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -2643,15 +2516,12 @@ main { * 1. Allow row to expand if the content is so long that it wraps. */ .kuiLocalNavRow { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -2659,11 +2529,9 @@ main { /* 1 */ } .kuiLocalNavRow__section { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } @@ -2678,14 +2546,12 @@ main { .kuiLocalNavRow--secondary { padding: 0 10px; /* 1 */ - -webkit-box-align: start; -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start; /* 1 */ } .kuiLocalSearch { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; @@ -2708,7 +2574,6 @@ main { transition: border-color 0.1s linear; min-height: 30px; /* 1 */ - -webkit-box-flex: 1; -webkit-flex: 1 1 100%; -ms-flex: 1 1 100%; flex: 1 1 100%; @@ -2738,7 +2603,6 @@ main { .kuiLocalSearchInput--secondary { height: 30px; - -webkit-box-flex: 0; -webkit-flex: 0 0 auto; -ms-flex: 0 0 auto; flex: 0 0 auto; @@ -2750,11 +2614,9 @@ main { border-right-color: #333333; } .kuiLocalSearchAssistedInput { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-flex: 1; -webkit-flex: 1 1 100%; -ms-flex: 1 1 100%; flex: 1 1 100%; @@ -2857,11 +2719,9 @@ main { * 1. We want the bottom border on selected tabs to be flush with the bottom of the container. */ .kuiLocalTabs { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: end; -webkit-align-items: flex-end; -ms-flex-align: end; align-items: flex-end; @@ -2906,11 +2766,9 @@ main { color: #dedede; } .kuiLocalTitle { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -3042,7 +2900,6 @@ main { /* 3 */ } .kuiMenuButtonGroup { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } @@ -3050,7 +2907,6 @@ main { margin-left: 4px; } .kuiMenuButtonGroup--alignRight { - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; } @@ -3073,7 +2929,6 @@ main { color: #191E23; } .kuiMicroButtonGroup { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } @@ -3087,15 +2942,12 @@ main { left: 0; right: 0; bottom: 0; - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -3121,15 +2973,12 @@ main { min-width: auto; } .kuiModalHeader { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -3170,11 +3019,9 @@ main { color: #cecece; } .kuiModalFooter { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; @@ -3213,11 +3060,9 @@ main { * 1. Put 10px of space between each child. */ .kuiPager { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } @@ -3237,21 +3082,16 @@ main { border-radius: 4px; } .kuiPanel--prompt { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; text-align: center; - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -3268,29 +3108,23 @@ main { border-radius: 0; } .kuiPanel--centered { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } .kuiPanelHeader { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -3352,22 +3186,18 @@ main { * 1. Undo what barSection mixin does. */ .kuiPanelHeaderSection { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; margin-right: 25px; } .kuiPanelHeaderSection:not(:first-child):not(:last-child):not(:only-child) { - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -3376,12 +3206,10 @@ main { margin-left: 0; } .kuiPanelHeaderSection:last-child { margin-right: 0; - -webkit-box-flex: 0; -webkit-flex: 0 1 auto; -ms-flex: 0 1 auto; flex: 0 1 auto; /* 4 */ - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; @@ -3406,7 +3234,6 @@ main { background-color: #FFF; border: 1px solid #D9D9D9; border-radius: 4px; - -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; } @@ -3419,7 +3246,6 @@ main { .kuiPanelSimple.kuiPanelSimple--shadow { box-shadow: 0 16px 16px -8px rgba(0, 0, 0, 0.1); } .kuiPanelSimple.kuiPanelSimple--flexGrowZero { - -webkit-box-flex: 0; -webkit-flex-grow: 0; -ms-flex-positive: 0; flex-grow: 0; } @@ -3513,16 +3339,12 @@ main { color: #ffffff; } .kuiEmptyTablePrompt { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; @@ -3537,11 +3359,9 @@ main { margin-top: 10px; } .kuiStatusText { - display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; - -webkit-box-align: baseline; -webkit-align-items: baseline; -ms-flex-align: baseline; align-items: baseline; } @@ -3657,11 +3477,9 @@ main { display: block; opacity: 1; } .kuiTableHeaderCellButton .kuiTableHeaderCell__liner { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } @@ -3762,7 +3580,6 @@ main { line-height: 1.5; } .kuiTabs { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; @@ -3886,15 +3703,12 @@ main { /* 1 */ } .kuiToolBar { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -3945,22 +3759,18 @@ main { border-color: #0079a5; } .kuiToolBarSection { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; margin-right: 25px; } .kuiToolBarSection:not(:first-child):not(:last-child):not(:only-child) { - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -3969,12 +3779,10 @@ main { margin-left: 0; } .kuiToolBarSection:last-child { margin-right: 0; - -webkit-box-flex: 0; -webkit-flex: 0 1 auto; -ms-flex: 0 1 auto; flex: 0 1 auto; /* 4 */ - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; @@ -3994,15 +3802,12 @@ main { /* 1 */ } .kuiToolBarFooter { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; @@ -4014,22 +3819,18 @@ main { border: 1px solid #D9D9D9; } .kuiToolBarFooterSection { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; margin-left: 25px; margin-right: 25px; } .kuiToolBarFooterSection:not(:first-child):not(:last-child):not(:only-child) { - -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; @@ -4038,12 +3839,10 @@ main { margin-left: 0; } .kuiToolBarFooterSection:last-child { margin-right: 0; - -webkit-box-flex: 0; -webkit-flex: 0 1 auto; -ms-flex: 0 1 auto; flex: 0 1 auto; /* 4 */ - -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; @@ -4061,17 +3860,14 @@ main { * kuiToolBarSection sibling. */ .kuiToolBarSearch { - display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; - -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; margin-left: 25px; margin-right: 25px; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; @@ -4087,7 +3883,6 @@ main { /* 1 */ } .kuiToolBarSearchBox { - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; @@ -4210,7 +4005,6 @@ main { .kuiView { background-color: #FFF; - -webkit-box-flex: 1; -webkit-flex: 1 1 auto; -ms-flex: 1 1 auto; flex: 1 1 auto; } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js index e39b25e8e522b..f2bac7a02b99c 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.js @@ -38,12 +38,12 @@ export class CollapsiblePanel extends Component { getTitle = () => { return ( - + -
- {this.props.title} -
+

+ {this.props.title} +

diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less index cc56943ffcda9..ffb065880c560 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less +++ b/x-pack/plugins/security/public/views/management/edit_role/components/collapsible_panel.less @@ -1,3 +1,4 @@ .collapsiblePanel__logo { - margin-right: 10px; + margin-right: 8px; + vertical-align: text-bottom; } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 38f3389ccace6..8b50c3b19e5fa 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -9,10 +9,10 @@ import { get } from 'lodash'; import { toastNotifications } from 'ui/notify'; import { EuiPanel, - EuiText, + EuiTitle, EuiSpacer, EuiPage, - EuiPageContent, + EuiButtonEmpty, EuiForm, EuiFormRow, EuiFieldText, @@ -57,27 +57,21 @@ export class EditRolePage extends Component {
- - - {this.getFormTitle()} + + {this.getFormTitle()} - + - {this.getRoleName()} + {this.getRoleName()} - + {this.getElasticsearchPrivileges()} - {this.getElasticsearchPrivileges()} + {this.getKibanaPrivileges()} - + - {this.getKibanaPrivileges()} - - - - {this.getFormButtons()} - - + {this.getFormButtons()} +
); @@ -94,12 +88,7 @@ export class EditRolePage extends Component { } return ( - - -

{titleText}

-
- {this.getActionButton()} -
+

{titleText}

); }; @@ -118,19 +107,22 @@ export class EditRolePage extends Component { getRoleName = () => { return ( - - - - - - - - + + + + ); } @@ -149,17 +141,20 @@ export class EditRolePage extends Component { getElasticsearchPrivileges() { return ( - +
+ + +
); } @@ -175,40 +170,41 @@ export class EditRolePage extends Component { } return ( - +
+ + +
); }; getFormButtons = () => { if (isReservedRole(this.props.role)) { return ( - - - - Return to role list - - - + + Return to role list + ); } const saveText = this.editingExistingRole() ? 'Update role' : 'Create role'; return ( - + {saveText} - + Cancel - + + + {this.getActionButton()} ); }; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js index 49be5cfc83c4e..a0c6eca6f0c6e 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/elasticsearch_privileges.js @@ -11,14 +11,11 @@ import { EuiSpacer, EuiComboBox, EuiFormRow, - EuiFlexGroup, - EuiFlexItem, EuiButton, EuiDescribedFormGroup, EuiTitle, EuiHorizontalRule, EuiLink, - EuiIcon, } from '@elastic/eui'; import './elasticsearch_privileges.less'; import { ClusterPrivileges } from './cluster_privileges'; @@ -71,14 +68,14 @@ export class ElasticsearchPrivileges extends Component { return ( Cluster privileges {this.learnMore(documentationLinks.esClusterPrivileges)}

} + title={

Cluster privileges

} description={

- Manage the actions this role can perform against your cluster. + Manage the actions this role can perform against your cluster. {this.learnMore(documentationLinks.esClusterPrivileges)}

} > - +
@@ -86,14 +83,14 @@ export class ElasticsearchPrivileges extends Component { Run As privileges {this.learnMore(documentationLinks.esRunAsPrivileges)}

} + title={

Run As privileges

} description={

- Allow requests to be submitted on behalf of other users. + Allow requests to be submitted on the behalf of other users. {this.learnMore(documentationLinks.esRunAsPrivileges)}

} > - + ({ id: username, label: username }))} @@ -106,32 +103,26 @@ export class ElasticsearchPrivileges extends Component { - -
- - -

Index privileges {this.learnMore(documentationLinks.esIndicesPrivileges)}

-

Control access to the data in your cluster.

-
- {this.props.editable && ( - - Add Index Privilege - - )} -
- -
-
- - - +

Index privileges

+ + +

Control access to the data in your cluster. {this.learnMore(documentationLinks.esIndicesPrivileges)}

+
+ + + + + + {this.props.editable && ( + Add index privilege + )}
); } learnMore = (href) => ( - + Learn more ); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index 615edfa032188..479762770fce6 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -6,7 +6,6 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { - EuiCallOut, EuiComboBox, EuiTextArea, EuiFormRow, @@ -15,8 +14,7 @@ import { EuiSwitch, EuiSpacer, EuiHorizontalRule, - EuiLink, - EuiIcon, + EuiButtonIcon, } from '@elastic/eui'; import { getIndexPrivileges } from '../../../../../services/role_privileges'; @@ -46,19 +44,21 @@ export class IndexPrivilegeForm extends Component { render() { return ( -
- + + + {this.getPrivilegeForm()} {this.props.allowDelete && ( - + + + )} - -
+ ); } @@ -90,6 +90,8 @@ export class IndexPrivilegeForm extends Component { {this.getGrantedFieldsControl()}
+ + {this.getGrantedDocumentsControl()} ); @@ -108,27 +110,36 @@ export class IndexPrivilegeForm extends Component { const { grant = [] } = indexPrivilege.field_security || {}; - let grantedFieldsWarning = null; + // TODO: Remove this completely, if ok with just putting the text in the helptext + // let grantedFieldsWarning = null; if (allowFieldLevelSecurity) { - if (grant.length === 0) { - grantedFieldsWarning = ( - - - -

- If no fields are granted, then users assigned to this role will not be able to - see any data for this index. Is this really what you want? -

-
-
- ); - } + // if (grant.length === 0) { + // grantedFieldsWarning = ( + // + // + // + //

+ // If no fields are granted, then users assigned to this role will not be able to + // see any data for this index. Is this really what you want? + //

+ //
+ //
+ // ); + // } return ( - + - {grantedFieldsWarning} @@ -158,9 +168,9 @@ export class IndexPrivilegeForm extends Component { } return ( - + {!this.props.isReservedRole && - + } {this.state.queryExpanded && - - + + Application privileges

} description={

Manage the actions this role can perform within Kibana.

} > - + + + ); } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js index 8d0c17bd062ab..8b750b19338ed 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.js @@ -9,9 +9,8 @@ import PropTypes from 'prop-types'; import { isReservedRole } from '../../../../lib/role'; import { - EuiBadge, + EuiIcon, EuiToolTip, - EuiFlexItem, } from '@elastic/eui'; @@ -22,11 +21,9 @@ export const ReservedRoleBadge = (props) => { if (isReservedRole(role)) { return ( - - - Reserved Role - - + + + ); } return null; diff --git a/x-pack/plugins/security/public/views/management/edit_role/edit_role.less b/x-pack/plugins/security/public/views/management/edit_role/edit_role.less index 7b6b150c0444e..d4f7ac04880d6 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/edit_role.less +++ b/x-pack/plugins/security/public/views/management/edit_role/edit_role.less @@ -1,8 +1,10 @@ -#editRoleReactRoot, .editRolePage, .editRolePage__content { +#editRoleReactRoot { background: #f5f5f5; + flex-grow: 1; } -.editRolePage__content { - border: none; - box-shadow: none; +.editRolePage { + max-width: 1000px; + margin-left: auto; + margin-right: auto; } From 87f8cd8f6539e60f1e9ba400efa7a5b6d1c5d269 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 1 Jun 2018 08:23:50 -0400 Subject: [PATCH 32/38] start addressing design feedback --- .../public/views/management/edit_role.js | 122 ------------------ .../edit_role/components/edit_role_page.js | 4 +- .../components/privileges/index_privileges.js | 5 + .../views/management/edit_role/index.js | 14 +- 4 files changed, 8 insertions(+), 137 deletions(-) delete mode 100644 x-pack/plugins/security/public/views/management/edit_role.js diff --git a/x-pack/plugins/security/public/views/management/edit_role.js b/x-pack/plugins/security/public/views/management/edit_role.js deleted file mode 100644 index 6c5c88f717cd0..0000000000000 --- a/x-pack/plugins/security/public/views/management/edit_role.js +++ /dev/null @@ -1,122 +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 _ from 'lodash'; -import routes from 'ui/routes'; -import { fatalError } from 'ui/notify'; -import template from 'plugins/security/views/management/edit_role.html'; -import 'plugins/security/views/management/edit_role/edit_role.less'; -import '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_privileges'; -import 'plugins/security/services/shield_indices'; - -import { IndexPatternsProvider } from 'ui/index_patterns/index_patterns'; -import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info'; -import { checkLicenseError } from 'plugins/security/lib/check_license_error'; -import { EDIT_ROLES_PATH, ROLES_PATH } from './management_urls'; - -import { EditRolePage } from './edit_role/components'; - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; - -routes.when(`${EDIT_ROLES_PATH}/:name?`, { - template, - resolve: { - role($route, ShieldRole, kbnUrl, Promise, Notifier) { - const name = $route.current.params.name; - if (name != null) { - return ShieldRole.get({ name }).$promise - .catch((response) => { - - if (response.status !== 404) { - return fatalError(response); - } - - const notifier = new Notifier(); - notifier.error(`No "${name}" role found.`); - kbnUrl.redirect(ROLES_PATH); - return Promise.halt(); - }); - } - return new ShieldRole({ - cluster: [], - indices: [], - run_as: [], - applications: [] - }); - }, - kibanaApplicationPrivilege(ApplicationPrivilege, kbnUrl, Promise, Private) { - return ApplicationPrivilege.query().$promise - .then(privileges => privileges.map(p => p.toJSON())) - .catch(checkLicenseError(kbnUrl, Promise, Private)); - }, - users(ShieldUser, kbnUrl, Promise, Private) { - // $promise is used here because the result is an ngResource, not a promise itself - return ShieldUser.query().$promise - .then(users => _.map(users, 'username')) - .catch(checkLicenseError(kbnUrl, Promise, Private)); - }, - indexPatterns(Private) { - const indexPatterns = Private(IndexPatternsProvider); - return indexPatterns.getTitles(); - } - }, - controllerAs: 'editRole', - controller($injector, $scope, $http, rbacEnabled, rbacApplication) { - const $route = $injector.get('$route'); - const Private = $injector.get('Private'); - - const Notifier = $injector.get('Notifier'); - - const kibanaApplicationPrivilege = $route.current.locals.kibanaApplicationPrivilege; - const role = $route.current.locals.role; - - const xpackInfo = Private(XPackInfoProvider); - const allowDocumentLevelSecurity = xpackInfo.get('features.security.allowRoleDocumentLevelSecurity'); - const allowFieldLevelSecurity = xpackInfo.get('features.security.allowRoleFieldLevelSecurity'); - - const domNode = document.getElementById('editRoleReactRoot'); - - const { - users, - indexPatterns, - } = $route.current.locals; - - const roleToEdit = role.toJSON(); - if (roleToEdit.indices.length === 0) { - roleToEdit.indices.push({ - names: [], - privileges: [], - field_security: { - grant: ['*'] - } - }); - } - - render(, domNode); - - // unmount react on controller destroy - $scope.$on('$destroy', () => { - unmountComponentAtNode(domNode); - }); - } -}); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 38f3389ccace6..0d7b89e4f2563 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -86,11 +86,11 @@ export class EditRolePage extends Component { getFormTitle = () => { let titleText; if (isReservedRole(this.props.role)) { - titleText = 'Reserved role'; + titleText = 'Viewing role'; } else if (this.editingExistingRole()) { titleText = 'Edit role'; } else { - titleText = 'New role'; + titleText = 'Create role'; } return ( diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js index cf04718c3e475..c4b961e60cd05 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -117,6 +117,11 @@ export class IndexPrivileges extends Component { }; loadAvailableFields(indices) { + // Reserved roles cannot be edited, and therefore do not need to fetch available fields. + if (isReservedRole(this.props.role)) { + return; + } + const patterns = indices.map(index => index.names.join(',')); const cachedPatterns = Object.keys(this.state.availableFields); diff --git a/x-pack/plugins/security/public/views/management/edit_role/index.js b/x-pack/plugins/security/public/views/management/edit_role/index.js index 9d3e10b75dba4..385fe444e0422 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/index.js +++ b/x-pack/plugins/security/public/views/management/edit_role/index.js @@ -57,19 +57,7 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { })); } - return role.then(roleToEdit => { - if (roleToEdit.indices.length === 0) { - roleToEdit.indices.push({ - names: [], - privileges: [], - field_security: { - grant: ['*'] - } - }); - } - return roleToEdit; - }); - + return role.then(res => res.toJSON()); }, kibanaApplicationPrivilege(ApplicationPrivilege, kbnUrl, Promise, Private) { return ApplicationPrivilege.query().$promise From bb1c129f247cf75ced21f8c4f18630fe611db8ed Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 1 Jun 2018 10:02:21 -0400 Subject: [PATCH 33/38] Don't hide delete button for placeholder privileges --- .../edit_role/components/privileges/index_privileges.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js index c4b961e60cd05..cb72ecd7aa4bf 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privileges.js @@ -53,7 +53,7 @@ export class IndexPrivileges extends Component { key={idx} {...props} validator={this.props.validator} - allowDelete={!props.isReservedRole && !(this.isPlaceholderPrivilege(indexPrivilege) && indices.length === 1)} + allowDelete={!props.isReservedRole} indexPrivilege={indexPrivilege} availableFields={this.state.availableFields[indexPrivilege.names.join(',')]} onChange={this.onIndexPrivilegeChange(idx)} From 829a6e763aeee78a19441b0f7ee3921304b63df5 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 1 Jun 2018 10:28:47 -0400 Subject: [PATCH 34/38] change Kibana Privileges to use a select instead of checkboxes --- .../privileges/kibana_privileges.js | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js index b13002feae003..41537bd27f7a2 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/kibana_privileges.js @@ -8,13 +8,16 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { isReservedRole } from '../../../../../lib/role'; import { getKibanaPrivileges } from '../../lib/get_application_privileges'; -import { togglePrivilege } from '../../lib/set_application_privileges'; +import { setApplicationPrivileges } from '../../lib/set_application_privileges'; import { CollapsiblePanel } from '../collapsible_panel'; import { - EuiCheckboxGroup, + EuiSelect, EuiDescribedFormGroup, } from '@elastic/eui'; + +const noPrivilegeValue = '-none-'; + export class KibanaPrivileges extends Component { static propTypes = { role: PropTypes.object.isRequired, @@ -46,26 +49,24 @@ export class KibanaPrivileges extends Component { const kibanaPrivileges = getKibanaPrivileges(kibanaAppPrivileges, role, rbacApplication); - const checkboxes = Object.keys(kibanaPrivileges).map(p => ({ - id: this.privilegeToId(p), - label: p - })); + const options = [ + { value: noPrivilegeValue, text: 'none' }, + ...Object.keys(kibanaPrivileges).map(p => ({ + value: p, + text: p + })) + ]; - const selectionMap = Object.keys(kibanaPrivileges).reduce((acc, p) => { - return { - ...acc, - [this.privilegeToId(p)]: kibanaPrivileges[p] - }; - }, {}); + const value = Object.keys(kibanaPrivileges).find(p => kibanaPrivileges[p]) || noPrivilegeValue; return ( Application privileges

} description={

Manage the actions this role can perform within Kibana.

} > - @@ -73,13 +74,24 @@ export class KibanaPrivileges extends Component { ); } - onKibanaPrivilegesChange = (privilege) => { + onKibanaPrivilegesChange = (e) => { const role = { ...this.props.role, applications: [...this.props.role.applications] }; - togglePrivilege(role, this.props.rbacApplication, this.idToPrivilege(privilege)); + const privilege = e.target.value; + + if (privilege === noPrivilegeValue) { + // unsetting all privileges -- only necessary until RBAC Phase 3 + const noPrivileges = {}; + setApplicationPrivileges(noPrivileges, role, this.props.rbacApplication); + } else { + const newPrivileges = { + [privilege]: true + }; + setApplicationPrivileges(newPrivileges, role, this.props.rbacApplication); + } this.props.onChange(role); } From c7776709ea0f63c1b70be000b7d84ab0c2aea928 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 1 Jun 2018 11:34:27 -0400 Subject: [PATCH 35/38] upgrade to EUI 0.0.51, and fix Role breadcrumbs --- package.json | 4 +- x-pack/package.json | 2 +- .../__snapshots__/page_header.test.js.snap | 4 +- .../edit_role/components/page_header.js | 14 +-- .../edit_role/components/page_header.test.js | 12 +- .../views/management/edit_role/edit_role.html | 2 +- .../views/management/edit_role/index.js | 18 ++- .../__snapshots__/page_header.test.js.snap | 16 ++- .../management/components/page_header.js | 20 ++-- .../management/components/page_header.test.js | 16 ++- x-pack/yarn.lock | 112 +++++++++++++----- yarn.lock | 103 +++++++++++++--- 12 files changed, 224 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index 5bdf13c020631..d6f2bffd002e8 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "url": "https://github.com/elastic/kibana.git" }, "dependencies": { - "@elastic/eui": "v0.0.47", + "@elastic/eui": "v0.0.51", "@elastic/filesaver": "1.1.2", "@elastic/numeral": "2.3.2", "@elastic/ui-ace": "0.2.3", @@ -205,8 +205,8 @@ "validate-npm-package-name": "2.2.2", "vega-lib": "^3.3.1", "vega-lite": "^2.4.0", - "vega-tooltip": "^0.9.14", "vega-schema-url-parser": "1.0.0", + "vega-tooltip": "^0.9.14", "vision": "4.1.0", "webpack": "3.6.0", "webpack-merge": "4.1.0", diff --git a/x-pack/package.json b/x-pack/package.json index bfcb1dd5a9905..95aa06158b637 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -75,7 +75,7 @@ "yargs": "4.7.1" }, "dependencies": { - "@elastic/eui": "0.0.47", + "@elastic/eui": "v0.0.51", "@elastic/node-crypto": "0.1.2", "@elastic/node-phantom-simple": "2.2.4", "@elastic/numeral": "2.3.2", diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap index 7dddfa8c5cdef..c7cbfdb53fd73 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap +++ b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap @@ -5,7 +5,9 @@ exports[`it renders without crashing 1`] = ` - + `; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js index ffbab9d0921a7..03347a01ec0fa 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js @@ -10,7 +10,6 @@ import PropTypes from 'prop-types'; import { EuiHeader, EuiHeaderSection, - EuiHeaderBreadcrumb, EuiHeaderBreadcrumbs } from '@elastic/eui'; @@ -19,20 +18,17 @@ export class PageHeader extends Component { return ( - - {this.props.breadcrumbs.map(this.buildBreadcrumb)} - + ); } buildBreadcrumb = (breadcrumb) => { - return ( - - {breadcrumb.display} - - ); + return { + text: breadcrumb.display, + href: breadcrumb.href, + }; } } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js index 89151673b9911..80248b4e75b83 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js @@ -7,7 +7,7 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; import { PageHeader } from './page_header'; -import { EuiHeaderBreadcrumbs, EuiHeaderBreadcrumb } from '@elastic/eui'; +import { EuiHeaderBreadcrumbs } from '@elastic/eui'; test('it renders without crashing', () => { const wrapper = shallow(); @@ -17,20 +17,16 @@ test('it renders without crashing', () => { test('it renders breadcrumbs', () => { const breadcrumbs = [{ - id: 'item-1', + display: 'item-1', href: '#item-1', - current: false }, { - id: 'item-2', + display: 'item-2', href: '#item-2', - current: false }, { - id: 'item-3', + display: 'item-3', href: '#item-3', - current: true }]; const wrapper = mount(); expect(wrapper.find(EuiHeaderBreadcrumbs)).toHaveLength(1); - expect(wrapper.find(EuiHeaderBreadcrumb)).toHaveLength(breadcrumbs.length); }); diff --git a/x-pack/plugins/security/public/views/management/edit_role/edit_role.html b/x-pack/plugins/security/public/views/management/edit_role/edit_role.html index 2e8134b17d29f..5539f0521dd79 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/edit_role.html +++ b/x-pack/plugins/security/public/views/management/edit_role/edit_role.html @@ -1 +1 @@ -
\ No newline at end of file +
diff --git a/x-pack/plugins/security/public/views/management/edit_role/index.js b/x-pack/plugins/security/public/views/management/edit_role/index.js index 385fe444e0422..e356b9d7c01b6 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/index.js +++ b/x-pack/plugins/security/public/views/management/edit_role/index.js @@ -96,6 +96,8 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, { indexPatterns, } = $route.current.locals; + const routeBreadcrumbs = routes.getBreadcrumbs(); + render( b.id === 'edit'); + + const hasEntryAfterEdit = indexOfEdit >= 0 && indexOfEdit < (routeBreadcrumbs.length - 1); + + if (hasEntryAfterEdit) { + // The entry after 'edit' is the name of the role being edited (if any). We don't want to use the "humanized" version of the role name here + const roleName = routeBreadcrumbs[indexOfEdit + 1]; + roleName.display = roleName.id; + } + + return routeBreadcrumbs.filter(b => b.id !== 'edit'); +} diff --git a/x-pack/plugins/spaces/public/views/management/components/__snapshots__/page_header.test.js.snap b/x-pack/plugins/spaces/public/views/management/components/__snapshots__/page_header.test.js.snap index 936f678055d41..c7cbfdb53fd73 100644 --- a/x-pack/plugins/spaces/public/views/management/components/__snapshots__/page_header.test.js.snap +++ b/x-pack/plugins/spaces/public/views/management/components/__snapshots__/page_header.test.js.snap @@ -1,15 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`it renders without crashing 1`] = ` -
-
+ -
-
-
+ + `; diff --git a/x-pack/plugins/spaces/public/views/management/components/page_header.js b/x-pack/plugins/spaces/public/views/management/components/page_header.js index 956c2809b325f..19a76b3d6ccf5 100644 --- a/x-pack/plugins/spaces/public/views/management/components/page_header.js +++ b/x-pack/plugins/spaces/public/views/management/components/page_header.js @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiHeader, EuiHeaderSection, - EuiHeaderBreadcrumb, EuiHeaderBreadcrumbs } from '@elastic/eui'; @@ -19,25 +18,20 @@ export class PageHeader extends Component { return ( - - {this.props.breadcrumbs.map(this.buildBreadcrumb)} - + ); } buildBreadcrumb = (breadcrumb) => { - return ( - - {breadcrumb.display} - - ); + return { + text: breadcrumb.display, + href: breadcrumb.href, + }; } } - - PageHeader.propTypes = { breadcrumbs: PropTypes.array.isRequired -}; +}; \ No newline at end of file diff --git a/x-pack/plugins/spaces/public/views/management/components/page_header.test.js b/x-pack/plugins/spaces/public/views/management/components/page_header.test.js index b960175b38deb..706a6de4a0abc 100644 --- a/x-pack/plugins/spaces/public/views/management/components/page_header.test.js +++ b/x-pack/plugins/spaces/public/views/management/components/page_header.test.js @@ -6,30 +6,28 @@ import React from 'react'; import { PageHeader } from './page_header'; -import { render } from 'enzyme'; -import renderer from 'react-test-renderer'; +import { mount, shallow } from 'enzyme'; test('it renders without crashing', () => { - const component = renderer.create( + const component = shallow( ); expect(component).toMatchSnapshot(); }); test('it renders breadcrumbs', () => { - const component = render( + const component = mount( ); - expect(component.find('a')).toHaveLength(2); + expect(component.find('a.euiBreadcrumb')).toHaveLength(1); + expect(component.find('span.euiBreadcrumb')).toHaveLength(1); }); diff --git a/x-pack/yarn.lock b/x-pack/yarn.lock index fb4184fbbb632..7c39d6e8fae06 100644 --- a/x-pack/yarn.lock +++ b/x-pack/yarn.lock @@ -10,9 +10,9 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@elastic/eui@0.0.47": - version "0.0.47" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.47.tgz#5bae27966bb1d68bb3106853610a407509053b44" +"@elastic/eui@v0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.51.tgz#5d809af270dd9994a609fd01eaa84e21a62fff98" dependencies: brace "^0.10.0" classnames "^2.2.5" @@ -990,8 +990,8 @@ brace@0.10.0, brace@^0.10.0: w3c-blob "0.0.1" brace@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.0.tgz#155cd80607687dc8cb908f0df94e62a033c1d563" + version "0.11.1" + resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" braces@^1.8.2: version "1.8.5" @@ -1072,6 +1072,10 @@ buffer-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" +buffer-from@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + buffer@^3.0.1: version "3.6.0" resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb" @@ -1400,7 +1404,16 @@ concat-stream@1.5.1: readable-stream "~2.0.0" typedarray "~0.0.5" -concat-stream@^1.4.7, concat-stream@~1.6.0: +concat-stream@^1.4.7: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-stream@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: @@ -1443,8 +1456,8 @@ core-js@^2.4.0, core-js@^2.5.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" core-js@^2.5.1: - version "2.5.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b" + version "2.5.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2470,8 +2483,8 @@ focus-trap-react@^3.0.4, focus-trap-react@^3.1.1: focus-trap "^2.0.1" focus-trap@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-2.4.2.tgz#44ea1c55a9c22c2b6529dcebbde6390eb2ee4c88" + version "2.4.5" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-2.4.5.tgz#91c9c9ffb907f8f4446d80202dda9c12c2853ddb" dependencies: tabbable "^1.0.3" @@ -3316,10 +3329,16 @@ icalendar@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/icalendar/-/icalendar-0.7.1.tgz#d0d3486795f8f1c5cf4f8cafac081b4b4e7a32ae" -iconv-lite@0.4.19, iconv-lite@^0.4.19, iconv-lite@~0.4.13: +iconv-lite@0.4.19, iconv-lite@^0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" +iconv-lite@~0.4.13: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" @@ -4513,7 +4532,11 @@ lodash@3.10.1, lodash@^3.10.0, lodash@^3.10.1: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.0.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1: +lodash@^4.0.1: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -5539,7 +5562,7 @@ prop-types@15.5.8: dependencies: fbjs "^0.8.9" -prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0: +prop-types@^15.5.0, prop-types@^15.5.4, prop-types@^15.6.0: version "15.6.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" dependencies: @@ -5547,7 +5570,7 @@ prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, loose-envify "^1.3.1" object-assign "^4.1.1" -prop-types@^15.6.1: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.1: version "15.6.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: @@ -5710,8 +5733,8 @@ react-clipboard.js@^1.1.2: prop-types "^15.5.0" react-color@^2.13.8: - version "2.13.8" - resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.13.8.tgz#bcc58f79a722b9bfc37c402e68cd18f26970aee4" + version "2.14.1" + resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.14.1.tgz#db8ad4f45d81e74896fc2e1c99508927c6d084e0" dependencies: lodash "^4.0.1" material-colors "^1.2.1" @@ -5743,6 +5766,10 @@ react-input-autosize@^2.1.2, react-input-autosize@^2.2.1: dependencies: prop-types "^15.5.8" +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + react-markdown-renderer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/react-markdown-renderer/-/react-markdown-renderer-1.4.0.tgz#f3b95bd9fc7f7bf8ab3f0150aa696b41740e7d01" @@ -5855,14 +5882,15 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.2.0: prop-types "^15.6.0" react-virtualized@^9.18.5: - version "9.18.5" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.18.5.tgz#42dd390ebaa7ea809bfcaf775d39872641679b89" + version "9.19.1" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.19.1.tgz#84b53253df2d9df61c85ce037141edccc70a73fd" dependencies: babel-runtime "^6.26.0" classnames "^2.2.3" dom-helpers "^2.4.0 || ^3.0.0" loose-envify "^1.3.0" prop-types "^15.6.0" + react-lifecycles-compat "^3.0.4" react-vis@^1.8.1: version "1.8.2" @@ -5924,7 +5952,7 @@ read-pkg@^1.0.0: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -5936,6 +5964,18 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.0.3" util-deprecate "~1.0.1" +readable-stream@^2.2.2: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^2.3.3, readable-stream@^2.3.5: version "2.3.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" @@ -6335,10 +6375,18 @@ rxjs@5.3.0: dependencies: symbol-observable "^1.0.1" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + samsam@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" @@ -6739,6 +6787,12 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -6898,8 +6952,8 @@ tabbable@1.1.0: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.0.tgz#2c9a9c9f09db5bb0659f587d532548dd6ef2067b" tabbable@^1.0.3, tabbable@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.2.tgz#b171680aea6e0a3e9281ff23532e2e5de11c0d94" + version "1.1.3" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.3.tgz#0e4ee376f3631e42d7977a074dbd2b3827843081" tar-fs@1.13.0: version "1.13.0" @@ -7160,8 +7214,8 @@ typedarray@^0.0.6, typedarray@~0.0.5: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" + version "0.7.18" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" uglify-js@^2.6: version "2.8.29" @@ -7293,10 +7347,14 @@ uuid@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" -uuid@^3.0.0, uuid@^3.1.0: +uuid@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +uuid@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + v8flags@^2.0.2: version "2.1.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" @@ -7489,8 +7547,8 @@ whatwg-encoding@^1.0.1: iconv-lite "0.4.19" whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" whatwg-url@^6.3.0: version "6.4.0" diff --git a/yarn.lock b/yarn.lock index 2098dda85233c..459cd99a1d3ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,9 +77,9 @@ version "0.0.0" uid "" -"@elastic/eui@0.0.47", "@elastic/eui@v0.0.47": - version "0.0.47" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.47.tgz#5bae27966bb1d68bb3106853610a407509053b44" +"@elastic/eui@v0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-0.0.51.tgz#5d809af270dd9994a609fd01eaa84e21a62fff98" dependencies: brace "^0.10.0" classnames "^2.2.5" @@ -1860,6 +1860,10 @@ buffer-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" +buffer-from@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -2576,7 +2580,16 @@ concat-stream@1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^1.4.7, concat-stream@^1.5.2, concat-stream@^1.6.0, concat-stream@~1.6.0: +concat-stream@^1.4.7: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-stream@^1.5.2, concat-stream@^1.6.0, concat-stream@~1.6.0: version "1.6.1" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26" dependencies: @@ -2699,10 +2712,14 @@ core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" -core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.1: +core-js@^2.2.0, core-js@^2.5.0: version "2.5.3" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" +core-js@^2.4.0, core-js@^2.5.1: + version "2.5.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -4772,8 +4789,8 @@ focus-trap-react@^3.0.4, focus-trap-react@^3.1.1: focus-trap "^2.0.1" focus-trap@^2.0.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-2.4.3.tgz#95edc23e77829b7772cb2486d61fd6371ce112f9" + version "2.4.5" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-2.4.5.tgz#91c9c9ffb907f8f4446d80202dda9c12c2853ddb" dependencies: tabbable "^1.0.3" @@ -5905,7 +5922,7 @@ icalendar@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/icalendar/-/icalendar-0.7.1.tgz#d0d3486795f8f1c5cf4f8cafac081b4b4e7a32ae" -iconv-lite@0.4, iconv-lite@0.4.19, iconv-lite@^0.4.13, iconv-lite@^0.4.17, iconv-lite@^0.4.19, iconv-lite@~0.4.13: +iconv-lite@0.4, iconv-lite@0.4.19, iconv-lite@^0.4.13, iconv-lite@^0.4.17, iconv-lite@^0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -5917,6 +5934,12 @@ iconv-lite@0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.7.tgz#89d32fec821bf8597f44609b4bc09bed5c209a23" +iconv-lite@~0.4.13: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -10041,7 +10064,16 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-ace@^5.5.0, react-ace@^5.9.0: +react-ace@^5.5.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-5.10.0.tgz#e328b37ac52759f700be5afdb86ada2f5ec84c5e" + dependencies: + brace "^0.11.0" + lodash.get "^4.4.2" + lodash.isequal "^4.1.1" + prop-types "^15.5.8" + +react-ace@^5.9.0: version "5.9.0" resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-5.9.0.tgz#427a1cc4869b960a6f9748aa7eb169a9269fc336" dependencies: @@ -10129,6 +10161,10 @@ react-input-range@^1.3.0: autobind-decorator "^1.3.4" prop-types "^15.5.8" +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + react-markdown-renderer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/react-markdown-renderer/-/react-markdown-renderer-1.4.0.tgz#f3b95bd9fc7f7bf8ab3f0150aa696b41740e7d01" @@ -10271,14 +10307,15 @@ react-toggle@4.0.2: classnames "^2.2.5" react-virtualized@^9.18.5: - version "9.18.5" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.18.5.tgz#42dd390ebaa7ea809bfcaf775d39872641679b89" + version "9.19.1" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.19.1.tgz#84b53253df2d9df61c85ce037141edccc70a73fd" dependencies: babel-runtime "^6.26.0" classnames "^2.2.3" dom-helpers "^2.4.0 || ^3.0.0" loose-envify "^1.3.0" prop-types "^15.6.0" + react-lifecycles-compat "^3.0.4" react-vis@^1.8.1: version "1.9.2" @@ -10381,7 +10418,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@~2.3.3: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@~2.3.3: version "2.3.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" dependencies: @@ -10393,6 +10430,18 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.0.3" util-deprecate "~1.0.1" +readable-stream@^2.2.2: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@~1.0.2: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -10928,10 +10977,14 @@ rxjs@5.4.3: dependencies: symbol-observable "^1.0.1" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + safe-json-stringify@~1: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.1.0.tgz#bd2b6dad1ebafab3c24672a395527f01804b7e19" @@ -10949,6 +11002,10 @@ safefs@^4.0.0: editions "^1.1.1" graceful-fs "^4.1.4" +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + samsam@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" @@ -11622,6 +11679,12 @@ string_decoder@~1.0.3: dependencies: safe-buffer "~5.1.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -11820,8 +11883,8 @@ tabbable@1.1.0: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.0.tgz#2c9a9c9f09db5bb0659f587d532548dd6ef2067b" tabbable@^1.0.3, tabbable@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.2.tgz#b171680aea6e0a3e9281ff23532e2e5de11c0d94" + version "1.1.3" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.3.tgz#0e4ee376f3631e42d7977a074dbd2b3827843081" table@^3.7.8: version "3.8.3" @@ -12276,8 +12339,8 @@ typescript@^2.8.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624" ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" + version "0.7.18" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.5" @@ -13116,7 +13179,11 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: dependencies: iconv-lite "0.4.19" -whatwg-fetch@>=0.10.0, whatwg-fetch@^2.0.3: +whatwg-fetch@>=0.10.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + +whatwg-fetch@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" From b05d9376a8ea5eb7811f6634887e05bfd459713a Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 5 Jun 2018 13:16:42 -0400 Subject: [PATCH 36/38] fix doc links --- x-pack/plugins/security/public/documentation_links.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security/public/documentation_links.js b/x-pack/plugins/security/public/documentation_links.js index d60092ea945e6..d357451d48ac7 100644 --- a/x-pack/plugins/security/public/documentation_links.js +++ b/x-pack/plugins/security/public/documentation_links.js @@ -8,7 +8,7 @@ import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; export const documentationLinks = { dashboardViewMode: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/xpack-view-modes.html`, - esClusterPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/current/security-privileges.html#security-privileges`, - esIndicesPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/current/security-privileges.html#privileges-list-indices`, - esRunAsPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/current/security-privileges.html#_run_as_privilege`, + esClusterPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/${DOC_LINK_VERSION}/security-privileges.html#security-privileges`, + esIndicesPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/${DOC_LINK_VERSION}/security-privileges.html#privileges-list-indices`, + esRunAsPrivileges: `${ELASTIC_WEBSITE_URL}guide/en/x-pack/${DOC_LINK_VERSION}/security-privileges.html#_run_as_privilege`, }; From 6adfe2af3cd6408d352f2171af61eb1c1cd36a74 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 5 Jun 2018 16:54:05 -0400 Subject: [PATCH 37/38] Fix tests and cleanup unused code --- .../collapsible_panel.test.js.snap | 10 +- .../elasticsearch_privileges.test.js.snap | 200 +++++++----------- .../index_privilege_form.test.js.snap | 56 +++-- .../privileges/index_privilege_form.js | 18 -- .../privileges/index_privilege_form.test.js | 12 +- .../components/reserved_role_badge.test.js | 4 +- 6 files changed, 117 insertions(+), 183 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap index d94f2f4c47bab..d946357354fe2 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap +++ b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/collapsible_panel.test.js.snap @@ -10,9 +10,9 @@ exports[`it renders without blowing up 1`] = ` alignItems="baseline" component="div" direction="row" - gutterSize="l" + gutterSize="s" justifyContent="flexStart" - responsive={true} + responsive={false} wrap={false} > -
+

Elasticsearch -

+
- Manage the actions this role can perform against your cluster. -

- } - fullWidth={false} - gutterSize="l" - title={ -

- Cluster privileges + Manage the actions this role can perform against your cluster. - + Learn more

} + fullWidth={false} + gutterSize="l" + title={ +

+ Cluster privileges +

+ } titleSize="xs" > - Allow requests to be submitted on behalf of other users. -

- } - fullWidth={false} - gutterSize="l" - title={ -

- Run As privileges + Allow requests to be submitted on the behalf of other users. - + Learn more

} + fullWidth={false} + gutterSize="l" + title={ +

+ Run As privileges +

+ } titleSize="xs" > - -
- - - -

- Index privileges - - - -

-
- -

- Control access to the data in your cluster. -

-
-
- - - Add Index Privilege - - -
- -
-
- + Index privileges + + + + - + Control access to the data in your cluster. + + Learn more + +

+
+ -
+ } + /> + + + Add index privilege + `; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap index 440bc8f832bbe..b84dcc659d5f9 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/__snapshots__/index_privilege_form.test.js.snap @@ -1,9 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`it renders without crashing 1`] = ` -
+ + - - - -

- If no fields are granted, then users assigned to this role will not be able to see any data for this index. Is this really what you want? -

-
-
+ - - - + - -
+ `; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index 479762770fce6..e5fafd0798f87 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -110,25 +110,7 @@ export class IndexPrivilegeForm extends Component { const { grant = [] } = indexPrivilege.field_security || {}; - // TODO: Remove this completely, if ok with just putting the text in the helptext - // let grantedFieldsWarning = null; - if (allowFieldLevelSecurity) { - - // if (grant.length === 0) { - // grantedFieldsWarning = ( - // - // - // - //

- // If no fields are granted, then users assigned to this role will not be able to - // see any data for this index. Is this really what you want? - //

- //
- //
- // ); - // } - return ( { const props = { @@ -60,7 +60,7 @@ describe('delete button', () => { allowDelete: false }; const wrapper = mount(); - expect(wrapper.find(EuiLink)).toHaveLength(0); + expect(wrapper.find(EuiButtonIcon)).toHaveLength(0); }); test('it is shown when allowDelete is true', () => { @@ -69,7 +69,7 @@ describe('delete button', () => { allowDelete: true }; const wrapper = mount(); - expect(wrapper.find(EuiLink)).toHaveLength(1); + expect(wrapper.find(EuiButtonIcon)).toHaveLength(1); }); test('it invokes onDelete when clicked', () => { @@ -78,7 +78,7 @@ describe('delete button', () => { allowDelete: true }; const wrapper = mount(); - wrapper.find(EuiLink).simulate('click'); + wrapper.find(EuiButtonIcon).simulate('click'); expect(testProps.onDelete).toHaveBeenCalledTimes(1); }); }); @@ -191,7 +191,7 @@ describe('field level security', () => { const wrapper = mount(); expect(wrapper.find("div.indexPrivilegeForm__grantedFieldsRow")).toHaveLength(1); - expect(wrapper.find(EuiCallOut)).toHaveLength(1); + expect(wrapper.find(".euiFormHelpText")).toHaveLength(1); }); test('it does not display a warning when fields are granted', () => { @@ -201,6 +201,6 @@ describe('field level security', () => { const wrapper = mount(); expect(wrapper.find("div.indexPrivilegeForm__grantedFieldsRow")).toHaveLength(1); - expect(wrapper.find(EuiCallOut)).toHaveLength(0); + expect(wrapper.find(".euiFormHelpText")).toHaveLength(0); }); }); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js index c9227a415f9ff..939348661a741 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/reserved_role_badge.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { - EuiBadge + EuiIcon } from '@elastic/eui'; import { ReservedRoleBadge } from './reserved_role_badge'; import { @@ -23,7 +23,7 @@ const unreservedRole = {}; test('it renders without crashing', () => { const wrapper = shallow(); - expect(wrapper.find(EuiBadge)).toHaveLength(1); + expect(wrapper.find(EuiIcon)).toHaveLength(1); }); test('it renders nothing for an unreserved role', () => { From e739e5c640cace21695e784c269d972d1b9be70f Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 7 Jun 2018 14:57:42 -0400 Subject: [PATCH 38/38] design updates --- .../__snapshots__/page_header.test.js.snap | 20 +++++++++------- .../edit_role/components/edit_role_page.js | 24 +++++++++---------- .../edit_role/components/page_header.js | 14 +++++------ .../edit_role/components/page_header.test.js | 6 ++--- .../privileges/index_privilege_form.js | 3 ++- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap index c7cbfdb53fd73..628a5c068f83a 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap +++ b/x-pack/plugins/security/public/views/management/edit_role/components/__snapshots__/page_header.test.js.snap @@ -1,13 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`it renders without crashing 1`] = ` - - - - - +
+ + +
`; diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js index 5843ece2ee3e2..13cd7a4f1823b 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/edit_role_page.js @@ -54,26 +54,24 @@ export class EditRolePage extends Component { render() { return ( -
+ - - - {this.getFormTitle()} + + {this.getFormTitle()} - + - {this.getRoleName()} + {this.getRoleName()} - {this.getElasticsearchPrivileges()} + {this.getElasticsearchPrivileges()} - {this.getKibanaPrivileges()} + {this.getKibanaPrivileges()} - + - {this.getFormButtons()} - - -
+ {this.getFormButtons()} + + ); } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js index 03347a01ec0fa..87aac82490817 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.js @@ -8,19 +8,17 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { - EuiHeader, - EuiHeaderSection, - EuiHeaderBreadcrumbs + EuiBreadcrumbs, + EuiSpacer, } from '@elastic/eui'; export class PageHeader extends Component { render() { return ( - - - - - +
+ + +
); } diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js index 80248b4e75b83..99f6994ea9898 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/page_header.test.js @@ -7,11 +7,11 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; import { PageHeader } from './page_header'; -import { EuiHeaderBreadcrumbs } from '@elastic/eui'; +import { EuiBreadcrumbs } from '@elastic/eui'; test('it renders without crashing', () => { const wrapper = shallow(); - expect(wrapper.find(EuiHeaderBreadcrumbs)).toHaveLength(1); + expect(wrapper.find(EuiBreadcrumbs)).toHaveLength(1); expect(wrapper).toMatchSnapshot(); }); @@ -28,5 +28,5 @@ test('it renders breadcrumbs', () => { }]; const wrapper = mount(); - expect(wrapper.find(EuiHeaderBreadcrumbs)).toHaveLength(1); + expect(wrapper.find(EuiBreadcrumbs)).toHaveLength(1); }); diff --git a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js index e5fafd0798f87..ee62b80c5e282 100644 --- a/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js +++ b/x-pack/plugins/security/public/views/management/edit_role/components/privileges/index_privilege_form.js @@ -102,6 +102,7 @@ export class IndexPrivilegeForm extends Component { allowFieldLevelSecurity, availableFields, indexPrivilege, + isReservedRole, } = this.props; if (!allowFieldLevelSecurity) { @@ -118,7 +119,7 @@ export class IndexPrivilegeForm extends Component { fullWidth={true} className="indexPrivilegeForm__grantedFieldsRow" helpText={ - grant.length === 0 ? + !isReservedRole && grant.length === 0 ? 'If no fields are granted, then users assigned to this role will not be able to see any data for this index.' : undefined } >