UNPKG

@elsikora/nestjs-crud-automator

Version:

A library for automating the creation of CRUD operations in NestJS.

116 lines (113 loc) 5.99 kB
'use strict'; var tslib_es6 = require('../../../external/tslib/tslib.es6.js'); var runtime_class = require('./runtime.class.js'); var metadataDecision_constant = require('../../../constant/class/authorization/metadata-decision.constant.js'); var controller_constant = require('../../../constant/decorator/api/controller.constant.js'); require('../../../constant/decorator/api/function.constant.js'); var method_constant = require('../../../constant/decorator/api/method.constant.js'); require('../../../enum/class/authorization/cache-mode.enum.js'); require('../../../enum/class/authorization/decision-type.enum.js'); var effect_enum = require('../../../enum/class/authorization/effect.enum.js'); require('../../../enum/class/authorization/mode.enum.js'); require('../../../enum/class/authorization/permission-match.enum.js'); require('../../../enum/class/authorization/policy/attachment-type.enum.js'); require('../../../enum/class/authorization/policy/on-type.enum.js'); require('../../../enum/class/authorization/policy/source-type.enum.js'); require('../../../enum/class/authorization/policy/status.enum.js'); require('../../../enum/class/authorization/principal-type.enum.js'); var common = require('@nestjs/common'); var exception_utility = require('../../../utility/error/exception.utility.js'); var logger_utility = require('../../../utility/logger.utility.js'); const authorizationGuardLogger = logger_utility.LoggerUtility.getLogger("ApiAuthorizationGuard"); exports.ApiAuthorizationGuard = class ApiAuthorizationGuard { runtime; constructor(runtime) { this.runtime = runtime; } async canActivate(context) { if (!this.isControllerSecurable(context)) { authorizationGuardLogger.debug("Controller is not marked as @ApiControllerSecurable, skipping authorization"); return true; } const properties = this.resolveControllerProperties(context); if (!properties?.authorization) { authorizationGuardLogger.error("ApiControllerSecurable requires an authorization block in @ApiController(...)"); throw exception_utility.ErrorException("ApiControllerSecurable requires an authorization block in @ApiController(...)"); } const routeMetadata = this.resolveRouteMetadata(context); const entityConstructor = routeMetadata.resource.entity; const action = routeMetadata.resource.action; authorizationGuardLogger.verbose(`Evaluating authorization for entity "${entityConstructor.name}" action "${action}"`); const request = context.switchToHttp().getRequest(); const authenticationRequest = request; const requestMetadata = this.resolveRequestMetadata(request); const decision = await this.runtime.evaluate({ action, authenticationRequest, authorization: properties.authorization, entity: entityConstructor, requestMetadata, resource: undefined, routeAuthorization: routeMetadata.security?.authorization, routeType: routeMetadata.route.type, }); this.attachDecisionToRequest(request, decision); if (decision.effect === effect_enum.EApiPolicyEffect.DENY) { authorizationGuardLogger.warn(`Access denied for entity "${entityConstructor.name}" action "${action}" principal "${decision.principal.id}"`); throw new common.ForbiddenException(); } authorizationGuardLogger.verbose(`Access granted for entity "${entityConstructor.name}" action "${action}" principal "${decision.principal.id}"`); return true; } attachDecisionToRequest(request, decision) { request.authorizationDecision = decision; request[metadataDecision_constant.AUTHORIZATION_DECISION_METADATA_CONSTANT.REQUEST_KEY] = decision; } isControllerSecurable(context) { return Boolean(Reflect.getMetadata(controller_constant.CONTROLLER_API_DECORATOR_CONSTANT.SECURABLE_METADATA_KEY, context.getClass())); } resolveControllerProperties(context) { return Reflect.getMetadata(controller_constant.CONTROLLER_API_DECORATOR_CONSTANT.PROPERTIES_METADATA_KEY, context.getClass()); } resolveHeaders(headers) { if (!headers) { return undefined; } const resolvedHeaders = {}; for (const [key, value] of Object.entries(headers)) { if (typeof value === "string") { resolvedHeaders[key] = value; } if (Array.isArray(value)) { const resolvedHeaderValue = value.find((headerValue) => typeof headerValue === "string"); if (resolvedHeaderValue) { resolvedHeaders[key] = resolvedHeaderValue; } } } return resolvedHeaders; } resolveRequestMetadata(request) { return { body: request.body, headers: this.resolveHeaders(request.headers), ip: typeof request.ip === "string" ? request.ip : undefined, parameters: request.params, query: request.query, }; } resolveRouteMetadata(context) { const metadata = Reflect.getMetadata(method_constant.METHOD_API_DECORATOR_CONSTANT.ROUTE_METADATA_KEY, context.getHandler()); if (metadata) { return metadata; } const handlerName = context.getHandler().name || "unknown"; authorizationGuardLogger.error(`ApiControllerSecurable handler "${handlerName}" requires method-level ApiRouteMetadata`); throw exception_utility.ErrorException(`ApiControllerSecurable handler "${handlerName}" requires method-level ApiRouteMetadata`); } }; exports.ApiAuthorizationGuard = tslib_es6.__decorate([ common.Injectable(), tslib_es6.__metadata("design:paramtypes", [runtime_class.ApiAuthorizationRuntime]) ], exports.ApiAuthorizationGuard); //# sourceMappingURL=guard.class.js.map