UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.

128 lines 6.54 kB
import { apiTokenSchema, apiTokensSchema, createRequestSchema, createResponseSchema, emptyResponse, resourceCreatedResponseSchema, } from '../../../openapi/index.js'; import { getStandardResponses } from '../../../openapi/util/standard-responses.js'; import { ADMIN, CREATE_PROJECT_API_TOKEN, DELETE_PROJECT_API_TOKEN, READ_PROJECT_API_TOKEN, serializeDates, } from '../../../types/index.js'; import { ApiTokenType } from '../../../types/model.js'; import Controller from '../../controller.js'; import { timingSafeEqual } from 'crypto'; import { OperationDeniedError } from '../../../error/index.js'; import { createProjectApiToken } from '../../../schema/create-project-api-token-schema.js'; const PATH = '/:projectId/api-tokens'; const PATH_TOKEN = `${PATH}/:token`; export class ProjectApiTokenController extends Controller { constructor(config, { apiTokenService, accessService, frontendApiService, openApiService, projectService, }) { super(config); this.apiTokenService = apiTokenService; this.accessService = accessService; this.frontendApiService = frontendApiService; this.openApiService = openApiService; this.projectService = projectService; this.logger = config.getLogger('project-api-token-controller.js'); this.route({ method: 'get', path: PATH, handler: this.getProjectApiTokens, permission: READ_PROJECT_API_TOKEN, middleware: [ openApiService.validPath({ tags: ['Projects'], operationId: 'getProjectApiTokens', summary: 'Get api tokens for project.', description: 'Returns the project-specific [API tokens](https://docs.getunleash.io/reference/api-tokens) that have been created for this project.', responses: { 200: createResponseSchema('apiTokensSchema'), ...getStandardResponses(401, 403, 404), }, }), ], }); this.route({ method: 'post', path: PATH, handler: this.createProjectApiToken, permission: CREATE_PROJECT_API_TOKEN, middleware: [ openApiService.validPath({ tags: ['Projects'], operationId: 'createProjectApiToken', requestBody: createRequestSchema('createProjectApiTokenSchema'), summary: 'Create a project API token.', description: 'Endpoint that allows creation of [project API tokens](https://docs.getunleash.io/reference/api-tokens-and-client-keys#api-token-visibility) for the specified project.', responses: { 201: resourceCreatedResponseSchema('apiTokenSchema'), ...getStandardResponses(400, 401, 403, 404), }, }), ], }); this.route({ method: 'delete', path: PATH_TOKEN, handler: this.deleteProjectApiToken, acceptAnyContentType: true, permission: DELETE_PROJECT_API_TOKEN, middleware: [ openApiService.validPath({ tags: ['Projects'], operationId: 'deleteProjectApiToken', summary: 'Delete a project API token.', description: `This operation deletes the API token specified in the request URL. If the token doesn't exist, returns an OK response (status code 200).`, responses: { 200: emptyResponse, ...getStandardResponses(400, 401, 403, 404), }, }), ], }); } async getProjectApiTokens(req, res) { const { user } = req; const { projectId } = req.params; const project = await this.projectService.getProject(projectId); // Validates that the project exists const projectTokens = await this.accessibleTokens(user, projectId); this.openApiService.respondWithValidation(200, res, apiTokensSchema.$id, { tokens: serializeDates(projectTokens) }); } async createProjectApiToken(req, res) { const createToken = await createProjectApiToken.validateAsync(req.body); const { projectId } = req.params; await this.projectService.getProject(projectId); // Validates that the project exists const permissionRequired = CREATE_PROJECT_API_TOKEN; const hasPermission = await this.accessService.hasPermission(req.user, permissionRequired, projectId); if (!hasPermission) { throw new OperationDeniedError(`You don't have the necessary access [${permissionRequired}] to perform this operation]`); } const token = await this.apiTokenService.createApiTokenWithProjects({ ...createToken, projects: [projectId] }, req.audit); this.openApiService.respondWithValidation(201, res, apiTokenSchema.$id, serializeDates(token), { location: `api-tokens` }); } async deleteProjectApiToken(req, res) { const { user } = req; const { projectId, token } = req.params; const storedToken = (await this.accessibleTokens(user, projectId)).find((currentToken) => this.tokenEquals(currentToken.secret, token)); if (storedToken && (storedToken.project === projectId || (storedToken.projects.length === 1 && storedToken.project[0] === projectId))) { await this.apiTokenService.delete(token, req.audit); await this.frontendApiService.deleteClientForFrontendApiToken(token); res.status(200).end(); } else if (!storedToken) { res.status(404).end(); } else { res.status(400).end(); } } tokenEquals(token1, token2) { return (token1.length === token2.length && timingSafeEqual(Buffer.from(token1), Buffer.from(token2))); } async accessibleTokens(user, project) { const allTokens = await this.apiTokenService.getAllTokens(); if (user.isAPI && user.permissions.includes(ADMIN)) { return allTokens; } return allTokens.filter((token) => token.type !== ApiTokenType.ADMIN && (token.project === project || token.projects.includes(project))); } } //# sourceMappingURL=api-token.js.map