UNPKG

@elsikora/nestjs-crud-automator

Version:

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

116 lines (113 loc) 5.88 kB
import { __decorate, __metadata } from '../../../external/tslib/tslib.es6.js'; import { ApiAuthorizationRuntime } from './runtime.class.js'; import { AUTHORIZATION_DECISION_METADATA_CONSTANT } from '../../../constant/class/authorization/metadata-decision.constant.js'; import { CONTROLLER_API_DECORATOR_CONSTANT } from '../../../constant/decorator/api/controller.constant.js'; import '../../../constant/decorator/api/function.constant.js'; import { METHOD_API_DECORATOR_CONSTANT } from '../../../constant/decorator/api/method.constant.js'; import '../../../enum/class/authorization/cache-mode.enum.js'; import '../../../enum/class/authorization/decision-type.enum.js'; import { EApiPolicyEffect } from '../../../enum/class/authorization/effect.enum.js'; import '../../../enum/class/authorization/mode.enum.js'; import '../../../enum/class/authorization/permission-match.enum.js'; import '../../../enum/class/authorization/policy/attachment-type.enum.js'; import '../../../enum/class/authorization/policy/on-type.enum.js'; import '../../../enum/class/authorization/policy/source-type.enum.js'; import '../../../enum/class/authorization/policy/status.enum.js'; import '../../../enum/class/authorization/principal-type.enum.js'; import { Injectable, ForbiddenException } from '@nestjs/common'; import { ErrorException } from '../../../utility/error/exception.utility.js'; import { LoggerUtility } from '../../../utility/logger.utility.js'; const authorizationGuardLogger = LoggerUtility.getLogger("ApiAuthorizationGuard"); let 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 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 === EApiPolicyEffect.DENY) { authorizationGuardLogger.warn(`Access denied for entity "${entityConstructor.name}" action "${action}" principal "${decision.principal.id}"`); throw new ForbiddenException(); } authorizationGuardLogger.verbose(`Access granted for entity "${entityConstructor.name}" action "${action}" principal "${decision.principal.id}"`); return true; } attachDecisionToRequest(request, decision) { request.authorizationDecision = decision; request[AUTHORIZATION_DECISION_METADATA_CONSTANT.REQUEST_KEY] = decision; } isControllerSecurable(context) { return Boolean(Reflect.getMetadata(CONTROLLER_API_DECORATOR_CONSTANT.SECURABLE_METADATA_KEY, context.getClass())); } resolveControllerProperties(context) { return Reflect.getMetadata(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_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 ErrorException(`ApiControllerSecurable handler "${handlerName}" requires method-level ApiRouteMetadata`); } }; ApiAuthorizationGuard = __decorate([ Injectable(), __metadata("design:paramtypes", [ApiAuthorizationRuntime]) ], ApiAuthorizationGuard); export { ApiAuthorizationGuard }; //# sourceMappingURL=guard.class.js.map