import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AbilityFactory, Action, Subjects } from '../abilities/ability.factory'; /** * Interface for required permissions */ export interface RequiredPermission { action: Action; subject: Subjects; } /** * Metadata key for permissions */ export const CHECK_ABILITY = 'check_ability'; /** * Guard that checks CASL abilities */ @Injectable() export class AbilitiesGuard implements CanActivate { constructor( private reflector: Reflector, private abilityFactory: AbilityFactory, ) {} async canActivate(context: ExecutionContext): Promise { const requiredPermissions = this.reflector.get( CHECK_ABILITY, context.getHandler(), ) || []; // If no permissions required, allow access if (requiredPermissions.length === 0) { return true; } const request = context.switchToHttp().getRequest(); const user = request.user; // User should be attached by JwtAuthGuard if (!user) { throw new ForbiddenException('User not authenticated'); } // Build abilities for user const ability = this.abilityFactory.defineAbilitiesFor(user); // Check if user has all required permissions const hasPermission = requiredPermissions.every((permission) => ability.can(permission.action, permission.subject), ); if (!hasPermission) { throw new ForbiddenException( `User does not have required permissions`, ); } return true; } }