Adding optional Description field to Roles APIs (#182039)

## Summary

In preparation for the KB UI and ES API to accept `description` for
Roles, Kibana `get`, `getAll`, and `put` Roles routes should handle a
description.

## Testing

Start KB/ES locally

In Dev Tools PUT role:
```
PUT kbn:api/security/role/mytestrole
{
  "description": "This is a test role",
  "metadata": {
    "version": 1
  },
  "elasticsearch": {
    "cluster": [ ],
    "indices": [ ]
  },
  "kibana": [
    {
      "base": [ ],
      "feature": {
       "discover": [ "all" ],
        "visualize": [ "all" ],
        "dashboard": [ "all" ],
        "dev_tools": [ "read" ],
        "advancedSettings": [ "read" ],
        "indexPatterns": [ "read" ],
        "graph": [ "all" ],
        "apm": [ "read" ],
        "maps": [ "read" ],
        "canvas": [ "read" ],
        "infrastructure": [ "all" ],
        "logs": [ "all" ],
        "uptime": [ "all"  ]
      },
      "spaces": [ "*" ]
    }
  ]
}
```

This will fail since ES doesn't accept `descriptions` yet

Pull the ES Role Description PR
https://github.com/elastic/elasticsearch/pull/107088

and run `yarn es source` and `yarn start`

Rerun the PUT above, receive 204!

Check the role `description` with a GET:

```
GET kbn:api/security/role/mytestrole
```

<img width="1712" alt="Screenshot 2024-04-30 at 9 43 32 PM"
src="20394086-f223-4be8-8660-eb8d3930116c">


It has a limit of 2048 per the requirements:
<img width="2248" alt="Screenshot 2024-04-30 at 9 45 13 PM"
src="7dc97a8e-1246-49e4-954e-885be433c7c7">
This commit is contained in:
Kurt 2024-05-01 13:20:35 -04:00 committed by GitHub
parent 126410ccfb
commit 565a447a8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 30 additions and 1 deletions

View file

@ -30,6 +30,7 @@ export interface RoleKibanaPrivilege {
export interface Role {
name: string;
description?: string;
elasticsearch: {
cluster: string[];
indices: RoleIndexPrivilege[];

View file

@ -18,7 +18,10 @@ import { getDetailedErrorMessage } from '../../errors';
import { PrivilegeSerializer } from '../privilege_serializer';
import { ResourceSerializer } from '../resource_serializer';
export type ElasticsearchRole = Pick<Role, 'name' | 'metadata' | 'transient_metadata'> & {
export type ElasticsearchRole = Pick<
Role,
'name' | 'description' | 'metadata' | 'transient_metadata'
> & {
applications: Array<{
application: string;
privileges: string[];
@ -48,6 +51,7 @@ export function transformElasticsearchRoleToRole(
);
return {
name,
...(elasticsearchRole.description && { description: elasticsearchRole.description }),
metadata: elasticsearchRole.metadata,
transient_metadata: elasticsearchRole.transient_metadata,
elasticsearch: {

View file

@ -123,6 +123,7 @@ describe('GET role', () => {
name: 'first_role',
apiResponse: () => ({
first_role: {
description: 'roleDescription',
cluster: ['manage_watcher'],
indices: [
{
@ -144,6 +145,7 @@ describe('GET role', () => {
statusCode: 200,
result: {
name: 'first_role',
description: 'roleDescription',
metadata: {
_reserved: true,
},
@ -174,6 +176,7 @@ describe('GET role', () => {
name: 'first_role',
apiResponse: () => ({
first_role: {
description: 'roleDescription',
cluster: [],
indices: [],
applications: [
@ -196,6 +199,7 @@ describe('GET role', () => {
statusCode: 200,
result: {
name: 'first_role',
description: 'roleDescription',
metadata: {
_reserved: true,
},

View file

@ -28,12 +28,14 @@ export function defineGetRolesRoutes({
createLicensedRouteHandler(async (context, request, response) => {
try {
const esClient = (await context.core).elasticsearch.client;
const [features, elasticsearchRoles] = await Promise.all([
getFeatures(),
await esClient.asCurrentUser.security.getRole({
name: request.params.name,
}),
]);
const elasticsearchRole = elasticsearchRoles[request.params.name];
if (elasticsearchRole) {

View file

@ -118,6 +118,7 @@ describe('GET all roles', () => {
getRolesTest(`transforms elasticsearch privileges`, {
apiResponse: () => ({
first_role: {
description: 'roleDescription',
cluster: ['manage_watcher'],
indices: [
{
@ -140,6 +141,7 @@ describe('GET all roles', () => {
result: [
{
name: 'first_role',
description: 'roleDescription',
metadata: {
_reserved: true,
},
@ -170,6 +172,7 @@ describe('GET all roles', () => {
{
apiResponse: () => ({
first_role: {
description: 'roleDescription',
cluster: [],
indices: [],
applications: [
@ -193,6 +196,7 @@ describe('GET all roles', () => {
result: [
{
name: 'first_role',
description: 'roleDescription',
metadata: {
_reserved: true,
},

View file

@ -171,6 +171,7 @@ describe('GET all roles by space id', () => {
getRolesTest(`returns roles for matching space`, {
apiResponse: () => ({
first_role: {
description: 'first role description',
cluster: [],
indices: [],
applications: [
@ -218,6 +219,7 @@ describe('GET all roles by space id', () => {
result: [
{
name: 'first_role',
description: 'first role description',
metadata: {
_reserved: true,
},
@ -251,6 +253,7 @@ describe('GET all roles by space id', () => {
getRolesTest(`returns roles with access to all spaces`, {
apiResponse: () => ({
first_role: {
description: 'first role description',
cluster: [],
indices: [],
applications: [
@ -292,6 +295,7 @@ describe('GET all roles by space id', () => {
result: [
{
name: 'first_role',
description: 'first role description',
metadata: {
_reserved: true,
},

View file

@ -31,6 +31,7 @@ export const transformPutPayloadToElasticsearchRole = (
);
return {
...(rolePayload.description && { description: rolePayload.description }),
metadata: rolePayload.metadata,
cluster: elasticsearch.cluster || [],
indices: elasticsearch.indices || [],
@ -47,6 +48,11 @@ export function getPutPayloadSchema(
getBasePrivilegeNames: () => { global: string[]; space: string[] }
) {
return schema.object({
/**
* Optional text to describe the Role
*/
description: schema.maybe(schema.string({ maxLength: 2048 })),
/**
* An optional meta-data dictionary. Within the metadata, keys that begin with _ are reserved
* for system usage.

View file

@ -464,6 +464,7 @@ describe('PUT role', () => {
putRoleTest(`creates role with everything`, {
name: 'foo-role',
payload: {
description: 'test description',
metadata: {
foo: 'test-metadata',
},
@ -540,6 +541,7 @@ describe('PUT role', () => {
},
],
cluster: ['test-cluster-privilege'],
description: 'test description',
indices: [
{
field_security: {

View file

@ -62,12 +62,14 @@ export function definePutRolesRoutes({
const { createOnly } = request.query;
try {
const esClient = (await context.core).elasticsearch.client;
const [features, rawRoles] = await Promise.all([
getFeatures(),
esClient.asCurrentUser.security.getRole({ name: request.params.name }, { ignore: [404] }),
]);
const { validationErrors } = validateKibanaPrivileges(features, request.body.kibana);
if (validationErrors.length) {
return response.badRequest({
body: {