@elsikora/nestjs-crud-automator
Version:
A library for automating the creation of CRUD operations in NestJS.
116 lines (113 loc) • 5.99 kB
JavaScript
;
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