From 6019e92c5d199f5ecffeeb87c95b689125da42bd Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 21:32:21 +0300 Subject: [PATCH] Add user permission guards and update device service methods --- src/device/controllers/device.controller.ts | 40 ++++++++--- src/device/device.module.ts | 2 + src/device/dtos/control.device.dto.ts | 8 --- src/device/services/device.service.ts | 79 +++++++++++++++++---- 4 files changed, 99 insertions(+), 30 deletions(-) diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index a3bbadd..d8149a7 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -9,6 +9,7 @@ import { HttpException, HttpStatus, UseGuards, + Req, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { @@ -23,6 +24,8 @@ import { ControlDeviceDto } from '../dtos/control.device.dto'; import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckGroupGuard } from 'src/guards/group.guard'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard'; +import { CheckUserHaveControllablePermission } from 'src/guards/user.device.controllable.permission.guard'; @ApiTags('Device Module') @Controller({ @@ -37,10 +40,13 @@ export class DeviceController { @Get('room') async getDevicesByRoomId( @Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, + @Req() req: any, ) { try { + const userUuid = req.user.uuid; return await this.deviceService.getDevicesByRoomId( getDeviceByRoomUuidDto, + userUuid, ); } catch (error) { throw new HttpException( @@ -68,10 +74,13 @@ export class DeviceController { @Get('group') async getDevicesByGroupId( @Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto, + @Req() req: any, ) { try { + const userUuid = req.user.uuid; return await this.deviceService.getDevicesByGroupId( getDeviceByGroupIdDto, + userUuid, ); } catch (error) { throw new HttpException( @@ -94,11 +103,18 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, CheckUserHavePermission) @Get(':deviceUuid') - async getDeviceDetailsByDeviceId(@Param('deviceUuid') deviceUuid: string) { + async getDeviceDetailsByDeviceId( + @Param('deviceUuid') deviceUuid: string, + @Req() req: any, + ) { try { - return await this.deviceService.getDeviceDetailsByDeviceId(deviceUuid); + const userUuid = req.user.uuid; + return await this.deviceService.getDeviceDetailsByDeviceId( + deviceUuid, + userUuid, + ); } catch (error) { throw new HttpException( error.message || 'Internal server error', @@ -107,7 +123,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, CheckUserHavePermission) @Get(':deviceUuid/functions') async getDeviceInstructionByDeviceId( @Param('deviceUuid') deviceUuid: string, @@ -124,7 +140,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard, CheckUserHavePermission) @Get(':deviceUuid/functions/status') async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) { try { @@ -138,11 +154,17 @@ export class DeviceController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Post('control') - async controlDevice(@Body() controlDeviceDto: ControlDeviceDto) { + @UseGuards(JwtAuthGuard, CheckUserHaveControllablePermission) + @Post(':deviceUuid/control') + async controlDevice( + @Body() controlDeviceDto: ControlDeviceDto, + @Param('deviceUuid') deviceUuid: string, + ) { try { - return await this.deviceService.controlDevice(controlDeviceDto); + return await this.deviceService.controlDevice( + controlDeviceDto, + deviceUuid, + ); } catch (error) { throw new HttpException( error.message || 'Internal server error', diff --git a/src/device/device.module.ts b/src/device/device.module.ts index ba9b019..e48861b 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -12,6 +12,7 @@ import { GroupDeviceRepository } from '@app/common/modules/group-device/reposito import { GroupRepository } from '@app/common/modules/group/repositories'; import { GroupRepositoryModule } from '@app/common/modules/group/group.repository.module'; import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { UserRepository } from '@app/common/modules/user/repositories'; @Module({ imports: [ ConfigModule, @@ -29,6 +30,7 @@ import { DeviceUserPermissionRepository } from '@app/common/modules/device-user- DeviceRepository, GroupDeviceRepository, GroupRepository, + UserRepository, ], exports: [DeviceService], }) diff --git a/src/device/dtos/control.device.dto.ts b/src/device/dtos/control.device.dto.ts index 1382dc0..ab2125d 100644 --- a/src/device/dtos/control.device.dto.ts +++ b/src/device/dtos/control.device.dto.ts @@ -2,14 +2,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString } from 'class-validator'; export class ControlDeviceDto { - @ApiProperty({ - description: 'deviceUuid', - required: true, - }) - @IsString() - @IsNotEmpty() - public deviceUuid: string; - @ApiProperty({ description: 'code', required: true, diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index ebd373d..29bddca 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -26,6 +26,7 @@ import { ControlDeviceDto } from '../dtos/control.device.dto'; import { convertKeysToCamelCase } from '@app/common/helper/camelCaseConverter'; import { DeviceRepository } from '@app/common/modules/device/repositories'; import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories'; +import { PermissionType } from '@app/common/constants/permission-type.enum'; @Injectable() export class DeviceService { @@ -47,13 +48,25 @@ export class DeviceService { async getDevicesByRoomId( getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, + userUuid: string, ): Promise { try { const devices = await this.deviceRepository.find({ where: { spaceDevice: { uuid: getDeviceByRoomUuidDto.roomUuid }, + permission: { + userUuid, + permissionType: { + type: PermissionType.READ || PermissionType.CONTROLLABLE, + }, + }, }, - relations: ['spaceDevice', 'productDevice'], + relations: [ + 'spaceDevice', + 'productDevice', + 'permission', + 'permission.permissionType', + ], }); const devicesData = await Promise.all( devices.map(async (device) => { @@ -64,6 +77,7 @@ export class DeviceService { uuid: device.uuid, productUuid: device.productDevice.uuid, productType: device.productDevice.prodType, + permissionType: device.permission[0].permissionType.type, } as GetDeviceDetailsInterface; }), ); @@ -77,11 +91,29 @@ export class DeviceService { } } - async getDevicesByGroupId(getDeviceByGroupIdDto: GetDeviceByGroupIdDto) { + async getDevicesByGroupId( + getDeviceByGroupIdDto: GetDeviceByGroupIdDto, + userUuid: string, + ) { try { const groupDevices = await this.groupDeviceRepository.find({ - where: { group: { uuid: getDeviceByGroupIdDto.groupUuid } }, - relations: ['device'], + where: { + group: { uuid: getDeviceByGroupIdDto.groupUuid }, + device: { + permission: { + userUuid, + permissionType: { + type: PermissionType.READ || PermissionType.CONTROLLABLE, + }, + }, + }, + }, + relations: [ + 'device', + 'device.productDevice', + 'device.permission', + 'device.permission.permissionType', + ], }); const devicesData = await Promise.all( groupDevices.map(async (device) => { @@ -89,9 +121,10 @@ export class DeviceService { ...(await this.getDeviceDetailsByDeviceIdTuya( device.device.deviceTuyaUuid, )), - uuid: device.uuid, + uuid: device.device.uuid, productUuid: device.device.productDevice.uuid, productType: device.device.productDevice.prodType, + permissionType: device.device.permission[0].permissionType.type, } as GetDeviceDetailsInterface; }), ); @@ -104,7 +137,6 @@ export class DeviceService { ); } } - async addDeviceInRoom(addDeviceInRoomDto: AddDeviceInRoomDto) { try { const device = await this.getDeviceDetailsByDeviceIdTuya( @@ -158,11 +190,11 @@ export class DeviceService { } } - async controlDevice(controlDeviceDto: ControlDeviceDto) { + async controlDevice(controlDeviceDto: ControlDeviceDto, deviceUuid: string) { try { const deviceDetails = await this.deviceRepository.findOne({ where: { - uuid: controlDeviceDto.deviceUuid, + uuid: deviceUuid, }, }); @@ -183,7 +215,10 @@ export class DeviceService { ); } } catch (error) { - throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + throw new HttpException( + error.message || 'Device Not Found', + error.status || HttpStatus.NOT_FOUND, + ); } } async controlDeviceTuya( @@ -211,13 +246,18 @@ export class DeviceService { } } - async getDeviceDetailsByDeviceId(deviceUuid: string) { + async getDeviceDetailsByDeviceId(deviceUuid: string, userUuid: string) { try { + const userDevicePermission = await this.getUserDevicePermission( + userUuid, + deviceUuid, + ); + const deviceDetails = await this.deviceRepository.findOne({ where: { uuid: deviceUuid, }, - relations: ['productDevice', 'permission'], + relations: ['productDevice'], }); if (!deviceDetails) { @@ -227,16 +267,19 @@ export class DeviceService { const response = await this.getDeviceDetailsByDeviceIdTuya( deviceDetails.deviceTuyaUuid, ); - console.log('response', deviceDetails); return { ...response, uuid: deviceDetails.uuid, productUuid: deviceDetails.productDevice.uuid, productType: deviceDetails.productDevice.prodType, + permissionType: userDevicePermission, }; } catch (error) { - throw new HttpException('Device Not Found', HttpStatus.NOT_FOUND); + throw new HttpException( + error.message || 'Device Not Found', + HttpStatus.NOT_FOUND, + ); } } async getDeviceDetailsByDeviceIdTuya( @@ -259,6 +302,7 @@ export class DeviceService { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { productName, productId, ...rest } = camelCaseResponse.result; + return { ...rest, productUuid: product.uuid, @@ -372,4 +416,13 @@ export class DeviceService { ); } } + private async getUserDevicePermission(userUuid: string, deviceUuid: string) { + const device = await this.deviceRepository.findOne({ + where: { + uuid: deviceUuid, + }, + relations: ['permission', 'permission.permissionType'], + }); + return device.permission[0].permissionType.type; + } }