mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-04 00:18:40 +01:00
Create endpoint that validates tokens for edge (#2039)
* Create new endpoint * Change edge url * Fix snapshot
This commit is contained in:
parent
0db2c08382
commit
ad546a054f
@ -111,6 +111,8 @@ import { proxyFeatureSchema } from './spec/proxy-feature-schema';
|
|||||||
import { proxyClientSchema } from './spec/proxy-client-schema';
|
import { proxyClientSchema } from './spec/proxy-client-schema';
|
||||||
import { proxyMetricsSchema } from './spec/proxy-metrics-schema';
|
import { proxyMetricsSchema } from './spec/proxy-metrics-schema';
|
||||||
import { setUiConfigSchema } from './spec/set-ui-config-schema';
|
import { setUiConfigSchema } from './spec/set-ui-config-schema';
|
||||||
|
import { edgeTokenSchema } from './spec/edge-token-schema';
|
||||||
|
import { validateEdgeTokensSchema } from './spec/validate-edge-tokens-schema';
|
||||||
|
|
||||||
// All schemas in `openapi/spec` should be listed here.
|
// All schemas in `openapi/spec` should be listed here.
|
||||||
export const schemas = {
|
export const schemas = {
|
||||||
@ -221,6 +223,8 @@ export const schemas = {
|
|||||||
proxyFeaturesSchema,
|
proxyFeaturesSchema,
|
||||||
proxyFeatureSchema,
|
proxyFeatureSchema,
|
||||||
proxyMetricsSchema,
|
proxyMetricsSchema,
|
||||||
|
edgeTokenSchema,
|
||||||
|
validateEdgeTokensSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Schemas must have an $id property on the form "#/components/schemas/mySchema".
|
// Schemas must have an $id property on the form "#/components/schemas/mySchema".
|
||||||
|
27
src/lib/openapi/spec/edge-token-schema.ts
Normal file
27
src/lib/openapi/spec/edge-token-schema.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
|
import { ApiTokenType } from '../../types/models/api-token';
|
||||||
|
|
||||||
|
export const edgeTokenSchema = {
|
||||||
|
$id: '#/components/schemas/edgeTokenSchema',
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['token', 'projects', 'type'],
|
||||||
|
properties: {
|
||||||
|
projects: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
enum: Object.values(ApiTokenType),
|
||||||
|
},
|
||||||
|
token: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type EdgeTokenSchema = FromSchema<typeof edgeTokenSchema>;
|
27
src/lib/openapi/spec/validate-edge-tokens-schema.ts
Normal file
27
src/lib/openapi/spec/validate-edge-tokens-schema.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
|
import { edgeTokenSchema } from './edge-token-schema';
|
||||||
|
|
||||||
|
export const validateEdgeTokensSchema = {
|
||||||
|
$id: '#/components/schemas/validateEdgeTokensSchema',
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['tokens'],
|
||||||
|
properties: {
|
||||||
|
tokens: {
|
||||||
|
type: 'array',
|
||||||
|
anyOf: [
|
||||||
|
{ items: { $ref: '#/components/schemas/edgeTokenSchema' } },
|
||||||
|
{ items: { type: 'string' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
schemas: {
|
||||||
|
edgeTokenSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type ValidateEdgeTokensSchema = FromSchema<
|
||||||
|
typeof validateEdgeTokensSchema
|
||||||
|
>;
|
@ -77,6 +77,7 @@ const OPENAPI_TAGS = [
|
|||||||
description:
|
description:
|
||||||
'Experimental endpoints that may change or disappear at any time.',
|
'Experimental endpoints that may change or disappear at any time.',
|
||||||
},
|
},
|
||||||
|
{ name: 'Edge', description: 'Endpoints related to Unleash on the Edge.' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// make the export mutable, so it can be used in a schema
|
// make the export mutable, so it can be used in a schema
|
||||||
|
69
src/lib/routes/edge-api/index.ts
Normal file
69
src/lib/routes/edge-api/index.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { Response } from 'express';
|
||||||
|
import Controller from '../controller';
|
||||||
|
import { IUnleashConfig, IUnleashServices } from '../../types';
|
||||||
|
import { Logger } from '../../logger';
|
||||||
|
import { NONE } from '../../types/permissions';
|
||||||
|
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||||
|
import { RequestBody } from '../unleash-types';
|
||||||
|
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||||
|
import {
|
||||||
|
validateEdgeTokensSchema,
|
||||||
|
ValidateEdgeTokensSchema,
|
||||||
|
} from '../../openapi/spec/validate-edge-tokens-schema';
|
||||||
|
import EdgeService from '../../services/edge-service';
|
||||||
|
import { OpenApiService } from '../../services/openapi-service';
|
||||||
|
|
||||||
|
export default class EdgeController extends Controller {
|
||||||
|
private readonly logger: Logger;
|
||||||
|
|
||||||
|
private edgeService: EdgeService;
|
||||||
|
|
||||||
|
private openApiService: OpenApiService;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
config: IUnleashConfig,
|
||||||
|
{
|
||||||
|
edgeService,
|
||||||
|
openApiService,
|
||||||
|
}: Pick<IUnleashServices, 'edgeService' | 'openApiService'>,
|
||||||
|
) {
|
||||||
|
super(config);
|
||||||
|
this.logger = config.getLogger('edge-api/index.ts');
|
||||||
|
this.edgeService = edgeService;
|
||||||
|
this.openApiService = openApiService;
|
||||||
|
|
||||||
|
this.route({
|
||||||
|
method: 'post',
|
||||||
|
path: '/validate',
|
||||||
|
handler: this.getValidTokens,
|
||||||
|
permission: NONE,
|
||||||
|
middleware: [
|
||||||
|
this.openApiService.validPath({
|
||||||
|
tags: ['Edge'],
|
||||||
|
operationId: 'getValidTokens',
|
||||||
|
requestBody: createRequestSchema(
|
||||||
|
'validateEdgeTokensSchema',
|
||||||
|
),
|
||||||
|
responses: {
|
||||||
|
200: createResponseSchema('validateEdgeTokensSchema'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getValidTokens(
|
||||||
|
req: RequestBody<ValidateEdgeTokensSchema>,
|
||||||
|
res: Response<ValidateEdgeTokensSchema>,
|
||||||
|
): Promise<void> {
|
||||||
|
const tokens = await this.edgeService.getValidTokens(
|
||||||
|
req.body.tokens as string[],
|
||||||
|
);
|
||||||
|
this.openApiService.respondWithValidation<ValidateEdgeTokensSchema>(
|
||||||
|
200,
|
||||||
|
res,
|
||||||
|
validateEdgeTokensSchema.$id,
|
||||||
|
tokens,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ const Controller = require('./controller');
|
|||||||
import { HealthCheckController } from './health-check';
|
import { HealthCheckController } from './health-check';
|
||||||
import ProxyController from './proxy-api';
|
import ProxyController from './proxy-api';
|
||||||
import { conditionalMiddleware } from '../middleware/conditional-middleware';
|
import { conditionalMiddleware } from '../middleware/conditional-middleware';
|
||||||
|
import EdgeController from './edge-api';
|
||||||
|
|
||||||
class IndexRouter extends Controller {
|
class IndexRouter extends Controller {
|
||||||
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
||||||
@ -37,6 +38,8 @@ class IndexRouter extends Controller {
|
|||||||
new ProxyController(config, services).router,
|
new ProxyController(config, services).router,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.use('/edge', new EdgeController(config, services).router);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,3 +11,7 @@ export interface IAuthRequest<
|
|||||||
logout: () => void;
|
logout: () => void;
|
||||||
session: any;
|
session: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RequestBody<T> extends Express.Request {
|
||||||
|
body: T;
|
||||||
|
}
|
||||||
|
40
src/lib/services/edge-service.ts
Normal file
40
src/lib/services/edge-service.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { IUnleashStores, IUnleashConfig } from '../types';
|
||||||
|
import { Logger } from '../logger';
|
||||||
|
import { IApiTokenStore } from '../types/stores/api-token-store';
|
||||||
|
import { EdgeTokenSchema } from '../openapi/spec/edge-token-schema';
|
||||||
|
import { constantTimeCompare } from '../util/constantTimeCompare';
|
||||||
|
import { ValidateEdgeTokensSchema } from '../openapi/spec/validate-edge-tokens-schema';
|
||||||
|
|
||||||
|
export default class EdgeService {
|
||||||
|
private logger: Logger;
|
||||||
|
|
||||||
|
private apiTokenStore: IApiTokenStore;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
{ apiTokenStore }: Pick<IUnleashStores, 'apiTokenStore'>,
|
||||||
|
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||||
|
) {
|
||||||
|
this.logger = getLogger('lib/services/edge-service.ts');
|
||||||
|
this.apiTokenStore = apiTokenStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getValidTokens(tokens: string[]): Promise<ValidateEdgeTokensSchema> {
|
||||||
|
const activeTokens = await this.apiTokenStore.getAllActive();
|
||||||
|
const edgeTokens = tokens.reduce((result: EdgeTokenSchema[], token) => {
|
||||||
|
const dbToken = activeTokens.find((activeToken) =>
|
||||||
|
constantTimeCompare(activeToken.secret, token),
|
||||||
|
);
|
||||||
|
if (dbToken) {
|
||||||
|
result.push({
|
||||||
|
token: token,
|
||||||
|
type: dbToken.type,
|
||||||
|
projects: dbToken.projects,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
return { tokens: edgeTokens };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EdgeService;
|
@ -34,6 +34,7 @@ import { ClientSpecService } from './client-spec-service';
|
|||||||
import { PlaygroundService } from './playground-service';
|
import { PlaygroundService } from './playground-service';
|
||||||
import { GroupService } from './group-service';
|
import { GroupService } from './group-service';
|
||||||
import { ProxyService } from './proxy-service';
|
import { ProxyService } from './proxy-service';
|
||||||
|
import EdgeService from './edge-service';
|
||||||
export const createServices = (
|
export const createServices = (
|
||||||
stores: IUnleashStores,
|
stores: IUnleashStores,
|
||||||
config: IUnleashConfig,
|
config: IUnleashConfig,
|
||||||
@ -98,6 +99,8 @@ export const createServices = (
|
|||||||
segmentService,
|
segmentService,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const edgeService = new EdgeService(stores, config);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessService,
|
accessService,
|
||||||
addonService,
|
addonService,
|
||||||
@ -132,6 +135,7 @@ export const createServices = (
|
|||||||
playgroundService,
|
playgroundService,
|
||||||
groupService,
|
groupService,
|
||||||
proxyService,
|
proxyService,
|
||||||
|
edgeService,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import { ClientSpecService } from '../services/client-spec-service';
|
|||||||
import { PlaygroundService } from 'lib/services/playground-service';
|
import { PlaygroundService } from 'lib/services/playground-service';
|
||||||
import { GroupService } from '../services/group-service';
|
import { GroupService } from '../services/group-service';
|
||||||
import { ProxyService } from '../services/proxy-service';
|
import { ProxyService } from '../services/proxy-service';
|
||||||
|
import EdgeService from '../services/edge-service';
|
||||||
|
|
||||||
export interface IUnleashServices {
|
export interface IUnleashServices {
|
||||||
accessService: AccessService;
|
accessService: AccessService;
|
||||||
@ -65,4 +66,5 @@ export interface IUnleashServices {
|
|||||||
clientSpecService: ClientSpecService;
|
clientSpecService: ClientSpecService;
|
||||||
playgroundService: PlaygroundService;
|
playgroundService: PlaygroundService;
|
||||||
proxyService: ProxyService;
|
proxyService: ProxyService;
|
||||||
|
edgeService: EdgeService;
|
||||||
}
|
}
|
||||||
|
@ -758,6 +758,34 @@ Object {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
"edgeTokenSchema": Object {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": Object {
|
||||||
|
"projects": Object {
|
||||||
|
"items": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
},
|
||||||
|
"token": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"type": Object {
|
||||||
|
"enum": Array [
|
||||||
|
"client",
|
||||||
|
"admin",
|
||||||
|
"frontend",
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": Array [
|
||||||
|
"token",
|
||||||
|
"projects",
|
||||||
|
"type",
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
"emailSchema": Object {
|
"emailSchema": Object {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
@ -3133,6 +3161,30 @@ Object {
|
|||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
},
|
},
|
||||||
|
"validateEdgeTokensSchema": Object {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": Object {
|
||||||
|
"tokens": Object {
|
||||||
|
"anyOf": Array [
|
||||||
|
Object {
|
||||||
|
"items": Object {
|
||||||
|
"$ref": "#/components/schemas/edgeTokenSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"items": Object {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"type": "array",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": Array [
|
||||||
|
"tokens",
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
"validatePasswordSchema": Object {
|
"validatePasswordSchema": Object {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": Object {
|
"properties": Object {
|
||||||
@ -6837,6 +6889,37 @@ If the provided project does not exist, the list of events will be empty.",
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"/edge/validate": Object {
|
||||||
|
"post": Object {
|
||||||
|
"operationId": "getValidTokens",
|
||||||
|
"requestBody": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/validateEdgeTokensSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "validateEdgeTokensSchema",
|
||||||
|
"required": true,
|
||||||
|
},
|
||||||
|
"responses": Object {
|
||||||
|
"200": Object {
|
||||||
|
"content": Object {
|
||||||
|
"application/json": Object {
|
||||||
|
"schema": Object {
|
||||||
|
"$ref": "#/components/schemas/validateEdgeTokensSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"description": "validateEdgeTokensSchema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"tags": Array [
|
||||||
|
"Edge",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"/health": Object {
|
"/health": Object {
|
||||||
"get": Object {
|
"get": Object {
|
||||||
"operationId": "getHealth",
|
"operationId": "getHealth",
|
||||||
@ -6907,6 +6990,10 @@ If the provided project does not exist, the list of events will be empty.",
|
|||||||
"description": "Create, update, and delete [context fields](https://docs.getunleash.io/user_guide/unleash_context) that Unleash is aware of.",
|
"description": "Create, update, and delete [context fields](https://docs.getunleash.io/user_guide/unleash_context) that Unleash is aware of.",
|
||||||
"name": "Context",
|
"name": "Context",
|
||||||
},
|
},
|
||||||
|
Object {
|
||||||
|
"description": "Endpoints related to Unleash on the Edge.",
|
||||||
|
"name": "Edge",
|
||||||
|
},
|
||||||
Object {
|
Object {
|
||||||
"description": "Create, update, delete, enable or disable [environments](https://docs.getunleash.io/user_guide/environments) for this Unleash instance.",
|
"description": "Create, update, delete, enable or disable [environments](https://docs.getunleash.io/user_guide/environments) for this Unleash instance.",
|
||||||
"name": "Environments",
|
"name": "Environments",
|
||||||
|
Loading…
Reference in New Issue
Block a user