import { Injectable, ExecutionContext, UnauthorizedException, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { Reflector } from '@nestjs/core'; import { RolePermissions } from '@app/common/constants/role-permissions'; import { RoleType } from '@app/common/constants/role.type.enum'; @Injectable() export class PermissionsGuard extends AuthGuard('jwt') { constructor(private reflector: Reflector) { super(); } async canActivate(context: ExecutionContext): Promise { // First, run the AuthGuard logic to validate the JWT const isAuthenticated = await super.canActivate(context); if (!isAuthenticated) { return false; } // Authorization logic const requiredPermissions = this.reflector.get( 'permissions', context.getHandler(), ); if (!requiredPermissions) { return true; // Allow if no permissions are specified } const request = context.switchToHttp().getRequest(); const user = request.user; // User is now available after AuthGuard const userRole = user?.role?.type as RoleType; if (!userRole || !RolePermissions[userRole]) { throw new UnauthorizedException({ message: `Only ${this.getAllowedRoles(requiredPermissions)} role(s) can access this route.`, }); } const userPermissions = RolePermissions[userRole]; const hasRequiredPermissions = requiredPermissions.every((perm) => userPermissions.includes(perm), ); if (!hasRequiredPermissions) { throw new UnauthorizedException({ message: `Only ${this.getAllowedRoles(requiredPermissions)} role(s) can access this route.`, }); } return true; } private getAllowedRoles(requiredPermissions: string[]): string { const allowedRoles = Object.entries(RolePermissions) .filter(([, permissions]) => requiredPermissions.every((perm) => permissions.includes(perm)), ) .map(([role]) => role); return allowedRoles.join(', '); } }