From bee140f517e0f50b44a1a6fc1eecff8de6cbfe78 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 7 May 2024 22:27:45 +0300 Subject: [PATCH 1/3] Add space permission service and guards for various entities --- libs/common/src/helper/helper.module.ts | 9 +++-- libs/common/src/helper/services/index.ts | 1 + .../services/space.permission.service.ts | 35 +++++++++++++++++++ src/guards/building.permission.guard.ts | 35 +++++++++++++++++++ src/guards/community.permission.guard.ts | 35 +++++++++++++++++++ src/guards/floor.permission.guard.ts | 35 +++++++++++++++++++ src/guards/room.permission.guard.ts | 35 +++++++++++++++++++ src/guards/unit.permission.guard.ts | 35 +++++++++++++++++++ 8 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 libs/common/src/helper/services/space.permission.service.ts create mode 100644 src/guards/building.permission.guard.ts create mode 100644 src/guards/community.permission.guard.ts create mode 100644 src/guards/floor.permission.guard.ts create mode 100644 src/guards/room.permission.guard.ts create mode 100644 src/guards/unit.permission.guard.ts diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index 826883a..baa8f07 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -1,11 +1,14 @@ import { Global, Module } from '@nestjs/common'; import { HelperHashService } from './services'; +import { SpacePermissionService } from './services/space.permission.service'; +import { SpaceRepository } from '../modules/space/repositories'; +import { SpaceRepositoryModule } from '../modules/space/space.repository.module'; @Global() @Module({ - providers: [HelperHashService], - exports: [HelperHashService], + providers: [HelperHashService, SpacePermissionService, SpaceRepository], + exports: [HelperHashService, SpacePermissionService], controllers: [], - imports: [], + imports: [SpaceRepositoryModule], }) export class HelperModule {} diff --git a/libs/common/src/helper/services/index.ts b/libs/common/src/helper/services/index.ts index 0ede816..46139ab 100644 --- a/libs/common/src/helper/services/index.ts +++ b/libs/common/src/helper/services/index.ts @@ -1 +1,2 @@ export * from './helper.hash.service'; +export * from './space.permission.service'; diff --git a/libs/common/src/helper/services/space.permission.service.ts b/libs/common/src/helper/services/space.permission.service.ts new file mode 100644 index 0000000..326c381 --- /dev/null +++ b/libs/common/src/helper/services/space.permission.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@nestjs/common'; +import { SpaceRepository } from '@app/common/modules/space/repositories'; +import { BadRequestException } from '@nestjs/common'; + +@Injectable() +export class SpacePermissionService { + constructor(private readonly spaceRepository: SpaceRepository) {} + + async checkUserPermission( + spaceUuid: string, + userUuid: string, + type: string, + ): Promise { + const spaceData = await this.spaceRepository.findOne({ + where: { + uuid: spaceUuid, + spaceType: { + type: type, + }, + userSpaces: { + user: { + uuid: userUuid, + }, + }, + }, + relations: ['spaceType', 'userSpaces', 'userSpaces.user'], + }); + + if (!spaceData) { + throw new BadRequestException( + `You do not have permission to access this ${type}`, + ); + } + } +} diff --git a/src/guards/building.permission.guard.ts b/src/guards/building.permission.guard.ts new file mode 100644 index 0000000..d73f19b --- /dev/null +++ b/src/guards/building.permission.guard.ts @@ -0,0 +1,35 @@ +import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common'; + +@Injectable() +export class BuildingPermissionGuard implements CanActivate { + constructor(private readonly permissionService: SpacePermissionService) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const { buildingUuid } = req.params; + const { user } = req; + + if (!buildingUuid) { + throw new BadRequestException('buildingUuid is required'); + } + + await this.permissionService.checkUserPermission( + buildingUuid, + user.uuid, + 'building', + ); + + return true; + } catch (error) { + throw error; + } + } +} diff --git a/src/guards/community.permission.guard.ts b/src/guards/community.permission.guard.ts new file mode 100644 index 0000000..2e44cd1 --- /dev/null +++ b/src/guards/community.permission.guard.ts @@ -0,0 +1,35 @@ +import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common'; + +@Injectable() +export class CommunityPermissionGuard implements CanActivate { + constructor(private readonly permissionService: SpacePermissionService) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const { communityUuid } = req.params; + const { user } = req; + + if (!communityUuid) { + throw new BadRequestException('communityUuid is required'); + } + + await this.permissionService.checkUserPermission( + communityUuid, + user.uuid, + 'community', + ); + + return true; + } catch (error) { + throw error; + } + } +} diff --git a/src/guards/floor.permission.guard.ts b/src/guards/floor.permission.guard.ts new file mode 100644 index 0000000..5e5c6ce --- /dev/null +++ b/src/guards/floor.permission.guard.ts @@ -0,0 +1,35 @@ +import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common'; + +@Injectable() +export class FloorPermissionGuard implements CanActivate { + constructor(private readonly permissionService: SpacePermissionService) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const { floorUuid } = req.params; + const { user } = req; + + if (!floorUuid) { + throw new BadRequestException('floorUuid is required'); + } + + await this.permissionService.checkUserPermission( + floorUuid, + user.uuid, + 'floor', + ); + + return true; + } catch (error) { + throw error; + } + } +} diff --git a/src/guards/room.permission.guard.ts b/src/guards/room.permission.guard.ts new file mode 100644 index 0000000..721a92f --- /dev/null +++ b/src/guards/room.permission.guard.ts @@ -0,0 +1,35 @@ +import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common'; + +@Injectable() +export class RoomPermissionGuard implements CanActivate { + constructor(private readonly permissionService: SpacePermissionService) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const { roomUuid } = req.params; + const { user } = req; + + if (!roomUuid) { + throw new BadRequestException('roomUuid is required'); + } + + await this.permissionService.checkUserPermission( + roomUuid, + user.uuid, + 'room', + ); + + return true; + } catch (error) { + throw error; + } + } +} diff --git a/src/guards/unit.permission.guard.ts b/src/guards/unit.permission.guard.ts new file mode 100644 index 0000000..e0d4958 --- /dev/null +++ b/src/guards/unit.permission.guard.ts @@ -0,0 +1,35 @@ +import { SpacePermissionService } from '@app/common/helper/services/space.permission.service'; +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common'; + +@Injectable() +export class UnitPermissionGuard implements CanActivate { + constructor(private readonly permissionService: SpacePermissionService) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const { unitUuid } = req.params; + const { user } = req; + + if (!unitUuid) { + throw new BadRequestException('unitUuid is required'); + } + + await this.permissionService.checkUserPermission( + unitUuid, + user.uuid, + 'unit', + ); + + return true; + } catch (error) { + throw error; + } + } +} From aff52be5408db3b1ee6ca5e3cc6a0a07a10ee190 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 7 May 2024 22:27:59 +0300 Subject: [PATCH 2/3] Add permission guards to controllers --- src/building/controllers/building.controller.ts | 9 +++++---- src/community/community.module.ts | 4 +++- src/community/controllers/community.controller.ts | 5 +++-- src/floor/controllers/floor.controller.ts | 9 +++++---- src/room/controllers/room.controller.ts | 7 ++++--- src/unit/controllers/unit.controller.ts | 9 +++++---- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts index dc4dbd7..9843c8e 100644 --- a/src/building/controllers/building.controller.ts +++ b/src/building/controllers/building.controller.ts @@ -18,6 +18,7 @@ import { GetBuildingChildDto } from '../dtos/get.building.dto'; import { UpdateBuildingNameDto } from '../dtos/update.building.dto'; import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard'; import { CheckUserBuildingGuard } from 'src/guards/user.building.guard'; +import { BuildingPermissionGuard } from 'src/guards/building.permission.guard'; @ApiTags('Building Module') @Controller({ @@ -43,7 +44,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, BuildingPermissionGuard) @Get(':buildingUuid') async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) { try { @@ -59,7 +60,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, BuildingPermissionGuard) @Get('child/:buildingUuid') async getBuildingChildByUuid( @Param('buildingUuid') buildingUuid: string, @@ -79,7 +80,7 @@ export class BuildingController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, BuildingPermissionGuard) @Get('parent/:buildingUuid') async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) { try { @@ -122,7 +123,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, BuildingPermissionGuard) @Put('rename/:buildingUuid') async renameBuildingByUuid( @Param('buildingUuid') buildingUuid: string, diff --git a/src/community/community.module.ts b/src/community/community.module.ts index 742e3ad..e27627e 100644 --- a/src/community/community.module.ts +++ b/src/community/community.module.ts @@ -10,6 +10,7 @@ import { UserSpaceRepositoryModule } from '@app/common/modules/user-space/user.s import { UserSpaceRepository } from '@app/common/modules/user-space/repositories'; import { UserRepositoryModule } from '@app/common/modules/user/user.repository.module'; import { UserRepository } from '@app/common/modules/user/repositories'; +import { SpacePermissionService } from '@app/common/helper/services'; @Module({ imports: [ @@ -26,7 +27,8 @@ import { UserRepository } from '@app/common/modules/user/repositories'; SpaceTypeRepository, UserSpaceRepository, UserRepository, + SpacePermissionService, ], - exports: [CommunityService], + exports: [CommunityService, SpacePermissionService], }) export class CommunityModule {} diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index 3fd2233..c7fd248 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -20,6 +20,7 @@ import { import { GetCommunityChildDto } from '../dtos/get.community.dto'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; +import { CommunityPermissionGuard } from 'src/guards/community.permission.guard'; @ApiTags('Community Module') @Controller({ @@ -46,7 +47,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, CommunityPermissionGuard) @Get(':communityUuid') async getCommunityByUuid(@Param('communityUuid') communityUuid: string) { try { @@ -62,7 +63,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, CommunityPermissionGuard) @Get('child/:communityUuid') async getCommunityChildByUuid( @Param('communityUuid') communityUuid: string, diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts index a42db7b..304d4a2 100644 --- a/src/floor/controllers/floor.controller.ts +++ b/src/floor/controllers/floor.controller.ts @@ -18,6 +18,7 @@ import { GetFloorChildDto } from '../dtos/get.floor.dto'; import { UpdateFloorNameDto } from '../dtos/update.floor.dto'; import { CheckBuildingTypeGuard } from 'src/guards/building.type.guard'; import { CheckUserFloorGuard } from 'src/guards/user.floor.guard'; +import { FloorPermissionGuard } from 'src/guards/floor.permission.guard'; @ApiTags('Floor Module') @Controller({ @@ -43,7 +44,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, FloorPermissionGuard) @Get(':floorUuid') async getFloorByUuid(@Param('floorUuid') floorUuid: string) { try { @@ -58,7 +59,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, FloorPermissionGuard) @Get('child/:floorUuid') async getFloorChildByUuid( @Param('floorUuid') floorUuid: string, @@ -78,7 +79,7 @@ export class FloorController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, FloorPermissionGuard) @Get('parent/:floorUuid') async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) { try { @@ -122,7 +123,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, FloorPermissionGuard) @Put('rename/:floorUuid') async renameFloorByUuid( @Param('floorUuid') floorUuid: string, diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts index 221fb39..f59300b 100644 --- a/src/room/controllers/room.controller.ts +++ b/src/room/controllers/room.controller.ts @@ -16,6 +16,7 @@ import { AddRoomDto, AddUserRoomDto } from '../dtos/add.room.dto'; import { UpdateRoomNameDto } from '../dtos/update.room.dto'; import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard'; import { CheckUserRoomGuard } from 'src/guards/user.room.guard'; +import { RoomPermissionGuard } from 'src/guards/room.permission.guard'; @ApiTags('Room Module') @Controller({ @@ -41,7 +42,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, RoomPermissionGuard) @Get(':roomUuid') async getRoomByUuid(@Param('roomUuid') roomUuid: string) { try { @@ -56,7 +57,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, RoomPermissionGuard) @Get('parent/:roomUuid') async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) { try { @@ -98,7 +99,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, RoomPermissionGuard) @Put('rename/:roomUuid') async renameRoomByUuid( @Param('roomUuid') roomUuid: string, diff --git a/src/unit/controllers/unit.controller.ts b/src/unit/controllers/unit.controller.ts index f0f3775..89ca053 100644 --- a/src/unit/controllers/unit.controller.ts +++ b/src/unit/controllers/unit.controller.ts @@ -18,6 +18,7 @@ import { GetUnitChildDto } from '../dtos/get.unit.dto'; import { UpdateUnitNameDto } from '../dtos/update.unit.dto'; import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard'; import { CheckUserUnitGuard } from 'src/guards/user.unit.guard'; +import { UnitPermissionGuard } from 'src/guards/unit.permission.guard'; @ApiTags('Unit Module') @Controller({ @@ -43,7 +44,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, UnitPermissionGuard) @Get(':unitUuid') async getUnitByUuid(@Param('unitUuid') unitUuid: string) { try { @@ -58,7 +59,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, UnitPermissionGuard) @Get('child/:unitUuid') async getUnitChildByUuid( @Param('unitUuid') unitUuid: string, @@ -75,7 +76,7 @@ export class UnitController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, UnitPermissionGuard) @Get('parent/:unitUuid') async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) { try { @@ -117,7 +118,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, UnitPermissionGuard) @Put('rename/:unitUuid') async renameUnitByUuid( @Param('unitUuid') unitUuid: string, From 616ddd4d4c1cf95b5944d24d9d9e0a06cae9fe52 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Wed, 8 May 2024 09:35:42 +0300 Subject: [PATCH 3/3] Refactor space permission service --- .../services/space.permission.service.ts | 36 ++++++++++--------- .../controllers/community.controller.ts | 2 +- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/libs/common/src/helper/services/space.permission.service.ts b/libs/common/src/helper/services/space.permission.service.ts index 326c381..0f11cbc 100644 --- a/libs/common/src/helper/services/space.permission.service.ts +++ b/libs/common/src/helper/services/space.permission.service.ts @@ -11,25 +11,29 @@ export class SpacePermissionService { userUuid: string, type: string, ): Promise { - const spaceData = await this.spaceRepository.findOne({ - where: { - uuid: spaceUuid, - spaceType: { - type: type, - }, - userSpaces: { - user: { - uuid: userUuid, + try { + const spaceData = await this.spaceRepository.findOne({ + where: { + uuid: spaceUuid, + spaceType: { + type: type, + }, + userSpaces: { + user: { + uuid: userUuid, + }, }, }, - }, - relations: ['spaceType', 'userSpaces', 'userSpaces.user'], - }); + relations: ['spaceType', 'userSpaces', 'userSpaces.user'], + }); - if (!spaceData) { - throw new BadRequestException( - `You do not have permission to access this ${type}`, - ); + if (!spaceData) { + throw new BadRequestException( + `You do not have permission to access this ${type}`, + ); + } + } catch (err) { + throw new BadRequestException(err.message || 'Invalid UUID'); } } } diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index c7fd248..4a80015 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -111,7 +111,7 @@ export class CommunityController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, CommunityPermissionGuard) @Put('rename/:communityUuid') async renameCommunityByUuid( @Param('communityUuid') communityUuid: string,