Skip to content

Commit

Permalink
Merge branch 'main' into task/olm-10840-dot-prefix-indices-ftr-api-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-tavares authored Oct 28, 2024
2 parents 94852b3 + 0220874 commit 8b7641e
Show file tree
Hide file tree
Showing 38 changed files with 573 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const COMMON_REQUEST_HEADERS = {
// possible change in 9.0 to match serverless
const STATEFUL_INTERNAL_REQUEST_HEADERS = {
...COMMON_REQUEST_HEADERS,
'x-elastic-internal-origin': 'kibana',
};

const SERVERLESS_INTERNAL_REQUEST_HEADERS = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { schema } from '@kbn/config-schema';
import { extractAuthzDescription } from './extract_authz_description';
import { InternalRouterRoute } from './type';
import { RouteSecurity } from '@kbn/core-http-server';

describe('extractAuthzDescription', () => {
it('should return empty if route does not require privileges', () => {
const route: InternalRouterRoute = {
path: '/foo',
options: { access: 'internal' },
handler: jest.fn(),
validationSchemas: { request: { body: schema.object({}) } },
method: 'get',
isVersioned: false,
};
const description = extractAuthzDescription(route.security);
expect(description).toBe('');
});

it('should return route authz description for simple privileges', () => {
const routeSecurity: RouteSecurity = {
authz: {
requiredPrivileges: ['manage_spaces'],
},
};
const description = extractAuthzDescription(routeSecurity);
expect(description).toBe('[Authz] Route required privileges: ALL of [manage_spaces].');
});

it('should return route authz description for privilege groups', () => {
{
const routeSecurity: RouteSecurity = {
authz: {
requiredPrivileges: [{ allRequired: ['console'] }],
},
};
const description = extractAuthzDescription(routeSecurity);
expect(description).toBe('[Authz] Route required privileges: ALL of [console].');
}
{
const routeSecurity: RouteSecurity = {
authz: {
requiredPrivileges: [
{
anyRequired: ['manage_spaces', 'taskmanager'],
},
],
},
};
const description = extractAuthzDescription(routeSecurity);
expect(description).toBe(
'[Authz] Route required privileges: ANY of [manage_spaces OR taskmanager].'
);
}
{
const routeSecurity: RouteSecurity = {
authz: {
requiredPrivileges: [
{
allRequired: ['console', 'filesManagement'],
anyRequired: ['manage_spaces', 'taskmanager'],
},
],
},
};
const description = extractAuthzDescription(routeSecurity);
expect(description).toBe(
'[Authz] Route required privileges: ALL of [console, filesManagement] AND ANY of [manage_spaces OR taskmanager].'
);
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { AuthzEnabled, AuthzDisabled, InternalRouteSecurity } from '@kbn/core-http-server';

interface PrivilegeGroupValue {
allRequired: string[];
anyRequired: string[];
}

export const extractAuthzDescription = (routeSecurity: InternalRouteSecurity | undefined) => {
if (!routeSecurity) {
return '';
}
if (!('authz' in routeSecurity) || (routeSecurity.authz as AuthzDisabled).enabled === false) {
return '';
}

const privileges = (routeSecurity.authz as AuthzEnabled).requiredPrivileges;

const groupedPrivileges = privileges.reduce<PrivilegeGroupValue>(
(groups, privilege) => {
if (typeof privilege === 'string') {
groups.allRequired.push(privilege);

return groups;
}
groups.allRequired.push(...(privilege.allRequired ?? []));
groups.anyRequired.push(...(privilege.anyRequired ?? []));

return groups;
},
{
anyRequired: [],
allRequired: [],
}
);

const getPrivilegesDescription = (allRequired: string[], anyRequired: string[]) => {
const allDescription = allRequired.length ? `ALL of [${allRequired.join(', ')}]` : '';
const anyDescription = anyRequired.length ? `ANY of [${anyRequired.join(' OR ')}]` : '';

return `${allDescription}${allDescription && anyDescription ? ' AND ' : ''}${anyDescription}`;
};

const getDescriptionForRoute = () => {
const allRequired = [...groupedPrivileges.allRequired];
const anyRequired = [...groupedPrivileges.anyRequired];

return `Route required privileges: ${getPrivilegesDescription(allRequired, anyRequired)}.`;
};

return `[Authz] ${getDescriptionForRoute()}`;
};
35 changes: 33 additions & 2 deletions packages/kbn-router-to-openapispec/src/process_router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { schema } from '@kbn/config-schema';
import { Router } from '@kbn/core-http-router-server-internal';
import { OasConverter } from './oas_converter';
import { createOperationIdCounter } from './operation_id_counter';
import { extractResponses, processRouter, type InternalRouterRoute } from './process_router';
import { extractResponses, processRouter } from './process_router';
import { type InternalRouterRoute } from './type';

describe('extractResponses', () => {
let oasConverter: OasConverter;
Expand Down Expand Up @@ -102,6 +103,24 @@ describe('processRouter', () => {
handler: jest.fn(),
validationSchemas: { request: { body: schema.object({}) } },
},
{
path: '/qux',
method: 'post',
options: {},
handler: jest.fn(),
validationSchemas: { request: { body: schema.object({}) } },
security: {
authz: {
requiredPrivileges: [
'manage_spaces',
{
allRequired: ['taskmanager'],
anyRequired: ['console'],
},
],
},
},
},
],
} as unknown as Router;

Expand All @@ -110,11 +129,23 @@ describe('processRouter', () => {
version: '2023-10-31',
});

expect(Object.keys(result1.paths!)).toHaveLength(3);
expect(Object.keys(result1.paths!)).toHaveLength(4);

const result2 = processRouter(testRouter, new OasConverter(), createOperationIdCounter(), {
version: '2024-10-31',
});
expect(Object.keys(result2.paths!)).toHaveLength(0);
});

it('updates description with privileges required', () => {
const result = processRouter(testRouter, new OasConverter(), createOperationIdCounter(), {
version: '2023-10-31',
});

expect(result.paths['/qux']?.post).toBeDefined();

expect(result.paths['/qux']?.post?.description).toEqual(
'[Authz] Route required privileges: ALL of [manage_spaces, taskmanager] AND ANY of [console].'
);
});
});
13 changes: 11 additions & 2 deletions packages/kbn-router-to-openapispec/src/process_router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import {
} from './util';
import type { OperationIdCounter } from './operation_id_counter';
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
import type { CustomOperationObject } from './type';
import type { CustomOperationObject, InternalRouterRoute } from './type';
import { extractAuthzDescription } from './extract_authz_description';

export const processRouter = (
appRouter: Router,
Expand Down Expand Up @@ -63,10 +64,19 @@ export const processRouter = (
parameters.push(...pathObjects, ...queryObjects);
}

let description = '';
if (route.security) {
const authzDescription = extractAuthzDescription(route.security);

description = `${route.options.description ?? ''}${authzDescription ?? ''}`;
}

const hasDeprecations = !!route.options.deprecated;

const operation: CustomOperationObject = {
summary: route.options.summary ?? '',
tags: route.options.tags ? extractTags(route.options.tags) : [],
...(description ? { description } : {}),
...(route.options.description ? { description: route.options.description } : {}),
...(hasDeprecations ? { deprecated: true } : {}),
...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}),
Expand Down Expand Up @@ -99,7 +109,6 @@ export const processRouter = (
return { paths };
};

export type InternalRouterRoute = ReturnType<Router['getRoutes']>[0];
export const extractResponses = (route: InternalRouterRoute, converter: OasConverter) => {
const responses: OpenAPIV3.ResponsesObject = {};
if (!route.validationSchemas) return responses;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,22 @@ describe('processVersionedRouter', () => {
'application/test+json; Elastic-Api-Version=2023-10-31',
]);
});

it('correctly updates the authz description for routes that require privileges', () => {
const results = processVersionedRouter(
{ getRoutes: () => [createTestRoute()] } as unknown as CoreVersionedRouter,
new OasConverter(),
createOperationIdCounter(),
{}
);
expect(results.paths['/foo']).toBeDefined();

expect(results.paths['/foo']!.get).toBeDefined();

expect(results.paths['/foo']!.get!.description).toBe(
'[Authz] Route required privileges: ALL of [manage_spaces].'
);
});
});

const createTestRoute: () => VersionedRouterRoute = () => ({
Expand All @@ -155,6 +171,11 @@ const createTestRoute: () => VersionedRouterRoute = () => ({
deprecated: true,
discontinued: 'discontinued versioned router',
options: { body: { access: ['application/test+json'] } as any },
security: {
authz: {
requiredPrivileges: ['manage_spaces'],
},
},
},
handlers: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@kbn/core-http-router-server-internal';
import type { RouteMethod, VersionedRouterRoute } from '@kbn/core-http-server';
import type { OpenAPIV3 } from 'openapi-types';
import { extractAuthzDescription } from './extract_authz_description';
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
import type { OasConverter } from './oas_converter';
import { isReferenceObject } from './oas_converter/common';
Expand Down Expand Up @@ -90,6 +91,13 @@ export const processVersionedRouter = (
];
}

let description = '';
if (route.options.security) {
const authzDescription = extractAuthzDescription(route.options.security);

description = `${route.options.description ?? ''}${authzDescription ?? ''}`;
}

const hasBody = Boolean(extractValidationSchemaFromVersionedHandler(handler)?.request?.body);
const contentType = extractContentType(route.options.options?.body);
const hasVersionFilter = Boolean(filters?.version);
Expand All @@ -98,6 +106,7 @@ export const processVersionedRouter = (
const operation: OpenAPIV3.OperationObject = {
summary: route.options.summary ?? '',
tags: route.options.options?.tags ? extractTags(route.options.options.tags) : [],
...(description ? { description } : {}),
...(route.options.description ? { description: route.options.description } : {}),
...(hasDeprecations ? { deprecated: true } : {}),
...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}),
Expand Down
3 changes: 3 additions & 0 deletions packages/kbn-router-to-openapispec/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { Router } from '@kbn/core-http-router-server-internal';
import type { OpenAPIV3 } from '../openapi-types';
export type { OpenAPIV3 } from '../openapi-types';
export interface KnownParameters {
Expand Down Expand Up @@ -39,3 +40,5 @@ export type CustomOperationObject = OpenAPIV3.OperationObject<{
// Custom OpenAPI from ES API spec based on @availability
'x-state'?: 'Technical Preview' | 'Beta';
}>;

export type InternalRouterRoute = ReturnType<Router['getRoutes']>[0];
2 changes: 1 addition & 1 deletion packages/kbn-router-to-openapispec/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
"@kbn/core-http-router-server-internal",
"@kbn/core-http-server",
"@kbn/config-schema",
"@kbn/zod"
"@kbn/zod",
]
}
Loading

0 comments on commit 8b7641e

Please sign in to comment.