UNPKG

vulcain-corejs

Version:
164 lines 6.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const system_1 = require("../globals/system"); const applicationRequestError_1 = require("../pipeline/errors/applicationRequestError"); const annotations_1 = require("../di/annotations"); const tokenService_1 = require("./services/tokenService"); /** * User context * * @export * @interface SecurityContext */ class SecurityContext { constructor(container, scopePolicy) { this.scopePolicy = scopePolicy; this.strategies = new Map(); // Default this.addOrReplaceStrategy(new tokenService_1.TokenService()); let strategies = container.getList(annotations_1.DefaultServiceNames.AuthenticationStrategy); for (let strategy of strategies) { this.addOrReplaceStrategy(strategy); } } addOrReplaceStrategy(strategy) { this.strategies.set(strategy.name.toLowerCase(), strategy); } /** * Get user scopes * * @readonly * @type {Array<string>} */ get scopes() { return this._isAnonymous || !this._scopes ? SecurityContext.EmptyScopes : this._scopes; } get isAnonymous() { return this._isAnonymous; } getClaims() { return this.claims; } setTenant(tenantOrCtx) { if (typeof tenantOrCtx === "string") { this.tenant = tenantOrCtx; } else if (tenantOrCtx) { this.tenant = tenantOrCtx.tenant; this.name = tenantOrCtx.name; this._scopes = tenantOrCtx.scopes; this.claims = tenantOrCtx.claims; } else this.tenant = system_1.Service.defaultTenant; } async process(ctx) { let authorization = ctx.request.headers['authorization']; // Perhaps in cookies if (!authorization) authorization = this.findInCookie(ctx); if (!authorization) { // Anonymous this.name = "Anonymous"; this._isAnonymous = true; this.claims = {}; ctx.logInfo(() => `No authentication context: User access is anonymous `); return; } let parts = authorization.split(' '); if (parts.length < 2) { throw new applicationRequestError_1.UnauthorizedRequestError("Invalid authorization header : " + authorization); } let scheme = parts[0], token = parts[1]; for (let [name, strategy] of this.strategies.entries()) { if (!scheme || scheme.substr(0, name.length).toLowerCase() !== name) continue; if (!token) { throw new applicationRequestError_1.UnauthorizedRequestError("Invalid authorization header."); } try { let userContext = await strategy.verifyToken(ctx, token, this.tenant); if (userContext) { this.name = userContext.name; this._scopes = userContext.scopes; this.tenant = userContext.tenant || this.tenant; this.bearer = userContext.bearer; // Assign all other fields as claims this.claims = userContext.claims || {}; Object.keys(userContext).forEach(k => { if (SecurityContext.UserFields.indexOf(k) < 0) this.claims[k] = userContext[k]; }); // For context propagation if (strategy instanceof tokenService_1.TokenService) this.bearer = token; ctx.logInfo(() => `User ${this.name} authenticated with tenant ${this.tenant}, scopes ${this.scopes}`); return; } } catch (err) { ctx.logError(err, () => `Authentication error ${err.message || err}`); throw err; } } throw new applicationRequestError_1.UnauthorizedRequestError(); } hasScope(handlerScope) { // this.logVerbose(() => `Check scopes [${this.scopes}] for user ${this.name} to handler scope ${handlerScope}`); return (!this._isAnonymous && (!handlerScope || handlerScope === "?")) || this.scopePolicy.hasScope(this, handlerScope); } getUserContext() { return { tenant: this.tenant, name: this.name, scopes: this._scopes, claims: this.claims }; } /** * Check if the current user is an admin * * @returns {boolean} */ get isAdmin() { return !this._isAnonymous && this.scopePolicy.isAdmin(this); } // From https://github.com/jshttp/cookie findInCookie(ctx) { function tryDecode(str) { try { return decodeURIComponent(str); } catch (e) { return str; } } const pairSplitRegExp = /; */; let cookies = ctx.request.headers.cookie; if (!cookies) return null; let pairs = cookies.split(pairSplitRegExp); for (let pair of pairs) { let eq_idx = pair.indexOf('='); // skip things that don't look like key=value if (eq_idx < 0) { continue; } let key = pair.substr(0, eq_idx).trim(); if (key !== "authorization") continue; let val = pair.substr(++eq_idx, pair.length).trim(); // quoted values if ('"' === val[0]) { val = val.slice(1, -1); } return tryDecode(val); } return null; } } SecurityContext.EmptyScopes = []; SecurityContext.UserFields = ["name", "scopes", "tenant", "bearer", "claims"]; exports.SecurityContext = SecurityContext; //# sourceMappingURL=securityContext.js.map