@elsikora/nestjs-crud-automator
Version:
A library for automating the creation of CRUD operations in NestJS.
203 lines (200 loc) • 10.7 kB
JavaScript
import { __decorate, __param, __metadata } from '../../../external/tslib/tslib.es6.js';
import { ApiAuthorizationEngine } from './engine.class.js';
import './hook/permission/cache.class.js';
import { ApiAuthorizationHookPermissionResolver } from './hook/permission/resolver.class.js';
import './iam/attachment/cache.class.js';
import { ApiAuthorizationIamAttachmentResolver } from './iam/attachment/resolver.class.js';
import './iam/document/cache.class.js';
import { ApiAuthorizationIamDocumentResolver } from './iam/document/resolver.class.js';
import './iam/document/validator.class.js';
import { ApiAuthorizationIamEngine } from './iam/engine.class.js';
import './iam/query-planner.class.js';
import { AUTHORIZATION_AUDIT_SINK_TOKEN } from '../../../constant/class/authorization/audit-sink-token.constant.js';
import { AUTHORIZATION_PRINCIPAL_RESOLVER_TOKEN } from '../../../constant/class/authorization/principal-resolver-token.constant.js';
import { AUTHORIZATION_POLICY_REGISTRY_TOKEN } from '../../../constant/class/authorization/token-registry.constant.js';
import '../../../enum/class/authorization/cache-mode.enum.js';
import '../../../enum/class/authorization/decision-type.enum.js';
import '../../../enum/class/authorization/effect.enum.js';
import { EApiAuthorizationMode } from '../../../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 '../../../enum/decorator/api/route/subscriber-authorization-expectation.enum.js';
import { EApiRouteType } from '../../../enum/decorator/api/route/type.enum.js';
import { Injectable, Inject, Optional } from '@nestjs/common';
import { AuthorizationResolveDefaultPrincipal } from '../../../utility/authorization/resolve-default-principal.utility.js';
import 'typeorm';
import { ErrorException } from '../../../utility/error/exception.utility.js';
import { LoggerUtility } from '../../../utility/logger.utility.js';
const authorizationRuntimeLogger = LoggerUtility.getLogger("ApiAuthorizationRuntime");
let ApiAuthorizationRuntime = class ApiAuthorizationRuntime {
policyRegistry;
authorizationEngine;
hookPermissionResolver;
iamAttachmentResolver;
iamDocumentResolver;
iamEngine;
principalResolver;
auditSink;
constructor(policyRegistry, authorizationEngine, hookPermissionResolver, iamAttachmentResolver, iamDocumentResolver, iamEngine, principalResolver, auditSink) {
this.policyRegistry = policyRegistry;
this.authorizationEngine = authorizationEngine;
this.hookPermissionResolver = hookPermissionResolver;
this.iamAttachmentResolver = iamAttachmentResolver;
this.iamDocumentResolver = iamDocumentResolver;
this.iamEngine = iamEngine;
this.principalResolver = principalResolver;
this.auditSink = auditSink;
}
async evaluate(options) {
const principal = await this.resolvePrincipal(options.principal, options.authenticationRequest);
const mode = this.resolveMode(options.authorization, options.routeAuthorization);
authorizationRuntimeLogger.verbose(`Dispatching authorization evaluation for entity "${options.entity.name}" action "${options.action}" principal "${principal.id}" using mode "${mode}".`);
const decision = mode === EApiAuthorizationMode.HOOKS
? await this.evaluateHooks({
action: options.action,
authenticationRequest: options.authenticationRequest,
entity: options.entity,
principal,
requestMetadata: options.requestMetadata,
resource: options.resource,
routeType: options.routeType,
})
: await this.evaluateIam({
action: options.action,
authorization: options.authorization,
principal,
requestMetadata: options.requestMetadata,
resource: options.resource,
routeType: options.routeType,
});
if (this.auditSink) {
authorizationRuntimeLogger.verbose(`Recording authorization audit event for principal "${principal.id}" action "${options.action}".`);
await this.auditSink.record(decision);
}
authorizationRuntimeLogger.verbose(`Completed authorization evaluation for entity "${options.entity.name}" action "${options.action}" with effect "${decision.effect}".`);
return decision;
}
createImplicitDenyHookPolicy(entity, action) {
return {
action,
entity,
policyId: `${entity.name?.toLowerCase() ?? "unknown"}-hooks-policy`,
policyIds: [],
rules: [],
};
}
async evaluateHooks(options) {
const permissions = await this.hookPermissionResolver.resolve(options.principal);
authorizationRuntimeLogger.verbose(`Resolved ${permissions.length} hook permissions for principal "${options.principal.id}" on entity "${options.entity.name}".`);
const policy = (await this.policyRegistry.buildAggregatedPolicy(options.entity, options.action, {
authenticationRequest: options.authenticationRequest,
permissions,
principal: options.principal,
requestMetadata: options.requestMetadata,
routeType: options.routeType,
})) ?? this.createImplicitDenyHookPolicy(options.entity, options.action);
return await this.authorizationEngine.evaluate({
action: options.action,
mode: EApiAuthorizationMode.HOOKS,
permissions,
policy,
principal: options.principal,
resource: options.resource,
resourceType: options.entity.name ?? "UnknownResource",
});
}
async evaluateIam(options) {
if (!options.authorization.policyNamespace) {
authorizationRuntimeLogger.error("IAM authorization requires policyNamespace");
throw ErrorException("IAM authorization requires policyNamespace");
}
if (!options.authorization.resourceDefinition) {
authorizationRuntimeLogger.error("IAM authorization requires resourceDefinition");
throw ErrorException("IAM authorization requires resourceDefinition");
}
const attachments = await this.iamAttachmentResolver.resolve(options.principal);
authorizationRuntimeLogger.verbose(`Resolved ${attachments.attachments.length} IAM attachments and ${attachments.boundaries.length} boundaries for principal "${options.principal.id}".`);
const policyIds = [...attachments.attachments.map((attachment) => attachment.policyId), ...attachments.boundaries.map((attachment) => attachment.policyId)];
const documents = await this.iamDocumentResolver.resolve(policyIds);
authorizationRuntimeLogger.verbose(`Resolved ${documents.length} IAM policy documents for namespace "${options.authorization.policyNamespace}" and principal "${options.principal.id}".`);
return await this.iamEngine.evaluate({
action: this.resolveIamAction(options.authorization.policyNamespace, options.action, options.routeType),
attachments,
documents,
policyNamespace: options.authorization.policyNamespace,
principal: options.principal,
requestMetadata: options.requestMetadata,
resource: options.resource,
resourceDefinition: options.authorization.resourceDefinition,
});
}
resolveIamAction(policyNamespace, action, routeType) {
let resolvedAction;
switch (routeType) {
case EApiRouteType.CREATE: {
resolvedAction = "create";
break;
}
case EApiRouteType.DELETE: {
resolvedAction = "delete";
break;
}
case EApiRouteType.GET: {
resolvedAction = "read";
break;
}
case EApiRouteType.GET_LIST: {
resolvedAction = "list";
break;
}
case EApiRouteType.PARTIAL_UPDATE: {
resolvedAction = "update";
break;
}
case EApiRouteType.UPDATE: {
resolvedAction = "update";
break;
}
}
return resolvedAction ? `${policyNamespace}:${resolvedAction}` : `${policyNamespace}:${action}`;
}
resolveMode(authorization, routeAuthorization) {
const mode = routeAuthorization?.mode ?? authorization.defaultMode;
if (mode !== EApiAuthorizationMode.HOOKS && mode !== EApiAuthorizationMode.IAM) {
authorizationRuntimeLogger.error(`Unknown authorization mode "${String(mode)}"`);
throw ErrorException(`Unknown authorization mode "${String(mode)}"`);
}
return mode;
}
async resolvePrincipal(principal, authenticationRequest) {
if (principal) {
authorizationRuntimeLogger.verbose(`Using explicit authorization principal "${principal.id}".`);
return principal;
}
if (this.principalResolver) {
authorizationRuntimeLogger.verbose("Resolving authorization principal via configured principalResolver.");
return await this.principalResolver.resolve(authenticationRequest?.user, authenticationRequest);
}
authorizationRuntimeLogger.verbose("Resolving authorization principal via default principal resolver.");
return AuthorizationResolveDefaultPrincipal(authenticationRequest?.user);
}
};
ApiAuthorizationRuntime = __decorate([
Injectable(),
__param(0, Inject(AUTHORIZATION_POLICY_REGISTRY_TOKEN)),
__param(6, Inject(AUTHORIZATION_PRINCIPAL_RESOLVER_TOKEN)),
__param(6, Optional()),
__param(7, Inject(AUTHORIZATION_AUDIT_SINK_TOKEN)),
__param(7, Optional()),
__metadata("design:paramtypes", [Object, ApiAuthorizationEngine,
ApiAuthorizationHookPermissionResolver,
ApiAuthorizationIamAttachmentResolver,
ApiAuthorizationIamDocumentResolver,
ApiAuthorizationIamEngine, Object, Object])
], ApiAuthorizationRuntime);
export { ApiAuthorizationRuntime };
//# sourceMappingURL=runtime.class.js.map