All files / src/common/guards roles.guard.ts

100% Statements 27/27
93.75% Branches 15/16
100% Functions 3/3
100% Lines 24/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 6320x 20x 20x     20x 90x   90x     23x           23x 1x       22x       22x     22x 1x 1x         21x         21x 1x 1x       20x 32x   20x 2x       2x         18x    
import { Injectable, CanActivate, ExecutionContext, ForbiddenException, Logger } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
 
@Injectable()
export class RolesGuard implements CanActivate {
  private readonly logger = new Logger(RolesGuard.name);
 
  constructor(private readonly reflector: Reflector) {}
 
  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
      context.getHandler(),
      context.getClass(),
    ]);
    
    // No roles required - allow access
    if (!requiredRoles || requiredRoles.length === 0) {
      return true;
    }
    
    // Extract user from context (support both HTTP and GraphQL)
    const request = context.getType() === 'http'
      ? context.switchToHttp().getRequest()
      : GqlExecutionContext.create(context).getContext().req;
    
    const user = request.user;
    
    // User must be authenticated
    if (!user) {
      this.logger.warn('RolesGuard: No user found in request context');
      throw new ForbiddenException('Authentication required');
    }
    
    // Normalize to an array of roles
    // Support both `user.role` (string) and `user.roles` (string[])
    const userRoles: string[] = Array.isArray(user.roles)
      ? user.roles
      : (user.role ? [String(user.role)] : []);
    
    // User must have at least one role
    if (userRoles.length === 0) {
      this.logger.warn(`RolesGuard: User ${user.id} has no roles assigned`);
      throw new ForbiddenException('User has no roles assigned');
    }
    
    // Check if user has any of the required roles
    const normalizedRequired = requiredRoles.map(String);
    const hasRole = normalizedRequired.some((role) => userRoles.includes(role));
    
    if (!hasRole) {
      this.logger.warn(
        `RolesGuard: User ${user.id} with roles [${userRoles.join(', ')}] ` +
        `attempted to access resource requiring roles [${requiredRoles.join(', ')}]`
      );
      throw new ForbiddenException(
        `Insufficient permissions. Required roles: ${requiredRoles.join(' or ')}`
      );
    }
    
    return true;
  }
}