All files / apps/api-gateway/src/auth roles.guard.ts

100% Statements 17/17
100% Branches 14/14
100% Functions 2/2
100% Lines 13/13

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 492x           2x   2x                         2x 5x     6x       6x   5x       5x   3x     3x 2x       1x      
import {
  CanActivate,
  ExecutionContext,
  ForbiddenException,
  Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import type { Request } from 'express';
import {
  AuthenticatedUser,
  IS_PUBLIC_KEY,
  ROLES_KEY,
  UserRole,
} from './auth.decorators';
 
/**
 * Authorization, after authentication: routes without @Roles() accept any
 * authenticated user; with it, the JWT's role must be in the list. 401 means
 * "who are you?", 403 means "I know who you are - you can't do this".
 */
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}
 
  canActivate(context: ExecutionContext): boolean {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) return true;
 
    const required = this.reflector.getAllAndOverride<UserRole[] | undefined>(
      ROLES_KEY,
      [context.getHandler(), context.getClass()],
    );
    if (!required || required.length === 0) return true;
 
    const { user } = context
      .switchToHttp()
      .getRequest<Request & { user?: AuthenticatedUser }>();
    if (!user || !required.includes(user.role)) {
      throw new ForbiddenException(
        `requires one of roles: ${required.join(', ')}`,
      );
    }
    return true;
  }
}