Skip to content

Commit

Permalink
Merge pull request #31 from fga-eps-mds/fix(#71)/gerenciar-roles
Browse files Browse the repository at this point in the history
  • Loading branch information
Neoprot authored Sep 3, 2024
2 parents 8536641 + b959f19 commit 11abf21
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 58 deletions.
28 changes: 17 additions & 11 deletions src/auth/guards/roles.guard.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import {
Injectable,
CanActivate,
ExecutionContext,
ForbiddenException,
} from '@nestjs/common';
/* eslint-disable prettier/prettier */
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { UserRole } from 'src/users/dtos/user-role.enum';
import { IncomingHttpHeaders } from 'http';

@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
constructor(
private readonly reflector: Reflector,
private readonly jwtService: JwtService,
) {}

canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<UserRole[]>(
Expand All @@ -21,12 +22,17 @@ export class RolesGuard implements CanActivate {
}

const request = context.switchToHttp().getRequest();
const user = request.user;
const token = this.extractTokenFromHeader(request);
const user = this.jwtService.decode(token);

if (!user || !requiredRoles.includes(user.role)) {
throw new ForbiddenException('Access denied');
if (!user.role.includes(UserRole.ADMIN)) {
return false;
}

return true;
}

private extractTokenFromHeader(request: Request): string | undefined {
const headers = request.headers as unknown as IncomingHttpHeaders;
return headers.authorization?.split(' ')[1];
}
}
103 changes: 57 additions & 46 deletions test/role.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,82 @@
import { RolesGuard } from 'src/auth/guards/roles.guard';
import { Test, TestingModule } from '@nestjs/testing';
import { Reflector } from '@nestjs/core';
import { ExecutionContext, ForbiddenException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ExecutionContext } from '@nestjs/common';
import { UserRole } from 'src/users/dtos/user-role.enum';
import { RolesGuard } from 'src/auth/guards/roles.guard';

describe('RolesGuard', () => {
let rolesGuard: RolesGuard;
let jwtService: JwtService;
let reflector: Reflector;

beforeEach(() => {
reflector = new Reflector();
rolesGuard = new RolesGuard(reflector);
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
RolesGuard,
{
provide: Reflector,
useValue: {
get: jest.fn(),
},
},
{
provide: JwtService,
useValue: {
decode: jest.fn(),
},
},
],
}).compile();

rolesGuard = module.get<RolesGuard>(RolesGuard);
reflector = module.get<Reflector>(Reflector);
jwtService = module.get<JwtService>(JwtService);
});

it('should be defined', () => {
expect(rolesGuard).toBeDefined();
});

describe('canActivate', () => {
it('should return true if no roles are defined', () => {
it('should return true if no roles are required', () => {
const context = createMockExecutionContext({});
jest.spyOn(reflector, 'get').mockReturnValue(undefined);

const context = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest
.fn()
.mockReturnValue({ user: { role: UserRole.ALUNO } }),
}),
getHandler: jest.fn().mockReturnValue(null),
} as unknown as ExecutionContext;

const result = rolesGuard.canActivate(context);

expect(result).toBe(true);
expect(rolesGuard.canActivate(context)).toBe(true);
});

it('should return true if user role is in required roles', () => {
const requiredRoles = [UserRole.ADMIN];
jest.spyOn(reflector, 'get').mockReturnValue(requiredRoles);
it('should return false if the user does not have the required role', () => {
const context = createMockExecutionContext({
headers: { authorization: 'Bearer validToken' },
});
jest.spyOn(reflector, 'get').mockReturnValue([UserRole.ADMIN]);
jest
.spyOn(jwtService, 'decode')
.mockReturnValue({ role: [UserRole.ALUNO] });

const context = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest
.fn()
.mockReturnValue({ user: { role: UserRole.ADMIN } }),
}),
getHandler: jest.fn().mockReturnValue(null),
} as unknown as ExecutionContext;

const result = rolesGuard.canActivate(context);

expect(result).toBe(true);
expect(rolesGuard.canActivate(context)).toBe(false);
});

it('should throw ForbiddenException if user role is not in required roles', () => {
const requiredRoles = [UserRole.ADMIN];
jest.spyOn(reflector, 'get').mockReturnValue(requiredRoles);
it('should return true if the user has the required role', () => {
const context = createMockExecutionContext({
headers: { authorization: 'Bearer validToken' },
});
jest.spyOn(reflector, 'get').mockReturnValue([UserRole.ADMIN]);
jest
.spyOn(jwtService, 'decode')
.mockReturnValue({ role: [UserRole.ADMIN] });

const context = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest
.fn()
.mockReturnValue({ user: { role: UserRole.ALUNO } }),
}),
getHandler: jest.fn().mockReturnValue(null),
} as unknown as ExecutionContext;

expect(() => rolesGuard.canActivate(context)).toThrow(ForbiddenException);
expect(rolesGuard.canActivate(context)).toBe(true);
});
});
});

function createMockExecutionContext(request: any) {
return {
switchToHttp: () => ({
getRequest: () => request,
}),
getHandler: () => jest.fn(),
} as unknown as ExecutionContext;
}
1 change: 0 additions & 1 deletion test/user.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,3 @@ describe('UsersController', () => {
).rejects.toThrow(NotFoundException);
});
});

0 comments on commit 11abf21

Please sign in to comment.