import { CanActivate, ExecutionContext, ForbiddenException, Injectable, } from "@nestjs/common"; import { Reflector } from "@nestjs/core"; import { Request } from "express"; import { ABILITY_KEY } from "./require-ability.decorator"; import { Ability, Role, can } from "./abilities"; /** * Enforces the ability matrix (abilities.ts) against req.user.role. A route * with no @RequireAbility passes through untouched — this guard only gates the * routes that declare one. It does NOT check authentication; always list it * after AuthenticatedGuard so an unauthenticated request is rejected first. */ @Injectable() export class AbilityGuard implements CanActivate { constructor(private readonly reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const ability = this.reflector.getAllAndOverride( ABILITY_KEY, [context.getHandler(), context.getClass()], ); if (!ability) return true; const req = context.switchToHttp().getRequest(); const user = req.user as { role?: Role } | undefined; if (!user?.role || !can(user.role, ability)) { throw new ForbiddenException("No tiene permisos para esta acción"); } return true; } }