From ebc1cd77d8f6c97b60aa9c9be876c9c353c7882a Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 1 Dec 2025 16:03:48 -0500 Subject: [PATCH] refactor(controllers): added ModuleAccessAllowed and Access decorators --- docs/swagger/swagger-spec.json | 14 +++++ prisma/schema.prisma | 11 ++++ .../decorators/modules-guard.decorators.ts | 8 +++ src/common/decorators/roles.decorators.ts | 8 --- src/common/guards/modules.guard.ts | 43 +++++++++++++ src/common/guards/roles.guard.ts | 61 ------------------- .../controllers/employees.controller.ts | 39 +++++------- src/main.ts | 11 ++-- .../controllers/bank-codes.controller.ts | 6 +- .../controllers/csv-exports.controller.ts | 7 +-- .../controllers/expense.controller.ts | 17 +++--- .../controllers/pay-periods.controller.ts | 22 ++++--- .../controller/schedule-presets.controller.ts | 39 +++++------- .../shifts/controllers/shift.controller.ts | 26 ++++---- .../controllers/timesheet.controller.ts | 13 ++-- 15 files changed, 156 insertions(+), 169 deletions(-) create mode 100644 src/common/decorators/modules-guard.decorators.ts delete mode 100644 src/common/decorators/roles.decorators.ts create mode 100644 src/common/guards/modules.guard.ts delete mode 100644 src/common/guards/roles.guard.ts diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index 52213ab..cb484fd 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -463,6 +463,20 @@ ] } }, + "/employees/personal-profile": { + "get": { + "operationId": "EmployeesController_findOwnProfile", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Employees" + ] + } + }, "/employees/profile": { "get": { "operationId": "EmployeesController_findProfile", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3fc5fd0..779dc74 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -381,6 +381,17 @@ enum Roles { @@map("roles") } +enum Modules { + timesheets + timesheets_approval + employee_list + employee_management + personal_profile + dashboard + + @@map("modules") +} + enum LeaveTypes { SICK // maladie ou repos VACATION // paye diff --git a/src/common/decorators/modules-guard.decorators.ts b/src/common/decorators/modules-guard.decorators.ts new file mode 100644 index 0000000..b318088 --- /dev/null +++ b/src/common/decorators/modules-guard.decorators.ts @@ -0,0 +1,8 @@ +import { SetMetadata } from '@nestjs/common'; +import { Modules } from 'src/common/mappers/module-access.mapper'; + +export const MODULES_KEY = 'modules'; +export const ModuleAccessAllowed = (...modules: Modules[]) => + SetMetadata(MODULES_KEY, modules); + + diff --git a/src/common/decorators/roles.decorators.ts b/src/common/decorators/roles.decorators.ts deleted file mode 100644 index 92020c9..0000000 --- a/src/common/decorators/roles.decorators.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SetMetadata } from '@nestjs/common'; -import { Roles } from '@prisma/client'; - -export const ROLES_KEY = 'roles'; -export const RolesAllowed = (...roles: Roles[]) => - SetMetadata(ROLES_KEY, roles); - - diff --git a/src/common/guards/modules.guard.ts b/src/common/guards/modules.guard.ts new file mode 100644 index 0000000..a37e83b --- /dev/null +++ b/src/common/guards/modules.guard.ts @@ -0,0 +1,43 @@ +import { + CanActivate, + ExecutionContext, + ForbiddenException, + Injectable, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { MODULES_KEY } from '../decorators/modules-guard.decorators'; +import { Modules, Roles } from '.prisma/client'; + + + +interface RequestWithUser extends Request { + // TODO: Create an actual user model based on OAuth signin + user: any; +} + +@Injectable() +export class ModulesGuard implements CanActivate { + constructor(private reflector: Reflector) { } + canActivate(ctx: ExecutionContext): boolean { + const requiredModules = this.reflector.getAllAndOverride( + MODULES_KEY, + [ctx.getHandler(), ctx.getClass()], + ); + //for "deny-by-default" when role is wrong or unavailable + if (!requiredModules || requiredModules.length === 0) { + return true; + } + const request = ctx.switchToHttp().getRequest(); + const user = request.user; + + if (!user) { + return false; + } + if (!requiredModules.includes(user.role)) { + throw new ForbiddenException( + `The role ${user.role} is not authorized to access this resource.`, + ); + } + return true; + } +} diff --git a/src/common/guards/roles.guard.ts b/src/common/guards/roles.guard.ts deleted file mode 100644 index 9a9244d..0000000 --- a/src/common/guards/roles.guard.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - CanActivate, - ExecutionContext, - ForbiddenException, - Injectable, -} from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { ROLES_KEY } from '../decorators/roles.decorators'; -import { Roles } from '.prisma/client'; - - - -interface RequestWithUser extends Request { - // TODO: Create an actual user model based on OAuth signin - user: any; -} - -@Injectable() -export class RolesGuard implements CanActivate { - constructor(private reflector: Reflector) { } - - /** - * @swagger - * @function canActivate - * @description - * Authorization guard that checks whether the current user has one of the required roles - * to access a specific route handler. It uses metadata defined by the `@Roles()` decorator - * and verifies the user's role accordingly. - * - * If no roles are specified for the route, access is granted by default. - * If the user is not authenticated or does not have a required role, access is denied. - * - * @param {ExecutionContext} ctx - The current execution context, which provides access - * to route metadata and the HTTP request. - * - * @returns {boolean} - Returns `true` if access is allowed, otherwise throws a `ForbiddenException` - * or returns `false` if the user is not authenticated. - */ - canActivate(ctx: ExecutionContext): boolean { - const requiredRoles = this.reflector.getAllAndOverride( - ROLES_KEY, - [ctx.getHandler(), ctx.getClass()], - ); - //for "deny-by-default" when role is wrong or unavailable - if (!requiredRoles || requiredRoles.length === 0) { - return true; - } - const request = ctx.switchToHttp().getRequest(); - const user = request.user; - - if (!user) { - return false; - } - if (!requiredRoles.includes(user.role)) { - throw new ForbiddenException( - `The role ${user.role} is not authorized to access this resource.`, - ); - } - return true; - } -} diff --git a/src/identity-and-account/employees/controllers/employees.controller.ts b/src/identity-and-account/employees/controllers/employees.controller.ts index 2cd9229..d1ce485 100644 --- a/src/identity-and-account/employees/controllers/employees.controller.ts +++ b/src/identity-and-account/employees/controllers/employees.controller.ts @@ -4,46 +4,37 @@ import { Result } from "src/common/errors/result-error.factory"; import { EmployeeDetailedDto } from "src/identity-and-account/employees/dtos/employee-detailed.dto"; import { EmployeeDto } from "src/identity-and-account/employees/dtos/employee.dto"; import { EmployeesService } from "src/identity-and-account/employees/services/employees.service"; -import { AccessGetService } from "src/identity-and-account/user-module-access/services/module-access-get.service"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; //TODO: create a custom decorator to replace the findModuleAcces call function @Controller('employees') export class EmployeesController { - constructor( - private readonly employeesService: EmployeesService, - private readonly accessGetService: AccessGetService, - ) { } + constructor(private readonly employeesService: EmployeesService) { } + + @Get('personal-profile') + @ModuleAccessAllowed(ModulesEnum.personal_profile) + async findOwnProfile(@Access('email') email: string): Promise, string>> { + return await this.employeesService.findOwnProfile(email); + } @Get('profile') + @ModuleAccessAllowed(ModulesEnum.employee_management) async findProfile(@Access('email') email: string, @Query('employee_email') employee_email?: string, ): Promise, string>> { - const granted_access = await this.accessGetService.findModuleAccess(email); - if (!granted_access.success) return { success: false, error: 'INVALID_USER' }; - if (!granted_access.data.employee_management) { - return await this.employeesService.findOwnProfile(email); - } else if (granted_access.data.employee_management) { - return await this.employeesService.findOneDetailedProfile(email,employee_email); - } else { - return { success: false, error: 'INVALID_USER' } - } + return await this.employeesService.findOneDetailedProfile(email, employee_email); } @Get('employee-list') - async findListEmployees(@Access('email') email: string - ): Promise> { - const granted_access = await this.accessGetService.findModuleAccess(email); - if (!granted_access.success) return { success: false, error: 'INVALID_USER' }; - if (!granted_access.data.employee_management) return { success: false, error: 'UNAUTHORIZED_ACCESS' }; + @ModuleAccessAllowed(ModulesEnum.employee_list) + async findListEmployees(): Promise> { return this.employeesService.findListEmployees(); } @Post() - async createEmployee(@Access('email') email: string, @Body() dto: EmployeeDetailedDto - ): Promise> { - const granted_access = await this.accessGetService.findModuleAccess(email); - if (!granted_access.success) return { success: false, error: 'INVALID_USER' }; - if (!granted_access.data.employee_management) return { success: false, error: 'UNAUTHORIZED_ACCESS' }; + @ModuleAccessAllowed(ModulesEnum.employee_management) + async createEmployee(@Body() dto: EmployeeDetailedDto): Promise> { return await this.employeesService.createEmployee(dto); } diff --git a/src/main.ts b/src/main.ts index 6d4b5d3..08c7c78 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,18 +1,15 @@ import 'reflect-metadata'; -//import and if case for @nestjs/schedule Cron jobs import * as nodeCrypto from 'crypto'; if (!(globalThis as any).crypto) { (globalThis as any).crypto = nodeCrypto; } import { ensureAttachmentsTmpDir } from './config/attachment.fs'; - import { resolveAttachmentsRoot } from './config/attachment.config';// log to be removed post dev import { ATT_TMP_DIR } from './config/attachment.config'; // log to be removed post dev - -import { ModuleRef, NestFactory, Reflector } from '@nestjs/core'; +import { NestFactory, Reflector } from '@nestjs/core'; import { AppModule } from './app.module'; // import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard'; -import { RolesGuard } from './common/guards/roles.guard'; +import { ModulesGuard } from './common/guards/modules.guard'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { writeFileSync } from 'fs'; import * as session from 'express-session'; @@ -26,11 +23,11 @@ const SESSION_TOKEN_DURATION_MINUTES = 180 async function bootstrap() { const app = await NestFactory.create(AppModule); - const reflector = app.get(Reflector); //setup Reflector for Roles() + const reflector = app.get(Reflector); app.useGlobalGuards( // new JwtAuthGuard(reflector), //Authentification JWT - new RolesGuard(reflector), //deny-by-default and Role-based Access Control + new ModulesGuard(reflector), //deny-by-default and Module-based Access Control ); // Authentication and session diff --git a/src/modules/bank-codes/controllers/bank-codes.controller.ts b/src/modules/bank-codes/controllers/bank-codes.controller.ts index 2e95855..8e0a515 100644 --- a/src/modules/bank-codes/controllers/bank-codes.controller.ts +++ b/src/modules/bank-codes/controllers/bank-codes.controller.ts @@ -1,11 +1,11 @@ import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; import { BankCodesService } from "../services/bank-codes.service"; import { BankCodeDto } from "../dtos/bank-code.dto"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { PAY_SERVICE } from "src/common/shared/role-groupes"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; @Controller('bank-codes') -@RolesAllowed(...PAY_SERVICE) +@ModuleAccessAllowed(ModulesEnum.employee_management) export class BankCodesControllers { constructor(private readonly bankCodesService: BankCodesService) {} //_____________________________________________________________________________________________ diff --git a/src/modules/exports/controllers/csv-exports.controller.ts b/src/modules/exports/controllers/csv-exports.controller.ts index 628a569..d39d084 100644 --- a/src/modules/exports/controllers/csv-exports.controller.ts +++ b/src/modules/exports/controllers/csv-exports.controller.ts @@ -1,16 +1,15 @@ import { Controller, Get, Header, Query} from "@nestjs/common"; import { CsvExportService } from "../services/csv-exports.service"; import { ExportCsvOptionsDto } from "../dtos/export-csv-options.dto"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { PAY_SERVICE } from "src/common/shared/role-groupes"; - +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; @Controller('exports') -@RolesAllowed(...PAY_SERVICE) export class CsvExportController { constructor(private readonly csvService: CsvExportService) {} @Get('csv') + @ModuleAccessAllowed(ModulesEnum.employee_management) @Header('Content-Type', 'text/csv; charset=utf-8') @Header('Content-Disposition', 'attachment; filename="export.csv"') async exportCsv(@Query() query: ExportCsvOptionsDto ): Promise { diff --git a/src/time-and-attendance/expenses/controllers/expense.controller.ts b/src/time-and-attendance/expenses/controllers/expense.controller.ts index b23d282..a35dae6 100644 --- a/src/time-and-attendance/expenses/controllers/expense.controller.ts +++ b/src/time-and-attendance/expenses/controllers/expense.controller.ts @@ -1,29 +1,30 @@ import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes"; -import { Result } from "src/common/errors/result-error.factory"; +import { Result } from "src/common/errors/result-error.factory"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; +import { Access } from "src/common/decorators/module-access.decorators"; @Controller('expense') -@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class ExpenseController { constructor(private readonly upsert_service: ExpenseUpsertService) { } @Post('create') - create(@Req() req, @Body() dto: ExpenseDto): Promise> { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.timesheets) + create(@Access('email') email: string, @Body() dto: ExpenseDto): Promise> { if (!email) throw new UnauthorizedException('Unauthorized User'); return this.upsert_service.createExpense(dto, email); } @Patch('update') - update(@Body() dto: ExpenseDto, @Req() req): Promise> { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.timesheets) + update(@Body() dto: ExpenseDto, @Access('email') email: string): Promise> { return this.upsert_service.updateExpense(dto, email); } @Delete('delete/:expense_id') + @ModuleAccessAllowed(ModulesEnum.timesheets) remove(@Param('expense_id') expense_id: number): Promise> { return this.upsert_service.deleteExpense(expense_id); } diff --git a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts index dc02a87..31e398a 100644 --- a/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts +++ b/src/time-and-attendance/pay-period/controllers/pay-periods.controller.ts @@ -1,15 +1,15 @@ -import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; +import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, UnauthorizedException } from "@nestjs/common"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; -import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; import { Result } from "src/common/errors/result-error.factory"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; +import { Access } from "src/common/decorators/module-access.decorators"; @Controller('pay-periods') -@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class PayPeriodsController { constructor( @@ -18,6 +18,7 @@ export class PayPeriodsController { ) { } @Get('current-and-all') + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) async getCurrentAndAll(@Query('date') date?: string): Promise> { const current = await this.queryService.findCurrent(date); if (!current.success) return { success: false, error: 'INVALID_PAY_PERIOD' }; @@ -29,11 +30,13 @@ export class PayPeriodsController { } @Get("date/:date") + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) async findByDate(@Param("date") date: string) { return this.queryService.findByDate(date); } @Get(":year/:periodNumber") + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) async findOneByYear( @Param("year", ParseIntPipe) year: number, @Param("periodNumber", ParseIntPipe) period_no: number, @@ -42,26 +45,25 @@ export class PayPeriodsController { } @Patch("crew/pay-period-approval") - @RolesAllowed(...MANAGER_ROLES) - async bulkApproval(@Req() req, @Body() dto: BulkCrewApprovalDto) { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) + async bulkApproval(@Access('email') email:string, @Body() dto: BulkCrewApprovalDto) { if (!email) throw new UnauthorizedException(`Session infos not found`); return this.commandService.bulkApproveCrew(email, dto); } @Get('crew/:year/:periodNumber') - @RolesAllowed(...MANAGER_ROLES) - async getCrewOverview(@Req() req, + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) + async getCrewOverview(@Access('email') email:string, @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) period_no: number, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, ): Promise> { - const email = req.user?.email; if (!email) throw new UnauthorizedException(`Session infos not found`); return this.queryService.getCrewOverview(year, period_no, email, include_subtree); } @Get('overview/:year/:periodNumber') + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) async getOverviewByYear( @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) period_no: number, diff --git a/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts b/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts index 9bf003d..6915dad 100644 --- a/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts +++ b/src/time-and-attendance/schedule-presets/controller/schedule-presets.controller.ts @@ -1,14 +1,14 @@ -import { Controller, Param, Query, Body, Get, Post, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; +import { Controller, Param, Body, Get, Post, ParseIntPipe, Delete, Patch } from "@nestjs/common"; import { SchedulePresetsDto } from "src/time-and-attendance/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsCreateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-create.service"; import { SchedulePresetUpdateDeleteService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-update-delete.service"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; +import { Access } from "src/common/decorators/module-access.decorators"; @Controller('schedule-presets') -@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class SchedulePresetsController { constructor( private readonly createService: SchedulePresetsCreateService, @@ -19,48 +19,39 @@ export class SchedulePresetsController { // used to create a schedule preset @Post('create') - @RolesAllowed(...MANAGER_ROLES) - async createPreset(@Req() req, @Body() dto: SchedulePresetsDto) { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.employee_management) + async createPreset(@Access('email') email: string, @Body() dto: SchedulePresetsDto) { return await this.createService.createPreset(email, dto); } //used to update an already existing schedule preset @Patch('update/:preset_id') - @RolesAllowed(...MANAGER_ROLES) + @ModuleAccessAllowed(ModulesEnum.employee_management) async updatePreset( - @Param('preset_id', ParseIntPipe) preset_id: number, - @Body() dto: SchedulePresetsDto, - @Req() req, - ) { - const email = req.user?.email; + @Param('preset_id', ParseIntPipe) preset_id: number, @Body() dto: SchedulePresetsDto, @Access('email') email: string) { return await this.updateDeleteService.updatePreset(preset_id, dto, email); } //used to delete a schedule preset @Delete('delete/:preset_id') - @RolesAllowed(...MANAGER_ROLES) - async deletePreset(@Param('preset_id', ParseIntPipe) preset_id: number, @Req() req) { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.employee_management) + async deletePreset( + @Param('preset_id', ParseIntPipe) preset_id: number, @Access('email') email: string) { return await this.updateDeleteService.deletePreset(preset_id, email); } //used to show the list of available schedule presets @Get('find-list') - @RolesAllowed(...MANAGER_ROLES) - async findListById(@Req() req) { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.employee_management) + async findListById(@Access('email') email: string) { return this.getService.getSchedulePresets(email); } //used to apply a preset to a timesheet @Post('apply-presets') + @ModuleAccessAllowed(ModulesEnum.timesheets) async applyPresets( - @Body('preset') preset_id: number, - @Body('start') start_date: string, - @Req() req - ) { - const email = req.user?.email; + @Body('preset') preset_id: number, @Body('start') start_date: string, @Access('email') email: string) { return this.applyPresetsService.applyToTimesheet(email, preset_id, start_date); } } \ No newline at end of file diff --git a/src/time-and-attendance/shifts/controllers/shift.controller.ts b/src/time-and-attendance/shifts/controllers/shift.controller.ts index c0fa32d..abe9e95 100644 --- a/src/time-and-attendance/shifts/controllers/shift.controller.ts +++ b/src/time-and-attendance/shifts/controllers/shift.controller.ts @@ -1,35 +1,35 @@ -import { Body, Controller, Delete, Param, Patch, Post, Req } from "@nestjs/common"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; -import { GLOBAL_CONTROLLER_ROLES } from "src/common/shared/role-groupes"; +import { Body, Controller, Delete, Param, Patch, Post } from "@nestjs/common"; import { Result } from "src/common/errors/result-error.factory"; import { ShiftDto } from "src/time-and-attendance/shifts/dtos/shift-create.dto"; import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service"; import { ShiftsUpdateDeleteService } from "src/time-and-attendance/shifts/services/shifts-update-delete.service"; - +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; +import { Access } from "src/common/decorators/module-access.decorators"; @Controller('shift') -@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class ShiftController { - constructor( + constructor( private readonly create_service: ShiftsCreateService, private readonly update_delete_service: ShiftsUpdateDeleteService, - ){} + ) { } @Post('create') - createBatch( @Req() req, @Body()dtos: ShiftDto[]): Promise> { - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.timesheets) + createBatch(@Access('email') email: string, @Body() dtos: ShiftDto[]): Promise> { return this.create_service.createOneOrManyShifts(email, dtos) } @Patch('update') - updateBatch( @Body() dtos: ShiftDto[], @Req() req): Promise>{ - const email = req.user?.email; + @ModuleAccessAllowed(ModulesEnum.timesheets) + updateBatch(@Access('email') email: string, @Body() dtos: ShiftDto[]): Promise> { return this.update_delete_service.updateOneOrManyShifts(dtos, email); } @Delete(':shift_id') - remove(@Param('shift_id') shift_id: number ): Promise> { + @ModuleAccessAllowed(ModulesEnum.timesheets) + remove(@Param('shift_id') shift_id: number): Promise> { return this.update_delete_service.deleteShift(shift_id); } - + } diff --git a/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts b/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts index 9f8d6fb..eb5050a 100644 --- a/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts +++ b/src/time-and-attendance/timesheets/controllers/timesheet.controller.ts @@ -1,12 +1,12 @@ import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common"; -import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { GetTimesheetsOverviewService } from "src/time-and-attendance/timesheets/services/timesheet-get-overview.service"; import { TimesheetApprovalService } from "src/time-and-attendance/timesheets/services/timesheet-approval.service"; -import { GLOBAL_CONTROLLER_ROLES, MANAGER_ROLES } from "src/common/shared/role-groupes"; +import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators"; +import { Modules as ModulesEnum } from ".prisma/client"; +import { Access } from "src/common/decorators/module-access.decorators"; @Controller('timesheets') -@RolesAllowed(...GLOBAL_CONTROLLER_ROLES) export class TimesheetController { constructor( private readonly timesheetOverview: GetTimesheetsOverviewService, @@ -14,19 +14,18 @@ export class TimesheetController { ) { } @Get(':year/:period_number') + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) getTimesheetByPayPeriod( - @Req() req, + @Access('email') email: string, @Param('year', ParseIntPipe) year: number, @Param('period_number', ParseIntPipe) period_number: number, @Query('employee_email') employee_email?: string, ) { - const email = req.user?.email; - if (!email) throw new UnauthorizedException('Unauthorized User'); return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number, employee_email); } @Patch('timesheet-approval') - @RolesAllowed(...MANAGER_ROLES) + @ModuleAccessAllowed(ModulesEnum.timesheets_approval) approveTimesheet( @Body('timesheet_id', ParseIntPipe) timesheet_id: number, @Body('is_approved', ParseBoolPipe) is_approved: boolean,