Translate security/roles component (#23984)
Translate security/roles component
This commit is contained in:
parent
7baea1d737
commit
cf64825ff2
|
@ -43,7 +43,11 @@ exports[`it renders without blowing up 1`] = `
|
|||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
hide
|
||||
<FormattedMessage
|
||||
defaultMessage="hide"
|
||||
id="xpack.security.management.editRole.collapsiblePanel.hideLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
*/
|
||||
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { CollapsiblePanel } from './collapsible_panel';
|
||||
|
||||
test('it renders without blowing up', () => {
|
||||
const wrapper = shallow(
|
||||
const wrapper = shallowWithIntl(
|
||||
<CollapsiblePanel iconType="logoElasticsearch" title="Elasticsearch">
|
||||
<p>child</p>
|
||||
</CollapsiblePanel>
|
||||
|
@ -20,7 +20,7 @@ test('it renders without blowing up', () => {
|
|||
});
|
||||
|
||||
test('it renders children by default', () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = mountWithIntl(
|
||||
<CollapsiblePanel iconType="logoElasticsearch" title="Elasticsearch">
|
||||
<p className="child">child 1</p>
|
||||
<p className="child">child 2</p>
|
||||
|
@ -32,7 +32,7 @@ test('it renders children by default', () => {
|
|||
});
|
||||
|
||||
test('it hides children when the "hide" link is clicked', () => {
|
||||
const wrapper = mount(
|
||||
const wrapper = mountWithIntl(
|
||||
<CollapsiblePanel iconType="logoElasticsearch" title="Elasticsearch">
|
||||
<p className="child">child 1</p>
|
||||
<p className="child">child 2</p>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
|
||||
interface Props {
|
||||
|
@ -55,7 +56,19 @@ export class CollapsiblePanel extends Component<Props, State> {
|
|||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLink onClick={this.toggleCollapsed}>{this.state.collapsed ? 'show' : 'hide'}</EuiLink>
|
||||
<EuiLink onClick={this.toggleCollapsed}>
|
||||
{this.state.collapsed ? (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.collapsiblePanel.showLinkText"
|
||||
defaultMessage="show"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.collapsiblePanel.hideLinkText"
|
||||
defaultMessage="hide"
|
||||
/>
|
||||
)}
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -9,20 +9,20 @@ import {
|
|||
// @ts-ignore
|
||||
EuiConfirmModal,
|
||||
} from '@elastic/eui';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { DeleteRoleButton } from './delete_role_button';
|
||||
|
||||
test('it renders without crashing', () => {
|
||||
const deleteHandler = jest.fn();
|
||||
const wrapper = shallow(<DeleteRoleButton canDelete={true} onDelete={deleteHandler} />);
|
||||
const wrapper = shallowWithIntl(<DeleteRoleButton canDelete={true} onDelete={deleteHandler} />);
|
||||
expect(wrapper.find(EuiButtonEmpty)).toHaveLength(1);
|
||||
expect(deleteHandler).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('it shows a confirmation dialog when clicked', () => {
|
||||
const deleteHandler = jest.fn();
|
||||
const wrapper = mount(<DeleteRoleButton canDelete={true} onDelete={deleteHandler} />);
|
||||
const wrapper = mountWithIntl(<DeleteRoleButton canDelete={true} onDelete={deleteHandler} />);
|
||||
|
||||
wrapper.find(EuiButtonEmpty).simulate('click');
|
||||
|
||||
|
@ -33,7 +33,7 @@ test('it shows a confirmation dialog when clicked', () => {
|
|||
|
||||
test('it renders nothing when canDelete is false', () => {
|
||||
const deleteHandler = jest.fn();
|
||||
const wrapper = shallow(<DeleteRoleButton canDelete={false} onDelete={deleteHandler} />);
|
||||
const wrapper = shallowWithIntl(<DeleteRoleButton canDelete={false} onDelete={deleteHandler} />);
|
||||
expect(wrapper.find('*')).toHaveLength(0);
|
||||
expect(deleteHandler).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
// @ts-ignore
|
||||
EuiOverlayMask,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
|
||||
interface Props {
|
||||
|
@ -35,7 +36,10 @@ export class DeleteRoleButton extends Component<Props, State> {
|
|||
return (
|
||||
<Fragment>
|
||||
<EuiButtonEmpty color={'danger'} onClick={this.showModal}>
|
||||
Delete role
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.deleteRoleButton.deleteRoleButtonLabel"
|
||||
defaultMessage="Delete role"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
{this.maybeShowModal()}
|
||||
</Fragment>
|
||||
|
@ -49,15 +53,40 @@ export class DeleteRoleButton extends Component<Props, State> {
|
|||
return (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={'Delete Role'}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.deleteRoleButton.deleteRoleTitle"
|
||||
defaultMessage="Delete Role"
|
||||
/>
|
||||
}
|
||||
onCancel={this.closeModal}
|
||||
onConfirm={this.onConfirmDelete}
|
||||
cancelButtonText={"No, don't delete"}
|
||||
confirmButtonText={'Yes, delete role'}
|
||||
cancelButtonText={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.deleteRoleButton.cancelButtonLabel"
|
||||
defaultMessage="No, don't delete"
|
||||
/>
|
||||
}
|
||||
confirmButtonText={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.deleteRoleButton.confirmButtonLabel"
|
||||
defaultMessage="Yes, delete role"
|
||||
/>
|
||||
}
|
||||
buttonColor={'danger'}
|
||||
>
|
||||
<p>Are you sure you want to delete this role?</p>
|
||||
<p>This action cannot be undone!</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.deleteRoleButton.deletingRoleConfirmationText"
|
||||
defaultMessage="Are you sure you want to delete this role?"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.deleteRoleButton.deletingRoleWarningText"
|
||||
defaultMessage="This action cannot be undone!"
|
||||
/>
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
|
@ -19,6 +20,7 @@ import {
|
|||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import { get } from 'lodash';
|
||||
import React, { ChangeEvent, Component, Fragment, HTMLProps } from 'react';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
@ -47,6 +49,7 @@ interface Props {
|
|||
spaces?: Space[];
|
||||
spacesEnabled: boolean;
|
||||
userProfile: UserProfile;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -54,7 +57,7 @@ interface State {
|
|||
formError: RoleValidationResult | null;
|
||||
}
|
||||
|
||||
export class EditRolePage extends Component<Props, State> {
|
||||
class EditRolePageUI extends Component<Props, State> {
|
||||
private validator: RoleValidator;
|
||||
|
||||
constructor(props: Props) {
|
||||
|
@ -67,9 +70,17 @@ export class EditRolePage extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const description = this.props.spacesEnabled
|
||||
? `Set privileges on your Elasticsearch data and control access to your Kibana spaces.`
|
||||
: `Set privileges on your Elasticsearch data and control access to Kibana.`;
|
||||
const description = this.props.spacesEnabled ? (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.setPrivilegesToKibanaSpacesDescription"
|
||||
defaultMessage="Set privileges on your Elasticsearch data and control access to your Kibana spaces."
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.setPrivilegesToKibanaDescription"
|
||||
defaultMessage="Set privileges on your Elasticsearch data and control access to Kibana."
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiPage className="editRolePage" restrictWidth>
|
||||
|
@ -86,7 +97,10 @@ export class EditRolePage extends Component<Props, State> {
|
|||
<EuiSpacer size="s" />
|
||||
<EuiText size="s" color="subdued">
|
||||
<p id="reservedRoleDescription" tabIndex={1}>
|
||||
Reserved roles are built-in and cannot be removed or modified.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.modifyingReversedRolesDescription"
|
||||
defaultMessage="Reserved roles are built-in and cannot be removed or modified."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</Fragment>
|
||||
|
@ -115,12 +129,27 @@ export class EditRolePage extends Component<Props, State> {
|
|||
tabIndex: 0,
|
||||
};
|
||||
if (isReservedRole(this.props.role)) {
|
||||
titleText = 'Viewing role';
|
||||
titleText = (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.viewingRoleTitle"
|
||||
defaultMessage="Viewing role"
|
||||
/>
|
||||
);
|
||||
props['aria-describedby'] = 'reservedRoleDescription';
|
||||
} else if (this.editingExistingRole()) {
|
||||
titleText = 'Edit role';
|
||||
titleText = (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.editRoleTitle"
|
||||
defaultMessage="Edit role"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
titleText = 'Create role';
|
||||
titleText = (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.createRoleTitle"
|
||||
defaultMessage="Create role"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -148,11 +177,21 @@ export class EditRolePage extends Component<Props, State> {
|
|||
return (
|
||||
<EuiPanel>
|
||||
<EuiFormRow
|
||||
label={'Role name'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.roleNameFormRowTitle"
|
||||
defaultMessage="Role name"
|
||||
/>
|
||||
}
|
||||
helpText={
|
||||
!isReservedRole(this.props.role) && this.editingExistingRole()
|
||||
? "A role's name cannot be changed once it has been created."
|
||||
: undefined
|
||||
!isReservedRole(this.props.role) && this.editingExistingRole() ? (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.roleNameFormRowHelpText"
|
||||
defaultMessage="A role's name cannot be changed once it has been created."
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
{...this.validator.validateRoleName(this.state.role)}
|
||||
>
|
||||
|
@ -225,10 +264,27 @@ export class EditRolePage extends Component<Props, State> {
|
|||
|
||||
public getFormButtons = () => {
|
||||
if (isReservedRole(this.props.role)) {
|
||||
return <EuiButton onClick={this.backToRoleList}>Return to role list</EuiButton>;
|
||||
return (
|
||||
<EuiButton onClick={this.backToRoleList}>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.returnToRoleListButtonLabel"
|
||||
defaultMessage="Return to role list"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
||||
}
|
||||
|
||||
const saveText = this.editingExistingRole() ? 'Update role' : 'Create role';
|
||||
const saveText = this.editingExistingRole() ? (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.updateRoleText"
|
||||
defaultMessage="Update role"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.createRoleText"
|
||||
defaultMessage="Create role"
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup responsive={false}>
|
||||
|
@ -244,7 +300,10 @@ export class EditRolePage extends Component<Props, State> {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty data-test-subj={`roleFormCancelButton`} onClick={this.backToRoleList}>
|
||||
Cancel
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRole.cancelButtonLabel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
|
@ -274,7 +333,7 @@ export class EditRolePage extends Component<Props, State> {
|
|||
formError: null,
|
||||
});
|
||||
|
||||
const { httpClient } = this.props;
|
||||
const { httpClient, intl } = this.props;
|
||||
|
||||
const role = {
|
||||
...this.state.role,
|
||||
|
@ -287,7 +346,12 @@ export class EditRolePage extends Component<Props, State> {
|
|||
|
||||
saveRole(httpClient, role)
|
||||
.then(() => {
|
||||
toastNotifications.addSuccess('Saved role');
|
||||
toastNotifications.addSuccess(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.security.management.editRole.roleSuccessfullySavedNotificationMessage',
|
||||
defaultMessage: 'Saved role',
|
||||
})
|
||||
);
|
||||
this.backToRoleList();
|
||||
})
|
||||
.catch((error: any) => {
|
||||
|
@ -297,11 +361,16 @@ export class EditRolePage extends Component<Props, State> {
|
|||
};
|
||||
|
||||
public handleDeleteRole = () => {
|
||||
const { httpClient, role } = this.props;
|
||||
const { httpClient, role, intl } = this.props;
|
||||
|
||||
deleteRole(httpClient, role.name)
|
||||
.then(() => {
|
||||
toastNotifications.addSuccess('Deleted role');
|
||||
toastNotifications.addSuccess(
|
||||
intl.formatMessage({
|
||||
id: 'xpack.security.management.editRole.roleSuccessfullyDeletedNotificationMessage',
|
||||
defaultMessage: 'Deleted role',
|
||||
})
|
||||
);
|
||||
this.backToRoleList();
|
||||
})
|
||||
.catch((error: any) => {
|
||||
|
@ -313,3 +382,5 @@ export class EditRolePage extends Component<Props, State> {
|
|||
window.location.hash = ROLES_PATH;
|
||||
};
|
||||
}
|
||||
|
||||
export const EditRolePage = injectI18n(EditRolePageUI);
|
||||
|
|
|
@ -1,181 +1,220 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`it renders without crashing 1`] = `
|
||||
<CollapsiblePanel
|
||||
iconType="logoElasticsearch"
|
||||
title="Elasticsearch"
|
||||
>
|
||||
<React.Fragment>
|
||||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<p>
|
||||
Manage the actions this role can perform against your cluster.
|
||||
|
||||
<EuiLink
|
||||
className="editRole__learnMore"
|
||||
color="primary"
|
||||
href="undefinedguide/en/x-pack/undefined/security-privileges.html#security-privileges"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
Learn more
|
||||
</EuiLink>
|
||||
</p>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
title={
|
||||
<h3>
|
||||
Cluster privileges
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={true}
|
||||
hasEmptyLabelSpace={true}
|
||||
>
|
||||
<ClusterPrivileges
|
||||
onChange={[Function]}
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<p>
|
||||
Allow requests to be submitted on the behalf of other users.
|
||||
|
||||
<EuiLink
|
||||
className="editRole__learnMore"
|
||||
color="primary"
|
||||
href="undefinedguide/en/x-pack/undefined/security-privileges.html#_run_as_privilege"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
Learn more
|
||||
</EuiLink>
|
||||
</p>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
title={
|
||||
<h3>
|
||||
Run As privileges
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
<I18nProvider>
|
||||
<CollapsiblePanel
|
||||
iconType="logoElasticsearch"
|
||||
title="Elasticsearch"
|
||||
>
|
||||
<React.Fragment>
|
||||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Manage the actions this role can perform against your cluster. "
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.manageRoleActionsDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
<EuiLink
|
||||
className="editRole__learnMore"
|
||||
color="primary"
|
||||
href="undefinedguide/en/x-pack/undefined/security-privileges.html#security-privileges"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.learnMoreLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</p>
|
||||
}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={true}
|
||||
gutterSize="l"
|
||||
title={
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Cluster privileges"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.clusterPrivilegesTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiComboBox
|
||||
compressed={false}
|
||||
fullWidth={false}
|
||||
isClearable={true}
|
||||
isDisabled={false}
|
||||
onChange={[Function]}
|
||||
options={Array []}
|
||||
placeholder="Add a user..."
|
||||
selectedOptions={Array []}
|
||||
singleSelection={false}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiTitle
|
||||
size="xs"
|
||||
textTransform="none"
|
||||
>
|
||||
<h3>
|
||||
Index privileges
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
grow={true}
|
||||
size="s"
|
||||
>
|
||||
<p>
|
||||
Control access to the data in your cluster.
|
||||
|
||||
<EuiLink
|
||||
className="editRole__learnMore"
|
||||
color="primary"
|
||||
href="undefinedguide/en/x-pack/undefined/security-privileges.html#privileges-list-indices"
|
||||
target="_blank"
|
||||
type="button"
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={true}
|
||||
hasEmptyLabelSpace={true}
|
||||
>
|
||||
Learn more
|
||||
</EuiLink>
|
||||
</p>
|
||||
</EuiText>
|
||||
<IndexPrivileges
|
||||
allowDocumentLevelSecurity={true}
|
||||
allowFieldLevelSecurity={true}
|
||||
httpClient={[MockFunction]}
|
||||
indexPatterns={Array []}
|
||||
onChange={[MockFunction]}
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
<ClusterPrivileges
|
||||
onChange={[Function]}
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Allow requests to be submitted on the behalf of other users. "
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.howToBeSubmittedOnBehalfOfOtherUsersDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
<EuiLink
|
||||
className="editRole__learnMore"
|
||||
color="primary"
|
||||
href="undefinedguide/en/x-pack/undefined/security-privileges.html#_run_as_privilege"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.learnMoreLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</p>
|
||||
}
|
||||
}
|
||||
validator={
|
||||
RoleValidator {
|
||||
"inProgressSpacePrivileges": Array [],
|
||||
"shouldValidate": undefined,
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
title={
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Run As privileges"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.runAsPrivilegesTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
}
|
||||
/>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill={false}
|
||||
iconSide="left"
|
||||
iconType="plusInCircle"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
type="button"
|
||||
>
|
||||
Add index privilege
|
||||
</EuiButton>
|
||||
</React.Fragment>
|
||||
</CollapsiblePanel>
|
||||
titleSize="xs"
|
||||
>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={true}
|
||||
>
|
||||
<EuiComboBox
|
||||
compressed={false}
|
||||
fullWidth={false}
|
||||
isClearable={true}
|
||||
isDisabled={false}
|
||||
onChange={[Function]}
|
||||
options={Array []}
|
||||
placeholder="Add a user..."
|
||||
selectedOptions={Array []}
|
||||
singleSelection={false}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<EuiTitle
|
||||
size="xs"
|
||||
textTransform="none"
|
||||
>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
defaultMessage="Index privileges"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.indexPrivilegesTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
grow={true}
|
||||
size="s"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Control access to the data in your cluster. "
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.controlAccessToClusterDataDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
<EuiLink
|
||||
className="editRole__learnMore"
|
||||
color="primary"
|
||||
href="undefinedguide/en/x-pack/undefined/security-privileges.html#privileges-list-indices"
|
||||
target="_blank"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Learn more"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.learnMoreLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</p>
|
||||
</EuiText>
|
||||
<IndexPrivileges
|
||||
allowDocumentLevelSecurity={true}
|
||||
allowFieldLevelSecurity={true}
|
||||
httpClient={[MockFunction]}
|
||||
indexPatterns={Array []}
|
||||
onChange={[MockFunction]}
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
validator={
|
||||
RoleValidator {
|
||||
"inProgressSpacePrivileges": Array [],
|
||||
"shouldValidate": undefined,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<EuiHorizontalRule
|
||||
margin="l"
|
||||
size="full"
|
||||
/>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill={false}
|
||||
iconSide="left"
|
||||
iconType="plusInCircle"
|
||||
onClick={[Function]}
|
||||
size="s"
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add index privilege"
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.addIndexPrivilegesButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</React.Fragment>
|
||||
</CollapsiblePanel>
|
||||
</I18nProvider>
|
||||
`;
|
||||
|
|
|
@ -39,7 +39,13 @@ exports[`it renders without crashing 1`] = `
|
|||
fullWidth={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
isInvalid={false}
|
||||
label="Indices"
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Indices"
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.indicesFormRowLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiComboBox
|
||||
compressed={false}
|
||||
|
@ -63,7 +69,13 @@ exports[`it renders without crashing 1`] = `
|
|||
describedByIds={Array []}
|
||||
fullWidth={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
label="Privileges"
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Privileges"
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.privilegesFormRowLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiComboBox
|
||||
compressed={false}
|
||||
|
@ -138,8 +150,20 @@ exports[`it renders without crashing 1`] = `
|
|||
describedByIds={Array []}
|
||||
fullWidth={true}
|
||||
hasEmptyLabelSpace={false}
|
||||
helpText="If no fields are granted, then users assigned to this role will not be able to see any data for this index."
|
||||
label="Granted fields (optional)"
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
defaultMessage="If no fields are granted, then users assigned to this role will not be able to see any data for this index."
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantedFieldsFormRowHelpText"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Granted fields (optional)"
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantedFieldsFormRowLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<React.Fragment>
|
||||
<EuiComboBox
|
||||
|
@ -177,7 +201,13 @@ exports[`it renders without crashing 1`] = `
|
|||
<EuiSwitch
|
||||
compressed={true}
|
||||
data-test-subj="restrictDocumentsQuery0"
|
||||
label="Grant read privileges to specific documents"
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Grant read privileges to specific documents"
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantReadPrivilegesLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
onChange={[Function]}
|
||||
value={false}
|
||||
/>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { RoleValidator } from '../../../lib/validate_role';
|
||||
import { ClusterPrivileges } from './cluster_privileges';
|
||||
import { ElasticsearchPrivileges } from './elasticsearch_privileges';
|
||||
|
@ -34,7 +34,9 @@ test('it renders without crashing', () => {
|
|||
allowFieldLevelSecurity: true,
|
||||
validator: new RoleValidator(),
|
||||
};
|
||||
const wrapper = shallow(<ElasticsearchPrivileges {...props} />);
|
||||
const wrapper = shallowWithIntl(
|
||||
<ElasticsearchPrivileges.WrappedComponent {...props} intl={null as any} />
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -61,7 +63,9 @@ test('it renders ClusterPrivileges', () => {
|
|||
allowFieldLevelSecurity: true,
|
||||
validator: new RoleValidator(),
|
||||
};
|
||||
const wrapper = mount(<ElasticsearchPrivileges {...props} />);
|
||||
const wrapper = mountWithIntl(
|
||||
<ElasticsearchPrivileges.WrappedComponent {...props} intl={null as any} />
|
||||
);
|
||||
expect(wrapper.find(ClusterPrivileges)).toHaveLength(1);
|
||||
});
|
||||
|
||||
|
@ -88,6 +92,8 @@ test('it renders IndexPrivileges', () => {
|
|||
allowFieldLevelSecurity: true,
|
||||
validator: new RoleValidator(),
|
||||
};
|
||||
const wrapper = mount(<ElasticsearchPrivileges {...props} />);
|
||||
const wrapper = mountWithIntl(
|
||||
<ElasticsearchPrivileges.WrappedComponent {...props} intl={null as any} />
|
||||
);
|
||||
expect(wrapper.find(IndexPrivileges)).toHaveLength(1);
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, I18nProvider, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Role } from '../../../../../../../common/model/role';
|
||||
// @ts-ignore
|
||||
|
@ -36,14 +37,17 @@ interface Props {
|
|||
indexPatterns: string[];
|
||||
allowDocumentLevelSecurity: boolean;
|
||||
allowFieldLevelSecurity: boolean;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
export class ElasticsearchPrivileges extends Component<Props, {}> {
|
||||
class ElasticsearchPrivilegesUI extends Component<Props, {}> {
|
||||
public render() {
|
||||
return (
|
||||
<CollapsiblePanel iconType={'logoElasticsearch'} title={'Elasticsearch'}>
|
||||
{this.getForm()}
|
||||
</CollapsiblePanel>
|
||||
<I18nProvider>
|
||||
<CollapsiblePanel iconType={'logoElasticsearch'} title={'Elasticsearch'}>
|
||||
{this.getForm()}
|
||||
</CollapsiblePanel>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -56,6 +60,7 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
indexPatterns,
|
||||
allowDocumentLevelSecurity,
|
||||
allowFieldLevelSecurity,
|
||||
intl,
|
||||
} = this.props;
|
||||
|
||||
const indexProps = {
|
||||
|
@ -71,10 +76,20 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
return (
|
||||
<Fragment>
|
||||
<EuiDescribedFormGroup
|
||||
title={<h3>Cluster privileges</h3>}
|
||||
title={
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.clusterPrivilegesTitle"
|
||||
defaultMessage="Cluster privileges"
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
description={
|
||||
<p>
|
||||
Manage the actions this role can perform against your cluster.{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.manageRoleActionsDescription"
|
||||
defaultMessage="Manage the actions this role can perform against your cluster. "
|
||||
/>
|
||||
{this.learnMore(documentationLinks.esClusterPrivileges)}
|
||||
</p>
|
||||
}
|
||||
|
@ -87,17 +102,35 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
<EuiSpacer />
|
||||
|
||||
<EuiDescribedFormGroup
|
||||
title={<h3>Run As privileges</h3>}
|
||||
title={
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.runAsPrivilegesTitle"
|
||||
defaultMessage="Run As privileges"
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
description={
|
||||
<p>
|
||||
Allow requests to be submitted on the behalf of other users.{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.howToBeSubmittedOnBehalfOfOtherUsersDescription"
|
||||
defaultMessage="Allow requests to be submitted on the behalf of other users. "
|
||||
/>
|
||||
{this.learnMore(documentationLinks.esRunAsPrivileges)}
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<EuiFormRow hasEmptyLabelSpace>
|
||||
<EuiComboBox
|
||||
placeholder={this.props.editable ? 'Add a user...' : undefined}
|
||||
placeholder={
|
||||
this.props.editable
|
||||
? intl.formatMessage({
|
||||
id:
|
||||
'xpack.security.management.editRoles.elasticSearchPrivileges.addUserTitle',
|
||||
defaultMessage: 'Add a user...',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
options={this.props.runAsUsers.map(username => ({
|
||||
id: username,
|
||||
label: username,
|
||||
|
@ -113,12 +146,20 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
<EuiSpacer />
|
||||
|
||||
<EuiTitle size={'xs'}>
|
||||
<h3>Index privileges</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.indexPrivilegesTitle"
|
||||
defaultMessage="Index privileges"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size={'s'} />
|
||||
<EuiText size={'s'} color={'subdued'}>
|
||||
<p>
|
||||
Control access to the data in your cluster.{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.controlAccessToClusterDataDescription"
|
||||
defaultMessage="Control access to the data in your cluster. "
|
||||
/>
|
||||
{this.learnMore(documentationLinks.esIndicesPrivileges)}
|
||||
</p>
|
||||
</EuiText>
|
||||
|
@ -129,7 +170,10 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
|
||||
{this.props.editable && (
|
||||
<EuiButton size={'s'} iconType={'plusInCircle'} onClick={this.addIndexPrivilege}>
|
||||
Add index privilege
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.addIndexPrivilegesButtonLabel"
|
||||
defaultMessage="Add index privilege"
|
||||
/>
|
||||
</EuiButton>
|
||||
)}
|
||||
</Fragment>
|
||||
|
@ -138,7 +182,10 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
|
||||
public learnMore = (href: string) => (
|
||||
<EuiLink className="editRole__learnMore" href={href} target={'_blank'}>
|
||||
Learn more
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.elasticSearchPrivileges.learnMoreLinkText"
|
||||
defaultMessage="Learn more"
|
||||
/>
|
||||
</EuiLink>
|
||||
);
|
||||
|
||||
|
@ -189,3 +236,5 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
|
|||
this.props.onChange(role);
|
||||
};
|
||||
}
|
||||
|
||||
export const ElasticsearchPrivileges = injectI18n(ElasticsearchPrivilegesUI);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { EuiButtonIcon, EuiSwitch, EuiTextArea } from '@elastic/eui';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { RoleValidator } from '../../../lib/validate_role';
|
||||
import { IndexPrivilegeForm } from './index_privilege_form';
|
||||
|
||||
|
@ -31,7 +31,7 @@ test('it renders without crashing', () => {
|
|||
onDelete: jest.fn(),
|
||||
};
|
||||
|
||||
const wrapper = shallow(<IndexPrivilegeForm {...props} />);
|
||||
const wrapper = shallowWithIntl(<IndexPrivilegeForm {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -62,7 +62,7 @@ describe('delete button', () => {
|
|||
...props,
|
||||
allowDelete: false,
|
||||
};
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find(EuiButtonIcon)).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
@ -71,7 +71,7 @@ describe('delete button', () => {
|
|||
...props,
|
||||
allowDelete: true,
|
||||
};
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find(EuiButtonIcon)).toHaveLength(1);
|
||||
});
|
||||
|
||||
|
@ -80,7 +80,7 @@ describe('delete button', () => {
|
|||
...props,
|
||||
allowDelete: true,
|
||||
};
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
wrapper.find(EuiButtonIcon).simulate('click');
|
||||
expect(testProps.onDelete).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
@ -114,7 +114,7 @@ describe(`document level security`, () => {
|
|||
allowDocumentLevelSecurity: false,
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find(EuiSwitch)).toHaveLength(0);
|
||||
expect(wrapper.find(EuiTextArea)).toHaveLength(0);
|
||||
});
|
||||
|
@ -128,7 +128,7 @@ describe(`document level security`, () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find(EuiSwitch)).toHaveLength(1);
|
||||
expect(wrapper.find(EuiTextArea)).toHaveLength(0);
|
||||
});
|
||||
|
@ -138,7 +138,7 @@ describe(`document level security`, () => {
|
|||
...props,
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find(EuiSwitch)).toHaveLength(1);
|
||||
expect(wrapper.find(EuiTextArea)).toHaveLength(1);
|
||||
});
|
||||
|
@ -172,7 +172,7 @@ describe('field level security', () => {
|
|||
allowFieldLevelSecurity: false,
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find('.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
@ -181,7 +181,7 @@ describe('field level security', () => {
|
|||
...props,
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1);
|
||||
});
|
||||
|
||||
|
@ -196,7 +196,7 @@ describe('field level security', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1);
|
||||
expect(wrapper.find('.euiFormHelpText')).toHaveLength(1);
|
||||
});
|
||||
|
@ -206,7 +206,7 @@ describe('field level security', () => {
|
|||
...props,
|
||||
};
|
||||
|
||||
const wrapper = mount(<IndexPrivilegeForm {...testProps} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivilegeForm {...testProps} />);
|
||||
expect(wrapper.find('div.indexPrivilegeForm__grantedFieldsRow')).toHaveLength(1);
|
||||
expect(wrapper.find('.euiFormHelpText')).toHaveLength(0);
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
EuiSwitch,
|
||||
EuiTextArea,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { ChangeEvent, Component, Fragment } from 'react';
|
||||
import { IndexPrivilege } from '../../../../../../../common/model/index_privilege';
|
||||
// @ts-ignore
|
||||
|
@ -81,7 +82,12 @@ export class IndexPrivilegeForm extends Component<Props, State> {
|
|||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={'Indices'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.indicesFormRowLabel"
|
||||
defaultMessage="Indices"
|
||||
/>
|
||||
}
|
||||
fullWidth={true}
|
||||
{...this.props.validator.validateIndexPrivilege(this.props.indexPrivilege)}
|
||||
>
|
||||
|
@ -96,7 +102,15 @@ export class IndexPrivilegeForm extends Component<Props, State> {
|
|||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label={'Privileges'} fullWidth={true}>
|
||||
<EuiFormRow
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.privilegesFormRowLabel"
|
||||
defaultMessage="Privileges"
|
||||
/>
|
||||
}
|
||||
fullWidth={true}
|
||||
>
|
||||
<EuiComboBox
|
||||
data-test-subj={`privilegesInput${this.props.formIndex}`}
|
||||
options={getIndexPrivileges().map(toOption)}
|
||||
|
@ -129,13 +143,23 @@ export class IndexPrivilegeForm extends Component<Props, State> {
|
|||
return (
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={'Granted fields (optional)'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantedFieldsFormRowLabel"
|
||||
defaultMessage="Granted fields (optional)"
|
||||
/>
|
||||
}
|
||||
fullWidth={true}
|
||||
className="indexPrivilegeForm__grantedFieldsRow"
|
||||
helpText={
|
||||
!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
|
||||
!isReservedRole && grant.length === 0 ? (
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantedFieldsFormRowHelpText"
|
||||
defaultMessage="If no fields are granted, then users assigned to this role will not be able to see any data for this index."
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
>
|
||||
<Fragment>
|
||||
|
@ -170,7 +194,12 @@ export class IndexPrivilegeForm extends Component<Props, State> {
|
|||
<EuiFlexItem>
|
||||
<EuiSwitch
|
||||
data-test-subj={`restrictDocumentsQuery${this.props.formIndex}`}
|
||||
label={'Grant read privileges to specific documents'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantReadPrivilegesLabel"
|
||||
defaultMessage="Grant read privileges to specific documents"
|
||||
/>
|
||||
}
|
||||
// @ts-ignore
|
||||
compressed={true}
|
||||
// @ts-ignore
|
||||
|
@ -181,7 +210,15 @@ export class IndexPrivilegeForm extends Component<Props, State> {
|
|||
)}
|
||||
{this.state.queryExpanded && (
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label={'Granted documents query'} fullWidth={true}>
|
||||
<EuiFormRow
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel"
|
||||
defaultMessage="Granted documents query"
|
||||
/>
|
||||
}
|
||||
fullWidth={true}
|
||||
>
|
||||
<EuiTextArea
|
||||
data-test-subj={`queryInput${this.props.formIndex}`}
|
||||
style={{ resize: 'none' }}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { RoleValidator } from '../../../lib/validate_role';
|
||||
import { IndexPrivilegeForm } from './index_privilege_form';
|
||||
import { IndexPrivileges } from './index_privileges';
|
||||
|
@ -31,7 +31,7 @@ test('it renders without crashing', () => {
|
|||
allowFieldLevelSecurity: true,
|
||||
validator: new RoleValidator(),
|
||||
};
|
||||
const wrapper = shallow(<IndexPrivileges {...props} />);
|
||||
const wrapper = shallowWithIntl(<IndexPrivileges {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -65,6 +65,6 @@ test('it renders a IndexPrivilegeForm for each privilege on the role', () => {
|
|||
allowFieldLevelSecurity: true,
|
||||
validator: new RoleValidator(),
|
||||
};
|
||||
const wrapper = mount(<IndexPrivileges {...props} />);
|
||||
const wrapper = mountWithIntl(<IndexPrivileges {...props} />);
|
||||
expect(wrapper.find(IndexPrivilegeForm)).toHaveLength(1);
|
||||
});
|
||||
|
|
|
@ -10,7 +10,11 @@ exports[`<ImpactedSpacesFlyout> renders without crashing 1`] = `
|
|||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
View summary of spaces privileges
|
||||
<FormattedMessage
|
||||
defaultMessage="View summary of spaces privileges"
|
||||
id="xpack.security.management.editRoles.impactedSpacesFlyout.viewSpacesPrivilegesSummaryLinkText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -1,56 +1,58 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<KibanaPrivileges> renders without crashing 1`] = `
|
||||
<CollapsiblePanel
|
||||
iconType="logoKibana"
|
||||
title="Kibana"
|
||||
>
|
||||
<SpaceAwarePrivilegeForm
|
||||
editable={true}
|
||||
kibanaAppPrivileges={
|
||||
Array [
|
||||
"all",
|
||||
]
|
||||
}
|
||||
onChange={[MockFunction]}
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
<I18nProvider>
|
||||
<CollapsiblePanel
|
||||
iconType="logoKibana"
|
||||
title="Kibana"
|
||||
>
|
||||
<InjectIntl(SpaceAwarePrivilegeFormUI)
|
||||
editable={true}
|
||||
kibanaAppPrivileges={
|
||||
Array [
|
||||
"all",
|
||||
]
|
||||
}
|
||||
}
|
||||
spaces={
|
||||
Array [
|
||||
onChange={[MockFunction]}
|
||||
role={
|
||||
Object {
|
||||
"_reserved": true,
|
||||
"id": "default",
|
||||
"name": "Default Space",
|
||||
},
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
spaces={
|
||||
Array [
|
||||
Object {
|
||||
"_reserved": true,
|
||||
"id": "default",
|
||||
"name": "Default Space",
|
||||
},
|
||||
Object {
|
||||
"id": "marketing",
|
||||
"name": "Marketing",
|
||||
},
|
||||
]
|
||||
}
|
||||
userProfile={
|
||||
Object {
|
||||
"id": "marketing",
|
||||
"name": "Marketing",
|
||||
},
|
||||
]
|
||||
}
|
||||
userProfile={
|
||||
Object {
|
||||
"hasCapability": [Function],
|
||||
"hasCapability": [Function],
|
||||
}
|
||||
}
|
||||
}
|
||||
validator={
|
||||
RoleValidator {
|
||||
"inProgressSpacePrivileges": Array [],
|
||||
"shouldValidate": undefined,
|
||||
validator={
|
||||
RoleValidator {
|
||||
"inProgressSpacePrivileges": Array [],
|
||||
"shouldValidate": undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
</CollapsiblePanel>
|
||||
/>
|
||||
</CollapsiblePanel>
|
||||
</I18nProvider>
|
||||
`;
|
||||
|
|
|
@ -3,13 +3,101 @@
|
|||
exports[`PrivilegeCalloutWarning renders without crashing 1`] = `
|
||||
<PrivilegeCalloutWarning
|
||||
basePrivilege="all"
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": Symbol(react.fragment),
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
isReservedRole={false}
|
||||
>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="iInCircle"
|
||||
size="m"
|
||||
title="Minimum privilege is too high to customize individual spaces"
|
||||
title={
|
||||
<FormattedMessage
|
||||
defaultMessage="Minimum privilege is too high to customize individual spaces"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.minimumPrivilegeTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="euiCallOut euiCallOut--warning"
|
||||
|
@ -61,7 +149,13 @@ exports[`PrivilegeCalloutWarning renders without crashing 1`] = `
|
|||
<span
|
||||
className="euiCallOutHeader__title"
|
||||
>
|
||||
Minimum privilege is too high to customize individual spaces
|
||||
<FormattedMessage
|
||||
defaultMessage="Minimum privilege is too high to customize individual spaces"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.minimumPrivilegeTitle"
|
||||
values={Object {}}
|
||||
>
|
||||
Minimum privilege is too high to customize individual spaces
|
||||
</FormattedMessage>
|
||||
</span>
|
||||
</div>
|
||||
<EuiText
|
||||
|
@ -72,19 +166,71 @@ exports[`PrivilegeCalloutWarning renders without crashing 1`] = `
|
|||
className="euiText euiText--small"
|
||||
>
|
||||
<p>
|
||||
Setting the minimum privilege to
|
||||
<strong>
|
||||
all
|
||||
</strong>
|
||||
grants full access to all spaces. To customize privileges for individual spaces, the minimum privilege must be either
|
||||
<strong>
|
||||
read
|
||||
</strong>
|
||||
or
|
||||
<strong>
|
||||
none
|
||||
</strong>
|
||||
.
|
||||
<FormattedMessage
|
||||
defaultMessage="Setting the minimum privilege to {allText} grants full access to all
|
||||
spaces. To customize privileges for individual spaces, the minimum privilege must be
|
||||
either {readText} or {noneText}."
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.howToCustomizePrivilegesForIndividualSpacesDescription"
|
||||
values={
|
||||
Object {
|
||||
"allText": <strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="all"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.allText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</strong>,
|
||||
"noneText": <strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="none"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.noneText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</strong>,
|
||||
"readText": <strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="read"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.readText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</strong>,
|
||||
}
|
||||
}
|
||||
>
|
||||
Setting the minimum privilege to
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="all"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.allText"
|
||||
values={Object {}}
|
||||
>
|
||||
all
|
||||
</FormattedMessage>
|
||||
</strong>
|
||||
grants full access to all
|
||||
spaces. To customize privileges for individual spaces, the minimum privilege must be
|
||||
either
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="read"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.readText"
|
||||
values={Object {}}
|
||||
>
|
||||
read
|
||||
</FormattedMessage>
|
||||
</strong>
|
||||
or
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="none"
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.noneText"
|
||||
values={Object {}}
|
||||
>
|
||||
none
|
||||
</FormattedMessage>
|
||||
</strong>
|
||||
.
|
||||
</FormattedMessage>
|
||||
</p>
|
||||
</div>
|
||||
</EuiText>
|
||||
|
|
|
@ -19,7 +19,13 @@ exports[`<PrivilegeSpaceForm> renders without crashing 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
isInvalid={false}
|
||||
label="Spaces"
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Spaces"
|
||||
id="xpack.security.management.editRoles.privilegeSpaceForm.spacesFormRowLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SpaceSelector
|
||||
onChange={[Function]}
|
||||
|
@ -51,7 +57,13 @@ exports[`<PrivilegeSpaceForm> renders without crashing 1`] = `
|
|||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
isInvalid={false}
|
||||
label="Privilege"
|
||||
label={
|
||||
<FormattedMessage
|
||||
defaultMessage="Privilege"
|
||||
id="xpack.security.management.editRoles.privilegeSpaceForm.privilegeFormRowLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<PrivilegeSelector
|
||||
availablePrivileges={
|
||||
|
|
|
@ -5,14 +5,22 @@ exports[`<SimplePrivilegeForm> renders without crashing 1`] = `
|
|||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<p>
|
||||
Specifies the Kibana privilege for this role.
|
||||
<FormattedMessage
|
||||
defaultMessage="Specifies the Kibana privilege for this role."
|
||||
id="xpack.security.management.editRoles.simplePrivilegeForm.specifyPrivilegeForRoleDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
title={
|
||||
<h3>
|
||||
Kibana privileges
|
||||
<FormattedMessage
|
||||
defaultMessage="Kibana privileges"
|
||||
id="xpack.security.management.editRoles.simplePrivilegeForm.kibanaPrivilegesTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SpaceAwarePrivilegeForm> hides the space table if there are no existing space privileges 1`] = `
|
||||
<PrivilegeSpaceTable
|
||||
<InjectIntl(PrivilegeSpaceTableUI)
|
||||
availablePrivileges={
|
||||
Array [
|
||||
"all",
|
||||
|
@ -39,7 +39,129 @@ exports[`<SpaceAwarePrivilegeForm> hides the space table if there are no existin
|
|||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
>
|
||||
<PrivilegeSpaceTableUI
|
||||
availablePrivileges={
|
||||
Array [
|
||||
"all",
|
||||
"read",
|
||||
]
|
||||
}
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": Symbol(react.fragment),
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
"cluster": Array [
|
||||
"manage",
|
||||
],
|
||||
"indices": Array [],
|
||||
"run_as": Array [],
|
||||
},
|
||||
"kibana": Object {
|
||||
"global": Array [],
|
||||
"space": Object {},
|
||||
},
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
spacePrivileges={Object {}}
|
||||
spaces={
|
||||
Array [
|
||||
Object {
|
||||
"_reserved": true,
|
||||
"id": "default",
|
||||
"name": "Default Space",
|
||||
},
|
||||
Object {
|
||||
"id": "marketing",
|
||||
"name": "Marketing",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</InjectIntl(PrivilegeSpaceTableUI)>
|
||||
`;
|
||||
|
||||
exports[`<SpaceAwarePrivilegeForm> renders without crashing 1`] = `
|
||||
|
@ -47,14 +169,22 @@ exports[`<SpaceAwarePrivilegeForm> renders without crashing 1`] = `
|
|||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<p>
|
||||
Specify the minimum actions users can perform in your spaces.
|
||||
<FormattedMessage
|
||||
defaultMessage="Specify the minimum actions users can perform in your spaces."
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.minimumActionsUserCanPerformInYourSpacesDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
fullWidth={false}
|
||||
gutterSize="l"
|
||||
title={
|
||||
<h3>
|
||||
Minimum privileges for all spaces
|
||||
<FormattedMessage
|
||||
defaultMessage="Minimum privileges for all spaces"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.minPrivilegesForAllSpacesTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
titleSize="xs"
|
||||
|
@ -89,7 +219,11 @@ exports[`<SpaceAwarePrivilegeForm> renders without crashing 1`] = `
|
|||
textTransform="none"
|
||||
>
|
||||
<h3>
|
||||
Higher privileges for individual spaces
|
||||
<FormattedMessage
|
||||
defaultMessage="Higher privileges for individual spaces"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.higherPrivilegesForIndividualSpacesTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer
|
||||
|
@ -101,24 +235,37 @@ exports[`<SpaceAwarePrivilegeForm> renders without crashing 1`] = `
|
|||
size="s"
|
||||
>
|
||||
<p>
|
||||
Grant more privileges on a per space basis. For example, if the privileges are
|
||||
|
||||
<strong>
|
||||
read
|
||||
</strong>
|
||||
for all spaces, you can set the privileges to
|
||||
<strong>
|
||||
all
|
||||
</strong>
|
||||
|
||||
for an individual space.
|
||||
<FormattedMessage
|
||||
defaultMessage="Grant more privileges on a per space basis. For example, if the privileges are
|
||||
{read} for all spaces, you can set the privileges to {all}
|
||||
for an individual space."
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.grantMorePrivilegesTitle"
|
||||
values={
|
||||
Object {
|
||||
"all": <strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="all"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.allText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</strong>,
|
||||
"read": <strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="read"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.readText"
|
||||
values={Object {}}
|
||||
/>
|
||||
</strong>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="s"
|
||||
/>
|
||||
<React.Fragment>
|
||||
<PrivilegeSpaceTable
|
||||
<InjectIntl(PrivilegeSpaceTableUI)
|
||||
availablePrivileges={
|
||||
Array [
|
||||
"all",
|
||||
|
@ -181,14 +328,18 @@ exports[`<SpaceAwarePrivilegeForm> renders without crashing 1`] = `
|
|||
size="s"
|
||||
type="button"
|
||||
>
|
||||
Add space privilege
|
||||
<FormattedMessage
|
||||
defaultMessage="Add space privilege"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.addSpacePrivilegeTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
component="div"
|
||||
grow={true}
|
||||
>
|
||||
<ImpactedSpacesFlyout
|
||||
<InjectIntl(ImpactedSpacesFlyoutUI)
|
||||
role={
|
||||
Object {
|
||||
"elasticsearch": Object {
|
||||
|
@ -237,20 +388,38 @@ exports[`<SpaceAwarePrivilegeForm> with user profile disabling "manageSpaces" re
|
|||
size="m"
|
||||
title={
|
||||
<p>
|
||||
Insufficient Privileges
|
||||
<FormattedMessage
|
||||
defaultMessage="Insufficient Privileges"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.insufficientPrivilegesDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
You are not authorized to view all available spaces.
|
||||
<FormattedMessage
|
||||
defaultMessage="You are not authorized to view all available spaces."
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
Please ensure your account has all privileges granted by the
|
||||
|
||||
<strong>
|
||||
kibana_user
|
||||
</strong>
|
||||
role, and try again.
|
||||
<FormattedMessage
|
||||
defaultMessage="Please ensure your account has all privileges granted by the
|
||||
{kibanaUser} role, and try again."
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.ensureAccountHasAllPrivilegesGrantedDescription"
|
||||
values={
|
||||
Object {
|
||||
"kibanaUser": <strong>
|
||||
<FormattedMessage
|
||||
defaultMessage="kibana_user"
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.kibanaUserTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</strong>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
|
|
@ -52,16 +52,24 @@ const buildProps = (customProps = {}) => {
|
|||
|
||||
describe('<ImpactedSpacesFlyout>', () => {
|
||||
it('renders without crashing', () => {
|
||||
expect(shallowWithIntl(<ImpactedSpacesFlyout {...buildProps()} />)).toMatchSnapshot();
|
||||
expect(
|
||||
shallowWithIntl(
|
||||
<ImpactedSpacesFlyout.WrappedComponent {...buildProps()} intl={null as any} />
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('does not immediately show the flyout', () => {
|
||||
const wrapper = mountWithIntl(<ImpactedSpacesFlyout {...buildProps()} />);
|
||||
const wrapper = mountWithIntl(
|
||||
<ImpactedSpacesFlyout.WrappedComponent {...buildProps()} intl={null as any} />
|
||||
);
|
||||
expect(wrapper.find(EuiFlyout)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows the flyout after clicking the link', () => {
|
||||
const wrapper = mountWithIntl(<ImpactedSpacesFlyout {...buildProps()} />);
|
||||
const wrapper = mountWithIntl(
|
||||
<ImpactedSpacesFlyout.WrappedComponent {...buildProps()} intl={null as any} />
|
||||
);
|
||||
wrapper.find(EuiLink).simulate('click');
|
||||
expect(wrapper.find(EuiFlyout)).toHaveLength(1);
|
||||
});
|
||||
|
@ -82,7 +90,9 @@ describe('<ImpactedSpacesFlyout>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = shallowWithIntl(<ImpactedSpacesFlyout {...props} />);
|
||||
const wrapper = shallowWithIntl(
|
||||
<ImpactedSpacesFlyout.WrappedComponent {...props} intl={null as any} />
|
||||
);
|
||||
wrapper.find(EuiLink).simulate('click');
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
|
@ -112,7 +122,9 @@ describe('<ImpactedSpacesFlyout>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = shallowWithIntl(<ImpactedSpacesFlyout {...props} />);
|
||||
const wrapper = shallowWithIntl(
|
||||
<ImpactedSpacesFlyout.WrappedComponent {...props} intl={null as any} />
|
||||
);
|
||||
wrapper.find(EuiLink).simulate('click');
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
|
@ -141,7 +153,9 @@ describe('<ImpactedSpacesFlyout>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = shallowWithIntl(<ImpactedSpacesFlyout {...props} />);
|
||||
const wrapper = shallowWithIntl(
|
||||
<ImpactedSpacesFlyout.WrappedComponent {...props} intl={null as any} />
|
||||
);
|
||||
wrapper.find(EuiLink).simulate('click');
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
EuiLink,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { PrivilegeSpaceTable } from './privilege_space_table';
|
||||
|
||||
|
@ -26,13 +27,14 @@ interface Props {
|
|||
role: Role;
|
||||
spaces: Space[];
|
||||
userProfile: UserProfile;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface State {
|
||||
showImpactedSpaces: boolean;
|
||||
}
|
||||
|
||||
export class ImpactedSpacesFlyout extends Component<Props, State> {
|
||||
class ImpactedSpacesFlyoutUI extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
@ -46,7 +48,10 @@ export class ImpactedSpacesFlyout extends Component<Props, State> {
|
|||
<Fragment>
|
||||
<div className="showImpactedSpaces">
|
||||
<EuiLink onClick={this.toggleShowImpactedSpaces}>
|
||||
View summary of spaces privileges
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.impactedSpacesFlyout.viewSpacesPrivilegesSummaryLinkText"
|
||||
defaultMessage="View summary of spaces privileges"
|
||||
/>
|
||||
</EuiLink>
|
||||
</div>
|
||||
{flyout}
|
||||
|
@ -61,13 +66,23 @@ export class ImpactedSpacesFlyout extends Component<Props, State> {
|
|||
};
|
||||
|
||||
public getHighestPrivilege(...privileges: KibanaPrivilege[]): KibanaPrivilege {
|
||||
const { intl } = this.props;
|
||||
if (privileges.indexOf('all') >= 0) {
|
||||
return 'all';
|
||||
return intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.impactedSpacesFlyout.allLabel',
|
||||
defaultMessage: 'all',
|
||||
}) as KibanaPrivilege;
|
||||
}
|
||||
if (privileges.indexOf('read') >= 0) {
|
||||
return 'read';
|
||||
return intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.impactedSpacesFlyout.readLabel',
|
||||
defaultMessage: 'read',
|
||||
}) as KibanaPrivilege;
|
||||
}
|
||||
return 'none';
|
||||
return intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.impactedSpacesFlyout.noneLabel',
|
||||
defaultMessage: 'none',
|
||||
}) as KibanaPrivilege;
|
||||
}
|
||||
|
||||
public getFlyout = () => {
|
||||
|
@ -106,7 +121,12 @@ export class ImpactedSpacesFlyout extends Component<Props, State> {
|
|||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h1 id="showImpactedSpacesTitle">Summary of space privileges</h1>
|
||||
<h1 id="showImpactedSpacesTitle">
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.impactedSpacesFlyout.spacePrivilegesSummaryTitle"
|
||||
defaultMessage="Summary of space privileges"
|
||||
/>
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
|
@ -125,3 +145,5 @@ export class ImpactedSpacesFlyout extends Component<Props, State> {
|
|||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const ImpactedSpacesFlyout = injectI18n(ImpactedSpacesFlyoutUI);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import React, { Component } from 'react';
|
||||
import { Space } from '../../../../../../../../spaces/common/model/space';
|
||||
import { UserProfile } from '../../../../../../../../xpack_main/public/services/user_profile';
|
||||
|
@ -28,9 +29,11 @@ interface Props {
|
|||
export class KibanaPrivileges extends Component<Props, {}> {
|
||||
public render() {
|
||||
return (
|
||||
<CollapsiblePanel iconType={'logoKibana'} title={'Kibana'}>
|
||||
{this.getForm()}
|
||||
</CollapsiblePanel>
|
||||
<I18nProvider>
|
||||
<CollapsiblePanel iconType={'logoKibana'} title={'Kibana'}>
|
||||
{this.getForm()}
|
||||
</CollapsiblePanel>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { PrivilegeCalloutWarning } from './privilege_callout_warning';
|
||||
|
||||
describe('PrivilegeCalloutWarning', () => {
|
||||
it('renders without crashing', () => {
|
||||
expect(
|
||||
mount(<PrivilegeCalloutWarning basePrivilege={'all'} isReservedRole={false} />)
|
||||
mountWithIntl(<PrivilegeCalloutWarning basePrivilege={'all'} isReservedRole={false} />)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { Component } from 'react';
|
||||
import { KibanaPrivilege } from '../../../../../../../common/model/kibana_privilege';
|
||||
import { NO_PRIVILEGE_VALUE } from '../../../lib/constants';
|
||||
|
@ -33,11 +34,19 @@ export class PrivilegeCalloutWarning extends Component<Props, State> {
|
|||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="iInCircle"
|
||||
title={"Cannot customize a reserved role's space privileges"}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.notPossibleToCustomizeReservedRoleSpacePrivilegesTitle"
|
||||
defaultMessage="Cannot customize a reserved role's space privileges"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
This role always grants full access to all spaces. To customize privileges for
|
||||
individual spaces, you must create a new role.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.howToCustomizePrivilegesDescription"
|
||||
defaultMessage="This role always grants full access to all spaces. To customize privileges for
|
||||
individual spaces, you must create a new role."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
@ -46,12 +55,46 @@ export class PrivilegeCalloutWarning extends Component<Props, State> {
|
|||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="iInCircle"
|
||||
title={'Minimum privilege is too high to customize individual spaces'}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.minimumPrivilegeTitle"
|
||||
defaultMessage="Minimum privilege is too high to customize individual spaces"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Setting the minimum privilege to <strong>all</strong> grants full access to all
|
||||
spaces. To customize privileges for individual spaces, the minimum privilege must be
|
||||
either <strong>read</strong> or <strong>none</strong>.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.howToCustomizePrivilegesForIndividualSpacesDescription"
|
||||
defaultMessage="Setting the minimum privilege to {allText} grants full access to all
|
||||
spaces. To customize privileges for individual spaces, the minimum privilege must be
|
||||
either {readText} or {noneText}."
|
||||
values={{
|
||||
allText: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.allText"
|
||||
defaultMessage="all"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
readText: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.readText"
|
||||
defaultMessage="read"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
noneText: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.noneText"
|
||||
defaultMessage="none"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
@ -64,11 +107,19 @@ export class PrivilegeCalloutWarning extends Component<Props, State> {
|
|||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="iInCircle"
|
||||
title={"Cannot customize a reserved role's space privileges"}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.notPossibleToCustomizeReservedRoleSpacePrivilegesTitle"
|
||||
defaultMessage="Cannot customize a reserved role's space privileges"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
This role always grants read access to all spaces. To customize privileges for
|
||||
individual spaces, you must create a new role.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.alwaysGrantReadAccessToAllSpacesTitle"
|
||||
defaultMessage="This role always grants read access to all spaces. To customize privileges for
|
||||
individual spaces, you must create a new role."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
@ -79,7 +130,20 @@ export class PrivilegeCalloutWarning extends Component<Props, State> {
|
|||
iconType="iInCircle"
|
||||
title={
|
||||
<span>
|
||||
The minimal possible privilege is <strong>read</strong>.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.minimalPossiblePrivilageTitle"
|
||||
defaultMessage="The minimal possible privilege is {readText}."
|
||||
values={{
|
||||
readText: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.readText"
|
||||
defaultMessage="read"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
|
@ -92,11 +156,19 @@ export class PrivilegeCalloutWarning extends Component<Props, State> {
|
|||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="iInCircle"
|
||||
title={"Cannot customize a reserved role's space privileges"}
|
||||
title={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.notPossibleToCustomizeReservedRoleSpacePrivilegesTitle"
|
||||
defaultMessage="Cannot customize a reserved role's space privileges"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
This role never grants access to any spaces within Kibana. To customize privileges for
|
||||
individual spaces, you must create a new role.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeCalloutWarning.neverGrantReadAccessToAllSpacesTitle"
|
||||
defaultMessage="This role never grants access to any spaces within Kibana. To customize privileges for
|
||||
individual spaces, you must create a new role."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { KibanaPrivilege } from '../../../../../../../common/model/kibana_privilege';
|
||||
import { RoleValidator } from '../../../lib/validate_role';
|
||||
import { PrivilegeSpaceForm } from './privilege_space_form';
|
||||
|
@ -40,6 +40,6 @@ const buildProps = (customProps = {}) => {
|
|||
|
||||
describe('<PrivilegeSpaceForm>', () => {
|
||||
it('renders without crashing', () => {
|
||||
expect(shallow(<PrivilegeSpaceForm {...buildProps()} />)).toMatchSnapshot();
|
||||
expect(shallowWithIntl(<PrivilegeSpaceForm {...buildProps()} />)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { Component } from 'react';
|
||||
import { Space } from '../../../../../../../../spaces/common/model/space';
|
||||
import { KibanaPrivilege } from '../../../../../../../common/model/kibana_privilege';
|
||||
|
@ -41,7 +42,12 @@ export class PrivilegeSpaceForm extends Component<Props, {}> {
|
|||
<EuiFlexGroup responsive={false}>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={'Spaces'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeSpaceForm.spacesFormRowLabel"
|
||||
defaultMessage="Spaces"
|
||||
/>
|
||||
}
|
||||
{...validator.validateSelectedSpaces(selectedSpaceIds, selectedPrivilege)}
|
||||
>
|
||||
<SpaceSelector
|
||||
|
@ -53,7 +59,12 @@ export class PrivilegeSpaceForm extends Component<Props, {}> {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
label={'Privilege'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeSpaceForm.privilegeFormRowLabel"
|
||||
defaultMessage="Privilege"
|
||||
/>
|
||||
}
|
||||
{...validator.validateSelectedPrivilege(selectedSpaceIds, selectedPrivilege)}
|
||||
>
|
||||
<PrivilegeSelector
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
EuiInMemoryTable,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React, { Component } from 'react';
|
||||
import { Space } from '../../../../../../../../spaces/common/model/space';
|
||||
import { SpaceAvatar } from '../../../../../../../../spaces/public/components';
|
||||
|
@ -25,6 +26,7 @@ interface Props {
|
|||
spacePrivileges: any;
|
||||
onChange?: (privs: { [spaceId: string]: KibanaPrivilege[] }) => void;
|
||||
readonly?: boolean;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -35,13 +37,13 @@ interface DeletedSpace extends Space {
|
|||
deleted: boolean;
|
||||
}
|
||||
|
||||
export class PrivilegeSpaceTable extends Component<Props, State> {
|
||||
class PrivilegeSpaceTableUI extends Component<Props, State> {
|
||||
public state = {
|
||||
searchTerm: '',
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { role, spaces, availablePrivileges, spacePrivileges } = this.props;
|
||||
const { role, spaces, availablePrivileges, spacePrivileges, intl } = this.props;
|
||||
|
||||
const { searchTerm } = this.state;
|
||||
|
||||
|
@ -74,7 +76,10 @@ export class PrivilegeSpaceTable extends Component<Props, State> {
|
|||
search={{
|
||||
box: {
|
||||
incremental: true,
|
||||
placeholder: 'Filter',
|
||||
placeholder: intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.privilegeSpaceTable.filterPlaceholder',
|
||||
defaultMessage: 'Filter',
|
||||
}),
|
||||
},
|
||||
onChange: (search: any) => {
|
||||
this.setState({
|
||||
|
@ -88,6 +93,7 @@ export class PrivilegeSpaceTable extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public getTableColumns = (role: Role, availablePrivileges: KibanaPrivilege[] = []) => {
|
||||
const { intl } = this.props;
|
||||
const columns: any[] = [
|
||||
{
|
||||
field: 'space',
|
||||
|
@ -103,11 +109,22 @@ export class PrivilegeSpaceTable extends Component<Props, State> {
|
|||
},
|
||||
{
|
||||
field: 'space',
|
||||
name: 'Space',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.privilegeSpaceTable.spaceName',
|
||||
defaultMessage: 'Space',
|
||||
}),
|
||||
width: this.props.readonly ? '75%' : '50%',
|
||||
render: (space: Space | DeletedSpace) => {
|
||||
if ('deleted' in space) {
|
||||
return <EuiText color={'subdued'}>{space.id} (deleted)</EuiText>;
|
||||
return (
|
||||
<EuiText color={'subdued'}>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.privilegeSpaceTable.deletedSpaceDescription"
|
||||
defaultMessage="{value} (deleted)"
|
||||
values={{ value: space.id }}
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
} else {
|
||||
return <EuiText>{space.name}</EuiText>;
|
||||
}
|
||||
|
@ -115,7 +132,10 @@ export class PrivilegeSpaceTable extends Component<Props, State> {
|
|||
},
|
||||
{
|
||||
field: 'privilege',
|
||||
name: 'Privilege',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.privilegeSpaceTable.privilegeName',
|
||||
defaultMessage: 'Privilege',
|
||||
}),
|
||||
width: this.props.readonly ? '25%' : undefined,
|
||||
render: (privilege: KibanaPrivilege, record: any) => {
|
||||
if (this.props.readonly || record.space.deleted) {
|
||||
|
@ -137,7 +157,10 @@ export class PrivilegeSpaceTable extends Component<Props, State> {
|
|||
];
|
||||
if (!this.props.readonly) {
|
||||
columns.push({
|
||||
name: 'Actions',
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.privilegeSpaceTable.actionsName',
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
actions: [
|
||||
{
|
||||
render: (record: any) => {
|
||||
|
@ -182,3 +205,5 @@ export class PrivilegeSpaceTable extends Component<Props, State> {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const PrivilegeSpaceTable = injectI18n(PrivilegeSpaceTableUI);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { PrivilegeSelector } from './privilege_selector';
|
||||
import { SimplePrivilegeForm } from './simple_privilege_form';
|
||||
|
||||
|
@ -32,12 +32,12 @@ const buildProps = (customProps?: any) => {
|
|||
|
||||
describe('<SimplePrivilegeForm>', () => {
|
||||
it('renders without crashing', () => {
|
||||
expect(shallow(<SimplePrivilegeForm {...buildProps()} />)).toMatchSnapshot();
|
||||
expect(shallowWithIntl(<SimplePrivilegeForm {...buildProps()} />)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('displays "none" when no privilege is selected', () => {
|
||||
const props = buildProps();
|
||||
const wrapper = shallow(<SimplePrivilegeForm {...props} />);
|
||||
const wrapper = shallowWithIntl(<SimplePrivilegeForm {...props} />);
|
||||
const selector = wrapper.find(PrivilegeSelector);
|
||||
expect(selector.props()).toMatchObject({
|
||||
value: 'none',
|
||||
|
@ -53,7 +53,7 @@ describe('<SimplePrivilegeForm>', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
const wrapper = shallow(<SimplePrivilegeForm {...props} />);
|
||||
const wrapper = shallowWithIntl(<SimplePrivilegeForm {...props} />);
|
||||
const selector = wrapper.find(PrivilegeSelector);
|
||||
expect(selector.props()).toMatchObject({
|
||||
value: 'read',
|
||||
|
@ -62,7 +62,7 @@ describe('<SimplePrivilegeForm>', () => {
|
|||
|
||||
it('fires its onChange callback when the privilege changes', () => {
|
||||
const props = buildProps();
|
||||
const wrapper = mount(<SimplePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SimplePrivilegeForm {...props} />);
|
||||
const selector = wrapper.find(PrivilegeSelector).find('select');
|
||||
selector.simulate('change', { target: { value: 'all' } });
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
EuiDescribedFormGroup,
|
||||
EuiFormRow,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { KibanaPrivilege } from '../../../../../../../common/model/kibana_privilege';
|
||||
import { Role } from '../../../../../../../common/model/role';
|
||||
|
@ -35,11 +36,28 @@ export class SimplePrivilegeForm extends Component<Props, {}> {
|
|||
? (assignedPrivileges.global[0] as KibanaPrivilege)
|
||||
: NO_PRIVILEGE_VALUE;
|
||||
|
||||
const description = <p>Specifies the Kibana privilege for this role.</p>;
|
||||
const description = (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.simplePrivilegeForm.specifyPrivilegeForRoleDescription"
|
||||
defaultMessage="Specifies the Kibana privilege for this role."
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiDescribedFormGroup title={<h3>Kibana privileges</h3>} description={description}>
|
||||
<EuiDescribedFormGroup
|
||||
title={
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.simplePrivilegeForm.kibanaPrivilegesTitle"
|
||||
defaultMessage="Kibana privileges"
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
description={description}
|
||||
>
|
||||
<EuiFormRow hasEmptyLabelSpace>
|
||||
<PrivilegeSelector
|
||||
data-test-subj={'kibanaPrivilege'}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { RoleValidator } from '../../../lib/validate_role';
|
||||
import { PrivilegeCalloutWarning } from './privilege_callout_warning';
|
||||
import { PrivilegeSpaceForm } from './privilege_space_form';
|
||||
|
@ -48,7 +48,9 @@ const buildProps = (customProps: any = {}) => {
|
|||
|
||||
describe('<SpaceAwarePrivilegeForm>', () => {
|
||||
it('renders without crashing', () => {
|
||||
expect(shallow(<SpaceAwarePrivilegeForm {...buildProps()} />)).toMatchSnapshot();
|
||||
expect(
|
||||
shallowWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...buildProps()} />)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('shows the space table if exisitng space privileges are declared', () => {
|
||||
|
@ -66,7 +68,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
expect(table).toHaveLength(1);
|
||||
|
@ -75,7 +77,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
it('hides the space table if there are no existing space privileges', () => {
|
||||
const props = buildProps();
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
expect(table).toMatchSnapshot();
|
||||
|
@ -96,7 +98,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
expect(wrapper.find(PrivilegeSpaceForm)).toHaveLength(0);
|
||||
|
||||
wrapper.find('button[data-test-subj="addSpacePrivilegeButton"]').simulate('click');
|
||||
|
@ -120,7 +122,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const warning = wrapper.find(PrivilegeCalloutWarning);
|
||||
expect(warning.props()).toMatchObject({
|
||||
|
@ -151,7 +153,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const warning = wrapper.find(PrivilegeCalloutWarning);
|
||||
expect(warning.props()).toMatchObject({
|
||||
|
@ -174,7 +176,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
expect(table).toHaveLength(1);
|
||||
|
@ -200,7 +202,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const warning = wrapper.find(PrivilegeCalloutWarning);
|
||||
expect(warning).toHaveLength(0);
|
||||
|
@ -221,7 +223,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = mount(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = mountWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
|
||||
const table = wrapper.find(PrivilegeSpaceTable);
|
||||
expect(table).toHaveLength(1);
|
||||
|
@ -244,7 +246,7 @@ describe('<SpaceAwarePrivilegeForm>', () => {
|
|||
},
|
||||
});
|
||||
|
||||
const wrapper = shallow(<SpaceAwarePrivilegeForm {...props} />);
|
||||
const wrapper = shallowWithIntl(<SpaceAwarePrivilegeForm.WrappedComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Space } from '../../../../../../../../spaces/common/model/space';
|
||||
import { UserProfile } from '../../../../../../../../xpack_main/public/services/user_profile';
|
||||
|
@ -40,6 +41,7 @@ interface Props {
|
|||
editable: boolean;
|
||||
validator: RoleValidator;
|
||||
userProfile: UserProfile;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface PrivilegeForm {
|
||||
|
@ -56,7 +58,7 @@ interface State {
|
|||
privilegeForms: PrivilegeForm[];
|
||||
}
|
||||
|
||||
export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
||||
class SpaceAwarePrivilegeFormUI extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { role } = props;
|
||||
|
@ -73,15 +75,44 @@ export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { kibanaAppPrivileges, role, userProfile } = this.props;
|
||||
const { kibanaAppPrivileges, role, userProfile, intl } = this.props;
|
||||
|
||||
if (!userProfile.hasCapability('manageSpaces')) {
|
||||
return (
|
||||
<EuiCallOut title={<p>Insufficient Privileges</p>} iconType="alert" color="danger">
|
||||
<p>You are not authorized to view all available spaces.</p>
|
||||
<EuiCallOut
|
||||
title={
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.insufficientPrivilegesDescription"
|
||||
defaultMessage="Insufficient Privileges"
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
iconType="alert"
|
||||
color="danger"
|
||||
>
|
||||
<p>
|
||||
Please ensure your account has all privileges granted by the{' '}
|
||||
<strong>kibana_user</strong> role, and try again.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription"
|
||||
defaultMessage="You are not authorized to view all available spaces."
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.ensureAccountHasAllPrivilegesGrantedDescription"
|
||||
defaultMessage="Please ensure your account has all privileges granted by the
|
||||
{kibanaUser} role, and try again."
|
||||
values={{
|
||||
kibanaUser: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.kibanaUserTitle"
|
||||
defaultMessage="kibana_user"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
|
@ -92,21 +123,46 @@ export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
|||
const basePrivilege =
|
||||
assignedPrivileges.global.length > 0 ? assignedPrivileges.global[0] : NO_PRIVILEGE_VALUE;
|
||||
|
||||
const description = <p>Specify the minimum actions users can perform in your spaces.</p>;
|
||||
const description = (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.minimumActionsUserCanPerformInYourSpacesDescription"
|
||||
defaultMessage="Specify the minimum actions users can perform in your spaces."
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
|
||||
let helptext;
|
||||
if (basePrivilege === NO_PRIVILEGE_VALUE) {
|
||||
helptext = 'No access to spaces';
|
||||
helptext = intl.formatMessage({
|
||||
id: 'xpack.security.management.editRoles.spaceAwarePrivilegeForm.noAccessToSpacesHelpText',
|
||||
defaultMessage: 'No access to spaces',
|
||||
});
|
||||
} else if (basePrivilege === 'all') {
|
||||
helptext = 'View, edit, and share objects and apps within all spaces';
|
||||
helptext = intl.formatMessage({
|
||||
id:
|
||||
'xpack.security.management.editRoles.spaceAwarePrivilegeForm.viewEditShareAppsWithinAllSpacesHelpText',
|
||||
defaultMessage: 'View, edit, and share objects and apps within all spaces',
|
||||
});
|
||||
} else if (basePrivilege === 'read') {
|
||||
helptext = 'View objects and apps within all spaces';
|
||||
helptext = intl.formatMessage({
|
||||
id:
|
||||
'xpack.security.management.editRoles.spaceAwarePrivilegeForm.viewObjectsAndAppsWithinAllSpacesHelpText',
|
||||
defaultMessage: 'View objects and apps within all spaces',
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiDescribedFormGroup
|
||||
title={<h3>Minimum privileges for all spaces</h3>}
|
||||
title={
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.minPrivilegesForAllSpacesTitle"
|
||||
defaultMessage="Minimum privileges for all spaces"
|
||||
/>
|
||||
</h3>
|
||||
}
|
||||
description={description}
|
||||
>
|
||||
<EuiFormRow hasEmptyLabelSpace helpText={helptext}>
|
||||
|
@ -147,7 +203,12 @@ export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
|||
return (
|
||||
<Fragment>
|
||||
<EuiTitle size={'xs'}>
|
||||
<h3>Higher privileges for individual spaces</h3>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.higherPrivilegesForIndividualSpacesTitle"
|
||||
defaultMessage="Higher privileges for individual spaces"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size={'s'} />
|
||||
<EuiText
|
||||
|
@ -157,9 +218,30 @@ export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
|||
color={'subdued'}
|
||||
>
|
||||
<p>
|
||||
Grant more privileges on a per space basis. For example, if the privileges are{' '}
|
||||
<strong>read</strong> for all spaces, you can set the privileges to <strong>all</strong>{' '}
|
||||
for an individual space.
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.grantMorePrivilegesTitle"
|
||||
defaultMessage="Grant more privileges on a per space basis. For example, if the privileges are
|
||||
{read} for all spaces, you can set the privileges to {all}
|
||||
for an individual space."
|
||||
values={{
|
||||
read: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.readText"
|
||||
defaultMessage="read"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
all: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.allText"
|
||||
defaultMessage="all"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size={'s'} />
|
||||
|
@ -200,7 +282,10 @@ export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
|||
iconType={'plusInCircle'}
|
||||
onClick={this.addSpacePrivilege}
|
||||
>
|
||||
Add space privilege
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.spaceAwarePrivilegeForm.addSpacePrivilegeTitle"
|
||||
defaultMessage="Add space privilege"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
@ -367,3 +452,5 @@ export class SpaceAwarePrivilegeForm extends Component<Props, State> {
|
|||
this.props.onChange(role);
|
||||
};
|
||||
}
|
||||
|
||||
export const SpaceAwarePrivilegeForm = injectI18n(SpaceAwarePrivilegeFormUI);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import { EuiIcon, EuiToolTip } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { Role } from '../../../../../common/model/role';
|
||||
import { isReservedRole } from '../../../../lib/role';
|
||||
|
||||
|
@ -19,7 +20,14 @@ export const ReservedRoleBadge = (props: Props) => {
|
|||
|
||||
if (isReservedRole(role)) {
|
||||
return (
|
||||
<EuiToolTip content={'Reserved roles are built-in and cannot be removed or modified.'}>
|
||||
<EuiToolTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.security.management.editRoles.reversedRoleBadget.reversedRolesCanNotBeRemovedTooltip"
|
||||
defaultMessage="Reserved roles are built-in and cannot be removed or modified."
|
||||
/>
|
||||
}
|
||||
>
|
||||
<EuiIcon style={{ verticalAlign: 'super' }} type={'lock'} />
|
||||
</EuiToolTip>
|
||||
);
|
||||
|
|
|
@ -28,6 +28,7 @@ import { EditRolePage } from './components';
|
|||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { KibanaAppPrivileges } from '../../../../common/model/kibana_privilege';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
routes.when(`${EDIT_ROLES_PATH}/:name?`, {
|
||||
template,
|
||||
|
@ -126,20 +127,23 @@ routes.when(`${EDIT_ROLES_PATH}/:name?`, {
|
|||
$scope.$$postDigest(() => {
|
||||
const domNode = document.getElementById('editRoleReactRoot');
|
||||
|
||||
render(<EditRolePage
|
||||
runAsUsers={users}
|
||||
role={role}
|
||||
kibanaAppPrivileges={KibanaAppPrivileges}
|
||||
indexPatterns={indexPatterns}
|
||||
rbacEnabled={true}
|
||||
rbacApplication={rbacApplication}
|
||||
httpClient={$http}
|
||||
allowDocumentLevelSecurity={allowDocumentLevelSecurity}
|
||||
allowFieldLevelSecurity={allowFieldLevelSecurity}
|
||||
spaces={spaces}
|
||||
spacesEnabled={enableSpaceAwarePrivileges}
|
||||
userProfile={userProfile}
|
||||
/>, domNode);
|
||||
render(
|
||||
<I18nProvider>
|
||||
<EditRolePage
|
||||
runAsUsers={users}
|
||||
role={role}
|
||||
kibanaAppPrivileges={KibanaAppPrivileges}
|
||||
indexPatterns={indexPatterns}
|
||||
rbacEnabled={true}
|
||||
rbacApplication={rbacApplication}
|
||||
httpClient={$http}
|
||||
allowDocumentLevelSecurity={allowDocumentLevelSecurity}
|
||||
allowFieldLevelSecurity={allowFieldLevelSecurity}
|
||||
spaces={spaces}
|
||||
spacesEnabled={enableSpaceAwarePrivileges}
|
||||
userProfile={userProfile}
|
||||
/>
|
||||
</I18nProvider>, domNode);
|
||||
|
||||
// unmount react on controller destroy
|
||||
$scope.$on('$destroy', () => {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`validateIndexPrivileges it throws when indices is not an array 1`] = `"Expected role.elasticsearch.indices to be an array"`;
|
||||
exports[`validateIndexPrivileges it throws when indices is not an array 1`] = `"Expected \\"role.elasticsearch.indices\\" to be an array"`;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IndexPrivilege } from '../../../../../common/model/index_privilege';
|
||||
import { KibanaPrivilege } from '../../../../../common/model/kibana_privilege';
|
||||
import { Role } from '../../../../../common/model/role';
|
||||
|
@ -52,14 +53,34 @@ export class RoleValidator {
|
|||
}
|
||||
|
||||
if (!role.name) {
|
||||
return invalid(`Please provide a role name`);
|
||||
return invalid(
|
||||
i18n.translate(
|
||||
'xpack.security.management.editRoles.validateRole.provideRoleNameWarningMessage',
|
||||
{
|
||||
defaultMessage: 'Please provide a role name',
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
if (role.name.length > 1024) {
|
||||
return invalid(`Name must not exceed 1024 characters`);
|
||||
return invalid(
|
||||
i18n.translate(
|
||||
'xpack.security.management.editRoles.validateRole.nameLengthWarningMessage',
|
||||
{
|
||||
defaultMessage: '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.`
|
||||
i18n.translate(
|
||||
'xpack.security.management.editRoles.validateRole.nameAllowedCharactersWarningMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Name must begin with a letter or underscore and contain only letters, underscores, and numbers.',
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
return valid();
|
||||
|
@ -71,7 +92,14 @@ export class RoleValidator {
|
|||
}
|
||||
|
||||
if (!Array.isArray(role.elasticsearch.indices)) {
|
||||
throw new TypeError(`Expected role.elasticsearch.indices to be an array`);
|
||||
throw new TypeError(
|
||||
i18n.translate('xpack.security.management.editRoles.validateRole.indicesTypeErrorMessage', {
|
||||
defaultMessage: 'Expected {elasticIndices} to be an array',
|
||||
values: {
|
||||
elasticIndices: '"role.elasticsearch.indices"',
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const areIndicesValid =
|
||||
|
@ -91,7 +119,14 @@ export class RoleValidator {
|
|||
}
|
||||
|
||||
if (indexPrivilege.names.length && !indexPrivilege.privileges.length) {
|
||||
return invalid(`At least one privilege is required`);
|
||||
return invalid(
|
||||
i18n.translate(
|
||||
'xpack.security.management.editRoles.validateRole.onePrivilegeRequiredWarningMessage',
|
||||
{
|
||||
defaultMessage: 'At least one privilege is required',
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
return valid();
|
||||
}
|
||||
|
@ -112,7 +147,14 @@ export class RoleValidator {
|
|||
if (Array.isArray(spaceIds) && spaceIds.length > 0) {
|
||||
return valid();
|
||||
}
|
||||
return invalid('At least one space is required');
|
||||
return invalid(
|
||||
i18n.translate(
|
||||
'xpack.security.management.editRoles.validateRole.oneSpaceRequiredWarningMessage',
|
||||
{
|
||||
defaultMessage: 'At least one space is required',
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public validateSelectedPrivilege(
|
||||
|
@ -131,7 +173,14 @@ export class RoleValidator {
|
|||
if (privilege) {
|
||||
return valid();
|
||||
}
|
||||
return invalid('Privilege is required');
|
||||
return invalid(
|
||||
i18n.translate(
|
||||
'xpack.security.management.editRoles.validateRole.privilegeRequiredWarningMessage',
|
||||
{
|
||||
defaultMessage: 'Privilege is required',
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public setInProgressSpacePrivileges(inProgressSpacePrivileges: any[]) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import '../../services/shield_user';
|
|||
import { ROLES_PATH, USERS_PATH } from './management_urls';
|
||||
|
||||
import { management } from 'ui/management';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
routes.defaults(/\/management/, {
|
||||
resolve: {
|
||||
|
@ -29,7 +30,10 @@ routes.defaults(/\/management/, {
|
|||
|
||||
function ensureSecurityRegistered() {
|
||||
const registerSecurity = () => management.register('security', {
|
||||
display: 'Security',
|
||||
display: i18n.translate(
|
||||
'xpack.security.management.securityTitle', {
|
||||
defaultMessage: 'Security',
|
||||
}),
|
||||
order: 10,
|
||||
icon: 'securityApp',
|
||||
});
|
||||
|
@ -41,7 +45,10 @@ routes.defaults(/\/management/, {
|
|||
security.register('users', {
|
||||
name: 'securityUsersLink',
|
||||
order: 10,
|
||||
display: 'Users',
|
||||
display: i18n.translate(
|
||||
'xpack.security.management.usersTitle', {
|
||||
defaultMessage: 'Users',
|
||||
}),
|
||||
url: `#${USERS_PATH}`,
|
||||
});
|
||||
}
|
||||
|
@ -50,7 +57,10 @@ routes.defaults(/\/management/, {
|
|||
security.register('roles', {
|
||||
name: 'securityRolesLink',
|
||||
order: 20,
|
||||
display: 'Roles',
|
||||
display: i18n.translate(
|
||||
'xpack.security.management.rolesTitle', {
|
||||
defaultMessage: 'Roles',
|
||||
}),
|
||||
url: `#${ROLES_PATH}`,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
<div class="kuiInfoPanel kuiInfoPanel--error" ng-if="forbidden">
|
||||
<div class="kuiInfoPanelHeader">
|
||||
<span class="kuiInfoPanelHeader__icon kuiIcon kuiIcon--error fa-warning"></span>
|
||||
<span class="kuiInfoPanelHeader__title">
|
||||
You do not have permission to manage roles.
|
||||
</span>
|
||||
<span
|
||||
class="kuiInfoPanelHeader__title"
|
||||
i18n-id="xpack.security.management.roles.noPermissionToManageRolesDescription"
|
||||
i18n-default-message="You do not have permission to manage roles."
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div class="kuiInfoPanelBody">
|
||||
<div class="kuiInfoPanelBody__message">
|
||||
Please contact your administrator.
|
||||
</div>
|
||||
<div
|
||||
class="kuiInfoPanelBody__message"
|
||||
i18n-id="xpack.security.management.roles.contactAdministratorDescription"
|
||||
i18n-default-message="Please contact your administrator."
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -30,7 +34,7 @@
|
|||
aria-label="Filter"
|
||||
ng-model="query"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiToolBarSection">
|
||||
|
@ -42,7 +46,10 @@
|
|||
>
|
||||
<span class="kuiButton__inner">
|
||||
<span class="kuiButton__icon kuiIcon fa-trash"></span>
|
||||
<span>Delete</span>
|
||||
<span
|
||||
i18n-id="xpack.security.management.roles.deleteButtonLabel"
|
||||
i18n-default-message="Delete"
|
||||
></span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
|
@ -54,7 +61,11 @@
|
|||
ng-if="!selectedRoles.length"
|
||||
data-test-subj="createRoleButton"
|
||||
>
|
||||
<span class="kuiButton__icon kuiIcon fa-plus"></span><span>Create role</span>
|
||||
<span class="kuiButton__icon kuiIcon fa-plus"></span>
|
||||
<span
|
||||
i18n-id="xpack.security.management.roles.createRoleButtonLabel"
|
||||
i18n-default-message="Create role"
|
||||
></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
@ -65,8 +76,23 @@
|
|||
|
||||
<!-- NoResults -->
|
||||
<div class="kuiPanel kuiPanel--centered" ng-show="!(roles | filter:query).length">
|
||||
<div class="kuiNoItems">
|
||||
No <span ng-show="query">matching</span> roles found.
|
||||
<div
|
||||
class="kuiNoItems"
|
||||
>
|
||||
<span
|
||||
i18n-id="xpack.security.management.roles.noFoundMatchingRolesDescription1"
|
||||
i18n-default-message="No "
|
||||
i18n-context="Part of composite label xpack.security.management.roles.noFoundMatchingRolesDescription1 + {matchingText} + xpack.security.management.roles.noFoundMatchingRolesDescription2"
|
||||
></span>
|
||||
<span
|
||||
ng-show="query"
|
||||
i18n-id="xpack.security.management.roles.matchingText"
|
||||
i18n-default-message="matching"
|
||||
></span>
|
||||
<span
|
||||
i18n-id="xpack.security.management.roles.noFoundMatchingRolesDescription2"
|
||||
i18n-default-message=" roles found"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -97,7 +123,10 @@
|
|||
aria-label="{{sort.reverse ? 'Sort role ascending' : 'Sort role descending'}}"
|
||||
>
|
||||
<span class="kuiTableHeaderCell__liner">
|
||||
Role
|
||||
<span
|
||||
i18n-id="xpack.security.management.roles.roleTitle"
|
||||
i18n-default-message="Role"
|
||||
></span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiTableSortIcon kuiIcon"
|
||||
|
@ -108,14 +137,18 @@
|
|||
</th>
|
||||
|
||||
<th scope="col" class="kuiTableHeaderCell">
|
||||
<span class="kuiTableHeaderCell__liner">
|
||||
Reserved
|
||||
<span
|
||||
class="kuiIcon fa-question-circle"
|
||||
tooltip="Reserved roles are built-in and cannot be removed or modified. Only the password may be changed."
|
||||
aria-label="Reserved roles are built-in and cannot be removed or modified. Only the password may be changed."
|
||||
></span>
|
||||
</span>
|
||||
<span
|
||||
class="kuiTableHeaderCell__liner"
|
||||
i18n-id="xpack.security.management.roles.reversedTitle"
|
||||
i18n-default-message="Reserved {icon}"
|
||||
i18n-values="{
|
||||
icon: '<span
|
||||
class=\'kuiIcon fa-question-circle\'
|
||||
tooltip={{reversedTooltip}}
|
||||
aria-label={{reversedAriaLabel}}
|
||||
></span>'
|
||||
}"
|
||||
></span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -151,9 +184,13 @@
|
|||
class="kuiBadge kuiBadge--default"
|
||||
tooltip="This role is currently disabled. You may only view or delete it."
|
||||
aria-label="This role is currently disabled. You may only view or delete it."
|
||||
i18n-id="xpack.security.management.roles.disableTitle"
|
||||
i18n-default-message="{icon} Disabled"
|
||||
i18n-values="{
|
||||
icon: '<span class=\'kuiIcon fa-warning\'></span>'
|
||||
}"
|
||||
>
|
||||
<span class="kuiIcon fa-warning"></span>
|
||||
Disabled
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -175,9 +212,13 @@
|
|||
<!-- ToolBarFooter -->
|
||||
<div class="kuiToolBarFooter">
|
||||
<div class="kuiToolBarFooterSection">
|
||||
<div class="kuiToolBarText" ng-hide="selectedRoles.length === 0">
|
||||
{{ selectedRoles.length }} roles selected
|
||||
</div>
|
||||
<div
|
||||
class="kuiToolBarText"
|
||||
ng-hide="selectedRoles.length === 0"
|
||||
i18n-id="xpack.security.management.roles.selectedRolesTitle"
|
||||
i18n-default-message="{selectedRolesLength} roles selected"
|
||||
i18n-values="{ selectedRolesLength: selectedRoles.length }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="kuiToolBarFooterSection">
|
||||
<!-- We need an empty section for the buttons to be positioned consistently. -->
|
||||
|
|
|
@ -19,25 +19,34 @@ routes.when(ROLES_PATH, {
|
|||
resolve: {
|
||||
roles(ShieldRole, kbnUrl, Promise, Private) {
|
||||
// $promise is used here because the result is an ngResource, not a promise itself
|
||||
return ShieldRole.query().$promise
|
||||
.catch(checkLicenseError(kbnUrl, Promise, Private))
|
||||
return ShieldRole.query()
|
||||
.$promise.catch(checkLicenseError(kbnUrl, Promise, Private))
|
||||
.catch(_.identity); // Return the error if there is one
|
||||
}
|
||||
},
|
||||
},
|
||||
controller($scope, $route, $q, confirmModal) {
|
||||
controller($scope, $route, $q, confirmModal, i18n) {
|
||||
$scope.roles = $route.current.locals.roles;
|
||||
$scope.forbidden = !_.isArray($scope.roles);
|
||||
$scope.selectedRoles = [];
|
||||
$scope.sort = { orderBy: 'name', reverse: false };
|
||||
$scope.editRolesHref = `#${EDIT_ROLES_PATH}`;
|
||||
$scope.getEditRoleHref = (role) => `#${EDIT_ROLES_PATH}/${role}`;
|
||||
$scope.getEditRoleHref = role => `#${EDIT_ROLES_PATH}/${role}`;
|
||||
|
||||
$scope.deleteRoles = () => {
|
||||
const doDelete = () => {
|
||||
$q.all($scope.selectedRoles.map((role) => role.$delete()))
|
||||
.then(() => toastNotifications.addSuccess(`Deleted ${$scope.selectedRoles.length > 1 ? 'roles' : 'role'}`))
|
||||
$q.all($scope.selectedRoles.map(role => role.$delete()))
|
||||
.then(() =>
|
||||
toastNotifications.addSuccess(
|
||||
i18n('xpack.security.management.roles.deleteRoleTitle', {
|
||||
defaultMessage: 'Deleted {value, plural, one {role} other {roles}}',
|
||||
values: {
|
||||
value: $scope.selectedRoles.length,
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
.then(() => {
|
||||
$scope.selectedRoles.map((role) => {
|
||||
$scope.selectedRoles.map(role => {
|
||||
const i = $scope.roles.indexOf(role);
|
||||
$scope.roles.splice(i, 1);
|
||||
});
|
||||
|
@ -45,12 +54,17 @@ routes.when(ROLES_PATH, {
|
|||
});
|
||||
};
|
||||
const confirmModalOptions = {
|
||||
confirmButtonText: 'Delete role(s)',
|
||||
onConfirm: doDelete
|
||||
confirmButtonText: i18n('xpack.security.management.roles.deleteRoleConfirmButtonLabel', {
|
||||
defaultMessage: 'Delete role(s)',
|
||||
}),
|
||||
onConfirm: doDelete,
|
||||
};
|
||||
confirmModal(`
|
||||
Are you sure you want to delete the selected role(s)? This action is irreversible!`,
|
||||
confirmModalOptions
|
||||
confirmModal(
|
||||
i18n('xpack.security.management.roles.deletingRolesWarningMessage', {
|
||||
defaultMessage:
|
||||
'Are you sure you want to delete the selected role(s)? This action is irreversible!',
|
||||
}),
|
||||
confirmModalOptions
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -83,7 +97,16 @@ routes.when(ROLES_PATH, {
|
|||
$scope.toggleSort = toggleSort;
|
||||
|
||||
function getActionableRoles() {
|
||||
return $scope.roles.filter((role) => !role.metadata._reserved);
|
||||
return $scope.roles.filter(role => !role.metadata._reserved);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.reversedTooltip = i18n('xpack.security.management.roles.reversedTooltip', {
|
||||
defaultMessage: 'Reserved roles are built-in and cannot be removed or modified. Only the password may be changed.',
|
||||
});
|
||||
|
||||
$scope.reversedAriaLabel = i18n('xpack.security.management.roles.reversedAriaLabel', {
|
||||
defaultMessage: 'Reserved roles are built-in and cannot be removed or modified. Only the password may be changed.',
|
||||
});
|
||||
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue