vulcain-corejs
Version:
Vulcain micro-service framework
164 lines • 6.12 kB
JavaScript
"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