New projects created: - michangarrito (marketplace mobile) - template-saas (SaaS template) - clinica-dental (dental ERP) - clinica-veterinaria (veterinary ERP) Architecture updates: - Move catalog from core/ to shared/ - Add MCP servers structure and templates - Add git management scripts - Update SUBREPOSITORIOS.md with 15 new repos - Update .gitignore for new projects Repository infrastructure: - 4 main repositories - 11 subrepositorios - Gitea remotes configured 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
90 lines
2.4 KiB
TypeScript
90 lines
2.4 KiB
TypeScript
/**
|
|
* ROLES GUARD - REFERENCE IMPLEMENTATION
|
|
*
|
|
* @description Guard de autorización basado en roles (RBAC).
|
|
* Verifica que el usuario tenga uno de los roles requeridos.
|
|
*
|
|
* @usage
|
|
* ```typescript
|
|
* @Get('admin')
|
|
* @UseGuards(JwtAuthGuard, RolesGuard)
|
|
* @Roles('admin', 'super_admin')
|
|
* adminOnly(@Request() req) {
|
|
* return { message: 'Admin data' };
|
|
* }
|
|
* ```
|
|
*
|
|
* @origin gamilit/apps/backend/src/modules/auth/guards/roles.guard.ts
|
|
*/
|
|
|
|
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
|
|
import { Reflector } from '@nestjs/core';
|
|
|
|
/**
|
|
* Metadata key para roles requeridos
|
|
*/
|
|
export const ROLES_KEY = 'roles';
|
|
|
|
@Injectable()
|
|
export class RolesGuard implements CanActivate {
|
|
constructor(private readonly reflector: Reflector) {}
|
|
|
|
/**
|
|
* Verificar si el usuario tiene los roles requeridos
|
|
*
|
|
* @description
|
|
* 1. Obtiene los roles requeridos del decorador @Roles()
|
|
* 2. Si no hay roles definidos, permite el acceso
|
|
* 3. Compara el rol del usuario con los roles permitidos
|
|
*/
|
|
canActivate(context: ExecutionContext): boolean {
|
|
// Obtener roles requeridos de handler y class
|
|
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
|
|
context.getHandler(),
|
|
context.getClass(),
|
|
]);
|
|
|
|
// Si no hay roles definidos, permitir acceso
|
|
if (!requiredRoles || requiredRoles.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
// Obtener usuario del request (inyectado por JwtAuthGuard)
|
|
const { user } = context.switchToHttp().getRequest();
|
|
|
|
if (!user || !user.role) {
|
|
throw new ForbiddenException('Usuario sin rol asignado');
|
|
}
|
|
|
|
// Verificar si el usuario tiene alguno de los roles requeridos
|
|
const hasRole = requiredRoles.includes(user.role);
|
|
|
|
if (!hasRole) {
|
|
throw new ForbiddenException(
|
|
`Acceso denegado. Roles requeridos: ${requiredRoles.join(', ')}`,
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// ============ DECORADOR ROLES ============
|
|
|
|
import { SetMetadata } from '@nestjs/common';
|
|
|
|
/**
|
|
* Decorador para definir roles requeridos en una ruta
|
|
*
|
|
* @param roles - Lista de roles que pueden acceder
|
|
*
|
|
* @usage
|
|
* ```typescript
|
|
* @Roles('admin', 'teacher')
|
|
* @UseGuards(JwtAuthGuard, RolesGuard)
|
|
* @Get('teachers')
|
|
* getTeacherData() { ... }
|
|
* ```
|
|
*/
|
|
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
|