From aa7a09391f8b07b06fb79098c867af8ea594f7db Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 18:38:59 +0300 Subject: [PATCH 01/12] Remove unused DeviceUserType related files and imports --- .../device/device.repository.module.ts | 6 +-- .../device/dtos/device-user-type.dto.ts | 19 --------- libs/common/src/modules/device/dtos/index.ts | 1 - .../entities/device-user-type.entity.ts | 42 ------------------- .../modules/device/entities/device.entity.ts | 2 +- .../src/modules/device/entities/index.ts | 1 - .../device-user-type.repository.ts | 10 ----- .../src/modules/device/repositories/index.ts | 1 - 8 files changed, 3 insertions(+), 79 deletions(-) delete mode 100644 libs/common/src/modules/device/dtos/device-user-type.dto.ts delete mode 100644 libs/common/src/modules/device/entities/device-user-type.entity.ts delete mode 100644 libs/common/src/modules/device/repositories/device-user-type.repository.ts diff --git a/libs/common/src/modules/device/device.repository.module.ts b/libs/common/src/modules/device/device.repository.module.ts index b3d35d7..438e268 100644 --- a/libs/common/src/modules/device/device.repository.module.ts +++ b/libs/common/src/modules/device/device.repository.module.ts @@ -1,13 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { DeviceEntity, DeviceUserPermissionEntity } from './entities'; +import { DeviceEntity } from './entities'; @Module({ providers: [], exports: [], controllers: [], - imports: [ - TypeOrmModule.forFeature([DeviceEntity, DeviceUserPermissionEntity]), - ], + imports: [TypeOrmModule.forFeature([DeviceEntity])], }) export class DeviceRepositoryModule {} diff --git a/libs/common/src/modules/device/dtos/device-user-type.dto.ts b/libs/common/src/modules/device/dtos/device-user-type.dto.ts deleted file mode 100644 index 0571356..0000000 --- a/libs/common/src/modules/device/dtos/device-user-type.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class DeviceUserTypeDto { - @IsString() - @IsNotEmpty() - public uuid: string; - - @IsString() - @IsNotEmpty() - public userUuid: string; - - @IsString() - @IsNotEmpty() - public deviceUuid: string; - - @IsString() - @IsNotEmpty() - public permissionTypeUuid: string; -} diff --git a/libs/common/src/modules/device/dtos/index.ts b/libs/common/src/modules/device/dtos/index.ts index 9d647c8..343f2bd 100644 --- a/libs/common/src/modules/device/dtos/index.ts +++ b/libs/common/src/modules/device/dtos/index.ts @@ -1,2 +1 @@ export * from './device.dto'; -export * from './device-user-type.dto'; diff --git a/libs/common/src/modules/device/entities/device-user-type.entity.ts b/libs/common/src/modules/device/entities/device-user-type.entity.ts deleted file mode 100644 index 904b18a..0000000 --- a/libs/common/src/modules/device/entities/device-user-type.entity.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; -import { AbstractEntity } from '../../abstract/entities/abstract.entity'; -import { DeviceUserTypeDto } from '../dtos/device-user-type.dto'; -import { DeviceEntity } from './device.entity'; -import { PermissionTypeEntity } from '../../permission/entities'; - -@Entity({ name: 'device-user-permission' }) -export class DeviceUserPermissionEntity extends AbstractEntity { - @Column({ - nullable: false, - }) - public userUuid: string; - - @Column({ - nullable: false, - }) - deviceUuid: string; - - @Column({ - nullable: false, - }) - public permissionTypeUuid: string; - - @ManyToOne(() => DeviceEntity, { - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }) - @JoinColumn({ name: 'device_uuid', referencedColumnName: 'uuid' }) - device: DeviceEntity; - - @ManyToOne(() => PermissionTypeEntity, { - onDelete: 'CASCADE', - onUpdate: 'CASCADE', - }) - @JoinColumn({ name: 'permission_type_uuid', referencedColumnName: 'uuid' }) - type: PermissionTypeEntity; - - constructor(partial: Partial) { - super(); - Object.assign(this, partial); - } -} diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index df27e44..a7f5548 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -1,10 +1,10 @@ import { Column, Entity, ManyToOne, OneToMany, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceDto } from '../dtos/device.dto'; -import { DeviceUserPermissionEntity } from './device-user-type.entity'; import { GroupDeviceEntity } from '../../group-device/entities'; import { SpaceEntity } from '../../space/entities'; import { ProductEntity } from '../../product/entities'; +import { DeviceUserPermissionEntity } from '../../device-user-permission/entities'; @Entity({ name: 'device' }) @Unique(['spaceDevice', 'deviceTuyaUuid']) diff --git a/libs/common/src/modules/device/entities/index.ts b/libs/common/src/modules/device/entities/index.ts index d6cf7b2..64911c7 100644 --- a/libs/common/src/modules/device/entities/index.ts +++ b/libs/common/src/modules/device/entities/index.ts @@ -1,2 +1 @@ export * from './device.entity'; -export * from './device-user-type.entity'; diff --git a/libs/common/src/modules/device/repositories/device-user-type.repository.ts b/libs/common/src/modules/device/repositories/device-user-type.repository.ts deleted file mode 100644 index e3d2176..0000000 --- a/libs/common/src/modules/device/repositories/device-user-type.repository.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource, Repository } from 'typeorm'; -import { Injectable } from '@nestjs/common'; -import { DeviceUserPermissionEntity } from '../entities'; - -@Injectable() -export class DeviceUserTypeRepository extends Repository { - constructor(private dataSource: DataSource) { - super(DeviceUserPermissionEntity, dataSource.createEntityManager()); - } -} diff --git a/libs/common/src/modules/device/repositories/index.ts b/libs/common/src/modules/device/repositories/index.ts index f91e07f..bf59e16 100644 --- a/libs/common/src/modules/device/repositories/index.ts +++ b/libs/common/src/modules/device/repositories/index.ts @@ -1,2 +1 @@ export * from './device.repository'; -export * from './device-user-type.repository'; From 46bc98da550df510d6c30e90c2ba2d65a80bb24f Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 18:39:23 +0300 Subject: [PATCH 02/12] Add device user permission module, DTOs, entities, and repositories --- ...evice.user.permission.repository.module.ts | 10 +++++ .../dtos/device.user.permission.dto.ts | 19 +++++++++ .../device-user-permission/dtos/index.ts | 1 + .../entities/device.user.permission.entity.ts | 42 +++++++++++++++++++ .../device-user-permission/entities/index.ts | 1 + .../device.user.permission.repository.ts | 10 +++++ .../repositories/index.ts | 1 + 7 files changed, 84 insertions(+) create mode 100644 libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts create mode 100644 libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts create mode 100644 libs/common/src/modules/device-user-permission/dtos/index.ts create mode 100644 libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts create mode 100644 libs/common/src/modules/device-user-permission/entities/index.ts create mode 100644 libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts create mode 100644 libs/common/src/modules/device-user-permission/repositories/index.ts diff --git a/libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts b/libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts new file mode 100644 index 0000000..d950fd8 --- /dev/null +++ b/libs/common/src/modules/device-user-permission/device.user.permission.repository.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { DeviceUserPermissionEntity } from './entities'; +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([DeviceUserPermissionEntity])], +}) +export class DeviceUserPermissionRepositoryModule {} diff --git a/libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts b/libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts new file mode 100644 index 0000000..e78ea3c --- /dev/null +++ b/libs/common/src/modules/device-user-permission/dtos/device.user.permission.dto.ts @@ -0,0 +1,19 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class DeviceUserPermissionDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public userUuid: string; + + @IsString() + @IsNotEmpty() + public deviceUuid: string; + + @IsString() + @IsNotEmpty() + public permissionTypeUuid: string; +} diff --git a/libs/common/src/modules/device-user-permission/dtos/index.ts b/libs/common/src/modules/device-user-permission/dtos/index.ts new file mode 100644 index 0000000..b95b695 --- /dev/null +++ b/libs/common/src/modules/device-user-permission/dtos/index.ts @@ -0,0 +1 @@ +export * from './device.user.permission.dto'; diff --git a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts new file mode 100644 index 0000000..fa6e986 --- /dev/null +++ b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts @@ -0,0 +1,42 @@ +import { Column, Entity, ManyToOne } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { DeviceUserPermissionDto } from '../dtos'; +import { PermissionTypeEntity } from '../../permission/entities'; +import { DeviceEntity } from '../../device/entities'; +import { UserEntity } from '../../user/entities'; + +@Entity({ name: 'device-user-permission' }) +export class DeviceUserPermissionEntity extends AbstractEntity { + @Column({ + nullable: false, + }) + public userUuid: string; + + @Column({ + nullable: false, + }) + deviceUuid: string; + + @ManyToOne(() => DeviceEntity, (device) => device.permission, { + nullable: false, + }) + device: DeviceEntity; + + @ManyToOne( + () => PermissionTypeEntity, + (permissionType) => permissionType.permission, + { + nullable: false, + }, + ) + permissionType: PermissionTypeEntity; + + @ManyToOne(() => UserEntity, (user) => user.userPermission, { + nullable: false, + }) + user: UserEntity; + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/device-user-permission/entities/index.ts b/libs/common/src/modules/device-user-permission/entities/index.ts new file mode 100644 index 0000000..9e95d82 --- /dev/null +++ b/libs/common/src/modules/device-user-permission/entities/index.ts @@ -0,0 +1 @@ +export * from './device.user.permission.entity'; diff --git a/libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts b/libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts new file mode 100644 index 0000000..a1776b3 --- /dev/null +++ b/libs/common/src/modules/device-user-permission/repositories/device.user.permission.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { DeviceUserPermissionEntity } from '../entities'; + +@Injectable() +export class DeviceUserPermissionRepository extends Repository { + constructor(private dataSource: DataSource) { + super(DeviceUserPermissionEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/device-user-permission/repositories/index.ts b/libs/common/src/modules/device-user-permission/repositories/index.ts new file mode 100644 index 0000000..6957209 --- /dev/null +++ b/libs/common/src/modules/device-user-permission/repositories/index.ts @@ -0,0 +1 @@ +export * from './device.user.permission.repository'; From b8ae3540c3eed045f9cc411f78d0abbebcdc9d9c Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 18:39:38 +0300 Subject: [PATCH 03/12] Update entity references in permission and user entities --- .../src/modules/permission/entities/permission.entity.ts | 4 ++-- libs/common/src/modules/user/entities/user.entity.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libs/common/src/modules/permission/entities/permission.entity.ts b/libs/common/src/modules/permission/entities/permission.entity.ts index f2ba950..3ee3943 100644 --- a/libs/common/src/modules/permission/entities/permission.entity.ts +++ b/libs/common/src/modules/permission/entities/permission.entity.ts @@ -2,7 +2,7 @@ import { Column, Entity, OneToMany } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { PermissionType } from '@app/common/constants/permission-type.enum'; import { PermissionTypeDto } from '../dtos/permission.dto'; -import { DeviceUserPermissionEntity } from '../../device/entities'; +import { DeviceUserPermissionEntity } from '../../device-user-permission/entities'; @Entity({ name: 'permission-type' }) export class PermissionTypeEntity extends AbstractEntity { @@ -14,7 +14,7 @@ export class PermissionTypeEntity extends AbstractEntity { @OneToMany( () => DeviceUserPermissionEntity, - (permission) => permission.type, + (permission) => permission.permissionType, { nullable: true, onDelete: 'CASCADE', diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index 0981bcc..4268ae8 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -1,3 +1,4 @@ +import { DeviceUserPermissionEntity } from '../../device-user-permission/entities/device.user.permission.entity'; import { Column, Entity, OneToMany } from 'typeorm'; import { UserDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; @@ -51,6 +52,11 @@ export class UserEntity extends AbstractEntity { @OneToMany(() => UserSpaceEntity, (userSpace) => userSpace.user) userSpaces: UserSpaceEntity[]; + @OneToMany( + () => DeviceUserPermissionEntity, + (userPermission) => userPermission.user, + ) + userPermission: DeviceUserPermissionEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); From e48c6b4a912f4648508535aa18edc712e91a394a Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 18:40:06 +0300 Subject: [PATCH 04/12] Update imports in database module --- libs/common/src/database/database.module.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 911cfb5..146b5ba 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -6,16 +6,14 @@ import { UserEntity } from '../modules/user/entities/user.entity'; import { UserSessionEntity } from '../modules/session/entities/session.entity'; import { UserOtpEntity } from '../modules/user-otp/entities'; import { ProductEntity } from '../modules/product/entities'; -import { - DeviceEntity, - DeviceUserPermissionEntity, -} from '../modules/device/entities'; +import { DeviceEntity } from '../modules/device/entities'; import { PermissionTypeEntity } from '../modules/permission/entities'; import { SpaceEntity } from '../modules/space/entities'; import { SpaceTypeEntity } from '../modules/space-type/entities'; import { UserSpaceEntity } from '../modules/user-space/entities'; import { GroupEntity } from '../modules/group/entities'; import { GroupDeviceEntity } from '../modules/group-device/entities'; +import { DeviceUserPermissionEntity } from '../modules/device-user-permission/entities'; @Module({ imports: [ @@ -43,6 +41,7 @@ import { GroupDeviceEntity } from '../modules/group-device/entities'; UserSpaceEntity, GroupEntity, GroupDeviceEntity, + DeviceUserPermissionEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), From 11c3b8015bccd2e3d5af21d479ebe2028003e9f3 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 18:40:28 +0300 Subject: [PATCH 05/12] Refactor imports and add relations in DeviceService --- src/device/device.module.ts | 8 +++----- src/device/services/device.service.ts | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/device/device.module.ts b/src/device/device.module.ts index a9fcdc4..ba9b019 100644 --- a/src/device/device.module.ts +++ b/src/device/device.module.ts @@ -5,15 +5,13 @@ import { ConfigModule } from '@nestjs/config'; import { ProductRepositoryModule } from '@app/common/modules/product/product.repository.module'; import { ProductRepository } from '@app/common/modules/product/repositories'; import { DeviceRepositoryModule } from '@app/common/modules/device'; -import { - DeviceRepository, - DeviceUserTypeRepository, -} from '@app/common/modules/device/repositories'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; import { SpaceRepository } from '@app/common/modules/space/repositories'; import { GroupDeviceRepository } from '@app/common/modules/group-device/repositories'; 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'; @Module({ imports: [ ConfigModule, @@ -25,7 +23,7 @@ import { GroupRepositoryModule } from '@app/common/modules/group/group.repositor providers: [ DeviceService, ProductRepository, - DeviceUserTypeRepository, + DeviceUserPermissionRepository, PermissionTypeRepository, SpaceRepository, DeviceRepository, diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 112deb5..ed61330 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -215,6 +215,7 @@ export class DeviceService { where: { uuid: deviceUuid, }, + relations: ['productDevice', 'permission'], }); if (!deviceDetails) { @@ -224,6 +225,7 @@ export class DeviceService { const response = await this.getDeviceDetailsByDeviceIdTuya( deviceDetails.deviceTuyaUuid, ); + console.log('response', deviceDetails); return { ...response, From 5e3ad8a52313c8d43affdc3708e19cc053e2f474 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 18:40:44 +0300 Subject: [PATCH 06/12] Update device permission guard and user device permission service --- src/guards/device.permission.guard.ts | 8 ++++---- .../services/user-device-permission.service.ts | 14 ++++++++------ .../user-device-permission.module.ts | 8 +++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/guards/device.permission.guard.ts b/src/guards/device.permission.guard.ts index 3fc80af..1ec21a9 100644 --- a/src/guards/device.permission.guard.ts +++ b/src/guards/device.permission.guard.ts @@ -1,6 +1,6 @@ import { PermissionType } from '@app/common/constants/permission-type.enum'; import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { DeviceUserTypeRepository } from '@app/common/modules/device/repositories'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; import { Injectable, @@ -18,7 +18,7 @@ import { Reflector } from '@nestjs/core'; export class DevicePermissionGuard implements CanActivate { constructor( private reflector: Reflector, - private readonly deviceUserTypeRepository: DeviceUserTypeRepository, + private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository, private readonly permissionTypeRepository: PermissionTypeRepository, ) {} @@ -52,7 +52,7 @@ export class DevicePermissionGuard implements CanActivate { requirePermission, ) { const [userPermissionDetails, permissionDetails] = await Promise.all([ - this.deviceUserTypeRepository.findOne({ + this.deviceUserPermissionRepository.findOne({ where: { deviceUuid: deviceId, userUuid: userId }, }), this.permissionTypeRepository.findOne({ @@ -64,7 +64,7 @@ export class DevicePermissionGuard implements CanActivate { if (!userPermissionDetails) { throw new BadRequestException('User Permission Details Not Found'); } - if (userPermissionDetails.permissionTypeUuid !== permissionDetails.uuid) { + if (userPermissionDetails.permissionType.uuid !== permissionDetails.uuid) { throw new BadRequestException( `User Does not have a ${requirePermission}`, ); diff --git a/src/user-device-permission/services/user-device-permission.service.ts b/src/user-device-permission/services/user-device-permission.service.ts index e50d59f..64073a1 100644 --- a/src/user-device-permission/services/user-device-permission.service.ts +++ b/src/user-device-permission/services/user-device-permission.service.ts @@ -1,16 +1,16 @@ -import { DeviceUserTypeRepository } from '@app/common/modules/device/repositories'; import { Injectable } from '@nestjs/common'; import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; @Injectable() export class UserDevicePermissionService { constructor( - private readonly deviceUserTypeRepository: DeviceUserTypeRepository, + private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository, ) {} async addUserPermission(userDevicePermissionDto: UserDevicePermissionAddDto) { - return await this.deviceUserTypeRepository.save({ + return await this.deviceUserPermissionRepository.save({ userUuid: userDevicePermissionDto.userId, deviceUuid: userDevicePermissionDto.deviceId, permissionTypeUuid: userDevicePermissionDto.permissionTypeId, @@ -21,16 +21,18 @@ export class UserDevicePermissionService { userId: string, userDevicePermissionEditDto: UserDevicePermissionEditDto, ) { - return await this.deviceUserTypeRepository.update( + return await this.deviceUserPermissionRepository.update( { userUuid: userId }, { deviceUuid: userDevicePermissionEditDto.deviceId, - permissionTypeUuid: userDevicePermissionEditDto.permissionTypeId, + permissionType: { + uuid: userDevicePermissionEditDto.permissionTypeId, + }, }, ); } async fetchuserPermission() { - return await this.deviceUserTypeRepository.find(); + return await this.deviceUserPermissionRepository.find(); } } diff --git a/src/user-device-permission/user-device-permission.module.ts b/src/user-device-permission/user-device-permission.module.ts index edd9991..b7e5dbd 100644 --- a/src/user-device-permission/user-device-permission.module.ts +++ b/src/user-device-permission/user-device-permission.module.ts @@ -1,18 +1,16 @@ import { DeviceRepositoryModule } from '@app/common/modules/device'; -import { - DeviceRepository, - DeviceUserTypeRepository, -} from '@app/common/modules/device/repositories'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { UserDevicePermissionService } from './services/user-device-permission.service'; import { UserDevicePermissionController } from './controllers/user-device-permission.controller'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [UserDevicePermissionController], providers: [ - DeviceUserTypeRepository, + DeviceUserPermissionRepository, DeviceRepository, UserDevicePermissionService, ], From 5aa69255be91746391c3ea2f257523f715e682d1 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 19:47:50 +0300 Subject: [PATCH 07/12] Add Unique constraint to DeviceUserPermissionEntity --- .../entities/device.user.permission.entity.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts index fa6e986..4577e29 100644 --- a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts +++ b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts @@ -1,4 +1,4 @@ -import { Column, Entity, ManyToOne } from 'typeorm'; +import { Column, Entity, ManyToOne, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { DeviceUserPermissionDto } from '../dtos'; import { PermissionTypeEntity } from '../../permission/entities'; @@ -6,6 +6,7 @@ import { DeviceEntity } from '../../device/entities'; import { UserEntity } from '../../user/entities'; @Entity({ name: 'device-user-permission' }) +@Unique(['userUuid', 'deviceUuid', 'permissionType']) export class DeviceUserPermissionEntity extends AbstractEntity { @Column({ nullable: false, From fa6929a4e5308b4aa505c4940ba49ccefe81d7d0 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 19:48:03 +0300 Subject: [PATCH 08/12] Add user device permission CRUD operations --- .../controllers/index.ts | 1 + .../user-device-permission.controller.ts | 54 ++++++--- src/user-device-permission/dtos/index.ts | 2 + .../dtos/user-device-permission.add.dto.ts | 19 ++-- .../dtos/user-device-permission.edit.dto.ts | 18 ++- src/user-device-permission/services/index.ts | 1 + .../user-device-permission.service.ts | 103 +++++++++++++++--- .../user-device-permission.module.ts | 2 + 8 files changed, 155 insertions(+), 45 deletions(-) diff --git a/src/user-device-permission/controllers/index.ts b/src/user-device-permission/controllers/index.ts index e69de29..9ea28cf 100644 --- a/src/user-device-permission/controllers/index.ts +++ b/src/user-device-permission/controllers/index.ts @@ -0,0 +1 @@ +export * from './user-device-permission.controller'; diff --git a/src/user-device-permission/controllers/user-device-permission.controller.ts b/src/user-device-permission/controllers/user-device-permission.controller.ts index f5e253f..825694f 100644 --- a/src/user-device-permission/controllers/user-device-permission.controller.ts +++ b/src/user-device-permission/controllers/user-device-permission.controller.ts @@ -1,7 +1,9 @@ import { Body, Controller, + Delete, Get, + HttpException, HttpStatus, Param, Post, @@ -24,8 +26,8 @@ export class UserDevicePermissionController { private readonly userDevicePermissionService: UserDevicePermissionService, ) {} - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + // @ApiBearerAuth() + // @UseGuards(JwtAuthGuard) @Post('add') async addDevicePermission( @Body() userDevicePermissionDto: UserDevicePermissionAddDto, @@ -40,40 +42,45 @@ export class UserDevicePermissionController { message: 'User Permission for Devices Added Successfully', data: addDetails, }; - } catch (err) { - throw new Error(err); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); } } @ApiBearerAuth() @UseGuards(JwtAuthGuard) - @Put('edit/:userId') + @Put('edit/:devicePermissionUuid') async editDevicePermission( - @Param('userId') userId: string, + @Param('devicePermissionUuid') devicePermissionUuid: string, @Body() userDevicePermissionEditDto: UserDevicePermissionEditDto, ) { try { await this.userDevicePermissionService.editUserPermission( - userId, + devicePermissionUuid, userDevicePermissionEditDto, ); return { statusCode: HttpStatus.OK, message: 'User Permission for Devices Updated Successfully', - data: {}, }; - } catch (err) { - throw new Error(err); + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); } } @ApiBearerAuth() @UseGuards(JwtAuthGuard) - @Get('list') - async fetchDevicePermission() { + @Get(':deviceUuid/list') + async fetchDevicePermission(@Param('deviceUuid') deviceUuid: string) { try { const deviceDetails = - await this.userDevicePermissionService.fetchuserPermission(); + await this.userDevicePermissionService.fetchUserPermission(deviceUuid); return { statusCode: HttpStatus.OK, message: 'Device Details fetched Successfully', @@ -83,4 +90,25 @@ export class UserDevicePermissionController { throw new Error(err); } } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Delete(':devicePermissionUuid') + async deleteDevicePermission( + @Param('devicePermissionUuid') devicePermissionUuid: string, + ) { + try { + await this.userDevicePermissionService.deleteDevicePermission( + devicePermissionUuid, + ); + return { + statusCode: HttpStatus.OK, + message: 'User Permission for Devices Deleted Successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } } diff --git a/src/user-device-permission/dtos/index.ts b/src/user-device-permission/dtos/index.ts index e69de29..7792d07 100644 --- a/src/user-device-permission/dtos/index.ts +++ b/src/user-device-permission/dtos/index.ts @@ -0,0 +1,2 @@ +export * from './user-device-permission.add.dto'; +export * from './user-device-permission.edit.dto'; diff --git a/src/user-device-permission/dtos/user-device-permission.add.dto.ts b/src/user-device-permission/dtos/user-device-permission.add.dto.ts index c7459df..12ee133 100644 --- a/src/user-device-permission/dtos/user-device-permission.add.dto.ts +++ b/src/user-device-permission/dtos/user-device-permission.add.dto.ts @@ -1,28 +1,29 @@ +import { PermissionType } from '@app/common/constants/permission-type.enum'; import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; export class UserDevicePermissionAddDto { @ApiProperty({ - description: 'user id', + description: 'user uuid', required: true, }) @IsString() @IsNotEmpty() - userId: string; + userUuid: string; @ApiProperty({ - description: 'permission type id', + description: 'permission type', + enum: PermissionType, required: true, }) - @IsString() - @IsNotEmpty() - permissionTypeId: string; + @IsEnum(PermissionType) + permissionType: PermissionType; @ApiProperty({ - description: 'device id', + description: 'device uuid', required: true, }) @IsString() @IsNotEmpty() - deviceId: string; + deviceUuid: string; } diff --git a/src/user-device-permission/dtos/user-device-permission.edit.dto.ts b/src/user-device-permission/dtos/user-device-permission.edit.dto.ts index 7cc4fce..ce537e1 100644 --- a/src/user-device-permission/dtos/user-device-permission.edit.dto.ts +++ b/src/user-device-permission/dtos/user-device-permission.edit.dto.ts @@ -1,7 +1,13 @@ -import { OmitType } from '@nestjs/swagger'; -import { UserDevicePermissionAddDto } from './user-device-permission.add.dto'; +import { PermissionType } from '@app/common/constants/permission-type.enum'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum } from 'class-validator'; -export class UserDevicePermissionEditDto extends OmitType( - UserDevicePermissionAddDto, - ['userId'], -) {} +export class UserDevicePermissionEditDto { + @ApiProperty({ + description: 'permission type', + enum: PermissionType, + required: true, + }) + @IsEnum(PermissionType) + permissionType: PermissionType; +} diff --git a/src/user-device-permission/services/index.ts b/src/user-device-permission/services/index.ts index e69de29..1dc9e53 100644 --- a/src/user-device-permission/services/index.ts +++ b/src/user-device-permission/services/index.ts @@ -0,0 +1 @@ +export * from './user-device-permission.service'; diff --git a/src/user-device-permission/services/user-device-permission.service.ts b/src/user-device-permission/services/user-device-permission.service.ts index 64073a1..b02cb6a 100644 --- a/src/user-device-permission/services/user-device-permission.service.ts +++ b/src/user-device-permission/services/user-device-permission.service.ts @@ -1,38 +1,107 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; @Injectable() export class UserDevicePermissionService { constructor( private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository, + private readonly permissionTypeRepository: PermissionTypeRepository, ) {} async addUserPermission(userDevicePermissionDto: UserDevicePermissionAddDto) { - return await this.deviceUserPermissionRepository.save({ - userUuid: userDevicePermissionDto.userId, - deviceUuid: userDevicePermissionDto.deviceId, - permissionTypeUuid: userDevicePermissionDto.permissionTypeId, - }); + try { + const permissionType = await this.getPermissionType( + userDevicePermissionDto.permissionType, + ); + return await this.deviceUserPermissionRepository.save({ + userUuid: userDevicePermissionDto.userUuid, + deviceUuid: userDevicePermissionDto.deviceUuid, + permissionType: { + uuid: permissionType.uuid, + }, + }); + } catch (error) { + if (error.code === '23505') { + throw new HttpException( + 'This Permission already belongs to this user', + HttpStatus.BAD_REQUEST, + ); + } + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } async editUserPermission( - userId: string, + devicePermissionUuid: string, userDevicePermissionEditDto: UserDevicePermissionEditDto, ) { - return await this.deviceUserPermissionRepository.update( - { userUuid: userId }, - { - deviceUuid: userDevicePermissionEditDto.deviceId, - permissionType: { - uuid: userDevicePermissionEditDto.permissionTypeId, + try { + const permissionType = await this.getPermissionType( + userDevicePermissionEditDto.permissionType, + ); + return await this.deviceUserPermissionRepository.update( + { uuid: devicePermissionUuid }, + { + permissionType: { + uuid: permissionType.uuid, + }, }, - }, - ); + ); + } catch (error) { + if (error.code === '23505') { + throw new HttpException( + 'This Permission already belongs to this user', + HttpStatus.BAD_REQUEST, + ); + } + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } - async fetchuserPermission() { - return await this.deviceUserPermissionRepository.find(); + async fetchUserPermission(deviceUuid: string) { + const devicePermissions = await this.deviceUserPermissionRepository.find({ + where: { + deviceUuid: deviceUuid, + }, + relations: ['permissionType', 'user'], + }); + return devicePermissions.map((permission) => { + return { + uuid: permission.uuid, + deviceUuid: permission.deviceUuid, + firstName: permission.user.firstName, + lastName: permission.user.lastName, + email: permission.user.email, + permissionType: permission.permissionType.type, + }; + }); + } + private async getPermissionType(permissionType: string) { + return await this.permissionTypeRepository.findOne({ + where: { + type: permissionType, + }, + }); + } + async deleteDevicePermission(devicePermissionUuid: string) { + try { + return await this.deviceUserPermissionRepository.delete({ + uuid: devicePermissionUuid, + }); + } catch (error) { + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } } diff --git a/src/user-device-permission/user-device-permission.module.ts b/src/user-device-permission/user-device-permission.module.ts index b7e5dbd..e2a8b46 100644 --- a/src/user-device-permission/user-device-permission.module.ts +++ b/src/user-device-permission/user-device-permission.module.ts @@ -5,12 +5,14 @@ import { ConfigModule } from '@nestjs/config'; import { UserDevicePermissionService } from './services/user-device-permission.service'; import { UserDevicePermissionController } from './controllers/user-device-permission.controller'; import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; @Module({ imports: [ConfigModule, DeviceRepositoryModule], controllers: [UserDevicePermissionController], providers: [ DeviceUserPermissionRepository, + PermissionTypeRepository, DeviceRepository, UserDevicePermissionService, ], From fae2fff2bab12d7b0e4652f5aa3789d9851bf679 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 21:32:00 +0300 Subject: [PATCH 09/12] Remove unnecessary field from Unique constraint --- .../entities/device.user.permission.entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts index 4577e29..d136df7 100644 --- a/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts +++ b/libs/common/src/modules/device-user-permission/entities/device.user.permission.entity.ts @@ -6,7 +6,7 @@ import { DeviceEntity } from '../../device/entities'; import { UserEntity } from '../../user/entities'; @Entity({ name: 'device-user-permission' }) -@Unique(['userUuid', 'deviceUuid', 'permissionType']) +@Unique(['userUuid', 'deviceUuid']) export class DeviceUserPermissionEntity extends AbstractEntity { @Column({ nullable: false, 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 10/12] 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; + } } From 16ed5b33fc8343d76108f57525a7970d7fb7a6cd Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 21:32:31 +0300 Subject: [PATCH 11/12] Add user device permission guards --- src/guards/device.permission.guard.ts | 97 ------------------- ...er.device.controllable.permission.guard.ts | 92 ++++++++++++++++++ src/guards/user.device.permission.guard.ts | 91 +++++++++++++++++ 3 files changed, 183 insertions(+), 97 deletions(-) delete mode 100644 src/guards/device.permission.guard.ts create mode 100644 src/guards/user.device.controllable.permission.guard.ts create mode 100644 src/guards/user.device.permission.guard.ts diff --git a/src/guards/device.permission.guard.ts b/src/guards/device.permission.guard.ts deleted file mode 100644 index 1ec21a9..0000000 --- a/src/guards/device.permission.guard.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { PermissionType } from '@app/common/constants/permission-type.enum'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; -import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; -import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; -import { - Injectable, - CanActivate, - HttpStatus, - ExecutionContext, - BadRequestException, - applyDecorators, - SetMetadata, - UseGuards, -} from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; - -@Injectable() -export class DevicePermissionGuard implements CanActivate { - constructor( - private reflector: Reflector, - private readonly deviceUserPermissionRepository: DeviceUserPermissionRepository, - private readonly permissionTypeRepository: PermissionTypeRepository, - ) {} - - async canActivate(context: ExecutionContext): Promise { - const req = context.switchToHttp().getRequest(); - try { - const { deviceId } = req.headers; - const userId = req.user.uuid; - - const requirePermission = - this.reflector.getAllAndOverride('permission', [ - context.getHandler(), - context.getClass(), - ]); - - if (!requirePermission) { - return true; - } - await this.checkDevicePermission(deviceId, userId, requirePermission); - - return true; - } catch (error) { - this.handleGuardError(error, context); - return false; - } - } - - async checkDevicePermission( - deviceId: string, - userId: string, - requirePermission, - ) { - const [userPermissionDetails, permissionDetails] = await Promise.all([ - this.deviceUserPermissionRepository.findOne({ - where: { deviceUuid: deviceId, userUuid: userId }, - }), - this.permissionTypeRepository.findOne({ - where: { - type: requirePermission, - }, - }), - ]); - if (!userPermissionDetails) { - throw new BadRequestException('User Permission Details Not Found'); - } - if (userPermissionDetails.permissionType.uuid !== permissionDetails.uuid) { - throw new BadRequestException( - `User Does not have a ${requirePermission}`, - ); - } - } - - private handleGuardError(error: Error, context: ExecutionContext) { - const response = context.switchToHttp().getResponse(); - console.error(error); - - if (error instanceof BadRequestException) { - response - .status(HttpStatus.BAD_REQUEST) - .json({ statusCode: HttpStatus.BAD_REQUEST, message: error.message }); - } else { - response.status(HttpStatus.NOT_FOUND).json({ - statusCode: HttpStatus.NOT_FOUND, - message: 'User Permission not found', - }); - } - } -} - -export function AuthGuardWithRoles(permission?: string) { - return applyDecorators( - SetMetadata('permission', permission), - UseGuards(JwtAuthGuard), - UseGuards(DevicePermissionGuard), - ); -} diff --git a/src/guards/user.device.controllable.permission.guard.ts b/src/guards/user.device.controllable.permission.guard.ts new file mode 100644 index 0000000..2684c8d --- /dev/null +++ b/src/guards/user.device.controllable.permission.guard.ts @@ -0,0 +1,92 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + HttpStatus, +} from '@nestjs/common'; +import { BadRequestException, NotFoundException } from '@nestjs/common'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { PermissionType } from '@app/common/constants/permission-type.enum'; + +@Injectable() +export class CheckUserHaveControllablePermission implements CanActivate { + constructor( + private readonly deviceRepository: DeviceRepository, + private readonly userRepository: UserRepository, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const userUuid = req.user.uuid; + const { deviceUuid } = req.params; + + const userIsFound = await this.checkUserIsFound(userUuid); + if (!userIsFound) { + throw new NotFoundException('User not found'); + } + + const userDevicePermission = await this.checkUserDevicePermission( + userUuid, + deviceUuid, + ); + + if (userDevicePermission === PermissionType.CONTROLLABLE) { + return true; + } else { + throw new BadRequestException( + 'You do not have controllable access to this device', + ); + } + } catch (error) { + this.handleGuardError(error, context); + return false; + } + } + + private async checkUserIsFound(userUuid: string) { + const userData = await this.userRepository.findOne({ + where: { uuid: userUuid }, + }); + return !!userData; + } + + private async checkUserDevicePermission( + userUuid: string, + deviceUuid: string, + ): Promise { + const device = await this.deviceRepository.findOne({ + where: { uuid: deviceUuid, permission: { userUuid: userUuid } }, + relations: ['permission', 'permission.permissionType'], + }); + + if (!device) { + throw new BadRequestException( + 'You do not have controllable access to this device', + ); + } + + return device.permission[0].permissionType.type; // Assuming permissionType is a string + } + + private handleGuardError(error: Error, context: ExecutionContext) { + const response = context.switchToHttp().getResponse(); + if (error instanceof NotFoundException) { + response + .status(HttpStatus.NOT_FOUND) + .json({ statusCode: HttpStatus.NOT_FOUND, message: error.message }); + } else if (error instanceof BadRequestException) { + response.status(HttpStatus.BAD_REQUEST).json({ + statusCode: HttpStatus.BAD_REQUEST, + message: error.message, + }); + } else { + response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + } + } +} diff --git a/src/guards/user.device.permission.guard.ts b/src/guards/user.device.permission.guard.ts new file mode 100644 index 0000000..e63a08e --- /dev/null +++ b/src/guards/user.device.permission.guard.ts @@ -0,0 +1,91 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + HttpStatus, +} from '@nestjs/common'; +import { BadRequestException, NotFoundException } from '@nestjs/common'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { PermissionType } from '@app/common/constants/permission-type.enum'; + +@Injectable() +export class CheckUserHavePermission implements CanActivate { + constructor( + private readonly deviceRepository: DeviceRepository, + private readonly userRepository: UserRepository, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + + try { + const userUuid = req.user.uuid; + const { deviceUuid } = req.params; + + const userIsFound = await this.checkUserIsFound(userUuid); + if (!userIsFound) { + throw new NotFoundException('User not found'); + } + + const userDevicePermission = await this.checkUserDevicePermission( + userUuid, + deviceUuid, + ); + + if ( + userDevicePermission === PermissionType.READ || + userDevicePermission === PermissionType.CONTROLLABLE + ) { + return true; + } else { + throw new BadRequestException('You do not have access to this device'); + } + } catch (error) { + this.handleGuardError(error, context); + return false; + } + } + + private async checkUserIsFound(userUuid: string) { + const userData = await this.userRepository.findOne({ + where: { uuid: userUuid }, + }); + return !!userData; + } + + private async checkUserDevicePermission( + userUuid: string, + deviceUuid: string, + ): Promise { + const device = await this.deviceRepository.findOne({ + where: { uuid: deviceUuid, permission: { userUuid: userUuid } }, + relations: ['permission', 'permission.permissionType'], + }); + + if (!device) { + throw new BadRequestException('You do not have access to this device'); + } + + return device.permission[0].permissionType.type; // Assuming permissionType is a string + } + + private handleGuardError(error: Error, context: ExecutionContext) { + const response = context.switchToHttp().getResponse(); + if (error instanceof NotFoundException) { + response + .status(HttpStatus.NOT_FOUND) + .json({ statusCode: HttpStatus.NOT_FOUND, message: error.message }); + } else if (error instanceof BadRequestException) { + response.status(HttpStatus.BAD_REQUEST).json({ + statusCode: HttpStatus.BAD_REQUEST, + message: error.message, + }); + } else { + response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + statusCode: HttpStatus.INTERNAL_SERVER_ERROR, + message: error.message, + }); + } + } +} From 0d83d10f0b9b978cf81be03283513d4f69565b10 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 5 May 2024 21:32:40 +0300 Subject: [PATCH 12/12] Update user-device-permission error message --- .../controllers/user-device-permission.controller.ts | 4 ++-- .../services/user-device-permission.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/user-device-permission/controllers/user-device-permission.controller.ts b/src/user-device-permission/controllers/user-device-permission.controller.ts index 825694f..fef4cc7 100644 --- a/src/user-device-permission/controllers/user-device-permission.controller.ts +++ b/src/user-device-permission/controllers/user-device-permission.controller.ts @@ -26,8 +26,8 @@ export class UserDevicePermissionController { private readonly userDevicePermissionService: UserDevicePermissionService, ) {} - // @ApiBearerAuth() - // @UseGuards(JwtAuthGuard) + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) @Post('add') async addDevicePermission( @Body() userDevicePermissionDto: UserDevicePermissionAddDto, diff --git a/src/user-device-permission/services/user-device-permission.service.ts b/src/user-device-permission/services/user-device-permission.service.ts index b02cb6a..c1f3d07 100644 --- a/src/user-device-permission/services/user-device-permission.service.ts +++ b/src/user-device-permission/services/user-device-permission.service.ts @@ -26,7 +26,7 @@ export class UserDevicePermissionService { } catch (error) { if (error.code === '23505') { throw new HttpException( - 'This Permission already belongs to this user', + 'This User already belongs to this device', HttpStatus.BAD_REQUEST, ); } @@ -56,7 +56,7 @@ export class UserDevicePermissionService { } catch (error) { if (error.code === '23505') { throw new HttpException( - 'This Permission already belongs to this user', + 'This User already belongs to this device', HttpStatus.BAD_REQUEST, ); }