[kbn/server-route-repository] Make security required (#216196)

To mirror the changes in https://github.com/elastic/kibana/pull/215180
This commit is contained in:
Milton Hultgren 2025-04-09 12:51:54 +02:00 committed by GitHub
parent 4f79e2480a
commit 53263fd9fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 81 additions and 15 deletions

View file

@ -11,6 +11,13 @@ import * as t from 'io-ts';
import { CoreSetup } from '@kbn/core-lifecycle-browser';
import { createRepositoryClient } from './create_repository_client';
const disabledAuthz = {
authz: {
enabled: false as const,
reason: 'This is a test',
},
};
describe('createRepositoryClient', () => {
const getMock = jest.fn();
const coreSetupMock = {
@ -28,6 +35,7 @@ describe('createRepositoryClient', () => {
'GET /internal/handler': {
endpoint: 'GET /internal/handler',
handler: jest.fn().mockResolvedValue('OK'),
security: disabledAuthz,
},
};
const { fetch } = createRepositoryClient<typeof repository>(coreSetupMock);
@ -47,6 +55,7 @@ describe('createRepositoryClient', () => {
'GET /api/handler 2024-08-05': {
endpoint: 'GET /api/handler 2024-08-05',
handler: jest.fn().mockResolvedValue('OK'),
security: disabledAuthz,
},
};
const { fetch } = createRepositoryClient<typeof repository>(coreSetupMock);
@ -66,6 +75,7 @@ describe('createRepositoryClient', () => {
'GET /internal/handler': {
endpoint: 'GET /internal/handler',
handler: jest.fn().mockResolvedValue('OK'),
security: disabledAuthz,
},
};
const { fetch } = createRepositoryClient<typeof repository>(coreSetupMock);
@ -97,6 +107,7 @@ describe('createRepositoryClient', () => {
}),
}),
handler: jest.fn().mockResolvedValue('OK'),
security: disabledAuthz,
},
};
const { fetch } = createRepositoryClient<typeof repository>(coreSetupMock);
@ -127,6 +138,7 @@ describe('createRepositoryClient', () => {
}),
}),
handler: jest.fn().mockResolvedValue('OK'),
security: disabledAuthz,
},
};
const { fetch } = createRepositoryClient<typeof repository>(coreSetupMock);
@ -159,6 +171,7 @@ describe('createRepositoryClient', () => {
}),
}),
handler: jest.fn().mockResolvedValue('OK'),
security: disabledAuthz,
},
};
const { fetch } = createRepositoryClient<typeof repository>(coreSetupMock);

View file

@ -151,7 +151,7 @@ export type CreateServerRouteFactory<
endpoint: ValidateEndpoint<TEndpoint, TRouteAccess> extends true ? TEndpoint : never;
handler: ServerRouteHandler<TRouteHandlerResources, TRouteParamsRT, TReturnType>;
params?: TRouteParamsRT;
security?: RouteSecurity;
security: RouteSecurity;
} & Required<
{
options?: (TRouteCreateOptions extends DefaultRouteCreateOptions ? TRouteCreateOptions : {}) &
@ -181,7 +181,7 @@ export type ServerRoute<
> = {
endpoint: TEndpoint;
handler: ServerRouteHandler<TRouteHandlerResources, TRouteParamsRT, TReturnType>;
security?: RouteSecurity;
security: RouteSecurity;
} & (TRouteParamsRT extends RouteParamsRT ? { params: TRouteParamsRT } : {}) &
(TRouteCreateOptions extends DefaultRouteCreateOptions ? { options: TRouteCreateOptions } : {});

View file

@ -17,6 +17,13 @@ import { registerRoutes } from './register_routes';
import { passThroughValidationObject, noParamsValidationObject } from './validation_objects';
import { ServerRouteRepository } from '@kbn/server-route-repository-utils';
const disabledAuthz = {
authz: {
enabled: false as const,
reason: 'This is a test',
},
};
describe('registerRoutes', () => {
const post = jest.fn();
const postAddVersion = jest.fn();
@ -55,10 +62,12 @@ describe('registerRoutes', () => {
'POST /internal/route': {
endpoint: 'POST /internal/route',
handler: jest.fn(),
security: disabledAuthz,
},
'POST /api/public_route version': {
endpoint: 'POST /api/public_route version',
handler: jest.fn(),
security: disabledAuthz,
},
'POST /api/internal_but_looks_like_public version': {
endpoint: 'POST /api/internal_but_looks_like_public version',
@ -66,6 +75,7 @@ describe('registerRoutes', () => {
access: 'internal',
},
handler: jest.fn(),
security: disabledAuthz,
},
'POST /internal/route_with_security': {
endpoint: `POST /internal/route_with_security`,

View file

@ -192,12 +192,7 @@ export function registerRoutes<TDependencies extends Record<string, any>>({
...options,
access,
},
security: security ?? {
authz: {
enabled: false,
reason: 'Delegates authorization to the es client',
},
},
security,
validate: validationObject,
},
wrappedHandler
@ -209,12 +204,7 @@ export function registerRoutes<TDependencies extends Record<string, any>>({
summary: options.summary,
description: options.description,
options: omit(options, 'access', 'description', 'summary', 'deprecated', 'discontinued'),
security: security ?? {
authz: {
enabled: false,
reason: 'Delegates authorization to the es client',
},
},
security,
}).addVersion(
{
version,

View file

@ -15,6 +15,13 @@ import { Observable, of } from 'rxjs';
import { createServerRouteFactory } from './create_server_route_factory';
import { decodeRequestParams } from './decode_request_params';
const disabledAuthz = {
authz: {
enabled: false as const,
reason: 'This is a test',
},
};
function assertType<TShape = never>(value: TShape) {
return value;
}
@ -27,6 +34,7 @@ createServerRouteFactory<{}, {}>()({
// @ts-expect-error Argument of type '{}' is not assignable to parameter of type '{ params: any; }'.
assertType<{ params: any }>(resources);
},
security: disabledAuthz,
});
// If a params codec is set, its type _should_ be available in the
@ -41,6 +49,7 @@ createServerRouteFactory<{}, {}>()({
handler: async (resources) => {
assertType<{ params: { path: { serviceName: string } } }>(resources);
},
security: disabledAuthz,
});
createServerRouteFactory<{}, {}>()({
@ -53,6 +62,7 @@ createServerRouteFactory<{}, {}>()({
handler: async (resources) => {
assertType<{ params: { path: { serviceName: string } } }>(resources);
},
security: disabledAuthz,
});
// Resources should be passed to the request handler.
@ -67,6 +77,7 @@ createServerRouteFactory<{ context: { getSpaceId: () => string } }, {}>()({
const spaceId = context.getSpaceId();
assertType<string>(spaceId);
},
security: disabledAuthz,
});
createServerRouteFactory<{ context: { getSpaceId: () => string } }, {}>()({
@ -80,6 +91,7 @@ createServerRouteFactory<{ context: { getSpaceId: () => string } }, {}>()({
const spaceId = context.getSpaceId();
assertType<string>(spaceId);
},
security: disabledAuthz,
});
// Create options are available when registering a route.
@ -93,6 +105,7 @@ createServerRouteFactory<{}, {}>()({
handler: async (resources) => {
assertType<{ params: { path: { serviceName: string } } }>(resources);
},
security: disabledAuthz,
});
// Public APIs should be versioned
@ -101,6 +114,7 @@ createServerRouteFactory<{}, { tags: string[] }>()({
endpoint: 'GET /api/endpoint_with_params',
tags: [],
handler: async (resources) => {},
security: disabledAuthz,
});
// `access` is respected
@ -111,6 +125,7 @@ createServerRouteFactory<{}, { tags: string[] }>()({
access: 'internal',
},
handler: async (resources) => {},
security: disabledAuthz,
});
// specifying additional options makes them required
@ -118,6 +133,7 @@ createServerRouteFactory<{}, { tags: string[] }>()({
createServerRouteFactory<{}, { tags: string[] }>()({
endpoint: 'GET /api/endpoint_with_params 2023-10-31',
handler: async (resources) => {},
security: disabledAuthz,
});
createServerRouteFactory<{}, { tags: string[] }>()({
@ -126,6 +142,7 @@ createServerRouteFactory<{}, { tags: string[] }>()({
tags: [],
},
handler: async (resources) => {},
security: disabledAuthz,
});
// cannot return observables that are not in the SSE structure
@ -135,6 +152,7 @@ const route = createServerRouteFactory<{}, {}>()({
handler: async () => {
return of({ streamed_response: true });
},
security: disabledAuthz,
});
const createServerRoute = createServerRouteFactory<{}, {}>();
@ -147,6 +165,7 @@ const repository = {
noParamsForMe: true,
};
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'GET /internal/endpoint_with_params',
@ -160,6 +179,7 @@ const repository = {
yesParamsForMe: true,
};
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'GET /internal/endpoint_with_optional_params',
@ -173,6 +193,7 @@ const repository = {
someParamsForMe: true,
};
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'GET /internal/endpoint_with_params_zod',
@ -186,6 +207,7 @@ const repository = {
yesParamsForMe: true,
};
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'GET /internal/endpoint_with_optional_params_zod',
@ -203,6 +225,7 @@ const repository = {
someParamsForMe: true,
};
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'GET /internal/endpoint_returning_result',
@ -211,6 +234,7 @@ const repository = {
result: true,
};
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'GET /internal/endpoint_returning_kibana_response',
@ -221,12 +245,14 @@ const repository = {
},
});
},
security: disabledAuthz,
}),
...createServerRoute({
endpoint: 'POST /internal/endpoint_returning_observable',
handler: async () => {
return of({ type: 'foo' as const, streamed_response: true });
},
security: disabledAuthz,
}),
};

View file

@ -25,6 +25,13 @@ export const listStreamsRoute = createServerRoute({
access: 'internal',
},
params: z.object({}),
security: {
authz: {
enabled: false,
reason:
'This API delegates security to the currently logged in user and their Elasticsearch permissions.',
},
},
handler: async ({ request, getScopedClients }): Promise<{ streams: ListStreamDetail[] }> => {
const { streamsClient, scopedClusterClient } = await getScopedClients({ request });
const streams = await streamsClient.listStreams();

View file

@ -15,6 +15,13 @@ import type { APMRouteHandlerResources } from './register_apm_server_routes';
import { registerRoutes } from './register_apm_server_routes';
import { NEVER } from 'rxjs';
const disabledAuthz = {
authz: {
enabled: false as const,
reason: 'This is a test',
},
};
type RegisterRouteDependencies = Parameters<typeof registerRoutes>[0];
const getRegisterRouteDependencies = () => {
@ -223,6 +230,7 @@ describe('createApi', () => {
endpoint: 'GET /foo',
options: { tags: [] },
handler: handlerMock,
security: disabledAuthz,
},
]);
@ -256,6 +264,7 @@ describe('createApi', () => {
tags: [],
},
handler: handlerMock,
security: disabledAuthz,
},
]);
await simulateRequest({
@ -282,7 +291,14 @@ describe('createApi', () => {
const {
simulateRequest,
mocks: { response },
} = initApi([{ endpoint: 'GET /foo', options: { tags: [] }, handler: handlerMock }]);
} = initApi([
{
endpoint: 'GET /foo',
options: { tags: [] },
handler: handlerMock,
security: disabledAuthz,
},
]);
await simulateRequest({
method: 'get',
pathname: '/foo',
@ -309,6 +325,7 @@ describe('createApi', () => {
endpoint: 'GET /foo',
options: { tags: [] },
handler: jest.fn().mockResolvedValue({}),
security: disabledAuthz,
},
]);
@ -354,6 +371,7 @@ describe('createApi', () => {
}),
}),
handler: handlerMock,
security: disabledAuthz,
},
]);
@ -428,6 +446,7 @@ describe('createApi', () => {
body: t.string,
}),
handler: handlerMock,
security: disabledAuthz,
},
]);
@ -477,6 +496,7 @@ describe('createApi', () => {
}),
}),
handler: handlerMock,
security: disabledAuthz,
},
]);