@@ -787,7 +832,7 @@ export const EditRolePage: FunctionComponent
= ({
)}
- {getRoleName()}
+ {getRoleNameAndDescription()}
{getElasticsearchPrivileges()}
{getKibanaPrivileges()}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts
index bf85f80df1fc1..a425578ed98e5 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts
+++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts
@@ -85,7 +85,6 @@ export class RoleValidator {
}
return valid();
}
-
public validateRemoteClusterPrivileges(role: Role): RoleValidationResult {
if (!this.shouldValidate) {
return valid();
diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx
index ed6f28ea8321f..6b666cfd378f4 100644
--- a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx
+++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx
@@ -56,6 +56,12 @@ describe('', () => {
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [{ base: [], spaces: [], feature: {} }],
},
+ {
+ name: 'test-role-with-description',
+ description: 'role-description',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [{ base: [], spaces: [], feature: {} }],
+ },
{
name: 'reserved-role',
elasticsearch: { cluster: [], indices: [], run_as: [] },
@@ -162,6 +168,10 @@ describe('', () => {
expect(wrapper.find('a[data-test-subj="edit-role-action-disabled-role"]')).toHaveLength(1);
expect(wrapper.find('a[data-test-subj="clone-role-action-disabled-role"]')).toHaveLength(1);
+
+ expect(findTestSubject(wrapper, 'roleRowDescription-test-role-with-description')).toHaveLength(
+ 1
+ );
});
it('hides reserved roles when instructed to', async () => {
@@ -201,6 +211,12 @@ describe('', () => {
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [{ base: [], spaces: [], feature: {} }],
},
+ {
+ name: 'test-role-with-description',
+ description: 'role-description',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [{ base: [], spaces: [], feature: {} }],
+ },
]);
findTestSubject(wrapper, 'showReservedRolesSwitch').simulate('click');
@@ -222,6 +238,12 @@ describe('', () => {
elasticsearch: { cluster: [], indices: [], run_as: [] },
kibana: [{ base: [], spaces: [], feature: {} }],
},
+ {
+ name: 'test-role-with-description',
+ description: 'role-description',
+ elasticsearch: { cluster: [], indices: [], run_as: [] },
+ kibana: [{ base: [], spaces: [], feature: {} }],
+ },
]);
});
diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx
index a21d0a1e99912..bb87cc61b0f84 100644
--- a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx
@@ -17,6 +17,7 @@ import {
EuiSpacer,
EuiSwitch,
EuiText,
+ EuiToolTip,
} from '@elastic/eui';
import _ from 'lodash';
import React, { Component } from 'react';
@@ -183,7 +184,6 @@ export class RolesGridPage extends Component {
{
);
},
},
+ {
+ field: 'description',
+ name: i18n.translate('xpack.security.management.roles.descriptionColumnName', {
+ defaultMessage: 'Role Description',
+ }),
+ sortable: true,
+ truncateText: { lines: 3 },
+ render: (description: string, record: Role) => {
+ return (
+
+
+ {description}
+
+
+ );
+ },
+ },
];
if (this.props.buildFlavor !== 'serverless') {
config.push({
diff --git a/x-pack/test/functional/apps/security/index.ts b/x-pack/test/functional/apps/security/index.ts
index 009c270d3c2a3..a65db6d3d3cf8 100644
--- a/x-pack/test/functional/apps/security/index.ts
+++ b/x-pack/test/functional/apps/security/index.ts
@@ -18,5 +18,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./user_email'));
loadTestFile(require.resolve('./role_mappings'));
loadTestFile(require.resolve('./remote_cluster_security_roles'));
+ loadTestFile(require.resolve('./role_description'));
});
}
diff --git a/x-pack/test/functional/apps/security/role_description.ts b/x-pack/test/functional/apps/security/role_description.ts
new file mode 100644
index 0000000000000..eb272dec3d0a5
--- /dev/null
+++ b/x-pack/test/functional/apps/security/role_description.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const testSubjects = getService('testSubjects');
+ const security = getService('security');
+ const PageObjects = getPageObjects(['security', 'settings', 'common', 'header']);
+
+ describe('Role Description', function () {
+ before(async () => {
+ await security.testUser.setRoles(['cluster_security_manager']);
+ await PageObjects.security.initTests();
+ await PageObjects.settings.navigateTo();
+ await PageObjects.security.clickElasticsearchRoles();
+ });
+
+ after(async () => {
+ // NOTE: Logout needs to happen before anything else to avoid flaky behavior
+ await PageObjects.security.forceLogout();
+ await security.role.delete('a-role-with-description');
+ await security.role.delete('a-role-without-description');
+ await security.testUser.restoreDefaults();
+ });
+
+ it('Can create role with description', async () => {
+ await PageObjects.security.clickCreateNewRole();
+ await testSubjects.setValue('roleFormNameInput', 'a-role-with-description');
+ await testSubjects.setValue('roleFormDescriptionInput', 'role description');
+ await PageObjects.security.clickSaveEditRole();
+
+ const columnDescription = await testSubjects.getVisibleText(
+ 'roleRowDescription-a-role-with-description'
+ );
+ expect(columnDescription).to.equal('role description');
+
+ await PageObjects.settings.clickLinkText('a-role-with-description');
+ const name = await testSubjects.getAttribute('roleFormNameInput', 'value');
+ const description = await testSubjects.getAttribute('roleFormDescriptionInput', 'value');
+
+ expect(name).to.equal('a-role-with-description');
+ expect(description).to.equal('role description');
+
+ await PageObjects.security.clickCancelEditRole();
+ });
+
+ it('Can create role without description', async () => {
+ await PageObjects.security.clickCreateNewRole();
+ await testSubjects.setValue('roleFormNameInput', 'a-role-without-description');
+ await PageObjects.security.clickSaveEditRole();
+
+ await PageObjects.settings.clickLinkText('a-role-without-description');
+ const name = await testSubjects.getAttribute('roleFormNameInput', 'value');
+ const description = await testSubjects.getAttribute('roleFormDescriptionInput', 'value');
+
+ expect(name).to.equal('a-role-without-description');
+ expect(description).to.equal('');
+
+ await PageObjects.security.clickCancelEditRole();
+ });
+ });
+}