feat(Roles): role guards et setup ownship(not implemented, yet)

This commit is contained in:
Matthieu Haineault 2025-07-18 10:20:52 -04:00
parent e53f646659
commit cc567b2b26
13 changed files with 150 additions and 6 deletions

View File

@ -0,0 +1,11 @@
import { SetMetadata } from "@nestjs/common";
export const OWNER_KEY = 'ownership';
export interface OwnershipMeta {
serviceToken: string;
idParam?: string;
ownerField?: string;
}
export const CheckOwnership = (meta: OwnershipMeta) =>
SetMetadata(OWNER_KEY, meta);

View File

@ -2,5 +2,7 @@ import { SetMetadata } from '@nestjs/common';
import { Roles } from '@prisma/client'; import { Roles } from '@prisma/client';
export const ROLES_KEY = 'roles'; export const ROLES_KEY = 'roles';
export const ROlesAllowed = (...roles: Roles[]) => export const RolesAllowed = (...roles: Roles[]) =>
SetMetadata(ROLES_KEY, roles); SetMetadata(ROLES_KEY, roles);

View File

@ -0,0 +1,51 @@
import {
CanActivate,
Injectable,
ExecutionContext,
ForbiddenException,
} from "@nestjs/common";
import { Reflector, ModuleRef } from "@nestjs/core";
import { OWNER_KEY, OwnershipMeta } from "../decorators/ownership.decorator";
import { Request } from 'express';
interface RequestWithUser extends Request {
user: { id: string, role: string };
}
@Injectable()
export class OwnershipGuard implements CanActivate {
constructor(
private reflector: Reflector,
private moduleRef: ModuleRef,
) {}
async canActivate(context: ExecutionContext): Promise<boolean>{
const meta = this.reflector.get<OwnershipMeta>(
OWNER_KEY, context.getHandler(),
);
if (!meta)
return true;
const request = context.switchToHttp().getRequest<RequestWithUser>();
const user = request.user;
const resourceId = request.params[meta.idParam || 'id'];
const service = this.moduleRef.get<any>(
meta.serviceToken,
{ strict: false },
);
const resource = await service.findOne(resourceId);
const ownerField = meta.ownerField || 'ownerId';
if (user.role === 'ADMIN') {
return true;
}
if (!resource || resource[ownerField] !== user.id) {
throw new ForbiddenException(
`You do not own the rights to this resource.`
);
}
return true;
}
}

View File

@ -22,8 +22,9 @@ export class RolesGuard implements CanActivate {
ROLES_KEY, ROLES_KEY,
ctx.getHandler(), ctx.getHandler(),
); );
if (!requiredRoles) { //for "deny-by-default" when role is wrong or unavailable
return true; if (!requiredRoles || requiredRoles.length === 0) {
return false;
} }
const request = ctx.switchToHttp().getRequest<RequestWithUser>(); const request = ctx.switchToHttp().getRequest<RequestWithUser>();
const user = request.user; const user = request.user;

View File

@ -1,9 +1,24 @@
import 'reflect-metadata'; import 'reflect-metadata';
import { NestFactory } from '@nestjs/core'; import { ModuleRef, NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard';
import { RolesGuard } from './common/guards/roles.guard';
import { OwnershipGuard } from './common/guards/ownership.guard';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
//setup Reflector for Roles()
const reflector = app.get(Reflector);
app.useGlobalPipes(
new ValidationPipe({ whitelist: true, transform: true}));
app.useGlobalGuards(
new JwtAuthGuard(reflector), //Authentification JWT
new RolesGuard(reflector), //deny-by-default and Role-based Access Control
new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet
);
await app.listen(process.env.PORT ?? 3000); await app.listen(process.env.PORT ?? 3000);
} }
bootstrap(); bootstrap();

View File

@ -1,5 +1,18 @@
import { Injectable } from '@nestjs/common'; import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {} export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
handleRequest(err, user, info) {
if(err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}

View File

@ -3,27 +3,33 @@ import { CustomersService } from '../services/customers.service';
import { Customers, Employees } from '@prisma/client'; import { Customers, Employees } from '@prisma/client';
import { CreateCustomerDto } from '../dtos/create-customer'; import { CreateCustomerDto } from '../dtos/create-customer';
import { UpdateCustomerDto } from '../dtos/update-customer'; import { UpdateCustomerDto } from '../dtos/update-customer';
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client';
@Controller('customers') @Controller('customers')
export class CustomersController { export class CustomersController {
constructor(private readonly customersService: CustomersService) {} constructor(private readonly customersService: CustomersService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR)
create(@Body() dto: CreateCustomerDto): Promise<Customers> { create(@Body() dto: CreateCustomerDto): Promise<Customers> {
return this.customersService.create(dto); return this.customersService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findAll(): Promise<Customers[]> { findAll(): Promise<Customers[]> {
return this.customersService.findAll(); return this.customersService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findOne(@Param('id', ParseIntPipe) id: number): Promise<Customers> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Customers> {
return this.customersService.findOne(id); return this.customersService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR)
update( update(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateCustomerDto, @Body() dto: UpdateCustomerDto,
@ -32,6 +38,7 @@ export class CustomersController {
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR)
remove(@Param('id', ParseIntPipe) id: number): Promise<Customers>{ remove(@Param('id', ParseIntPipe) id: number): Promise<Customers>{
return this.customersService.remove(id); return this.customersService.remove(id);
} }

View File

@ -13,26 +13,34 @@ import { EmployeesService } from '../services/employees.service';
import { CreateEmployeeDto } from '../dtos/create-employee.dto'; import { CreateEmployeeDto } from '../dtos/create-employee.dto';
import { UpdateEmployeeDto } from '../dtos/update-employee.dto'; import { UpdateEmployeeDto } from '../dtos/update-employee.dto';
//decorators and roles imports
import { RolesAllowed } from '../../../common/decorators/roles.decorators';
import { Roles as RoleEnum } from 'prisma/prisma-client';
@Controller('employees') @Controller('employees')
export class EmployeesController { export class EmployeesController {
constructor(private readonly employeesService: EmployeesService) {} constructor(private readonly employeesService: EmployeesService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
create(@Body() dto: CreateEmployeeDto): Promise<Employees> { create(@Body() dto: CreateEmployeeDto): Promise<Employees> {
return this.employeesService.create(dto); return this.employeesService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING)
findAll(): Promise<Employees[]> { findAll(): Promise<Employees[]> {
return this.employeesService.findAll(); return this.employeesService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING )
findOne(@Param('id', ParseIntPipe) id: number): Promise<Employees> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Employees> {
return this.employeesService.findOne(id); return this.employeesService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING )
update( update(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateEmployeeDto, @Body() dto: UpdateEmployeeDto,
@ -41,6 +49,7 @@ export class EmployeesController {
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR )
remove(@Param('id', ParseIntPipe) id: number): Promise<Employees> { remove(@Param('id', ParseIntPipe) id: number): Promise<Employees> {
return this.employeesService.remove(id); return this.employeesService.remove(id);
} }

View File

@ -3,27 +3,33 @@ import { LeaveRequestsService } from "../services/leave-request.service";
import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto"; import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto";
import { LeaveRequests } from "@prisma/client"; import { LeaveRequests } from "@prisma/client";
import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto"; import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client';
@Controller('leave-requests') @Controller('leave-requests')
export class LeaveRequestController { export class LeaveRequestController {
constructor(private readonly leaveRequetsService: LeaveRequestsService){} constructor(private readonly leaveRequetsService: LeaveRequestsService){}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
create(@Body() dto: CreateLeaveRequestsDto): Promise<LeaveRequests> { create(@Body() dto: CreateLeaveRequestsDto): Promise<LeaveRequests> {
return this. leaveRequetsService.create(dto); return this. leaveRequetsService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
findAll(): Promise<LeaveRequests[]> { findAll(): Promise<LeaveRequests[]> {
return this.leaveRequetsService.findAll(); return this.leaveRequetsService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findOne(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> { findOne(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> {
return this.leaveRequetsService.findOne(id); return this.leaveRequetsService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
update( update(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateLeaveRequestsDto, @Body() dto: UpdateLeaveRequestsDto,
@ -32,6 +38,7 @@ export class LeaveRequestController {
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
remove(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> { remove(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> {
return this.leaveRequetsService.remove(id); return this.leaveRequetsService.remove(id);
} }

View File

@ -3,32 +3,39 @@ import { OauthAccessTokensService } from '../services/oauth-access-tokens.servic
import { CreateOauthAccessTokenDto } from '../dtos/create-oauth-access-token.dto'; import { CreateOauthAccessTokenDto } from '../dtos/create-oauth-access-token.dto';
import { OAuthAccessTokens } from '@prisma/client'; import { OAuthAccessTokens } from '@prisma/client';
import { UpdateOauthAccessTokenDto } from '../dtos/update-oauth-access-token.dto'; import { UpdateOauthAccessTokenDto } from '../dtos/update-oauth-access-token.dto';
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client';
@Controller('oauth-access-tokens') @Controller('oauth-access-tokens')
export class OauthAccessTokensController { export class OauthAccessTokensController {
constructor(private readonly oauthAccessTokensService: OauthAccessTokensService){} constructor(private readonly oauthAccessTokensService: OauthAccessTokensService){}
@Post() @Post()
@RolesAllowed(RoleEnum.ADMIN)
create(@Body()dto: CreateOauthAccessTokenDto): Promise<OAuthAccessTokens> { create(@Body()dto: CreateOauthAccessTokenDto): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.create(dto); return this.oauthAccessTokensService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ADMIN)
findAll(): Promise<OAuthAccessTokens[]> { findAll(): Promise<OAuthAccessTokens[]> {
return this.oauthAccessTokensService.findAll(); return this.oauthAccessTokensService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ADMIN)
findOne(@Param('id') id: string): Promise<OAuthAccessTokens> { findOne(@Param('id') id: string): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.findOne(id); return this.oauthAccessTokensService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ADMIN)
update(@Param('id') id: string, @Body() dto: UpdateOauthAccessTokenDto): Promise<OAuthAccessTokens> { update(@Param('id') id: string, @Body() dto: UpdateOauthAccessTokenDto): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.update(id,dto); return this.oauthAccessTokensService.update(id,dto);
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ADMIN)
remove(@Param('id') id: string): Promise<OAuthAccessTokens> { remove(@Param('id') id: string): Promise<OAuthAccessTokens> {
return this.oauthAccessTokensService.remove(id); return this.oauthAccessTokensService.remove(id);
} }

View File

@ -3,33 +3,40 @@ import { ShiftCodesService } from "../services/shift-codes.service";
import { CreateShiftCodesDto } from "../dtos/create-shift-codes.dto"; import { CreateShiftCodesDto } from "../dtos/create-shift-codes.dto";
import { ShiftCodes } from "@prisma/client"; import { ShiftCodes } from "@prisma/client";
import { UpdateShiftCodesDto } from "../dtos/update-shift-codes.dto"; import { UpdateShiftCodesDto } from "../dtos/update-shift-codes.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client';
@Controller() @Controller()
export class ShiftCodesController { export class ShiftCodesController {
constructor(private readonly shiftCodesService: ShiftCodesService) {} constructor(private readonly shiftCodesService: ShiftCodesService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
create(@Body()dto: CreateShiftCodesDto): Promise<ShiftCodes> { create(@Body()dto: CreateShiftCodesDto): Promise<ShiftCodes> {
return this.shiftCodesService.create(dto); return this.shiftCodesService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
findAll(): Promise<ShiftCodes[]> { findAll(): Promise<ShiftCodes[]> {
return this.shiftCodesService.findAll(); return this.shiftCodesService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
findOne(@Param('id', ParseIntPipe) id: number): Promise<ShiftCodes> { findOne(@Param('id', ParseIntPipe) id: number): Promise<ShiftCodes> {
return this.shiftCodesService.findOne(id); return this.shiftCodesService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
update(@Param('id', ParseIntPipe) id: number, update(@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateShiftCodesDto): Promise<ShiftCodes> { @Body() dto: UpdateShiftCodesDto): Promise<ShiftCodes> {
return this.shiftCodesService.update(id,dto); return this.shiftCodesService.update(id,dto);
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR)
remove(@Param('id', ParseIntPipe)id: number): Promise<ShiftCodes> { remove(@Param('id', ParseIntPipe)id: number): Promise<ShiftCodes> {
return this.shiftCodesService.remove(id); return this.shiftCodesService.remove(id);
} }

View File

@ -3,6 +3,8 @@ import { ShiftsService } from "../services/shifts.service";
import { Shifts } from "@prisma/client"; import { Shifts } from "@prisma/client";
import { CreateShiftDto } from "../dtos/create-shifts.dto"; import { CreateShiftDto } from "../dtos/create-shifts.dto";
import { UpdateShiftsDto } from "../dtos/update-shifts.dto"; import { UpdateShiftsDto } from "../dtos/update-shifts.dto";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client';
@Controller('shifts') @Controller('shifts')
export class ShiftsController { export class ShiftsController {
@ -10,21 +12,25 @@ export class ShiftsController {
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
create(@Body() dto: CreateShiftDto): Promise<Shifts> { create(@Body() dto: CreateShiftDto): Promise<Shifts> {
return this.shiftsService.create(dto); return this.shiftsService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findAll(): Promise<Shifts[]> { findAll(): Promise<Shifts[]> {
return this.shiftsService.findAll(); return this.shiftsService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findOne(@Param('id', ParseIntPipe) id: number): Promise<Shifts> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
return this.shiftsService.findOne(id); return this.shiftsService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
update( update(
@Param('id', ParseIntPipe) id: number, @Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateShiftsDto, @Body() dto: UpdateShiftsDto,
@ -33,6 +39,7 @@ export class ShiftsController {
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
remove(@Param('id', ParseIntPipe) id: number): Promise<Shifts> { remove(@Param('id', ParseIntPipe) id: number): Promise<Shifts> {
return this.shiftsService.remove(id); return this.shiftsService.remove(id);
} }

View File

@ -3,27 +3,33 @@ import { TimesheetsService } from '../services/timesheets.service';
import { CreateTimesheetDto } from '../dtos/create-timesheet.dto'; import { CreateTimesheetDto } from '../dtos/create-timesheet.dto';
import { Timesheets } from '@prisma/client'; import { Timesheets } from '@prisma/client';
import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto'; import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto';
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client';
@Controller('timesheets') @Controller('timesheets')
export class TimesheetsController { export class TimesheetsController {
constructor(private readonly timesheetsService: TimesheetsService) {} constructor(private readonly timesheetsService: TimesheetsService) {}
@Post() @Post()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
create(@Body() dto: CreateTimesheetDto): Promise<Timesheets> { create(@Body() dto: CreateTimesheetDto): Promise<Timesheets> {
return this.timesheetsService.create(dto); return this.timesheetsService.create(dto);
} }
@Get() @Get()
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findAll(): Promise<Timesheets[]> { findAll(): Promise<Timesheets[]> {
return this.timesheetsService.findAll(); return this.timesheetsService.findAll();
} }
@Get(':id') @Get(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
findOne(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> { findOne(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
return this.timesheetsService.findOne(id); return this.timesheetsService.findOne(id);
} }
@Patch(':id') @Patch(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
update( update(
@Param('id', ParseIntPipe) id:number, @Param('id', ParseIntPipe) id:number,
@Body() dto: UpdateTimesheetDto, @Body() dto: UpdateTimesheetDto,
@ -32,6 +38,7 @@ export class TimesheetsController {
} }
@Delete(':id') @Delete(':id')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> { remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
return this.timesheetsService.remove(id); return this.timesheetsService.remove(id);
} }