Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/adapters/api/controller/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';

Check failure on line 1 in src/adapters/api/controller/auth.controller.ts

View workflow job for this annotation

GitHub Actions / lint

'UseGuards' is defined but never used
import { CreateUserUseCase } from '../../../core/usecases/create-user.use-case';
import { LoginUseCase } from '../../../core/usecases/login.use-case';
import { CreateUserRequest } from '../request/create-user.request';
Expand All @@ -6,7 +6,7 @@
import { UserMapper } from '../mapper/user.mapper';
import { LoginMapper } from '../mapper/login.mapper';
import { LoginResponse } from '../response/create-user.response';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';

Check failure on line 9 in src/adapters/api/controller/auth.controller.ts

View workflow job for this annotation

GitHub Actions / lint

'JwtAuthGuard' is defined but never used
import { CurrentUser } from '../decorator/current-user.decorator';
import { ProfileMapper } from '../mapper/profile.mapper';
import { ProfileRequest } from '../request/profile.request';
Expand All @@ -19,6 +19,7 @@
ApiOkResponse,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';
import { Public } from '../decorator/public.decorator';

@Controller('/auth')
export class UserController {
Expand All @@ -27,6 +28,7 @@
private readonly loginUseCase: LoginUseCase,
) {}

@Public()
@Post('/register')
@ApiCreatedResponse({
description: 'User successfully registered',
Expand All @@ -46,6 +48,7 @@
return this.createUserUseCase.execute(command);
}

@Public()
@Post('/login')
@ApiCreatedResponse({
description: 'User successfully logged in',
Expand All @@ -63,7 +66,6 @@
return LoginMapper.fromDomain(token);
}

@UseGuards(JwtAuthGuard)
@Get('/me')
@ApiOkResponse({
description: 'User profile retrieved successfully',
Expand Down
4 changes: 3 additions & 1 deletion src/adapters/api/controller/room.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';

Check failure on line 1 in src/adapters/api/controller/room.controller.ts

View workflow job for this annotation

GitHub Actions / lint

'UseGuards' is defined but never used
import { CreateRoomUseCase } from '../../../core/usecases/create-room.use-case';
import { CreateRoomRequest } from '../request/create-room.request';
import { CreateRoomMapper } from '../mapper/create-room.mapper';
Expand All @@ -6,7 +6,7 @@
import { GetRoomByIdUseCase } from '../../../core/usecases/get-room-by-id.use-case';
import { CreateRoomResponse } from '../response/create-room.response';
import { GetAllRoomsResponse } from '../response/get-all-rooms.response';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';

Check failure on line 9 in src/adapters/api/controller/room.controller.ts

View workflow job for this annotation

GitHub Actions / lint

'JwtAuthGuard' is defined but never used
import {
ApiBadRequestResponse,
ApiConflictResponse,
Expand All @@ -19,6 +19,7 @@
} from '@nestjs/swagger';
import { Roles } from '../decorator/roles.decorator';
import { UserType } from '../../../core/domain/type/UserType';
import { Public } from '../decorator/public.decorator';

@Controller('/rooms')
export class RoomController {
Expand All @@ -28,6 +29,7 @@
private readonly getRoomByIdUseCase: GetRoomByIdUseCase,
) {}

@Public()
@Get()
@ApiOperation({ summary: 'Get all rooms' })
@ApiOkResponse({
Expand All @@ -47,6 +49,7 @@
);
}

@Public()
@Get(':id')
@ApiOperation({ summary: 'Get room by ID' })
@ApiOkResponse({
Expand All @@ -64,7 +67,6 @@
return CreateRoomMapper.fromDomain(room);
}

@UseGuards(JwtAuthGuard)
@Roles(UserType.PLANNER)
@Post()
@ApiOperation({ summary: 'Create a new room' })
Expand Down
7 changes: 3 additions & 4 deletions src/adapters/api/controller/talk.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
Controller,
Post,
Param,
UseGuards,

Check failure on line 6 in src/adapters/api/controller/talk.controller.ts

View workflow job for this annotation

GitHub Actions / lint

'UseGuards' is defined but never used
Get,
Query,
} from '@nestjs/common';
import { CreateTalkCreationRequestUseCase } from '../../../core/usecases/create-talk-creation-request.use-case';
import { ApproveOrRejectTalkUseCase } from '../../../core/usecases/approve-or-reject-talk-use.case';
import { CreateTalkRequest } from '../request/create-talk.request';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';

Check failure on line 13 in src/adapters/api/controller/talk.controller.ts

View workflow job for this annotation

GitHub Actions / lint

'JwtAuthGuard' is defined but never used
import {
ApiBadRequestResponse,
ApiConflictResponse,
Expand All @@ -34,6 +34,7 @@
import { GetAllTalksWithRoomDetailMapper } from '../mapper/get-all-talks-with-room-detail.mapper';
import { UserType } from '../../../core/domain/type/UserType';
import { Roles } from '../decorator/roles.decorator';
import { Public } from '../decorator/public.decorator';
import { UpdateTalkRequest } from '../request/update-talk.request';
import { UpdateTalkMapper } from '../mapper/update-talk.mapper';
import { UpdateTalkCreationRequestUseCase } from '../../../core/usecases/update-talk-creation-request.use-case';
Expand All @@ -48,6 +49,7 @@
private readonly getAllTalksByStatusUseCase: GetAllTalksByStatusUseCase,
) {}

@Public()
@Get()
@ApiQuery({
name: 'status',
Expand All @@ -73,9 +75,8 @@
return GetAllTalksWithRoomDetailMapper.fromDomain(talksWithRoomDetail);
}

@UseGuards(JwtAuthGuard)
@Post()
@Roles(UserType.PLANNER, UserType.SPEAKER)
@Post()
@ApiOperation({ summary: 'Create a new talk' })
@ApiCreatedResponse({
description: 'Talk successfully created',
Expand Down Expand Up @@ -106,7 +107,6 @@
return CreateTalkMapper.fromDomain(talk);
}

@UseGuards(JwtAuthGuard)
@Post('/:talkId')
@Roles(UserType.PLANNER, UserType.SPEAKER)
@ApiOperation({ summary: 'Update a talk' })
Expand Down Expand Up @@ -140,7 +140,6 @@
return UpdateTalkMapper.fromDomain(talk);
}

@UseGuards(JwtAuthGuard)
@Roles(UserType.PLANNER)
@Post('/:talkId/approve-or-reject')
@ApiOperation({ summary: 'Accept or reject a talk' })
Expand Down
4 changes: 4 additions & 0 deletions src/adapters/api/decorator/public.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
13 changes: 12 additions & 1 deletion src/adapters/api/guards/jwt-auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,25 @@ import {
import { Request } from 'express';
import { TokenService } from '../../../core/domain/service/token.service';
import { ProfileRequest } from '../request/profile.request';
import { IS_PUBLIC_KEY } from '../decorator/public.decorator';
import { Reflector } from '@nestjs/core';

@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
@Inject('TokenService') private readonly tokenService: TokenService,
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 request = context.switchToHttp().getRequest<Request>();
const authHeader: string | undefined = request.headers.authorization;

Expand All @@ -31,7 +42,7 @@ export class JwtAuthGuard implements CanActivate {

try {
const payload = this.tokenService.verifyToken(token) as ProfileRequest;
(request as Request & { user?: unknown }).user = payload;
(request as Request & { user?: ProfileRequest }).user = payload;
return true;
} catch {
throw new UnauthorizedException('Invalid or expired token');
Expand Down
6 changes: 5 additions & 1 deletion src/adapters/api/guards/roles.guard.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Request } from 'express';
import { UserType } from '../../../core/domain/type/UserType';
import { ROLES_KEY } from '../decorator/roles.decorator';
import { log } from 'console';

Check failure on line 12 in src/adapters/api/guards/roles.guard.ts

View workflow job for this annotation

GitHub Actions / lint

'log' is defined but never used

type AuthenticatedUser = {
id: string;
Expand Down Expand Up @@ -39,7 +41,9 @@
}

if (!requiredRoles.includes(user.type)) {
throw new UnauthorizedException('Insufficient permissions');
throw new ForbiddenException(
`User with type ${user.type} does not have the required roles`,
);
}

return true;
Expand Down
1 change: 1 addition & 0 deletions src/adapters/api/mapper/profile.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class ProfileMapper {
return {
id: user.id,
email: user.email,
type: user.type,
};
}
}
3 changes: 3 additions & 0 deletions src/adapters/api/request/profile.request.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { UserType } from '../../../core/domain/type/UserType';

export type ProfileRequest = {
id: string;
email: string;
type: UserType;
};
3 changes: 2 additions & 1 deletion src/adapters/jwt/jwt.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { TokenService } from '../../core/domain/service/token.service';
import { JwtService } from '@nestjs/jwt';
import { ProfileRequest } from '../api/request/profile.request';

@Injectable()
export class JwtServiceAdapter implements TokenService {
Expand All @@ -16,7 +17,7 @@ export class JwtServiceAdapter implements TokenService {
});
}

verifyToken(token: string): any {
verifyToken(token: string): ProfileRequest {
return this.jwtService.verify(token, {
secret: this.secret,
});
Expand Down
2 changes: 0 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { TokenService } from './core/domain/service/token.service';
import { UserController } from './adapters/api/controller/auth.controller';
import { UserRepository } from './core/domain/repository/user.repository';
import { ApproveOrRejectTalkUseCase } from './core/usecases/approve-or-reject-talk-use.case';
import { JwtAuthGuard } from './adapters/api/guards/jwt-auth.guard';
import { GetAllTalksByStatusUseCase } from './core/usecases/get-all-talks-by-status.use-case';
import { GetRoomByIdUseCase } from './core/usecases/get-room-by-id.use-case';
import { UpdateTalkCreationRequestUseCase } from './core/usecases/update-talk-creation-request.use-case';
Expand All @@ -36,7 +35,6 @@ import { UpdateTalkCreationRequestUseCase } from './core/usecases/update-talk-cr
providers: [
PrismaService,
JwtService,
JwtAuthGuard,
{
provide: 'TokenService',
useFactory: (jwtService: JwtService) => new JwtServiceAdapter(jwtService),
Expand Down
1 change: 1 addition & 0 deletions src/core/usecases/__test__/login.use-case.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('LoginUseCase', () => {
expect(tokenService.generateToken).toHaveBeenCalledWith({
id: user.id,
email: user.email,
type: user.type,
});
});

Expand Down
1 change: 1 addition & 0 deletions src/core/usecases/login.use-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class LoginUseCase implements UseCase<LoginCommand, string> {
const token = this.tokenService.generateToken({
id: user.id,
email: user.email,
type: user.type,
});

return token;
Expand Down
9 changes: 9 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { DomainErrorFilter } from './config/domain-error.filter';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { apiReference } from '@scalar/nestjs-api-reference';
import { ValidationPipe } from '@nestjs/common';
import { RolesGuard } from './adapters/api/guards/roles.guard'; // 👈 Importe ton guard
import { Reflector } from '@nestjs/core';
import { JwtAuthGuard } from './adapters/api/guards/jwt-auth.guard';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand All @@ -13,6 +16,12 @@ async function bootstrap() {
origin: process.env.CORS_ORIGIN || 'http://localhost:8080',
});

const reflector = app.get(Reflector);
app.useGlobalGuards(
new JwtAuthGuard(app.get('TokenService'), reflector),
new RolesGuard(reflector),
);

const config = new DocumentBuilder()
.setTitle('API Documentation')
.setDescription('The API description')
Expand Down
Loading