chore: import DH V2 D5.6.4 production baseline

This commit is contained in:
DH V2
2026-09-05 10:12:35 -03:00
commit 82213e72f5
757 changed files with 84218 additions and 0 deletions
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { PhaseADataModule } from '../core-data/phase-a-data.module';
import { PermissionsGuard } from './guards/permissions.guard';
import { PermissionResolverService } from './permission-resolver.service';
@Module({
imports: [PhaseADataModule],
providers: [PermissionResolverService, PermissionsGuard],
exports: [PermissionResolverService, PermissionsGuard],
})
export class AuthorizationModule {}
@@ -0,0 +1,15 @@
import { SetMetadata } from '@nestjs/common';
export const REQUIRED_PERMISSIONS_KEY = 'requiredPermissions';
export function RequirePermissions(...permissions: string[]) {
const normalized = [
...new Set(permissions.map((permission) => permission.trim()).filter(Boolean)),
];
if (normalized.length === 0) {
throw new Error('RequirePermissions needs at least one permission code');
}
return SetMetadata(REQUIRED_PERMISSIONS_KEY, normalized);
}
@@ -0,0 +1,59 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../../auth/decorators/public.decorator';
import type { RequestWithContext } from '../../common/http/request-context';
import { REQUIRED_PERMISSIONS_KEY } from '../decorators/require-permissions.decorator';
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true;
const required =
this.reflector.getAllAndMerge<string[]>(REQUIRED_PERMISSIONS_KEY, [
context.getHandler(),
context.getClass(),
]) ?? [];
const uniqueRequired = [...new Set(required)];
if (uniqueRequired.length === 0) return true;
const request = context.switchToHttp().getRequest<RequestWithContext>();
if (!request.auth) {
throw new UnauthorizedException({
code: 'UNAUTHORIZED',
message: 'Autenticación requerida',
});
}
if (request.auth.mustChangePassword) {
throw new ForbiddenException({
code: 'PASSWORD_CHANGE_REQUIRED',
message: 'Debe cambiar la contraseña temporal antes de continuar',
});
}
const granted = new Set(request.auth.permissions);
const missing = uniqueRequired.filter((permission) => !granted.has(permission));
if (missing.length > 0) {
throw new ForbiddenException({
code: 'FORBIDDEN',
message: 'Permisos insuficientes',
});
}
return true;
}
}
@@ -0,0 +1,21 @@
import { Injectable } from '@nestjs/common';
import { RolesRepository } from '../core-data/repositories/roles.repository';
export interface ResolvedPermissions {
roles: string[];
permissions: string[];
}
@Injectable()
export class PermissionResolverService {
constructor(private readonly roles: RolesRepository) {}
async resolveForUser(userId: string): Promise<ResolvedPermissions> {
const [roles, permissions] = await Promise.all([
this.roles.findRoleCodesForUser(userId),
this.roles.findPermissionCodesForUser(userId),
]);
return { roles, permissions };
}
}