UNPKG

recoder-code

Version:

🚀 AI-powered development platform - Chat with 32+ models, build projects, automate workflows. Free models included!

225 lines • 9.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.security = security; const types_1 = require("../framework/types"); const defaultSecurityHandler = (req, scopes, schema) => true; function extractErrorsFromResults(results) { return results.map(result => { if (Array.isArray(result)) { return result.map(it => it).filter(it => !it.success); } return [result].filter(it => !it.success); }).flatMap(it => [...it]); } function didAllSecurityRequirementsPass(results) { return results.every(it => it.success); } function didOneSchemaPassValidation(results) { return results.some(result => Array.isArray(result) ? didAllSecurityRequirementsPass(result) : result.success); } function security(apiDoc, securityHandlers) { return async (req, res, next) => { var _a, _b, _c; // TODO move the following 3 check conditions to a dedicated upstream middleware if (!req.openapi) { // this path was not found in open api and // this path is not defined under an openapi base path // skip it return next(); } const openapi = req.openapi; // use the local security object or fallback to api doc's security or undefined const securities = (_a = openapi.schema.security) !== null && _a !== void 0 ? _a : apiDoc.security; const path = openapi.openApiRoute; if (!path || !Array.isArray(securities) || securities.length === 0) { return next(); } const securitySchemes = (_b = apiDoc.components) === null || _b === void 0 ? void 0 : _b.securitySchemes; if (!securitySchemes) { const message = `security referenced at path ${path}, but not defined in 'components.securitySchemes'`; return next(new types_1.InternalServerError({ path: path, message: message })); } try { const results = await new SecuritySchemes(securitySchemes, securityHandlers, securities).executeHandlers(req); // TODO handle AND'd and OR'd security // This assumes OR only! i.e. at least one security passed authentication const success = didOneSchemaPassValidation(results); if (success) { next(); } else { const errors = extractErrorsFromResults(results); throw errors[0]; } } catch (e) { const message = ((_c = e === null || e === void 0 ? void 0 : e.error) === null || _c === void 0 ? void 0 : _c.message) || 'unauthorized'; const err = types_1.HttpError.create({ status: e.status, path: path, message: message, }); /*const err = e.status == 500 ? new InternalServerError({ path: path, message: message }) : e.status == 403 ? new Forbidden({ path: path, message: message }) : new Unauthorized({ path: path, message: message });*/ next(err); } }; } class SecuritySchemes { constructor(securitySchemes, securityHandlers, securities) { this.securitySchemes = securitySchemes; this.securityHandlers = securityHandlers; this.securities = securities; } async executeHandlers(req) { // use a fallback handler if security handlers is not specified // This means if security handlers is specified, the user must define // all security handlers const fallbackHandler = !this.securityHandlers ? defaultSecurityHandler : null; const promises = this.securities.map(async (s) => { if (Util.isEmptyObject(s)) { // anonymous security return [{ success: true }]; } return Promise.all(Object.keys(s).map(async (securityKey) => { var _a, _b, _c; try { const scheme = this.securitySchemes[securityKey]; const handler = (_b = (_a = this.securityHandlers) === null || _a === void 0 ? void 0 : _a[securityKey]) !== null && _b !== void 0 ? _b : fallbackHandler; const scopesTmp = s[securityKey]; const scopes = Array.isArray(scopesTmp) ? scopesTmp : []; if (!scheme) { const message = `components.securitySchemes.${securityKey} does not exist`; throw new types_1.InternalServerError({ message }); } if (!scheme.hasOwnProperty('type')) { const message = `components.securitySchemes.${securityKey} must have property 'type'`; throw new types_1.InternalServerError({ message }); } if (!handler) { const message = `a security handler for '${securityKey}' does not exist`; throw new types_1.InternalServerError({ message }); } new AuthValidator(req, scheme, scopes).validate(); // expected handler results are: // - throw exception, // - return true, // - return Promise<true>, // - return false, // - return Promise<false> // everything else should be treated as false const securityScheme = scheme; const success = await handler(req, scopes, securityScheme); if (success === true) { return { success }; } else { throw Error(); } } catch (e) { return { success: false, status: (_c = e.status) !== null && _c !== void 0 ? _c : 401, error: e, }; } })); }); return Promise.all(promises); } } class AuthValidator { constructor(req, scheme, scopes = []) { const openapi = req.openapi; this.req = req; this.scheme = scheme; this.path = openapi.openApiRoute; this.scopes = scopes; } validate() { this.validateApiKey(); this.validateHttp(); this.validateOauth2(); this.validateOpenID(); } validateOauth2() { const { req, scheme, path } = this; if (['oauth2'].includes(scheme.type.toLowerCase())) { // TODO oauth2 validation } } validateOpenID() { const { req, scheme, path } = this; if (['openIdConnect'].includes(scheme.type.toLowerCase())) { // TODO openidconnect validation } } validateHttp() { var _a, _b; const { req, scheme, path } = this; if (['http'].includes(scheme.type.toLowerCase())) { const authHeader = req.headers['authorization'] && req.headers['authorization'].toLowerCase(); // req.cookies will be `undefined` without `cookie-parser` middleware const authCookie = ((_a = req.cookies) === null || _a === void 0 ? void 0 : _a[scheme.name]) || ((_b = req.signedCookies) === null || _b === void 0 ? void 0 : _b[scheme.name]); const type = scheme.scheme && scheme.scheme.toLowerCase(); if (type === 'bearer') { if (authHeader && !authHeader.includes('bearer')) { throw Error(`Authorization header with scheme 'Bearer' required`); } if (!authHeader && !authCookie) { if (scheme.in === 'cookie') { throw Error(`Cookie authentication required`); } else { throw Error(`Authorization header required`); } } } if (type === 'basic') { if (!authHeader) { throw Error(`Authorization header required`); } if (!authHeader.includes('basic')) { throw Error(`Authorization header with scheme 'Basic' required`); } } } } validateApiKey() { var _a; const { req, scheme, path } = this; if (scheme.type === 'apiKey') { if (scheme.in === 'header') { if (!req.headers[scheme.name.toLowerCase()]) { throw Error(`'${scheme.name}' header required`); } } else if (scheme.in === 'query') { if (!req.query[scheme.name]) { throw Error(`query parameter '${scheme.name}' required`); } } else if (scheme.in === 'cookie') { if (!req.cookies[scheme.name] && !((_a = req.signedCookies) === null || _a === void 0 ? void 0 : _a[scheme.name])) { throw Error(`cookie '${scheme.name}' required`); } } } } } class Util { static isEmptyObject(o) { return (typeof o === 'object' && Object.entries(o).length === 0 && o.constructor === Object); } } //# sourceMappingURL=openapi.security.js.map