UNPKG

@elsikora/nestjs-crud-automator

Version:

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

156 lines (153 loc) 6.7 kB
import { AUTHORIZATION_POLICY_DECORATOR_CONSTANT } from '../../../../constant/class/authorization/policy/decorator.constant.js'; import '../../../../external/@elsikora/cladi/dist/esm/domain/enum/logger-log-level.enum.js'; import '../../../../external/@elsikora/cladi/dist/esm/infrastructure/constant/console-logger-default-options.constant.js'; import { createRegistry } from '../../../../external/@elsikora/cladi/dist/esm/presentation/utility/create/registry.utility.js'; import { EApiRouteType } from '../../../../enum/decorator/api/route-type.enum.js'; import { GenerateEntityInformation } from '../../../../utility/generate-entity-information.utility.js'; import { LoggerUtility } from '../../../../utility/logger.utility.js'; import { ApiAuthorizationPolicyExecutor } from './executor.class.js'; const policyRegistryLogger = LoggerUtility.getLogger("ApiAuthorizationPolicyRegistry"); class ApiAuthorizationPolicyRegistry { POLICY_CACHE; POLICY_REGISTRY; constructor() { this.POLICY_CACHE = new Map(); this.POLICY_REGISTRY = createRegistry({}); } async buildAggregatedPolicy(entity, action) { const entityName = this.getEntityName(entity); const cacheKey = this.createCacheKey(entity, action); policyRegistryLogger.debug(`Building aggregated policy for entity "${entityName}" action "${action}" (cache key: ${cacheKey})`); const cachedPolicy = this.POLICY_CACHE.get(cacheKey); if (cachedPolicy) { policyRegistryLogger.debug(`Returning cached policy for "${cacheKey}"`); return cachedPolicy; } const registrationWrapper = this.POLICY_REGISTRY.get(entityName); const registrations = registrationWrapper?.registrations ?? []; policyRegistryLogger.debug(`Found ${registrations.length} registration(s) for entity "${entityName}"`); policyRegistryLogger.debug(`All registered entities: [${this.POLICY_REGISTRY.getAll() .map((wrapper) => wrapper.getName()) .join(", ")}]`); if (registrations.length === 0) { return undefined; } const entityMetadata = GenerateEntityInformation(entity); const routeType = this.resolveRouteType(action); const aggregatedRules = []; for (const registration of registrations) { const context = { action, entity, entityMetadata, routeType, }; const rules = await ApiAuthorizationPolicyExecutor.execute(registration.subscriber, action, context); if (rules.length === 0) { continue; } const normalizedRules = rules.map((rule) => this.normalizeRule(registration.policyId, registration.priority ?? 0, rule, action)); aggregatedRules.push(...normalizedRules); } if (aggregatedRules.length === 0) { return undefined; } aggregatedRules.sort((a, b) => b.priority - a.priority); const policyDescription = registrations.find((registration) => Boolean(registration.description))?.description; const policy = { action, description: policyDescription, entity, policyId: this.resolvePolicyId(entity), rules: aggregatedRules, }; this.cachePolicy(cacheKey, policy); return policy; } clear() { this.POLICY_CACHE.clear(); this.POLICY_REGISTRY.clear(); } registerSubscriber(registration) { const normalizedRegistration = { description: registration.description, entity: registration.entity, policyId: registration.policyId, priority: registration.priority ?? 0, subscriber: registration.subscriber, }; const entityName = this.getEntityName(normalizedRegistration.entity); policyRegistryLogger.verbose(`Registering policy subscriber for entity "${entityName}" with policyId "${normalizedRegistration.policyId}" and priority ${normalizedRegistration.priority}`); let wrapper = this.POLICY_REGISTRY.get(entityName); if (!wrapper) { wrapper = new PolicySubscriberWrapper(entityName); this.POLICY_REGISTRY.register(wrapper); } wrapper.addRegistration(normalizedRegistration); policyRegistryLogger.debug(`Total registrations for entity "${entityName}": ${wrapper.getRegistrationCount()}`); this.invalidateCacheForEntity(entityName); } cachePolicy(cacheKey, policy) { this.POLICY_CACHE.set(cacheKey, this.toBasePolicy(policy)); } createCacheKey(entity, action) { return `${this.getEntityName(entity)}::${action.toLowerCase()}`; } getEntityName(entity) { return (entity.name ?? "UnknownResource").toLowerCase(); } invalidateCacheForEntity(entityName) { for (const cacheKey of this.POLICY_CACHE.keys()) { if (cacheKey.startsWith(`${entityName}::`)) { this.POLICY_CACHE.delete(cacheKey); } } } normalizeRule(policyId, subscriberPriority, rule, action) { const rulePriority = rule.priority ?? 0; return { action, condition: rule.condition, description: rule.description, effect: rule.effect, policyId, priority: subscriberPriority + rulePriority, resultTransform: rule.resultTransform, scope: rule.scope, }; } resolvePolicyId(entity) { return `${this.getEntityName(entity)}${AUTHORIZATION_POLICY_DECORATOR_CONSTANT.DEFAULT_POLICY_ID_SUFFIX}`; } resolveRouteType(action) { const routeTypes = Object.values(EApiRouteType); return routeTypes.find((routeType) => routeType === action); } toBasePolicy(policy) { return policy; } } const apiAuthorizationPolicyRegistry = new ApiAuthorizationPolicyRegistry(); class PolicySubscriberWrapper { name; registrations; constructor(name, registrations = []) { this.name = name; this.registrations = registrations; } addRegistration(registration) { this.registrations.push(registration); this.registrations.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); } getName() { return this.getNormalizedName(); } getRegistrationCount() { return this.registrations.length; } getNormalizedName() { return this.name; } } export { ApiAuthorizationPolicyRegistry, apiAuthorizationPolicyRegistry }; //# sourceMappingURL=registry.class.js.map