From 9e0923c675f02aad6aaf3df155e8848cacb633ae Mon Sep 17 00:00:00 2001 From: mahyargdz Date: Tue, 15 Jul 2025 15:12:27 +0330 Subject: [PATCH] feat: add admin route guard for the authentication --- src/common/decorators/auth-guard.decorator.ts | 3 ++- src/modules/auth/guards/admin.guard.ts | 3 ++- src/modules/auth/guards/auth.guard.ts | 1 + .../businesses/businesses.controller.ts | 5 +++++ src/modules/domains/domains.controller.ts | 8 ++++++++ src/modules/mailbox/mailbox.controller.ts | 6 ++++++ .../signatures/email-signatures.controller.ts | 5 +++++ .../repositories/template.repository.ts | 15 +++++++++++++++ .../templates/services/templates.service.ts | 19 +++++-------------- src/modules/templates/templates.controller.ts | 9 ++++++++- 10 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/common/decorators/auth-guard.decorator.ts b/src/common/decorators/auth-guard.decorator.ts index 2255561..b5a40e4 100755 --- a/src/common/decorators/auth-guard.decorator.ts +++ b/src/common/decorators/auth-guard.decorator.ts @@ -1,8 +1,9 @@ import { UseGuards, applyDecorators } from "@nestjs/common"; import { ApiBearerAuth } from "@nestjs/swagger"; +import { AdminRouteGuard } from "../../modules/auth/guards/admin.guard"; import { JwtAuthGuard } from "../../modules/auth/guards/auth.guard"; export function AuthGuards() { - return applyDecorators(UseGuards(JwtAuthGuard), ApiBearerAuth("authorization")); + return applyDecorators(UseGuards(JwtAuthGuard, AdminRouteGuard), ApiBearerAuth("authorization")); } diff --git a/src/modules/auth/guards/admin.guard.ts b/src/modules/auth/guards/admin.guard.ts index ca87433..c87960e 100755 --- a/src/modules/auth/guards/admin.guard.ts +++ b/src/modules/auth/guards/admin.guard.ts @@ -4,6 +4,7 @@ import { FastifyRequest } from "fastify"; import { ADMIN_ROUTE } from "../../../common/decorators/admin.decorator"; import { AuthMessage } from "../../../common/enums/message.enum"; +import { RoleEnum } from "../../users/enums/role.enum"; @Injectable() export class AdminRouteGuard implements CanActivate { @@ -18,7 +19,7 @@ export class AdminRouteGuard implements CanActivate { if (!user) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); - if (!user.role) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); + if (user.isAdmin === undefined || user.role === RoleEnum.USER) throw new ForbiddenException(AuthMessage.UNAUTHORIZED_ACCESS); return true; } diff --git a/src/modules/auth/guards/auth.guard.ts b/src/modules/auth/guards/auth.guard.ts index 5027875..523e655 100755 --- a/src/modules/auth/guards/auth.guard.ts +++ b/src/modules/auth/guards/auth.guard.ts @@ -21,6 +21,7 @@ export class JwtAuthGuard extends AuthGuard([CONSOLE_JWT_STRATEGY_NAME, LOCAL_JW return super.canActivate(context); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any handleRequest(err: unknown, user: any, _info: unknown) { if (err || !user) throw new UnauthorizedException(AuthMessage.TOKEN_EXPIRED_OR_INVALID); return user; diff --git a/src/modules/businesses/businesses.controller.ts b/src/modules/businesses/businesses.controller.ts index d455a67..9ef168d 100644 --- a/src/modules/businesses/businesses.controller.ts +++ b/src/modules/businesses/businesses.controller.ts @@ -6,6 +6,7 @@ import { BusinessSlugParamDto } from "./DTO/business-slug-param.dto"; import { PurchaseQuotaDto } from "./DTO/purchase-quota.dto"; import { UpdateBusinessSettingsDto } from "./DTO/update-business-settings.dto"; import { BusinessesService } from "./services/businesses.service"; +import { AdminRoute } from "../../common/decorators/admin.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { BusinessDec } from "../../common/decorators/business.decorator"; import { BusinessInterceptor } from "../../core/interceptors/business.interceptor"; @@ -22,6 +23,7 @@ export class BusinessesController { } @Patch("settings") + @AdminRoute() @UseInterceptors(BusinessInterceptor) @ApiOperation({ summary: "Update business settings" }) @AuthGuards() @@ -30,6 +32,7 @@ export class BusinessesController { } @Get("settings") + @AdminRoute() @UseInterceptors(BusinessInterceptor) @ApiOperation({ summary: "Get business settings" }) @AuthGuards() @@ -38,6 +41,7 @@ export class BusinessesController { } @Get("quota") + @AdminRoute() @UseInterceptors(BusinessInterceptor) @ApiOperation({ summary: "Get business quota information" }) @ApiResponse({ status: 200, description: "Business quota information retrieved successfully", type: BusinessQuotaResponseDto }) @@ -47,6 +51,7 @@ export class BusinessesController { } @Post("quota/purchase") + @AdminRoute() @UseInterceptors(BusinessInterceptor) @ApiOperation({ summary: "Purchase additional quota for business" }) @ApiResponse({ status: 201, description: "Quota purchase initiated successfully" }) diff --git a/src/modules/domains/domains.controller.ts b/src/modules/domains/domains.controller.ts index 0dbd1df..ba152da 100644 --- a/src/modules/domains/domains.controller.ts +++ b/src/modules/domains/domains.controller.ts @@ -4,6 +4,7 @@ import { ApiHeader, ApiOperation, ApiResponse } from "@nestjs/swagger"; import { CreateDomainDto } from "./DTO/create-domain.dto"; import { UpdateDomainDto } from "./DTO/update-domain.dto"; import { DomainsService } from "./services/domains.service"; +import { AdminRoute } from "../../common/decorators/admin.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { BusinessDec } from "../../common/decorators/business.decorator"; import { PaginationDto } from "../../common/DTO/pagination.dto"; @@ -18,6 +19,7 @@ export class DomainsController { constructor(private readonly domainsService: DomainsService) {} @Post() + @AdminRoute() @ApiOperation({ summary: "Create a new domain and get setup instructions" }) @ApiResponse({ status: 201, description: "Domain created with setup instructions" }) createDomain(@Body() createDomainDto: CreateDomainDto, @BusinessDec("id") businessId: string) { @@ -25,6 +27,7 @@ export class DomainsController { } @Get() + @AdminRoute() @ApiOperation({ summary: "Get domain for business" }) @ApiResponse({ status: 200, description: "Domains retrieved successfully" }) getBusinessDomain(@BusinessDec("id") businessId: string, @Query() _query: PaginationDto) { @@ -32,6 +35,7 @@ export class DomainsController { } @Patch(":id") + @AdminRoute() @ApiOperation({ summary: "Update domain" }) @ApiResponse({ status: 200, description: "Domain updated successfully" }) updateDomain(@Param() paramDto: ParamDto, @Body() updateDomainDto: UpdateDomainDto, @BusinessDec("id") businessId: string) { @@ -39,6 +43,7 @@ export class DomainsController { } @Delete(":id") + @AdminRoute() @ApiOperation({ summary: "Delete domain" }) @ApiResponse({ status: 200, description: "Domain deleted successfully" }) deleteDomain(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) { @@ -46,6 +51,7 @@ export class DomainsController { } @Get("setup") + @AdminRoute() @ApiOperation({ summary: "Get domain setup instructions" }) @ApiResponse({ status: 200, description: "Domain setup instructions retrieved" }) getDomainSetupInstructions(@BusinessDec("id") businessId: string) { @@ -53,6 +59,7 @@ export class DomainsController { } @Get("dns-records") + @AdminRoute() @ApiOperation({ summary: "Get DNS records for domain" }) @ApiResponse({ status: 200, description: "DNS records retrieved successfully" }) getDomainDnsRecords(@BusinessDec("id") businessId: string) { @@ -60,6 +67,7 @@ export class DomainsController { } @Get("verify-dns") + @AdminRoute() @ApiOperation({ summary: "Check DNS records, verify domain configuration and return verification status" }) @ApiResponse({ status: 200, description: "DNS records verified and status returned" }) verifyDnsRecords(@BusinessDec("id") businessId: string) { diff --git a/src/modules/mailbox/mailbox.controller.ts b/src/modules/mailbox/mailbox.controller.ts index 9959801..6e276bf 100644 --- a/src/modules/mailbox/mailbox.controller.ts +++ b/src/modules/mailbox/mailbox.controller.ts @@ -3,6 +3,7 @@ import { ApiOperation, ApiParam, ApiQuery, ApiResponse } from "@nestjs/swagger"; import { MailboxQueryDto } from "./DTO/mailbox.dto"; import { MailboxService } from "./services/mailbox.service"; +import { AdminRoute } from "../../common/decorators/admin.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { UserDec } from "../../common/decorators/user.decorator"; import { CreateMailboxDto, UpdateMailboxDto } from "../mail-server/DTO"; @@ -21,6 +22,7 @@ export class MailboxController { } @Get("mailboxes") + @AdminRoute() @ApiOperation({ summary: "List mailboxes for a user" }) @ApiParam({ name: "userId", description: "User ID" }) @ApiQuery({ name: "counters", required: false, description: "Include message counters" }) @@ -31,6 +33,7 @@ export class MailboxController { } @Get("mailboxes/:mailboxId") + @AdminRoute() @ApiOperation({ summary: "Get mailbox details" }) @ApiParam({ name: "userId", description: "User ID" }) @ApiParam({ name: "mailboxId", description: "Mailbox ID" }) @@ -41,6 +44,7 @@ export class MailboxController { } @Post("mailboxes") + @AdminRoute() @ApiOperation({ summary: "Create a new mailbox" }) @ApiParam({ name: "userId", description: "User ID" }) @ApiResponse({ status: 201, description: "Mailbox created successfully" }) @@ -50,6 +54,7 @@ export class MailboxController { } @Patch("mailboxes/:mailboxId") + @AdminRoute() @ApiOperation({ summary: "Update mailbox" }) @ApiParam({ name: "userId", description: "User ID" }) @ApiParam({ name: "mailboxId", description: "Mailbox ID" }) @@ -60,6 +65,7 @@ export class MailboxController { } @Delete("mailboxes/:mailboxId") + @AdminRoute() @ApiOperation({ summary: "Delete a mailbox" }) @ApiParam({ name: "userId", description: "User ID" }) @ApiParam({ name: "mailboxId", description: "Mailbox ID" }) diff --git a/src/modules/signatures/email-signatures.controller.ts b/src/modules/signatures/email-signatures.controller.ts index 676c3ce..519ed71 100644 --- a/src/modules/signatures/email-signatures.controller.ts +++ b/src/modules/signatures/email-signatures.controller.ts @@ -5,6 +5,7 @@ import { ApiOperation, ApiResponse } from "@nestjs/swagger"; import { CreateEmailSignatureDto } from "./DTO/create-email-signature.dto"; import { UpdateEmailSignatureDto } from "./DTO/update-email-signature.dto"; import { EmailSignaturesService } from "./services/email-signatures.service"; +import { AdminRoute } from "../../common/decorators/admin.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { BusinessDec } from "../../common/decorators/business.decorator"; import { BusinessInterceptor } from "../../core/interceptors/business.interceptor"; @@ -17,6 +18,7 @@ export class EmailSignaturesController { constructor(private readonly emailSignaturesService: EmailSignaturesService) {} @Post() + @AdminRoute() @ApiOperation({ summary: "Create a new email signature" }) @ApiResponse({ status: 201, description: "Email signature created successfully" }) @ApiResponse({ status: 400, description: "Bad request" }) @@ -25,6 +27,7 @@ export class EmailSignaturesController { } @Get() + @AdminRoute() @ApiOperation({ summary: "Get a specific email signature" }) @ApiResponse({ status: 200, description: "Email signature retrieved successfully" }) @ApiResponse({ status: 404, description: "Email signature not found" }) @@ -33,6 +36,7 @@ export class EmailSignaturesController { } @Patch() + @AdminRoute() @ApiOperation({ summary: "Update an email signature" }) @ApiResponse({ status: 200, description: "Email signature updated successfully" }) @ApiResponse({ status: 404, description: "Email signature not found" }) @@ -41,6 +45,7 @@ export class EmailSignaturesController { } @Delete(":id") + @AdminRoute() @ApiOperation({ summary: "Delete an email signature" }) @ApiResponse({ status: 200, description: "Email signature deleted successfully" }) @ApiResponse({ status: 404, description: "Email signature not found" }) diff --git a/src/modules/templates/repositories/template.repository.ts b/src/modules/templates/repositories/template.repository.ts index 64bd76d..fd169fb 100644 --- a/src/modules/templates/repositories/template.repository.ts +++ b/src/modules/templates/repositories/template.repository.ts @@ -28,5 +28,20 @@ export class TemplateRepository extends EntityRepository