UNPKG

@loopback/authorization

Version:

A LoopBack component for authorization support.

111 lines 5.13 kB
"use strict"; // Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved. // Node module: @loopback/authorization // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthorizationInterceptor = void 0; const tslib_1 = require("tslib"); const core_1 = require("@loopback/core"); const security_1 = require("@loopback/security"); const debug_1 = tslib_1.__importDefault(require("debug")); const authorize_1 = require("./decorators/authorize"); const keys_1 = require("./keys"); const types_1 = require("./types"); const util_1 = require("./util"); const debug = (0, debug_1.default)('loopback:authorization:interceptor'); let AuthorizationInterceptor = class AuthorizationInterceptor { constructor(options = {}) { this.options = { defaultDecision: types_1.AuthorizationDecision.DENY, precedence: types_1.AuthorizationDecision.DENY, defaultStatusCodeForDeny: 403, ...options, }; debug('Authorization options', this.options); } value() { return this.intercept.bind(this); } async intercept(invocationCtx, next) { var _a; const description = debug.enabled ? invocationCtx.description : ''; let metadata = (0, authorize_1.getAuthorizationMetadata)(invocationCtx.target, invocationCtx.methodName); if (!metadata) { debug('No authorization metadata is found for %s', description); } metadata = metadata !== null && metadata !== void 0 ? metadata : this.options.defaultMetadata; if (!metadata || (metadata === null || metadata === void 0 ? void 0 : metadata.skip)) { debug('Authorization is skipped for %s', description); const result = await next(); return result; } debug('Authorization metadata for %s', description, metadata); // retrieve it from authentication module const user = await invocationCtx.get(security_1.SecurityBindings.USER, { optional: true, }); debug('Current user', user); const authorizationCtx = { principals: user ? [(0, util_1.createPrincipalFromUserProfile)(user)] : [], roles: [], scopes: [], resource: invocationCtx.targetName, invocationContext: invocationCtx, }; debug('Security context for %s', description, authorizationCtx); const authorizers = await loadAuthorizers(invocationCtx, (_a = metadata.voters) !== null && _a !== void 0 ? _a : []); let finalDecision = this.options.defaultDecision; for (const fn of authorizers) { const decision = await fn(authorizationCtx, metadata); debug('Decision', decision); // Reset the final decision if an explicit Deny or Allow is voted if (decision && decision !== types_1.AuthorizationDecision.ABSTAIN) { finalDecision = decision; } // we can add another interceptor to process the error if (decision === types_1.AuthorizationDecision.DENY && this.options.precedence === types_1.AuthorizationDecision.DENY) { debug('Access denied'); const error = new types_1.AuthorizationError('Access denied'); error.statusCode = this.options.defaultStatusCodeForDeny; throw error; } if (decision === types_1.AuthorizationDecision.ALLOW && this.options.precedence === types_1.AuthorizationDecision.ALLOW) { debug('Access allowed'); break; } } debug('Final decision', finalDecision); // Handle the final decision if (finalDecision === types_1.AuthorizationDecision.DENY) { const error = new types_1.AuthorizationError('Access denied'); error.statusCode = this.options.defaultStatusCodeForDeny; throw error; } return next(); } }; exports.AuthorizationInterceptor = AuthorizationInterceptor; exports.AuthorizationInterceptor = AuthorizationInterceptor = tslib_1.__decorate([ (0, core_1.injectable)((0, core_1.asGlobalInterceptor)('authorization')), tslib_1.__param(0, (0, core_1.config)({ fromBinding: keys_1.AuthorizationBindings.COMPONENT })), tslib_1.__metadata("design:paramtypes", [Object]) ], AuthorizationInterceptor); async function loadAuthorizers(ctx, authorizers) { const authorizerFunctions = []; const bindings = ctx.findByTag(keys_1.AuthorizationTags.AUTHORIZER); authorizers = authorizers.concat(bindings.map(b => b.key)); for (const keyOrFn of authorizers) { if (typeof keyOrFn === 'function') { authorizerFunctions.push(keyOrFn); } else { const fn = await ctx.get(keyOrFn); authorizerFunctions.push(fn); } } return authorizerFunctions; } //# sourceMappingURL=authorize-interceptor.js.map