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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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, ); } From b65bef982d60d052bfbb7a14ef4769b5cbcb86a9 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 6 May 2024 10:30:29 +0300 Subject: [PATCH 13/33] Add role type enum, dto, entity, repository, and module --- libs/common/src/constants/role.type.enum.ts | 4 ++++ .../common/src/modules/role-type/dtos/index.ts | 1 + .../modules/role-type/dtos/role.type.dto.ts | 11 +++++++++++ .../src/modules/role-type/entities/index.ts | 1 + .../role-type/entities/role.type.entity.ts | 18 ++++++++++++++++++ .../modules/role-type/repositories/index.ts | 1 + .../repositories/role.type.repository.ts | 10 ++++++++++ .../role-type/role.type.repository.module.ts | 11 +++++++++++ 8 files changed, 57 insertions(+) create mode 100644 libs/common/src/constants/role.type.enum.ts create mode 100644 libs/common/src/modules/role-type/dtos/index.ts create mode 100644 libs/common/src/modules/role-type/dtos/role.type.dto.ts create mode 100644 libs/common/src/modules/role-type/entities/index.ts create mode 100644 libs/common/src/modules/role-type/entities/role.type.entity.ts create mode 100644 libs/common/src/modules/role-type/repositories/index.ts create mode 100644 libs/common/src/modules/role-type/repositories/role.type.repository.ts create mode 100644 libs/common/src/modules/role-type/role.type.repository.module.ts diff --git a/libs/common/src/constants/role.type.enum.ts b/libs/common/src/constants/role.type.enum.ts new file mode 100644 index 0000000..34b3929 --- /dev/null +++ b/libs/common/src/constants/role.type.enum.ts @@ -0,0 +1,4 @@ +export enum RoleType { + USER = 'USER', + ADMIN = 'ADMIN', +} diff --git a/libs/common/src/modules/role-type/dtos/index.ts b/libs/common/src/modules/role-type/dtos/index.ts new file mode 100644 index 0000000..6452280 --- /dev/null +++ b/libs/common/src/modules/role-type/dtos/index.ts @@ -0,0 +1 @@ +export * from './role.type.dto'; diff --git a/libs/common/src/modules/role-type/dtos/role.type.dto.ts b/libs/common/src/modules/role-type/dtos/role.type.dto.ts new file mode 100644 index 0000000..77c9c46 --- /dev/null +++ b/libs/common/src/modules/role-type/dtos/role.type.dto.ts @@ -0,0 +1,11 @@ +import { RoleType } from '@app/common/constants/role.type.enum'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; + +export class RoleTypeDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsEnum(RoleType) + public type: RoleType; +} diff --git a/libs/common/src/modules/role-type/entities/index.ts b/libs/common/src/modules/role-type/entities/index.ts new file mode 100644 index 0000000..a6c063c --- /dev/null +++ b/libs/common/src/modules/role-type/entities/index.ts @@ -0,0 +1 @@ +export * from './role.type.entity'; diff --git a/libs/common/src/modules/role-type/entities/role.type.entity.ts b/libs/common/src/modules/role-type/entities/role.type.entity.ts new file mode 100644 index 0000000..974365c --- /dev/null +++ b/libs/common/src/modules/role-type/entities/role.type.entity.ts @@ -0,0 +1,18 @@ +import { Column, Entity } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { RoleTypeDto } from '../dtos/role.type.dto'; +import { RoleType } from '@app/common/constants/role.type.enum'; + +@Entity({ name: 'role-type' }) +export class RoleTypeEntity extends AbstractEntity { + @Column({ + nullable: false, + enum: Object.values(RoleType), + }) + type: string; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/role-type/repositories/index.ts b/libs/common/src/modules/role-type/repositories/index.ts new file mode 100644 index 0000000..e2581bc --- /dev/null +++ b/libs/common/src/modules/role-type/repositories/index.ts @@ -0,0 +1 @@ +export * from './role.type.repository'; diff --git a/libs/common/src/modules/role-type/repositories/role.type.repository.ts b/libs/common/src/modules/role-type/repositories/role.type.repository.ts new file mode 100644 index 0000000..d21469f --- /dev/null +++ b/libs/common/src/modules/role-type/repositories/role.type.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { RoleTypeEntity } from '../entities/role.type.entity'; + +@Injectable() +export class RoleTypeRepository extends Repository { + constructor(private dataSource: DataSource) { + super(RoleTypeEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/role-type/role.type.repository.module.ts b/libs/common/src/modules/role-type/role.type.repository.module.ts new file mode 100644 index 0000000..85ecc41 --- /dev/null +++ b/libs/common/src/modules/role-type/role.type.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { RoleTypeEntity } from './entities/role.type.entity'; + +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([RoleTypeEntity])], +}) +export class RoleTypeRepositoryModule {} From 7d6964208cedc7f79c2ad7080eeae0ea6c1af03b Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 6 May 2024 18:33:49 +0300 Subject: [PATCH 14/33] Add UserRoleEntity and related modules for user role management --- .../role-type/entities/role.type.entity.ts | 10 ++++++-- .../src/modules/user-role/dtos/index.ts | 1 + .../modules/user-role/dtos/user.role.dto.ts | 15 ++++++++++++ .../src/modules/user-role/entities/index.ts | 1 + .../user-role/entities/user.role.entity.ts | 23 +++++++++++++++++++ .../modules/user-role/repositories/index.ts | 1 + .../repositories/user.role.repository.ts | 10 ++++++++ .../user-role/user.role.repository.module.ts | 10 ++++++++ .../src/modules/user/entities/user.entity.ts | 8 +++++++ 9 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 libs/common/src/modules/user-role/dtos/index.ts create mode 100644 libs/common/src/modules/user-role/dtos/user.role.dto.ts create mode 100644 libs/common/src/modules/user-role/entities/index.ts create mode 100644 libs/common/src/modules/user-role/entities/user.role.entity.ts create mode 100644 libs/common/src/modules/user-role/repositories/index.ts create mode 100644 libs/common/src/modules/user-role/repositories/user.role.repository.ts create mode 100644 libs/common/src/modules/user-role/user.role.repository.module.ts diff --git a/libs/common/src/modules/role-type/entities/role.type.entity.ts b/libs/common/src/modules/role-type/entities/role.type.entity.ts index 974365c..c65db72 100644 --- a/libs/common/src/modules/role-type/entities/role.type.entity.ts +++ b/libs/common/src/modules/role-type/entities/role.type.entity.ts @@ -1,7 +1,8 @@ -import { Column, Entity } from 'typeorm'; +import { Column, Entity, OneToMany } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { RoleTypeDto } from '../dtos/role.type.dto'; import { RoleType } from '@app/common/constants/role.type.enum'; +import { UserRoleEntity } from '../../user-role/entities'; @Entity({ name: 'role-type' }) export class RoleTypeEntity extends AbstractEntity { @@ -10,7 +11,12 @@ export class RoleTypeEntity extends AbstractEntity { enum: Object.values(RoleType), }) type: string; - + @OneToMany(() => UserRoleEntity, (role) => role.roleType, { + nullable: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + role: UserRoleEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/user-role/dtos/index.ts b/libs/common/src/modules/user-role/dtos/index.ts new file mode 100644 index 0000000..7879674 --- /dev/null +++ b/libs/common/src/modules/user-role/dtos/index.ts @@ -0,0 +1 @@ +export * from './user.role.dto'; diff --git a/libs/common/src/modules/user-role/dtos/user.role.dto.ts b/libs/common/src/modules/user-role/dtos/user.role.dto.ts new file mode 100644 index 0000000..3ff4dab --- /dev/null +++ b/libs/common/src/modules/user-role/dtos/user.role.dto.ts @@ -0,0 +1,15 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class UserRoleDto { + @IsString() + @IsNotEmpty() + public uuid: string; + + @IsString() + @IsNotEmpty() + public userUuid: string; + + @IsString() + @IsNotEmpty() + public roleTypeUuid: string; +} diff --git a/libs/common/src/modules/user-role/entities/index.ts b/libs/common/src/modules/user-role/entities/index.ts new file mode 100644 index 0000000..b6f3bd9 --- /dev/null +++ b/libs/common/src/modules/user-role/entities/index.ts @@ -0,0 +1 @@ +export * from './user.role.entity'; diff --git a/libs/common/src/modules/user-role/entities/user.role.entity.ts b/libs/common/src/modules/user-role/entities/user.role.entity.ts new file mode 100644 index 0000000..e375723 --- /dev/null +++ b/libs/common/src/modules/user-role/entities/user.role.entity.ts @@ -0,0 +1,23 @@ +import { Entity, ManyToOne } from 'typeorm'; +import { AbstractEntity } from '../../abstract/entities/abstract.entity'; +import { UserRoleDto } from '../dtos'; +import { UserEntity } from '../../user/entities'; +import { RoleTypeEntity } from '../../role-type/entities'; + +@Entity({ name: 'user-role' }) +export class UserRoleEntity extends AbstractEntity { + @ManyToOne(() => UserEntity, (user) => user.role, { + nullable: false, + }) + user: UserEntity; + + @ManyToOne(() => RoleTypeEntity, (roleType) => roleType.role, { + nullable: false, + }) + roleType: RoleTypeEntity; + + constructor(partial: Partial) { + super(); + Object.assign(this, partial); + } +} diff --git a/libs/common/src/modules/user-role/repositories/index.ts b/libs/common/src/modules/user-role/repositories/index.ts new file mode 100644 index 0000000..abf7247 --- /dev/null +++ b/libs/common/src/modules/user-role/repositories/index.ts @@ -0,0 +1 @@ +export * from './user.role.repository'; diff --git a/libs/common/src/modules/user-role/repositories/user.role.repository.ts b/libs/common/src/modules/user-role/repositories/user.role.repository.ts new file mode 100644 index 0000000..9bc9a24 --- /dev/null +++ b/libs/common/src/modules/user-role/repositories/user.role.repository.ts @@ -0,0 +1,10 @@ +import { DataSource, Repository } from 'typeorm'; +import { Injectable } from '@nestjs/common'; +import { UserRoleEntity } from '../entities'; + +@Injectable() +export class UserRoleRepository extends Repository { + constructor(private dataSource: DataSource) { + super(UserRoleEntity, dataSource.createEntityManager()); + } +} diff --git a/libs/common/src/modules/user-role/user.role.repository.module.ts b/libs/common/src/modules/user-role/user.role.repository.module.ts new file mode 100644 index 0000000..540787e --- /dev/null +++ b/libs/common/src/modules/user-role/user.role.repository.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserRoleEntity } from './entities'; +@Module({ + providers: [], + exports: [], + controllers: [], + imports: [TypeOrmModule.forFeature([UserRoleEntity])], +}) +export class UserRoleRepositoryModule {} diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index 4268ae8..ffe402c 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -3,6 +3,7 @@ import { Column, Entity, OneToMany } from 'typeorm'; import { UserDto } from '../dtos'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { UserSpaceEntity } from '../../user-space/entities'; +import { UserRoleEntity } from '../../user-role/entities'; @Entity({ name: 'user' }) export class UserEntity extends AbstractEntity { @@ -57,6 +58,13 @@ export class UserEntity extends AbstractEntity { (userPermission) => userPermission.user, ) userPermission: DeviceUserPermissionEntity[]; + + @OneToMany(() => UserRoleEntity, (role) => role.user, { + nullable: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + role: UserRoleEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); From 6bd827fe57bb912c94ee0630ee792ffd2f0fec38 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 6 May 2024 18:34:03 +0300 Subject: [PATCH 15/33] Add UserRoleEntity and RoleTypeEntity imports to database module --- libs/common/src/database/database.module.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/common/src/database/database.module.ts b/libs/common/src/database/database.module.ts index 146b5ba..ce51bae 100644 --- a/libs/common/src/database/database.module.ts +++ b/libs/common/src/database/database.module.ts @@ -14,6 +14,8 @@ 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'; +import { UserRoleEntity } from '../modules/user-role/entities'; +import { RoleTypeEntity } from '../modules/role-type/entities'; @Module({ imports: [ @@ -42,6 +44,8 @@ import { DeviceUserPermissionEntity } from '../modules/device-user-permission/en GroupEntity, GroupDeviceEntity, DeviceUserPermissionEntity, + UserRoleEntity, + RoleTypeEntity, ], namingStrategy: new SnakeNamingStrategy(), synchronize: Boolean(JSON.parse(configService.get('DB_SYNC'))), From 1dcb0a3052f099fc6d1b93a453845ae6c5c6dd46 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 6 May 2024 19:10:24 +0300 Subject: [PATCH 16/33] Add UserRoleRepository and RoleTypeRepository imports --- libs/common/src/auth/services/auth.service.ts | 4 ++ src/auth/auth.module.ts | 4 ++ src/auth/controllers/user-auth.controller.ts | 2 +- src/auth/services/user-auth.service.ts | 41 +++++++++++++++++-- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 5e57550..45b19fa 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -22,7 +22,9 @@ export class AuthService { where: { email, }, + relations: ['role.roleType'], }); + if (!user.isUserVerified) { throw new BadRequestException('User is not verified'); } @@ -68,7 +70,9 @@ export class AuthService { uuid: user.uuid, type: user.type, sessionId: user.sessionId, + roles: user.roles, }; + const tokens = await this.getTokens(payload); await this.updateRefreshToken(user.uuid, tokens.refreshToken); return tokens; diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 2165d5e..10515e7 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -9,6 +9,8 @@ import { UserAuthService } from './services'; import { UserRepository } from '../../libs/common/src/modules/user/repositories'; import { UserSessionRepository } from '../../libs/common/src/modules/session/repositories/session.repository'; import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/repositories/user-otp.repository'; +import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; +import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; @Module({ imports: [ConfigModule, UserRepositoryModule, CommonModule], @@ -19,6 +21,8 @@ import { UserOtpRepository } from '../../libs/common/src/modules/user-otp/reposi UserRepository, UserSessionRepository, UserOtpRepository, + UserRoleRepository, + RoleTypeRepository, ], exports: [AuthenticationService, UserAuthService], }) diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index f8c45f9..3796649 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -47,7 +47,7 @@ export class UserAuthController { return { statusCode: HttpStatus.CREATED, data: accessToken, - message: 'User Loggedin Successfully', + message: 'User Logged in Successfully', }; } diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 7b278b2..705ba9e 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -1,3 +1,5 @@ +import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository'; +import { UserRoleRepository } from './../../../libs/common/src/modules/user-role/repositories/user.role.repository'; import { UserRepository } from '../../../libs/common/src/modules/user/repositories'; import { BadRequestException, @@ -16,6 +18,7 @@ import { EmailService } from '../../../libs/common/src/util/email.service'; import { OtpType } from '../../../libs/common/src/constants/otp-type.enum'; import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity'; import * as argon2 from 'argon2'; +import { RoleType } from '@app/common/constants/role.type.enum'; @Injectable() export class UserAuthService { @@ -26,6 +29,8 @@ export class UserAuthService { private readonly helperHashService: HelperHashService, private readonly authService: AuthService, private readonly emailService: EmailService, + private readonly userRoleRepository: UserRoleRepository, + private readonly roleTypeRepository: RoleTypeRepository, ) {} async signUp(userSignUpDto: UserSignUpDto): Promise { @@ -33,12 +38,31 @@ export class UserAuthService { if (findUser) { throw new BadRequestException('User already registered with given email'); } - const salt = this.helperHashService.randomSalt(10); - const password = this.helperHashService.bcrypt( + const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt + const hashedPassword = await this.helperHashService.bcrypt( userSignUpDto.password, salt, ); - return await this.userRepository.save({ ...userSignUpDto, password }); + + try { + const user = await this.userRepository.save({ + ...userSignUpDto, + password: hashedPassword, + }); + + const defaultUserRoleUuid = await this.getRoleUuidByRoleType( + RoleType.USER, + ); + + await this.userRoleRepository.save({ + user: { uuid: user.uuid }, + roleType: { uuid: defaultUserRoleUuid }, + }); + + return user; + } catch (error) { + throw new BadRequestException('Failed to register user'); + } } async findUser(email: string) { @@ -67,6 +91,7 @@ export class UserAuthService { async userLogin(data: UserLoginDto) { const user = await this.authService.validateUser(data.email, data.password); + if (!user) { throw new UnauthorizedException('Invalid login credentials.'); } @@ -89,6 +114,9 @@ export class UserAuthService { email: user.email, userId: user.uuid, uuid: user.uuid, + roles: user.role.map((role) => { + return { uuid: role.uuid, type: role.roleType.type }; + }), sessionId: session[1].uuid, }); } @@ -186,4 +214,11 @@ export class UserAuthService { await this.authService.updateRefreshToken(user.uuid, tokens.refreshToken); return tokens; } + private async getRoleUuidByRoleType(roleType: string) { + const role = await this.roleTypeRepository.findOne({ + where: { type: roleType }, + }); + + return role.uuid; + } } From a093fd3f72176d2615d3c7027d86e44f3935fcad Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Mon, 6 May 2024 23:49:09 +0300 Subject: [PATCH 17/33] Add Role Module with Role Controller, Service, and DTOs --- src/app.module.ts | 2 + src/role/controllers/index.ts | 1 + src/role/controllers/role.controller.ts | 58 +++++++++++++++++++++++++ src/role/dtos/index.ts | 1 + src/role/dtos/role.edit.dto.ts | 13 ++++++ src/role/role.module.ts | 25 +++++++++++ src/role/services/index.ts | 1 + src/role/services/role.service.ts | 44 +++++++++++++++++++ 8 files changed, 145 insertions(+) create mode 100644 src/role/controllers/index.ts create mode 100644 src/role/controllers/role.controller.ts create mode 100644 src/role/dtos/index.ts create mode 100644 src/role/dtos/role.edit.dto.ts create mode 100644 src/role/role.module.ts create mode 100644 src/role/services/index.ts create mode 100644 src/role/services/role.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index 4243057..c9f7275 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { CommunityModule } from './community/community.module'; import { BuildingModule } from './building/building.module'; import { FloorModule } from './floor/floor.module'; import { UnitModule } from './unit/unit.module'; +import { RoleModule } from './role/role.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -19,6 +20,7 @@ import { UnitModule } from './unit/unit.module'; }), AuthenticationModule, UserModule, + RoleModule, CommunityModule, BuildingModule, FloorModule, diff --git a/src/role/controllers/index.ts b/src/role/controllers/index.ts new file mode 100644 index 0000000..fa5a3e4 --- /dev/null +++ b/src/role/controllers/index.ts @@ -0,0 +1 @@ +export * from './role.controller'; diff --git a/src/role/controllers/role.controller.ts b/src/role/controllers/role.controller.ts new file mode 100644 index 0000000..1bc87ce --- /dev/null +++ b/src/role/controllers/role.controller.ts @@ -0,0 +1,58 @@ +import { + Body, + Controller, + Get, + HttpException, + HttpStatus, + Param, + Put, + UseGuards, +} from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { RoleService } from '../services/role.service'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; +import { UserRoleEditDto } from '../dtos'; + +@ApiTags('Role Module') +@Controller({ + version: '1', + path: 'role', +}) +export class RoleController { + constructor(private readonly roleService: RoleService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Get('types') + async fetchRoleTypes() { + try { + const roleTypes = await this.roleService.fetchRoleTypes(); + return { + statusCode: HttpStatus.OK, + message: 'Role Types fetched Successfully', + data: roleTypes, + }; + } catch (err) { + throw new Error(err); + } + } + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Put('edit/user/:userUuid') + async editUserRoleType( + @Param('userUuid') userUuid: string, + @Body() userRoleEditDto: UserRoleEditDto, + ) { + try { + await this.roleService.editUserRoleType(userUuid, userRoleEditDto); + return { + statusCode: HttpStatus.OK, + message: 'User Role Updated Successfully', + }; + } catch (error) { + throw new HttpException( + error.message || 'Internal server error', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } +} diff --git a/src/role/dtos/index.ts b/src/role/dtos/index.ts new file mode 100644 index 0000000..8fc5216 --- /dev/null +++ b/src/role/dtos/index.ts @@ -0,0 +1 @@ +export * from './role.edit.dto'; diff --git a/src/role/dtos/role.edit.dto.ts b/src/role/dtos/role.edit.dto.ts new file mode 100644 index 0000000..5cd0aac --- /dev/null +++ b/src/role/dtos/role.edit.dto.ts @@ -0,0 +1,13 @@ +import { RoleType } from '@app/common/constants/role.type.enum'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum } from 'class-validator'; + +export class UserRoleEditDto { + @ApiProperty({ + description: 'role type', + enum: RoleType, + required: true, + }) + @IsEnum(RoleType) + roleType: RoleType; +} diff --git a/src/role/role.module.ts b/src/role/role.module.ts new file mode 100644 index 0000000..a487979 --- /dev/null +++ b/src/role/role.module.ts @@ -0,0 +1,25 @@ +import { DeviceRepositoryModule } from '@app/common/modules/device'; +import { DeviceRepository } from '@app/common/modules/device/repositories'; +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { RoleService } from './services/role.service'; +import { RoleController } from './controllers/role.controller'; +import { DeviceUserPermissionRepository } from '@app/common/modules/device-user-permission/repositories'; +import { PermissionTypeRepository } from '@app/common/modules/permission/repositories'; +import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; +import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; + +@Module({ + imports: [ConfigModule, DeviceRepositoryModule], + controllers: [RoleController], + providers: [ + DeviceUserPermissionRepository, + PermissionTypeRepository, + DeviceRepository, + RoleService, + RoleTypeRepository, + UserRoleRepository, + ], + exports: [RoleService], +}) +export class RoleModule {} diff --git a/src/role/services/index.ts b/src/role/services/index.ts new file mode 100644 index 0000000..1adbb54 --- /dev/null +++ b/src/role/services/index.ts @@ -0,0 +1 @@ +export * from './role.service'; diff --git a/src/role/services/role.service.ts b/src/role/services/role.service.ts new file mode 100644 index 0000000..b8db739 --- /dev/null +++ b/src/role/services/role.service.ts @@ -0,0 +1,44 @@ +import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { UserRoleEditDto } from '../dtos/role.edit.dto'; +import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; + +@Injectable() +export class RoleService { + constructor( + private readonly roleTypeRepository: RoleTypeRepository, + private readonly userRoleRepository: UserRoleRepository, + ) {} + + async editUserRoleType(userUuid: string, userRoleEditDto: UserRoleEditDto) { + try { + const roleType = await this.fetchRoleByType(userRoleEditDto.roleType); + return await this.userRoleRepository.update( + { user: { uuid: userUuid } }, + { + roleType: { + uuid: roleType.uuid, + }, + }, + ); + } catch (error) { + throw new HttpException( + error.message || 'Internal Server Error', + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + async fetchRoleTypes() { + const roleTypes = await this.roleTypeRepository.find(); + + return roleTypes; + } + private async fetchRoleByType(roleType: string) { + return await this.roleTypeRepository.findOne({ + where: { + type: roleType, + }, + }); + } +} From 85424554cb93baf1a977dc3d85fa1510c46f0c01 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 7 May 2024 00:32:53 +0300 Subject: [PATCH 18/33] Add roles to user payload in JWT and refresh token strategies --- .../src/auth/interfaces/auth.interface.ts | 1 + .../src/auth/strategies/jwt.strategy.ts | 3 ++- .../auth/strategies/refresh-token.strategy.ts | 3 ++- src/guards/admin.role.guard.ts | 17 +++++++++++++++ src/guards/user.role.guard.ts | 21 +++++++++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/guards/admin.role.guard.ts create mode 100644 src/guards/user.role.guard.ts diff --git a/libs/common/src/auth/interfaces/auth.interface.ts b/libs/common/src/auth/interfaces/auth.interface.ts index ae85fe1..8e57cd6 100644 --- a/libs/common/src/auth/interfaces/auth.interface.ts +++ b/libs/common/src/auth/interfaces/auth.interface.ts @@ -4,4 +4,5 @@ export class AuthInterface { uuid: string; sessionId: string; id: number; + roles: string[]; } diff --git a/libs/common/src/auth/strategies/jwt.strategy.ts b/libs/common/src/auth/strategies/jwt.strategy.ts index 67faecf..ed29a3a 100644 --- a/libs/common/src/auth/strategies/jwt.strategy.ts +++ b/libs/common/src/auth/strategies/jwt.strategy.ts @@ -28,9 +28,10 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { if (validateUser) { return { email: payload.email, - userId: payload.id, + userUuid: payload.uuid, uuid: payload.uuid, sessionId: payload.sessionId, + roles: payload.roles, }; } else { throw new BadRequestException('Unauthorized'); diff --git a/libs/common/src/auth/strategies/refresh-token.strategy.ts b/libs/common/src/auth/strategies/refresh-token.strategy.ts index 9b21010..ee36eac 100644 --- a/libs/common/src/auth/strategies/refresh-token.strategy.ts +++ b/libs/common/src/auth/strategies/refresh-token.strategy.ts @@ -31,9 +31,10 @@ export class RefreshTokenStrategy extends PassportStrategy( if (validateUser) { return { email: payload.email, - userId: payload.id, + userUuid: payload.uuid, uuid: payload.uuid, sessionId: payload.sessionId, + roles: payload.roles, }; } else { throw new BadRequestException('Unauthorized'); diff --git a/src/guards/admin.role.guard.ts b/src/guards/admin.role.guard.ts new file mode 100644 index 0000000..0c3b259 --- /dev/null +++ b/src/guards/admin.role.guard.ts @@ -0,0 +1,17 @@ +import { RoleType } from '@app/common/constants/role.type.enum'; +import { BadRequestException, UnauthorizedException } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +export class AdminRoleGuard extends AuthGuard('jwt') { + handleRequest(err, user) { + const isAdmin = user.roles.some((role) => role.type === RoleType.ADMIN); + if (err || !user) { + throw err || new UnauthorizedException(); + } else { + if (!isAdmin) { + throw new BadRequestException('Only admin role can access this route'); + } + } + return user; + } +} diff --git a/src/guards/user.role.guard.ts b/src/guards/user.role.guard.ts new file mode 100644 index 0000000..b21632a --- /dev/null +++ b/src/guards/user.role.guard.ts @@ -0,0 +1,21 @@ +import { RoleType } from '@app/common/constants/role.type.enum'; +import { BadRequestException, UnauthorizedException } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +export class UserRoleGuard extends AuthGuard('jwt') { + handleRequest(err, user) { + const isUserOrAdmin = user.roles.some( + (role) => role.type === RoleType.ADMIN || role.type === RoleType.USER, + ); + if (err || !user) { + throw err || new UnauthorizedException(); + } else { + if (!isUserOrAdmin) { + throw new BadRequestException( + 'Only admin or user role can access this route', + ); + } + } + return user; + } +} From 4058449f4b591d860faf057135aaec3546f0a8af Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 7 May 2024 00:33:04 +0300 Subject: [PATCH 19/33] Update guard usage in controllers --- src/auth/controllers/user-auth.controller.ts | 6 +++--- .../controllers/building.controller.ts | 17 +++++++++-------- .../controllers/community.controller.ts | 15 ++++++++------- src/device/controllers/device.controller.ts | 18 +++++++++--------- src/floor/controllers/floor.controller.ts | 17 +++++++++-------- src/group/controllers/group.controller.ts | 14 +++++++------- src/role/controllers/role.controller.ts | 6 +++--- src/room/controllers/room.controller.ts | 15 ++++++++------- src/unit/controllers/unit.controller.ts | 17 +++++++++-------- .../user-device-permission.controller.ts | 10 +++++----- src/users/controllers/user.controller.ts | 4 ++-- 11 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index 3796649..fd139f9 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -14,9 +14,9 @@ import { UserSignUpDto } from '../dtos/user-auth.dto'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ResponseMessage } from '../../../libs/common/src/response/response.decorator'; import { UserLoginDto } from '../dtos/user-login.dto'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; import { RefreshTokenGuard } from '@app/common/guards/jwt-refresh.auth.guard'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; @Controller({ version: '1', @@ -52,7 +52,7 @@ export class UserAuthController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Delete('user/delete/:id') async userDelete(@Param('id') id: string) { await this.userAuthService.deleteUser(id); @@ -98,7 +98,7 @@ export class UserAuthController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Get('user/list') async userList() { const userList = await this.userAuthService.userList(); diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts index dc4dbd7..b561ff9 100644 --- a/src/building/controllers/building.controller.ts +++ b/src/building/controllers/building.controller.ts @@ -12,12 +12,13 @@ import { UseGuards, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { AddBuildingDto, AddUserBuildingDto } from '../dtos/add.building.dto'; 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 { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { UserRoleGuard } from 'src/guards/user.role.guard'; @ApiTags('Building Module') @Controller({ @@ -28,7 +29,7 @@ export class BuildingController { constructor(private readonly buildingService: BuildingService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckCommunityTypeGuard) + @UseGuards(AdminRoleGuard, CheckCommunityTypeGuard) @Post() async addBuilding(@Body() addBuildingDto: AddBuildingDto) { try { @@ -43,7 +44,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get(':buildingUuid') async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) { try { @@ -59,7 +60,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('child/:buildingUuid') async getBuildingChildByUuid( @Param('buildingUuid') buildingUuid: string, @@ -79,7 +80,7 @@ export class BuildingController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('parent/:buildingUuid') async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) { try { @@ -94,7 +95,7 @@ export class BuildingController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserBuildingGuard) + @UseGuards(AdminRoleGuard, CheckUserBuildingGuard) @Post('user') async addUserBuilding(@Body() addUserBuildingDto: AddUserBuildingDto) { try { @@ -108,7 +109,7 @@ export class BuildingController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('user/:userUuid') async getBuildingsByUserId(@Param('userUuid') userUuid: string) { try { @@ -122,7 +123,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Put('rename/:buildingUuid') async renameBuildingByUuid( @Param('buildingUuid') buildingUuid: string, diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index 3fd2233..144041d 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -12,7 +12,6 @@ import { UseGuards, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { AddCommunityDto, AddUserCommunityDto, @@ -20,6 +19,8 @@ import { import { GetCommunityChildDto } from '../dtos/get.community.dto'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { UserRoleGuard } from 'src/guards/user.role.guard'; @ApiTags('Community Module') @Controller({ @@ -30,7 +31,7 @@ export class CommunityController { constructor(private readonly communityService: CommunityService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Post() async addCommunity(@Body() addCommunityDto: AddCommunityDto) { try { @@ -46,7 +47,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get(':communityUuid') async getCommunityByUuid(@Param('communityUuid') communityUuid: string) { try { @@ -62,7 +63,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('child/:communityUuid') async getCommunityChildByUuid( @Param('communityUuid') communityUuid: string, @@ -83,7 +84,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('user/:userUuid') async getCommunitiesByUserId(@Param('userUuid') userUuid: string) { try { @@ -96,7 +97,7 @@ export class CommunityController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserCommunityGuard) + @UseGuards(AdminRoleGuard, CheckUserCommunityGuard) @Post('user') async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) { try { @@ -110,7 +111,7 @@ export class CommunityController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Put('rename/:communityUuid') async renameCommunityByUuid( @Param('communityUuid') communityUuid: string, diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index d8149a7..7ebf1ca 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -23,9 +23,9 @@ import { 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'; +import { UserRoleGuard } from 'src/guards/user.role.guard'; @ApiTags('Device Module') @Controller({ @@ -36,7 +36,7 @@ export class DeviceController { constructor(private readonly deviceService: DeviceService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckRoomGuard) + @UseGuards(UserRoleGuard, CheckRoomGuard) @Get('room') async getDevicesByRoomId( @Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, @@ -57,7 +57,7 @@ export class DeviceController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckRoomGuard) + @UseGuards(UserRoleGuard, CheckRoomGuard) @Post('room') async addDeviceInRoom(@Body() addDeviceInRoomDto: AddDeviceInRoomDto) { try { @@ -70,7 +70,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckGroupGuard) + @UseGuards(UserRoleGuard, CheckGroupGuard) @Get('group') async getDevicesByGroupId( @Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto, @@ -90,7 +90,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckGroupGuard) + @UseGuards(UserRoleGuard, CheckGroupGuard) @Post('group') async addDeviceInGroup(@Body() addDeviceInGroupDto: AddDeviceInGroupDto) { try { @@ -103,7 +103,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserHavePermission) + @UseGuards(UserRoleGuard, CheckUserHavePermission) @Get(':deviceUuid') async getDeviceDetailsByDeviceId( @Param('deviceUuid') deviceUuid: string, @@ -123,7 +123,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserHavePermission) + @UseGuards(UserRoleGuard, CheckUserHavePermission) @Get(':deviceUuid/functions') async getDeviceInstructionByDeviceId( @Param('deviceUuid') deviceUuid: string, @@ -140,7 +140,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserHavePermission) + @UseGuards(UserRoleGuard, CheckUserHavePermission) @Get(':deviceUuid/functions/status') async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) { try { @@ -154,7 +154,7 @@ export class DeviceController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserHaveControllablePermission) + @UseGuards(UserRoleGuard, CheckUserHaveControllablePermission) @Post(':deviceUuid/control') async controlDevice( @Body() controlDeviceDto: ControlDeviceDto, diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts index a42db7b..2fd9dfc 100644 --- a/src/floor/controllers/floor.controller.ts +++ b/src/floor/controllers/floor.controller.ts @@ -12,12 +12,13 @@ import { UseGuards, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { AddFloorDto, AddUserFloorDto } from '../dtos/add.floor.dto'; 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 { UserRoleGuard } from 'src/guards/user.role.guard'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; @ApiTags('Floor Module') @Controller({ @@ -28,7 +29,7 @@ export class FloorController { constructor(private readonly floorService: FloorService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckBuildingTypeGuard) + @UseGuards(AdminRoleGuard, CheckBuildingTypeGuard) @Post() async addFloor(@Body() addFloorDto: AddFloorDto) { try { @@ -43,7 +44,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get(':floorUuid') async getFloorByUuid(@Param('floorUuid') floorUuid: string) { try { @@ -58,7 +59,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('child/:floorUuid') async getFloorChildByUuid( @Param('floorUuid') floorUuid: string, @@ -78,7 +79,7 @@ export class FloorController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('parent/:floorUuid') async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) { try { @@ -93,7 +94,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserFloorGuard) + @UseGuards(AdminRoleGuard, CheckUserFloorGuard) @Post('user') async addUserFloor(@Body() addUserFloorDto: AddUserFloorDto) { try { @@ -108,7 +109,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('user/:userUuid') async getFloorsByUserId(@Param('userUuid') userUuid: string) { try { @@ -122,7 +123,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Put('rename/:floorUuid') async renameFloorByUuid( @Param('floorUuid') floorUuid: string, diff --git a/src/group/controllers/group.controller.ts b/src/group/controllers/group.controller.ts index 0f74ff6..c00ff92 100644 --- a/src/group/controllers/group.controller.ts +++ b/src/group/controllers/group.controller.ts @@ -12,11 +12,11 @@ import { HttpStatus, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { AddGroupDto } from '../dtos/add.group.dto'; import { ControlGroupDto } from '../dtos/control.group.dto'; import { RenameGroupDto } from '../dtos/rename.group.dto copy'; import { CheckProductUuidForAllDevicesGuard } from 'src/guards/device.product.guard'; +import { UserRoleGuard } from 'src/guards/user.role.guard'; @ApiTags('Group Module') @Controller({ @@ -27,7 +27,7 @@ export class GroupController { constructor(private readonly groupService: GroupService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('space/:spaceUuid') async getGroupsBySpaceUuid(@Param('spaceUuid') spaceUuid: string) { try { @@ -40,7 +40,7 @@ export class GroupController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get(':groupUuid') async getGroupsByGroupId(@Param('groupUuid') groupUuid: string) { try { @@ -53,7 +53,7 @@ export class GroupController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckProductUuidForAllDevicesGuard) + @UseGuards(UserRoleGuard, CheckProductUuidForAllDevicesGuard) @Post() async addGroup(@Body() addGroupDto: AddGroupDto) { try { @@ -67,7 +67,7 @@ export class GroupController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Post('control') async controlGroup(@Body() controlGroupDto: ControlGroupDto) { try { @@ -81,7 +81,7 @@ export class GroupController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Put('rename/:groupUuid') async renameGroupByUuid( @Param('groupUuid') groupUuid: string, @@ -101,7 +101,7 @@ export class GroupController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Delete(':groupUuid') async deleteGroup(@Param('groupUuid') groupUuid: string) { try { diff --git a/src/role/controllers/role.controller.ts b/src/role/controllers/role.controller.ts index 1bc87ce..ebabe93 100644 --- a/src/role/controllers/role.controller.ts +++ b/src/role/controllers/role.controller.ts @@ -10,8 +10,8 @@ import { } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { RoleService } from '../services/role.service'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { UserRoleEditDto } from '../dtos'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; @ApiTags('Role Module') @Controller({ @@ -21,7 +21,7 @@ import { UserRoleEditDto } from '../dtos'; export class RoleController { constructor(private readonly roleService: RoleService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Get('types') async fetchRoleTypes() { try { @@ -36,7 +36,7 @@ export class RoleController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Put('edit/user/:userUuid') async editUserRoleType( @Param('userUuid') userUuid: string, diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts index 221fb39..d39b584 100644 --- a/src/room/controllers/room.controller.ts +++ b/src/room/controllers/room.controller.ts @@ -11,11 +11,12 @@ import { UseGuards, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; 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 { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { UserRoleGuard } from 'src/guards/user.role.guard'; @ApiTags('Room Module') @Controller({ @@ -26,7 +27,7 @@ export class RoomController { constructor(private readonly roomService: RoomService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUnitTypeGuard) + @UseGuards(AdminRoleGuard, CheckUnitTypeGuard) @Post() async addRoom(@Body() addRoomDto: AddRoomDto) { try { @@ -41,7 +42,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get(':roomUuid') async getRoomByUuid(@Param('roomUuid') roomUuid: string) { try { @@ -56,7 +57,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('parent/:roomUuid') async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) { try { @@ -70,7 +71,7 @@ export class RoomController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserRoomGuard) + @UseGuards(AdminRoleGuard, CheckUserRoomGuard) @Post('user') async addUserRoom(@Body() addUserRoomDto: AddUserRoomDto) { try { @@ -84,7 +85,7 @@ export class RoomController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('user/:userUuid') async getRoomsByUserId(@Param('userUuid') userUuid: string) { try { @@ -98,7 +99,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @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..c42793c 100644 --- a/src/unit/controllers/unit.controller.ts +++ b/src/unit/controllers/unit.controller.ts @@ -12,12 +12,13 @@ import { UseGuards, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; import { AddUnitDto, AddUserUnitDto } from '../dtos/add.unit.dto'; 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 { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { UserRoleGuard } from 'src/guards/user.role.guard'; @ApiTags('Unit Module') @Controller({ @@ -28,7 +29,7 @@ export class UnitController { constructor(private readonly unitService: UnitService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckFloorTypeGuard) + @UseGuards(AdminRoleGuard, CheckFloorTypeGuard) @Post() async addUnit(@Body() addUnitDto: AddUnitDto) { try { @@ -43,7 +44,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get(':unitUuid') async getUnitByUuid(@Param('unitUuid') unitUuid: string) { try { @@ -58,7 +59,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('child/:unitUuid') async getUnitChildByUuid( @Param('unitUuid') unitUuid: string, @@ -75,7 +76,7 @@ export class UnitController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('parent/:unitUuid') async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) { try { @@ -89,7 +90,7 @@ export class UnitController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard, CheckUserUnitGuard) + @UseGuards(AdminRoleGuard, CheckUserUnitGuard) @Post('user') async addUserUnit(@Body() addUserUnitDto: AddUserUnitDto) { try { @@ -103,7 +104,7 @@ export class UnitController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Get('user/:userUuid') async getUnitsByUserId(@Param('userUuid') userUuid: string) { try { @@ -117,7 +118,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(UserRoleGuard) @Put('rename/:unitUuid') async renameUnitByUuid( @Param('unitUuid') unitUuid: string, 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 fef4cc7..2f68708 100644 --- a/src/user-device-permission/controllers/user-device-permission.controller.ts +++ b/src/user-device-permission/controllers/user-device-permission.controller.ts @@ -13,8 +13,8 @@ import { import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { UserDevicePermissionService } from '../services/user-device-permission.service'; import { UserDevicePermissionAddDto } from '../dtos/user-device-permission.add.dto'; -import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { UserDevicePermissionEditDto } from '../dtos/user-device-permission.edit.dto'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; @ApiTags('Device Permission Module') @Controller({ @@ -27,7 +27,7 @@ export class UserDevicePermissionController { ) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Post('add') async addDevicePermission( @Body() userDevicePermissionDto: UserDevicePermissionAddDto, @@ -51,7 +51,7 @@ export class UserDevicePermissionController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Put('edit/:devicePermissionUuid') async editDevicePermission( @Param('devicePermissionUuid') devicePermissionUuid: string, @@ -75,7 +75,7 @@ export class UserDevicePermissionController { } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Get(':deviceUuid/list') async fetchDevicePermission(@Param('deviceUuid') deviceUuid: string) { try { @@ -91,7 +91,7 @@ export class UserDevicePermissionController { } } @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Delete(':devicePermissionUuid') async deleteDevicePermission( @Param('devicePermissionUuid') devicePermissionUuid: string, diff --git a/src/users/controllers/user.controller.ts b/src/users/controllers/user.controller.ts index 4620cd0..09518e8 100644 --- a/src/users/controllers/user.controller.ts +++ b/src/users/controllers/user.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { UserService } from '../services/user.service'; import { UserListDto } from '../dtos/user.list.dto'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../../libs/common/src/guards/jwt.auth.guard'; +import { AdminRoleGuard } from 'src/guards/admin.role.guard'; @ApiTags('User Module') @Controller({ @@ -13,7 +13,7 @@ export class UserController { constructor(private readonly userService: UserService) {} @ApiBearerAuth() - @UseGuards(JwtAuthGuard) + @UseGuards(AdminRoleGuard) @Get('list') async userList(@Query() userListDto: UserListDto) { try { From c30a21b04f5d81042e6952a01774f4b10721e864 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Tue, 7 May 2024 09:48:16 +0300 Subject: [PATCH 20/33] Add Unique constraint to user and roleType fields in UserRoleEntity --- libs/common/src/modules/user-role/entities/user.role.entity.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/user-role/entities/user.role.entity.ts b/libs/common/src/modules/user-role/entities/user.role.entity.ts index e375723..34b49c4 100644 --- a/libs/common/src/modules/user-role/entities/user.role.entity.ts +++ b/libs/common/src/modules/user-role/entities/user.role.entity.ts @@ -1,10 +1,11 @@ -import { Entity, ManyToOne } from 'typeorm'; +import { Entity, ManyToOne, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { UserRoleDto } from '../dtos'; import { UserEntity } from '../../user/entities'; import { RoleTypeEntity } from '../../role-type/entities'; @Entity({ name: 'user-role' }) +@Unique(['user', 'roleType']) export class UserRoleEntity extends AbstractEntity { @ManyToOne(() => UserEntity, (user) => user.role, { nullable: false, 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 21/33] 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 22/33] 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 23/33] 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, From d1fa15cff1cfbd6716799fc60f6578353abae2ec Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 11 May 2024 21:01:39 +0300 Subject: [PATCH 24/33] Update role types in enums and role guards --- libs/common/src/constants/role.type.enum.ts | 3 ++- src/guards/admin.role.guard.ts | 5 ++++- src/guards/user.role.guard.ts | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libs/common/src/constants/role.type.enum.ts b/libs/common/src/constants/role.type.enum.ts index 34b3929..72bf14e 100644 --- a/libs/common/src/constants/role.type.enum.ts +++ b/libs/common/src/constants/role.type.enum.ts @@ -1,4 +1,5 @@ export enum RoleType { - USER = 'USER', + SUPER_ADMIN = 'SUPER_ADMIN', ADMIN = 'ADMIN', + USER = 'USER', } diff --git a/src/guards/admin.role.guard.ts b/src/guards/admin.role.guard.ts index 0c3b259..fc26067 100644 --- a/src/guards/admin.role.guard.ts +++ b/src/guards/admin.role.guard.ts @@ -4,7 +4,10 @@ import { AuthGuard } from '@nestjs/passport'; export class AdminRoleGuard extends AuthGuard('jwt') { handleRequest(err, user) { - const isAdmin = user.roles.some((role) => role.type === RoleType.ADMIN); + const isAdmin = user.roles.some( + (role) => + role.type === RoleType.SUPER_ADMIN || role.type === RoleType.ADMIN, + ); if (err || !user) { throw err || new UnauthorizedException(); } else { diff --git a/src/guards/user.role.guard.ts b/src/guards/user.role.guard.ts index b21632a..4864fe0 100644 --- a/src/guards/user.role.guard.ts +++ b/src/guards/user.role.guard.ts @@ -5,7 +5,10 @@ import { AuthGuard } from '@nestjs/passport'; export class UserRoleGuard extends AuthGuard('jwt') { handleRequest(err, user) { const isUserOrAdmin = user.roles.some( - (role) => role.type === RoleType.ADMIN || role.type === RoleType.USER, + (role) => + role.type === RoleType.SUPER_ADMIN || + role.type === RoleType.ADMIN || + role.type === RoleType.USER, ); if (err || !user) { throw err || new UnauthorizedException(); From 3e9fff3822e44f8d855b0f9a8efdf7072283436b Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 11 May 2024 21:02:25 +0300 Subject: [PATCH 25/33] Add super admin configuration and service --- libs/common/src/config/index.ts | 3 +- libs/common/src/config/super.admin.config.ts | 9 +++ libs/common/src/helper/helper.module.ts | 25 ++++++- .../helper/services/super.admin.sarvice.ts | 72 +++++++++++++++++++ src/main.ts | 4 ++ 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 libs/common/src/config/super.admin.config.ts create mode 100644 libs/common/src/helper/services/super.admin.sarvice.ts diff --git a/libs/common/src/config/index.ts b/libs/common/src/config/index.ts index 94380ce..d4cbbdb 100644 --- a/libs/common/src/config/index.ts +++ b/libs/common/src/config/index.ts @@ -1,2 +1,3 @@ import emailConfig from './email.config'; -export default [emailConfig]; +import superAdminConfig from './super.admin.config'; +export default [emailConfig, superAdminConfig]; diff --git a/libs/common/src/config/super.admin.config.ts b/libs/common/src/config/super.admin.config.ts new file mode 100644 index 0000000..90a156e --- /dev/null +++ b/libs/common/src/config/super.admin.config.ts @@ -0,0 +1,9 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs( + 'super-admin', + (): Record => ({ + SUPER_ADMIN_EMAIL: process.env.SUPER_ADMIN_EMAIL, + SUPER_ADMIN_PASSWORD: process.env.SUPER_ADMIN_PASSWORD, + }), +); diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index 826883a..f5fc400 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -1,11 +1,30 @@ import { Global, Module } from '@nestjs/common'; import { HelperHashService } from './services'; +import { UserRepository } from '../modules/user/repositories'; +import { UserRepositoryModule } from '../modules/user/user.repository.module'; +import { UserRoleRepository } from '../modules/user-role/repositories'; +import { UserRoleRepositoryModule } from '../modules/user-role/user.role.repository.module'; +import { RoleTypeRepository } from '../modules/role-type/repositories'; +import { RoleTypeRepositoryModule } from '../modules/role-type/role.type.repository.module'; +import { ConfigModule } from '@nestjs/config'; +import { SuperAdminService } from './services/super.admin.sarvice'; @Global() @Module({ - providers: [HelperHashService], - exports: [HelperHashService], + providers: [ + HelperHashService, + SuperAdminService, + UserRepository, + UserRoleRepository, + RoleTypeRepository, + ], + exports: [HelperHashService, SuperAdminService], controllers: [], - imports: [], + imports: [ + ConfigModule.forRoot(), + UserRepositoryModule, + UserRoleRepositoryModule, + RoleTypeRepositoryModule, + ], }) export class HelperModule {} diff --git a/libs/common/src/helper/services/super.admin.sarvice.ts b/libs/common/src/helper/services/super.admin.sarvice.ts new file mode 100644 index 0000000..e9efbce --- /dev/null +++ b/libs/common/src/helper/services/super.admin.sarvice.ts @@ -0,0 +1,72 @@ +import { HelperHashService } from './helper.hash.service'; +import { Injectable } from '@nestjs/common'; +import { UserRepository } from '@app/common/modules/user/repositories'; +import { RoleType } from '@app/common/constants/role.type.enum'; +import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; +import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class SuperAdminService { + constructor( + private readonly configService: ConfigService, + private readonly userRepository: UserRepository, + private readonly userRoleRepository: UserRoleRepository, + private readonly roleTypeRepository: RoleTypeRepository, + private readonly helperHashService: HelperHashService, + ) {} + + async createSuperAdminIfNotFound(): Promise { + try { + const superAdminData = await this.userRoleRepository.find({ + where: { roleType: { type: RoleType.SUPER_ADMIN } }, + relations: ['roleType'], + }); + + if (superAdminData.length <= 0) { + // Create the super admin user if not found + console.log('Creating super admin user...'); + + await this.createSuperAdmin(); + } + } catch (err) { + console.error('Error while checking super admin:', err); + throw err; + } + } + private async getRoleUuidByRoleType(roleType: string) { + const role = await this.roleTypeRepository.findOne({ + where: { type: roleType }, + }); + + return role.uuid; + } + private async createSuperAdmin(): Promise { + const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt + const hashedPassword = await this.helperHashService.bcrypt( + this.configService.get('super-admin.SUPER_ADMIN_PASSWORD'), + salt, + ); + try { + const user = await this.userRepository.save({ + email: this.configService.get('super-admin.SUPER_ADMIN_EMAIL'), + password: hashedPassword, + firstName: 'Super', + lastName: 'Admin', + isUserVerified: true, + isActive: true, + }); + const defaultUserRoleUuid = await this.getRoleUuidByRoleType( + RoleType.SUPER_ADMIN, + ); + + await this.userRoleRepository.save({ + user: { uuid: user.uuid }, + roleType: { uuid: defaultUserRoleUuid }, + }); + } catch (err) { + console.error('Error while creating super admin:', err); + throw err; + } + } +} diff --git a/src/main.ts b/src/main.ts index 129f319..61dfb92 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; import { ValidationPipe } from '@nestjs/common'; +import { SuperAdminService } from '@app/common/helper/services/super.admin.sarvice'; async function bootstrap() { const app = await NestFactory.create(AuthModule); @@ -33,6 +34,9 @@ async function bootstrap() { }, }), ); + // Create super admin user + const superAdminService = app.get(SuperAdminService); + await superAdminService.createSuperAdminIfNotFound(); await app.listen(process.env.PORT || 4000); } From e7024a5cb86fd773d904306902046b8f0adee148 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 11 May 2024 21:02:35 +0300 Subject: [PATCH 26/33] Add Unique constraint to 'type' column in RoleTypeEntity --- libs/common/src/modules/role-type/entities/role.type.entity.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/common/src/modules/role-type/entities/role.type.entity.ts b/libs/common/src/modules/role-type/entities/role.type.entity.ts index c65db72..0621b7d 100644 --- a/libs/common/src/modules/role-type/entities/role.type.entity.ts +++ b/libs/common/src/modules/role-type/entities/role.type.entity.ts @@ -1,10 +1,11 @@ -import { Column, Entity, OneToMany } from 'typeorm'; +import { Column, Entity, OneToMany, Unique } from 'typeorm'; import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { RoleTypeDto } from '../dtos/role.type.dto'; import { RoleType } from '@app/common/constants/role.type.enum'; import { UserRoleEntity } from '../../user-role/entities'; @Entity({ name: 'role-type' }) +@Unique(['type']) export class RoleTypeEntity extends AbstractEntity { @Column({ nullable: false, From ea19361a5995a0009e3c30ce37bcec18bb7ca1a1 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 11 May 2024 21:19:53 +0300 Subject: [PATCH 27/33] Update role guards to differentiate between admin and super admin roles --- src/auth/controllers/user-auth.controller.ts | 6 +++--- src/guards/admin.role.guard.ts | 8 ++++---- src/guards/super.admin.role.guard.ts | 21 ++++++++++++++++++++ src/guards/user.role.guard.ts | 12 +++++------ src/role/controllers/role.controller.ts | 6 +++--- src/role/dtos/role.edit.dto.ts | 9 ++++++--- 6 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 src/guards/super.admin.role.guard.ts diff --git a/src/auth/controllers/user-auth.controller.ts b/src/auth/controllers/user-auth.controller.ts index fd139f9..86f9ce6 100644 --- a/src/auth/controllers/user-auth.controller.ts +++ b/src/auth/controllers/user-auth.controller.ts @@ -16,7 +16,7 @@ import { ResponseMessage } from '../../../libs/common/src/response/response.deco import { UserLoginDto } from '../dtos/user-login.dto'; import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; import { RefreshTokenGuard } from '@app/common/guards/jwt-refresh.auth.guard'; -import { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; @Controller({ version: '1', @@ -52,7 +52,7 @@ export class UserAuthController { } @ApiBearerAuth() - @UseGuards(AdminRoleGuard) + @UseGuards(SuperAdminRoleGuard) @Delete('user/delete/:id') async userDelete(@Param('id') id: string) { await this.userAuthService.deleteUser(id); @@ -98,7 +98,7 @@ export class UserAuthController { } @ApiBearerAuth() - @UseGuards(AdminRoleGuard) + @UseGuards(SuperAdminRoleGuard) @Get('user/list') async userList() { const userList = await this.userAuthService.userList(); diff --git a/src/guards/admin.role.guard.ts b/src/guards/admin.role.guard.ts index fc26067..f7d64a0 100644 --- a/src/guards/admin.role.guard.ts +++ b/src/guards/admin.role.guard.ts @@ -4,13 +4,13 @@ import { AuthGuard } from '@nestjs/passport'; export class AdminRoleGuard extends AuthGuard('jwt') { handleRequest(err, user) { - const isAdmin = user.roles.some( - (role) => - role.type === RoleType.SUPER_ADMIN || role.type === RoleType.ADMIN, - ); if (err || !user) { throw err || new UnauthorizedException(); } else { + const isAdmin = user.roles.some( + (role) => + role.type === RoleType.SUPER_ADMIN || role.type === RoleType.ADMIN, + ); if (!isAdmin) { throw new BadRequestException('Only admin role can access this route'); } diff --git a/src/guards/super.admin.role.guard.ts b/src/guards/super.admin.role.guard.ts new file mode 100644 index 0000000..ef93a75 --- /dev/null +++ b/src/guards/super.admin.role.guard.ts @@ -0,0 +1,21 @@ +import { RoleType } from '@app/common/constants/role.type.enum'; +import { BadRequestException, UnauthorizedException } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +export class SuperAdminRoleGuard extends AuthGuard('jwt') { + handleRequest(err, user) { + if (err || !user) { + throw err || new UnauthorizedException(); + } else { + const isSuperAdmin = user.roles.some( + (role) => role.type === RoleType.SUPER_ADMIN, + ); + if (!isSuperAdmin) { + throw new BadRequestException( + 'Only super admin role can access this route', + ); + } + } + return user; + } +} diff --git a/src/guards/user.role.guard.ts b/src/guards/user.role.guard.ts index 4864fe0..59abad8 100644 --- a/src/guards/user.role.guard.ts +++ b/src/guards/user.role.guard.ts @@ -4,15 +4,15 @@ import { AuthGuard } from '@nestjs/passport'; export class UserRoleGuard extends AuthGuard('jwt') { handleRequest(err, user) { - const isUserOrAdmin = user.roles.some( - (role) => - role.type === RoleType.SUPER_ADMIN || - role.type === RoleType.ADMIN || - role.type === RoleType.USER, - ); if (err || !user) { throw err || new UnauthorizedException(); } else { + const isUserOrAdmin = user.roles.some( + (role) => + role.type === RoleType.SUPER_ADMIN || + role.type === RoleType.ADMIN || + role.type === RoleType.USER, + ); if (!isUserOrAdmin) { throw new BadRequestException( 'Only admin or user role can access this route', diff --git a/src/role/controllers/role.controller.ts b/src/role/controllers/role.controller.ts index ebabe93..2a6c917 100644 --- a/src/role/controllers/role.controller.ts +++ b/src/role/controllers/role.controller.ts @@ -11,7 +11,7 @@ import { import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { RoleService } from '../services/role.service'; import { UserRoleEditDto } from '../dtos'; -import { AdminRoleGuard } from 'src/guards/admin.role.guard'; +import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; @ApiTags('Role Module') @Controller({ @@ -21,7 +21,7 @@ import { AdminRoleGuard } from 'src/guards/admin.role.guard'; export class RoleController { constructor(private readonly roleService: RoleService) {} @ApiBearerAuth() - @UseGuards(AdminRoleGuard) + @UseGuards(SuperAdminRoleGuard) @Get('types') async fetchRoleTypes() { try { @@ -36,7 +36,7 @@ export class RoleController { } } @ApiBearerAuth() - @UseGuards(AdminRoleGuard) + @UseGuards(SuperAdminRoleGuard) @Put('edit/user/:userUuid') async editUserRoleType( @Param('userUuid') userUuid: string, diff --git a/src/role/dtos/role.edit.dto.ts b/src/role/dtos/role.edit.dto.ts index 5cd0aac..9e9b394 100644 --- a/src/role/dtos/role.edit.dto.ts +++ b/src/role/dtos/role.edit.dto.ts @@ -1,13 +1,16 @@ import { RoleType } from '@app/common/constants/role.type.enum'; import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum } from 'class-validator'; +import { IsEnum, IsIn } from 'class-validator'; export class UserRoleEditDto { @ApiProperty({ - description: 'role type', - enum: RoleType, + description: 'Role type (USER or ADMIN)', + enum: [RoleType.USER, RoleType.ADMIN], required: true, }) @IsEnum(RoleType) + @IsIn([RoleType.USER, RoleType.ADMIN], { + message: 'roleType must be one of the following values: USER, ADMIN', + }) roleType: RoleType; } From ad15164e15c8802a194adb8f462f271a3794b12d Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 18 May 2024 21:53:27 +0300 Subject: [PATCH 28/33] feat: Add seeder module and services for all lookup tables --- libs/common/src/constants/space-type.enum.ts | 7 +++ libs/common/src/helper/helper.module.ts | 25 ++------- libs/common/src/seed/seeder.module.ts | 43 +++++++++++++++ .../seed/services/permission.type.seeder.ts | 49 +++++++++++++++++ .../src/seed/services/role.type.seeder.ts | 46 ++++++++++++++++ .../src/seed/services/seeder.service.ts | 21 ++++++++ .../src/seed/services/space.type.seeder.ts | 52 +++++++++++++++++++ .../services/supper.admin.seeder.ts} | 4 +- src/app.module.ts | 2 + src/main.ts | 12 +++-- 10 files changed, 233 insertions(+), 28 deletions(-) create mode 100644 libs/common/src/constants/space-type.enum.ts create mode 100644 libs/common/src/seed/seeder.module.ts create mode 100644 libs/common/src/seed/services/permission.type.seeder.ts create mode 100644 libs/common/src/seed/services/role.type.seeder.ts create mode 100644 libs/common/src/seed/services/seeder.service.ts create mode 100644 libs/common/src/seed/services/space.type.seeder.ts rename libs/common/src/{helper/services/super.admin.sarvice.ts => seed/services/supper.admin.seeder.ts} (96%) diff --git a/libs/common/src/constants/space-type.enum.ts b/libs/common/src/constants/space-type.enum.ts new file mode 100644 index 0000000..22688e7 --- /dev/null +++ b/libs/common/src/constants/space-type.enum.ts @@ -0,0 +1,7 @@ +export enum SpaceType { + COMMUNITY = 'community', + BUILDING = 'building', + FLOOR = 'floor', + UNIT = 'unit', + ROOM = 'room', +} diff --git a/libs/common/src/helper/helper.module.ts b/libs/common/src/helper/helper.module.ts index f5fc400..826883a 100644 --- a/libs/common/src/helper/helper.module.ts +++ b/libs/common/src/helper/helper.module.ts @@ -1,30 +1,11 @@ import { Global, Module } from '@nestjs/common'; import { HelperHashService } from './services'; -import { UserRepository } from '../modules/user/repositories'; -import { UserRepositoryModule } from '../modules/user/user.repository.module'; -import { UserRoleRepository } from '../modules/user-role/repositories'; -import { UserRoleRepositoryModule } from '../modules/user-role/user.role.repository.module'; -import { RoleTypeRepository } from '../modules/role-type/repositories'; -import { RoleTypeRepositoryModule } from '../modules/role-type/role.type.repository.module'; -import { ConfigModule } from '@nestjs/config'; -import { SuperAdminService } from './services/super.admin.sarvice'; @Global() @Module({ - providers: [ - HelperHashService, - SuperAdminService, - UserRepository, - UserRoleRepository, - RoleTypeRepository, - ], - exports: [HelperHashService, SuperAdminService], + providers: [HelperHashService], + exports: [HelperHashService], controllers: [], - imports: [ - ConfigModule.forRoot(), - UserRepositoryModule, - UserRoleRepositoryModule, - RoleTypeRepositoryModule, - ], + imports: [], }) export class HelperModule {} diff --git a/libs/common/src/seed/seeder.module.ts b/libs/common/src/seed/seeder.module.ts new file mode 100644 index 0000000..a6e2585 --- /dev/null +++ b/libs/common/src/seed/seeder.module.ts @@ -0,0 +1,43 @@ +import { Global, Module } from '@nestjs/common'; +import { SeederService } from './services/seeder.service'; +import { PermissionTypeRepository } from '../modules/permission/repositories'; +import { PermissionTypeSeeder } from './services/permission.type.seeder'; +import { PermissionTypeRepositoryModule } from '../modules/permission/permission.repository.module'; +import { ConfigModule } from '@nestjs/config'; +import { RoleTypeRepositoryModule } from '../modules/role-type/role.type.repository.module'; +import { RoleTypeRepository } from '../modules/role-type/repositories'; +import { RoleTypeSeeder } from './services/role.type.seeder'; +import { SpaceTypeRepository } from '../modules/space-type/repositories'; +import { SpaceTypeSeeder } from './services/space.type.seeder'; +import { SpaceTypeRepositoryModule } from '../modules/space-type/space.type.repository.module'; +import { SuperAdminSeeder } from './services/supper.admin.seeder'; +import { UserRepository } from '../modules/user/repositories'; +import { UserRoleRepository } from '../modules/user-role/repositories'; +import { UserRoleRepositoryModule } from '../modules/user-role/user.role.repository.module'; +import { UserRepositoryModule } from '../modules/user/user.repository.module'; +@Global() +@Module({ + providers: [ + PermissionTypeSeeder, + RoleTypeSeeder, + SpaceTypeSeeder, + SeederService, + PermissionTypeRepository, + RoleTypeRepository, + SpaceTypeRepository, + SuperAdminSeeder, + UserRepository, + UserRoleRepository, + ], + exports: [SeederService], + controllers: [], + imports: [ + ConfigModule.forRoot(), + PermissionTypeRepositoryModule, + RoleTypeRepositoryModule, + UserRepositoryModule, + UserRoleRepositoryModule, + SpaceTypeRepositoryModule, + ], +}) +export class SeederModule {} diff --git a/libs/common/src/seed/services/permission.type.seeder.ts b/libs/common/src/seed/services/permission.type.seeder.ts new file mode 100644 index 0000000..537bddb --- /dev/null +++ b/libs/common/src/seed/services/permission.type.seeder.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; +import { PermissionTypeRepository } from '../../modules/permission/repositories'; +import { PermissionType } from '../../constants/permission-type.enum'; + +@Injectable() +export class PermissionTypeSeeder { + constructor( + private readonly permissionTypeRepository: PermissionTypeRepository, + ) {} + + async addPermissionTypeDataIfNotFound(): Promise { + try { + const existingPermissionTypes = + await this.permissionTypeRepository.find(); + + const permissionTypeNames = existingPermissionTypes.map((pt) => pt.type); + + const missingPermissionTypes = []; + if (!permissionTypeNames.includes(PermissionType.CONTROLLABLE)) { + missingPermissionTypes.push(PermissionType.CONTROLLABLE); + } + if (!permissionTypeNames.includes(PermissionType.READ)) { + missingPermissionTypes.push(PermissionType.READ); + } + + if (missingPermissionTypes.length > 0) { + await this.addPermissionTypeData(missingPermissionTypes); + } + } catch (err) { + console.error('Error while checking permission type data:', err); + throw err; + } + } + + private async addPermissionTypeData( + permissionTypes: string[], + ): Promise { + try { + const permissionTypeEntities = permissionTypes.map((type) => ({ + type, + })); + + await this.permissionTypeRepository.save(permissionTypeEntities); + } catch (err) { + console.error('Error while adding permission type data:', err); + throw err; + } + } +} diff --git a/libs/common/src/seed/services/role.type.seeder.ts b/libs/common/src/seed/services/role.type.seeder.ts new file mode 100644 index 0000000..ea8bf15 --- /dev/null +++ b/libs/common/src/seed/services/role.type.seeder.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@nestjs/common'; +import { RoleType } from '../../constants/role.type.enum'; +import { RoleTypeRepository } from '../../modules/role-type/repositories'; + +@Injectable() +export class RoleTypeSeeder { + constructor(private readonly roleTypeRepository: RoleTypeRepository) {} + + async addRoleTypeDataIfNotFound(): Promise { + try { + const existingRoleTypes = await this.roleTypeRepository.find(); + + const roleTypeNames = existingRoleTypes.map((pt) => pt.type); + + const missingRoleTypes = []; + if (!roleTypeNames.includes(RoleType.SUPER_ADMIN)) { + missingRoleTypes.push(RoleType.SUPER_ADMIN); + } + if (!roleTypeNames.includes(RoleType.ADMIN)) { + missingRoleTypes.push(RoleType.ADMIN); + } + if (!roleTypeNames.includes(RoleType.USER)) { + missingRoleTypes.push(RoleType.USER); + } + if (missingRoleTypes.length > 0) { + await this.addRoleTypeData(missingRoleTypes); + } + } catch (err) { + console.error('Error while checking role type data:', err); + throw err; + } + } + + private async addRoleTypeData(roleTypes: string[]): Promise { + try { + const roleTypeEntities = roleTypes.map((type) => ({ + type, + })); + + await this.roleTypeRepository.save(roleTypeEntities); + } catch (err) { + console.error('Error while adding role type data:', err); + throw err; + } + } +} diff --git a/libs/common/src/seed/services/seeder.service.ts b/libs/common/src/seed/services/seeder.service.ts new file mode 100644 index 0000000..92b5f51 --- /dev/null +++ b/libs/common/src/seed/services/seeder.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { PermissionTypeSeeder } from './permission.type.seeder'; +import { RoleTypeSeeder } from './role.type.seeder'; +import { SpaceTypeSeeder } from './space.type.seeder'; +import { SuperAdminSeeder } from './supper.admin.seeder'; +@Injectable() +export class SeederService { + constructor( + private readonly permissionTypeSeeder: PermissionTypeSeeder, + private readonly roleTypeSeeder: RoleTypeSeeder, + private readonly spaceTypeSeeder: SpaceTypeSeeder, + private readonly superAdminSeeder: SuperAdminSeeder, + ) {} + + async seed() { + await this.permissionTypeSeeder.addPermissionTypeDataIfNotFound(); + await this.roleTypeSeeder.addRoleTypeDataIfNotFound(); + await this.spaceTypeSeeder.addSpaceTypeDataIfNotFound(); + await this.superAdminSeeder.createSuperAdminIfNotFound(); + } +} diff --git a/libs/common/src/seed/services/space.type.seeder.ts b/libs/common/src/seed/services/space.type.seeder.ts new file mode 100644 index 0000000..3cbcb87 --- /dev/null +++ b/libs/common/src/seed/services/space.type.seeder.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@nestjs/common'; +import { SpaceType } from '../../constants/space-type.enum'; +import { SpaceTypeRepository } from '../../modules/space-type/repositories'; + +@Injectable() +export class SpaceTypeSeeder { + constructor(private readonly spaceTypeRepository: SpaceTypeRepository) {} + + async addSpaceTypeDataIfNotFound(): Promise { + try { + const existingSpaceTypes = await this.spaceTypeRepository.find(); + + const spaceTypeNames = existingSpaceTypes.map((pt) => pt.type); + + const missingSpaceTypes = []; + if (!spaceTypeNames.includes(SpaceType.COMMUNITY)) { + missingSpaceTypes.push(SpaceType.COMMUNITY); + } + if (!spaceTypeNames.includes(SpaceType.BUILDING)) { + missingSpaceTypes.push(SpaceType.BUILDING); + } + if (!spaceTypeNames.includes(SpaceType.FLOOR)) { + missingSpaceTypes.push(SpaceType.FLOOR); + } + if (!spaceTypeNames.includes(SpaceType.UNIT)) { + missingSpaceTypes.push(SpaceType.UNIT); + } + if (!spaceTypeNames.includes(SpaceType.ROOM)) { + missingSpaceTypes.push(SpaceType.ROOM); + } + if (missingSpaceTypes.length > 0) { + await this.addSpaceTypeData(missingSpaceTypes); + } + } catch (err) { + console.error('Error while checking space type data:', err); + throw err; + } + } + + private async addSpaceTypeData(spaceTypes: string[]): Promise { + try { + const spaceTypeEntities = spaceTypes.map((type) => ({ + type, + })); + + await this.spaceTypeRepository.save(spaceTypeEntities); + } catch (err) { + console.error('Error while adding space type data:', err); + throw err; + } + } +} diff --git a/libs/common/src/helper/services/super.admin.sarvice.ts b/libs/common/src/seed/services/supper.admin.seeder.ts similarity index 96% rename from libs/common/src/helper/services/super.admin.sarvice.ts rename to libs/common/src/seed/services/supper.admin.seeder.ts index e9efbce..6cb5f60 100644 --- a/libs/common/src/helper/services/super.admin.sarvice.ts +++ b/libs/common/src/seed/services/supper.admin.seeder.ts @@ -1,13 +1,13 @@ -import { HelperHashService } from './helper.hash.service'; import { Injectable } from '@nestjs/common'; import { UserRepository } from '@app/common/modules/user/repositories'; import { RoleType } from '@app/common/constants/role.type.enum'; import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; import { RoleTypeRepository } from '@app/common/modules/role-type/repositories'; import { ConfigService } from '@nestjs/config'; +import { HelperHashService } from '../../helper/services'; @Injectable() -export class SuperAdminService { +export class SuperAdminSeeder { constructor( private readonly configService: ConfigService, private readonly userRepository: UserRepository, diff --git a/src/app.module.ts b/src/app.module.ts index c9f7275..6add0be 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -13,6 +13,7 @@ import { BuildingModule } from './building/building.module'; import { FloorModule } from './floor/floor.module'; import { UnitModule } from './unit/unit.module'; import { RoleModule } from './role/role.module'; +import { SeederModule } from '@app/common/seed/seeder.module'; @Module({ imports: [ ConfigModule.forRoot({ @@ -30,6 +31,7 @@ import { RoleModule } from './role/role.module'; GroupModule, DeviceModule, UserDevicePermissionModule, + SeederModule, ], controllers: [AuthenticationController], }) diff --git a/src/main.ts b/src/main.ts index 61dfb92..0253ad0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; import { ValidationPipe } from '@nestjs/common'; -import { SuperAdminService } from '@app/common/helper/services/super.admin.sarvice'; +import { SeederService } from '@app/common/seed/services/seeder.service'; async function bootstrap() { const app = await NestFactory.create(AuthModule); @@ -34,10 +34,14 @@ async function bootstrap() { }, }), ); - // Create super admin user - const superAdminService = app.get(SuperAdminService); - await superAdminService.createSuperAdminIfNotFound(); + const seederService = app.get(SeederService); + try { + await seederService.seed(); + console.log('Seeding complete!'); + } catch (error) { + console.error('Seeding failed!', error); + } await app.listen(process.env.PORT || 4000); } console.log('Starting auth at port ...', process.env.PORT || 4000); From 1bc8fee061bd161b7828723ffb2aaea054ce9e5a Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 18 May 2024 22:10:48 +0300 Subject: [PATCH 29/33] Remove onDelete and onUpdate properties from entity relationships --- libs/common/src/modules/device/entities/device.entity.ts | 2 -- libs/common/src/modules/group/entities/group.entity.ts | 1 - .../common/src/modules/permission/entities/permission.entity.ts | 2 -- libs/common/src/modules/role-type/entities/role.type.entity.ts | 2 -- libs/common/src/modules/user/entities/user.entity.ts | 2 -- 5 files changed, 9 deletions(-) diff --git a/libs/common/src/modules/device/entities/device.entity.ts b/libs/common/src/modules/device/entities/device.entity.ts index a7f5548..53dbbb3 100644 --- a/libs/common/src/modules/device/entities/device.entity.ts +++ b/libs/common/src/modules/device/entities/device.entity.ts @@ -25,8 +25,6 @@ export class DeviceEntity extends AbstractEntity { (permission) => permission.device, { nullable: true, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', }, ) permission: DeviceUserPermissionEntity[]; diff --git a/libs/common/src/modules/group/entities/group.entity.ts b/libs/common/src/modules/group/entities/group.entity.ts index 7cea8e8..525f84d 100644 --- a/libs/common/src/modules/group/entities/group.entity.ts +++ b/libs/common/src/modules/group/entities/group.entity.ts @@ -19,7 +19,6 @@ export class GroupEntity extends AbstractEntity { @OneToMany(() => GroupDeviceEntity, (groupDevice) => groupDevice.group, { cascade: true, - onDelete: 'CASCADE', }) groupDevices: GroupDeviceEntity[]; diff --git a/libs/common/src/modules/permission/entities/permission.entity.ts b/libs/common/src/modules/permission/entities/permission.entity.ts index 3ee3943..d15d936 100644 --- a/libs/common/src/modules/permission/entities/permission.entity.ts +++ b/libs/common/src/modules/permission/entities/permission.entity.ts @@ -17,8 +17,6 @@ export class PermissionTypeEntity extends AbstractEntity { (permission) => permission.permissionType, { nullable: true, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', }, ) permission: DeviceUserPermissionEntity[]; diff --git a/libs/common/src/modules/role-type/entities/role.type.entity.ts b/libs/common/src/modules/role-type/entities/role.type.entity.ts index 0621b7d..1697327 100644 --- a/libs/common/src/modules/role-type/entities/role.type.entity.ts +++ b/libs/common/src/modules/role-type/entities/role.type.entity.ts @@ -14,8 +14,6 @@ export class RoleTypeEntity extends AbstractEntity { type: string; @OneToMany(() => UserRoleEntity, (role) => role.roleType, { nullable: true, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', }) role: UserRoleEntity[]; constructor(partial: Partial) { diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index ffe402c..ddf487b 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -61,8 +61,6 @@ export class UserEntity extends AbstractEntity { @OneToMany(() => UserRoleEntity, (role) => role.user, { nullable: true, - onDelete: 'CASCADE', - onUpdate: 'CASCADE', }) role: UserRoleEntity[]; constructor(partial: Partial) { From 6415d789923f3fb68c1be204e70e4cec71361166 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 18 May 2024 23:00:22 +0300 Subject: [PATCH 30/33] Remove USER role --- .../src/auth/interfaces/auth.interface.ts | 2 +- libs/common/src/auth/services/auth.service.ts | 4 +-- .../src/auth/strategies/jwt.strategy.ts | 2 +- .../auth/strategies/refresh-token.strategy.ts | 2 +- libs/common/src/constants/role.type.enum.ts | 1 - .../role-type/entities/role.type.entity.ts | 2 +- .../user-role/entities/user.role.entity.ts | 4 +-- .../src/modules/user/entities/user.entity.ts | 2 +- .../src/seed/services/role.type.seeder.ts | 4 +-- src/auth/services/user-auth.service.ts | 18 +---------- .../controllers/building.controller.ts | 12 +++---- .../controllers/community.controller.ts | 10 +++--- src/device/controllers/device.controller.ts | 18 +++++------ src/floor/controllers/floor.controller.ts | 12 +++---- src/group/controllers/group.controller.ts | 14 ++++---- src/guards/user.role.guard.ts | 24 -------------- src/role/controllers/role.controller.ts | 16 ++++------ src/role/dtos/index.ts | 2 +- src/role/dtos/role.add.dto.ts | 24 ++++++++++++++ src/role/dtos/role.edit.dto.ts | 16 ---------- src/role/services/role.service.ts | 32 ++++++++++++------- src/room/controllers/room.controller.ts | 10 +++--- src/unit/controllers/unit.controller.ts | 12 +++---- 23 files changed, 107 insertions(+), 136 deletions(-) delete mode 100644 src/guards/user.role.guard.ts create mode 100644 src/role/dtos/role.add.dto.ts delete mode 100644 src/role/dtos/role.edit.dto.ts diff --git a/libs/common/src/auth/interfaces/auth.interface.ts b/libs/common/src/auth/interfaces/auth.interface.ts index 8e57cd6..9b73050 100644 --- a/libs/common/src/auth/interfaces/auth.interface.ts +++ b/libs/common/src/auth/interfaces/auth.interface.ts @@ -4,5 +4,5 @@ export class AuthInterface { uuid: string; sessionId: string; id: number; - roles: string[]; + roles?: string[]; } diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 45b19fa..f1c1c0d 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -22,7 +22,7 @@ export class AuthService { where: { email, }, - relations: ['role.roleType'], + relations: ['roles.roleType'], }); if (!user.isUserVerified) { @@ -70,7 +70,7 @@ export class AuthService { uuid: user.uuid, type: user.type, sessionId: user.sessionId, - roles: user.roles, + roles: user?.roles, }; const tokens = await this.getTokens(payload); diff --git a/libs/common/src/auth/strategies/jwt.strategy.ts b/libs/common/src/auth/strategies/jwt.strategy.ts index ed29a3a..d548dd8 100644 --- a/libs/common/src/auth/strategies/jwt.strategy.ts +++ b/libs/common/src/auth/strategies/jwt.strategy.ts @@ -31,7 +31,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { userUuid: payload.uuid, uuid: payload.uuid, sessionId: payload.sessionId, - roles: payload.roles, + roles: payload?.roles, }; } else { throw new BadRequestException('Unauthorized'); diff --git a/libs/common/src/auth/strategies/refresh-token.strategy.ts b/libs/common/src/auth/strategies/refresh-token.strategy.ts index ee36eac..b6d6c2a 100644 --- a/libs/common/src/auth/strategies/refresh-token.strategy.ts +++ b/libs/common/src/auth/strategies/refresh-token.strategy.ts @@ -34,7 +34,7 @@ export class RefreshTokenStrategy extends PassportStrategy( userUuid: payload.uuid, uuid: payload.uuid, sessionId: payload.sessionId, - roles: payload.roles, + roles: payload?.roles, }; } else { throw new BadRequestException('Unauthorized'); diff --git a/libs/common/src/constants/role.type.enum.ts b/libs/common/src/constants/role.type.enum.ts index 72bf14e..3051b04 100644 --- a/libs/common/src/constants/role.type.enum.ts +++ b/libs/common/src/constants/role.type.enum.ts @@ -1,5 +1,4 @@ export enum RoleType { SUPER_ADMIN = 'SUPER_ADMIN', ADMIN = 'ADMIN', - USER = 'USER', } diff --git a/libs/common/src/modules/role-type/entities/role.type.entity.ts b/libs/common/src/modules/role-type/entities/role.type.entity.ts index 1697327..fd332fb 100644 --- a/libs/common/src/modules/role-type/entities/role.type.entity.ts +++ b/libs/common/src/modules/role-type/entities/role.type.entity.ts @@ -15,7 +15,7 @@ export class RoleTypeEntity extends AbstractEntity { @OneToMany(() => UserRoleEntity, (role) => role.roleType, { nullable: true, }) - role: UserRoleEntity[]; + roles: UserRoleEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/modules/user-role/entities/user.role.entity.ts b/libs/common/src/modules/user-role/entities/user.role.entity.ts index 34b49c4..c733594 100644 --- a/libs/common/src/modules/user-role/entities/user.role.entity.ts +++ b/libs/common/src/modules/user-role/entities/user.role.entity.ts @@ -7,12 +7,12 @@ import { RoleTypeEntity } from '../../role-type/entities'; @Entity({ name: 'user-role' }) @Unique(['user', 'roleType']) export class UserRoleEntity extends AbstractEntity { - @ManyToOne(() => UserEntity, (user) => user.role, { + @ManyToOne(() => UserEntity, (user) => user.roles, { nullable: false, }) user: UserEntity; - @ManyToOne(() => RoleTypeEntity, (roleType) => roleType.role, { + @ManyToOne(() => RoleTypeEntity, (roleType) => roleType.roles, { nullable: false, }) roleType: RoleTypeEntity; diff --git a/libs/common/src/modules/user/entities/user.entity.ts b/libs/common/src/modules/user/entities/user.entity.ts index ddf487b..17a9268 100644 --- a/libs/common/src/modules/user/entities/user.entity.ts +++ b/libs/common/src/modules/user/entities/user.entity.ts @@ -62,7 +62,7 @@ export class UserEntity extends AbstractEntity { @OneToMany(() => UserRoleEntity, (role) => role.user, { nullable: true, }) - role: UserRoleEntity[]; + roles: UserRoleEntity[]; constructor(partial: Partial) { super(); Object.assign(this, partial); diff --git a/libs/common/src/seed/services/role.type.seeder.ts b/libs/common/src/seed/services/role.type.seeder.ts index ea8bf15..5f7a4b2 100644 --- a/libs/common/src/seed/services/role.type.seeder.ts +++ b/libs/common/src/seed/services/role.type.seeder.ts @@ -19,9 +19,7 @@ export class RoleTypeSeeder { if (!roleTypeNames.includes(RoleType.ADMIN)) { missingRoleTypes.push(RoleType.ADMIN); } - if (!roleTypeNames.includes(RoleType.USER)) { - missingRoleTypes.push(RoleType.USER); - } + if (missingRoleTypes.length > 0) { await this.addRoleTypeData(missingRoleTypes); } diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index 705ba9e..e74967d 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -50,15 +50,6 @@ export class UserAuthService { password: hashedPassword, }); - const defaultUserRoleUuid = await this.getRoleUuidByRoleType( - RoleType.USER, - ); - - await this.userRoleRepository.save({ - user: { uuid: user.uuid }, - roleType: { uuid: defaultUserRoleUuid }, - }); - return user; } catch (error) { throw new BadRequestException('Failed to register user'); @@ -114,7 +105,7 @@ export class UserAuthService { email: user.email, userId: user.uuid, uuid: user.uuid, - roles: user.role.map((role) => { + roles: user?.roles?.map((role) => { return { uuid: role.uuid, type: role.roleType.type }; }), sessionId: session[1].uuid, @@ -214,11 +205,4 @@ export class UserAuthService { await this.authService.updateRefreshToken(user.uuid, tokens.refreshToken); return tokens; } - private async getRoleUuidByRoleType(roleType: string) { - const role = await this.roleTypeRepository.findOne({ - where: { type: roleType }, - }); - - return role.uuid; - } } diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts index b561ff9..220b96e 100644 --- a/src/building/controllers/building.controller.ts +++ b/src/building/controllers/building.controller.ts @@ -18,7 +18,7 @@ import { UpdateBuildingNameDto } from '../dtos/update.building.dto'; import { CheckCommunityTypeGuard } from 'src/guards/community.type.guard'; import { CheckUserBuildingGuard } from 'src/guards/user.building.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @ApiTags('Building Module') @Controller({ @@ -44,7 +44,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get(':buildingUuid') async getBuildingByUuid(@Param('buildingUuid') buildingUuid: string) { try { @@ -60,7 +60,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('child/:buildingUuid') async getBuildingChildByUuid( @Param('buildingUuid') buildingUuid: string, @@ -80,7 +80,7 @@ export class BuildingController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('parent/:buildingUuid') async getBuildingParentByUuid(@Param('buildingUuid') buildingUuid: string) { try { @@ -109,7 +109,7 @@ export class BuildingController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('user/:userUuid') async getBuildingsByUserId(@Param('userUuid') userUuid: string) { try { @@ -123,7 +123,7 @@ export class BuildingController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Put('rename/:buildingUuid') async renameBuildingByUuid( @Param('buildingUuid') buildingUuid: string, diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index 144041d..c3d2739 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -20,7 +20,7 @@ import { GetCommunityChildDto } from '../dtos/get.community.dto'; import { UpdateCommunityNameDto } from '../dtos/update.community.dto'; import { CheckUserCommunityGuard } from 'src/guards/user.community.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @ApiTags('Community Module') @Controller({ @@ -47,7 +47,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get(':communityUuid') async getCommunityByUuid(@Param('communityUuid') communityUuid: string) { try { @@ -63,7 +63,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('child/:communityUuid') async getCommunityChildByUuid( @Param('communityUuid') communityUuid: string, @@ -84,7 +84,7 @@ export class CommunityController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('user/:userUuid') async getCommunitiesByUserId(@Param('userUuid') userUuid: string) { try { @@ -111,7 +111,7 @@ export class CommunityController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Put('rename/:communityUuid') async renameCommunityByUuid( @Param('communityUuid') communityUuid: string, diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index 7ebf1ca..e014c0c 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -25,7 +25,7 @@ import { CheckRoomGuard } from 'src/guards/room.guard'; import { CheckGroupGuard } from 'src/guards/group.guard'; import { CheckUserHavePermission } from 'src/guards/user.device.permission.guard'; import { CheckUserHaveControllablePermission } from 'src/guards/user.device.controllable.permission.guard'; -import { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @ApiTags('Device Module') @Controller({ @@ -36,7 +36,7 @@ export class DeviceController { constructor(private readonly deviceService: DeviceService) {} @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckRoomGuard) + @UseGuards(JwtAuthGuard, CheckRoomGuard) @Get('room') async getDevicesByRoomId( @Query() getDeviceByRoomUuidDto: GetDeviceByRoomUuidDto, @@ -57,7 +57,7 @@ export class DeviceController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckRoomGuard) + @UseGuards(JwtAuthGuard, CheckRoomGuard) @Post('room') async addDeviceInRoom(@Body() addDeviceInRoomDto: AddDeviceInRoomDto) { try { @@ -70,7 +70,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckGroupGuard) + @UseGuards(JwtAuthGuard, CheckGroupGuard) @Get('group') async getDevicesByGroupId( @Query() getDeviceByGroupIdDto: GetDeviceByGroupIdDto, @@ -90,7 +90,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckGroupGuard) + @UseGuards(JwtAuthGuard, CheckGroupGuard) @Post('group') async addDeviceInGroup(@Body() addDeviceInGroupDto: AddDeviceInGroupDto) { try { @@ -103,7 +103,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckUserHavePermission) + @UseGuards(JwtAuthGuard, CheckUserHavePermission) @Get(':deviceUuid') async getDeviceDetailsByDeviceId( @Param('deviceUuid') deviceUuid: string, @@ -123,7 +123,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckUserHavePermission) + @UseGuards(JwtAuthGuard, CheckUserHavePermission) @Get(':deviceUuid/functions') async getDeviceInstructionByDeviceId( @Param('deviceUuid') deviceUuid: string, @@ -140,7 +140,7 @@ export class DeviceController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckUserHavePermission) + @UseGuards(JwtAuthGuard, CheckUserHavePermission) @Get(':deviceUuid/functions/status') async getDevicesInstructionStatus(@Param('deviceUuid') deviceUuid: string) { try { @@ -154,7 +154,7 @@ export class DeviceController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckUserHaveControllablePermission) + @UseGuards(JwtAuthGuard, CheckUserHaveControllablePermission) @Post(':deviceUuid/control') async controlDevice( @Body() controlDeviceDto: ControlDeviceDto, diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts index 2fd9dfc..ea27429 100644 --- a/src/floor/controllers/floor.controller.ts +++ b/src/floor/controllers/floor.controller.ts @@ -17,7 +17,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 { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; @ApiTags('Floor Module') @@ -44,7 +44,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get(':floorUuid') async getFloorByUuid(@Param('floorUuid') floorUuid: string) { try { @@ -59,7 +59,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('child/:floorUuid') async getFloorChildByUuid( @Param('floorUuid') floorUuid: string, @@ -79,7 +79,7 @@ export class FloorController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('parent/:floorUuid') async getFloorParentByUuid(@Param('floorUuid') floorUuid: string) { try { @@ -109,7 +109,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('user/:userUuid') async getFloorsByUserId(@Param('userUuid') userUuid: string) { try { @@ -123,7 +123,7 @@ export class FloorController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Put('rename/:floorUuid') async renameFloorByUuid( @Param('floorUuid') floorUuid: string, diff --git a/src/group/controllers/group.controller.ts b/src/group/controllers/group.controller.ts index c00ff92..e936e06 100644 --- a/src/group/controllers/group.controller.ts +++ b/src/group/controllers/group.controller.ts @@ -16,7 +16,7 @@ import { AddGroupDto } from '../dtos/add.group.dto'; import { ControlGroupDto } from '../dtos/control.group.dto'; import { RenameGroupDto } from '../dtos/rename.group.dto copy'; import { CheckProductUuidForAllDevicesGuard } from 'src/guards/device.product.guard'; -import { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @ApiTags('Group Module') @Controller({ @@ -27,7 +27,7 @@ export class GroupController { constructor(private readonly groupService: GroupService) {} @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('space/:spaceUuid') async getGroupsBySpaceUuid(@Param('spaceUuid') spaceUuid: string) { try { @@ -40,7 +40,7 @@ export class GroupController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get(':groupUuid') async getGroupsByGroupId(@Param('groupUuid') groupUuid: string) { try { @@ -53,7 +53,7 @@ export class GroupController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard, CheckProductUuidForAllDevicesGuard) + @UseGuards(JwtAuthGuard, CheckProductUuidForAllDevicesGuard) @Post() async addGroup(@Body() addGroupDto: AddGroupDto) { try { @@ -67,7 +67,7 @@ export class GroupController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Post('control') async controlGroup(@Body() controlGroupDto: ControlGroupDto) { try { @@ -81,7 +81,7 @@ export class GroupController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Put('rename/:groupUuid') async renameGroupByUuid( @Param('groupUuid') groupUuid: string, @@ -101,7 +101,7 @@ export class GroupController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Delete(':groupUuid') async deleteGroup(@Param('groupUuid') groupUuid: string) { try { diff --git a/src/guards/user.role.guard.ts b/src/guards/user.role.guard.ts deleted file mode 100644 index 59abad8..0000000 --- a/src/guards/user.role.guard.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { RoleType } from '@app/common/constants/role.type.enum'; -import { BadRequestException, UnauthorizedException } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; - -export class UserRoleGuard extends AuthGuard('jwt') { - handleRequest(err, user) { - if (err || !user) { - throw err || new UnauthorizedException(); - } else { - const isUserOrAdmin = user.roles.some( - (role) => - role.type === RoleType.SUPER_ADMIN || - role.type === RoleType.ADMIN || - role.type === RoleType.USER, - ); - if (!isUserOrAdmin) { - throw new BadRequestException( - 'Only admin or user role can access this route', - ); - } - } - return user; - } -} diff --git a/src/role/controllers/role.controller.ts b/src/role/controllers/role.controller.ts index 2a6c917..cb2038d 100644 --- a/src/role/controllers/role.controller.ts +++ b/src/role/controllers/role.controller.ts @@ -4,13 +4,12 @@ import { Get, HttpException, HttpStatus, - Param, - Put, + Post, UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { RoleService } from '../services/role.service'; -import { UserRoleEditDto } from '../dtos'; +import { AddUserRoleDto } from '../dtos'; import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard'; @ApiTags('Role Module') @@ -37,16 +36,13 @@ export class RoleController { } @ApiBearerAuth() @UseGuards(SuperAdminRoleGuard) - @Put('edit/user/:userUuid') - async editUserRoleType( - @Param('userUuid') userUuid: string, - @Body() userRoleEditDto: UserRoleEditDto, - ) { + @Post() + async addUserRoleType(@Body() addUserRoleDto: AddUserRoleDto) { try { - await this.roleService.editUserRoleType(userUuid, userRoleEditDto); + await this.roleService.addUserRoleType(addUserRoleDto); return { statusCode: HttpStatus.OK, - message: 'User Role Updated Successfully', + message: 'User Role Added Successfully', }; } catch (error) { throw new HttpException( diff --git a/src/role/dtos/index.ts b/src/role/dtos/index.ts index 8fc5216..a9b9771 100644 --- a/src/role/dtos/index.ts +++ b/src/role/dtos/index.ts @@ -1 +1 @@ -export * from './role.edit.dto'; +export * from './role.add.dto'; diff --git a/src/role/dtos/role.add.dto.ts b/src/role/dtos/role.add.dto.ts new file mode 100644 index 0000000..8f17920 --- /dev/null +++ b/src/role/dtos/role.add.dto.ts @@ -0,0 +1,24 @@ +import { RoleType } from '@app/common/constants/role.type.enum'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsIn, IsNotEmpty, IsString } from 'class-validator'; + +export class AddUserRoleDto { + @ApiProperty({ + description: 'userUuid', + required: true, + }) + @IsString() + @IsNotEmpty() + public userUuid: string; + + @ApiProperty({ + description: 'Role type (ADMIN)', + enum: [RoleType.ADMIN], + required: true, + }) + @IsEnum(RoleType) + @IsIn([RoleType.ADMIN], { + message: 'roleType must be one of the following values: ADMIN', + }) + roleType: RoleType; +} diff --git a/src/role/dtos/role.edit.dto.ts b/src/role/dtos/role.edit.dto.ts deleted file mode 100644 index 9e9b394..0000000 --- a/src/role/dtos/role.edit.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { RoleType } from '@app/common/constants/role.type.enum'; -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsIn } from 'class-validator'; - -export class UserRoleEditDto { - @ApiProperty({ - description: 'Role type (USER or ADMIN)', - enum: [RoleType.USER, RoleType.ADMIN], - required: true, - }) - @IsEnum(RoleType) - @IsIn([RoleType.USER, RoleType.ADMIN], { - message: 'roleType must be one of the following values: USER, ADMIN', - }) - roleType: RoleType; -} diff --git a/src/role/services/role.service.ts b/src/role/services/role.service.ts index b8db739..ece3780 100644 --- a/src/role/services/role.service.ts +++ b/src/role/services/role.service.ts @@ -1,7 +1,8 @@ import { RoleTypeRepository } from './../../../libs/common/src/modules/role-type/repositories/role.type.repository'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { UserRoleEditDto } from '../dtos/role.edit.dto'; +import { AddUserRoleDto } from '../dtos/role.add.dto'; import { UserRoleRepository } from '@app/common/modules/user-role/repositories'; +import { QueryFailedError } from 'typeorm'; @Injectable() export class RoleService { @@ -10,18 +11,27 @@ export class RoleService { private readonly userRoleRepository: UserRoleRepository, ) {} - async editUserRoleType(userUuid: string, userRoleEditDto: UserRoleEditDto) { + async addUserRoleType(addUserRoleDto: AddUserRoleDto) { try { - const roleType = await this.fetchRoleByType(userRoleEditDto.roleType); - return await this.userRoleRepository.update( - { user: { uuid: userUuid } }, - { - roleType: { - uuid: roleType.uuid, - }, - }, - ); + const roleType = await this.fetchRoleByType(addUserRoleDto.roleType); + + if (roleType.uuid) { + return await this.userRoleRepository.save({ + user: { uuid: addUserRoleDto.userUuid }, + roleType: { uuid: roleType.uuid }, + }); + } } catch (error) { + if ( + error instanceof QueryFailedError && + error.driverError.code === '23505' + ) { + // Postgres unique constraint violation error code + throw new HttpException( + 'This role already exists for this user', + HttpStatus.CONFLICT, + ); + } throw new HttpException( error.message || 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR, diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts index d39b584..984e8a5 100644 --- a/src/room/controllers/room.controller.ts +++ b/src/room/controllers/room.controller.ts @@ -16,7 +16,7 @@ import { UpdateRoomNameDto } from '../dtos/update.room.dto'; import { CheckUnitTypeGuard } from 'src/guards/unit.type.guard'; import { CheckUserRoomGuard } from 'src/guards/user.room.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @ApiTags('Room Module') @Controller({ @@ -42,7 +42,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get(':roomUuid') async getRoomByUuid(@Param('roomUuid') roomUuid: string) { try { @@ -57,7 +57,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('parent/:roomUuid') async getRoomParentByUuid(@Param('roomUuid') roomUuid: string) { try { @@ -85,7 +85,7 @@ export class RoomController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('user/:userUuid') async getRoomsByUserId(@Param('userUuid') userUuid: string) { try { @@ -99,7 +99,7 @@ export class RoomController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @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 c42793c..885b018 100644 --- a/src/unit/controllers/unit.controller.ts +++ b/src/unit/controllers/unit.controller.ts @@ -18,7 +18,7 @@ import { UpdateUnitNameDto } from '../dtos/update.unit.dto'; import { CheckFloorTypeGuard } from 'src/guards/floor.type.guard'; import { CheckUserUnitGuard } from 'src/guards/user.unit.guard'; import { AdminRoleGuard } from 'src/guards/admin.role.guard'; -import { UserRoleGuard } from 'src/guards/user.role.guard'; +import { JwtAuthGuard } from '@app/common/guards/jwt.auth.guard'; @ApiTags('Unit Module') @Controller({ @@ -44,7 +44,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get(':unitUuid') async getUnitByUuid(@Param('unitUuid') unitUuid: string) { try { @@ -59,7 +59,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('child/:unitUuid') async getUnitChildByUuid( @Param('unitUuid') unitUuid: string, @@ -76,7 +76,7 @@ export class UnitController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('parent/:unitUuid') async getUnitParentByUuid(@Param('unitUuid') unitUuid: string) { try { @@ -104,7 +104,7 @@ export class UnitController { } } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Get('user/:userUuid') async getUnitsByUserId(@Param('userUuid') userUuid: string) { try { @@ -118,7 +118,7 @@ export class UnitController { } @ApiBearerAuth() - @UseGuards(UserRoleGuard) + @UseGuards(JwtAuthGuard) @Put('rename/:unitUuid') async renameUnitByUuid( @Param('unitUuid') unitUuid: string, From cab86dbd69e7923264e84dc0d8a6a0cc6e4044f2 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sat, 18 May 2024 23:10:29 +0300 Subject: [PATCH 31/33] Remove unused import of RoleType enum in UserAuthService --- src/auth/services/user-auth.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/auth/services/user-auth.service.ts b/src/auth/services/user-auth.service.ts index e74967d..a514c1c 100644 --- a/src/auth/services/user-auth.service.ts +++ b/src/auth/services/user-auth.service.ts @@ -18,7 +18,6 @@ import { EmailService } from '../../../libs/common/src/util/email.service'; import { OtpType } from '../../../libs/common/src/constants/otp-type.enum'; import { UserEntity } from '../../../libs/common/src/modules/user/entities/user.entity'; import * as argon2 from 'argon2'; -import { RoleType } from '@app/common/constants/role.type.enum'; @Injectable() export class UserAuthService { From 835fde8304858c840c63822f70c96553496cfd1b Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 19 May 2024 14:25:19 +0300 Subject: [PATCH 32/33] feat: Add success response with data in controllers --- src/building/controllers/building.controller.ts | 13 +++++++++++-- src/community/controllers/community.controller.ts | 13 +++++++++++-- src/device/controllers/device.controller.ts | 10 +++++++++- src/floor/controllers/floor.controller.ts | 13 +++++++++++-- src/room/controllers/room.controller.ts | 13 +++++++++++-- src/unit/controllers/unit.controller.ts | 13 +++++++++++-- 6 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/building/controllers/building.controller.ts b/src/building/controllers/building.controller.ts index 9843c8e..62c846f 100644 --- a/src/building/controllers/building.controller.ts +++ b/src/building/controllers/building.controller.ts @@ -34,7 +34,12 @@ export class BuildingController { async addBuilding(@Body() addBuildingDto: AddBuildingDto) { try { const building = await this.buildingService.addBuilding(addBuildingDto); - return { message: 'Building added successfully', uuid: building.uuid }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'Building added successfully', + data: building, + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', @@ -100,7 +105,11 @@ export class BuildingController { async addUserBuilding(@Body() addUserBuildingDto: AddUserBuildingDto) { try { await this.buildingService.addUserBuilding(addUserBuildingDto); - return { message: 'user building added successfully' }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'user building added successfully', + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', diff --git a/src/community/controllers/community.controller.ts b/src/community/controllers/community.controller.ts index 4a80015..14cf37e 100644 --- a/src/community/controllers/community.controller.ts +++ b/src/community/controllers/community.controller.ts @@ -37,7 +37,12 @@ export class CommunityController { try { const community = await this.communityService.addCommunity(addCommunityDto); - return { message: 'Community added successfully', uuid: community.uuid }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'Community added successfully', + data: community, + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', @@ -102,7 +107,11 @@ export class CommunityController { async addUserCommunity(@Body() addUserCommunityDto: AddUserCommunityDto) { try { await this.communityService.addUserCommunity(addUserCommunityDto); - return { message: 'user community added successfully' }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'user community added successfully', + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', diff --git a/src/device/controllers/device.controller.ts b/src/device/controllers/device.controller.ts index d8149a7..72321ef 100644 --- a/src/device/controllers/device.controller.ts +++ b/src/device/controllers/device.controller.ts @@ -61,7 +61,15 @@ export class DeviceController { @Post('room') async addDeviceInRoom(@Body() addDeviceInRoomDto: AddDeviceInRoomDto) { try { - return await this.deviceService.addDeviceInRoom(addDeviceInRoomDto); + const device = + await this.deviceService.addDeviceInRoom(addDeviceInRoomDto); + + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'device added in room successfully', + data: device, + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', diff --git a/src/floor/controllers/floor.controller.ts b/src/floor/controllers/floor.controller.ts index 304d4a2..1216018 100644 --- a/src/floor/controllers/floor.controller.ts +++ b/src/floor/controllers/floor.controller.ts @@ -34,7 +34,12 @@ export class FloorController { async addFloor(@Body() addFloorDto: AddFloorDto) { try { const floor = await this.floorService.addFloor(addFloorDto); - return { message: 'Floor added successfully', uuid: floor.uuid }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'Floor added successfully', + data: floor, + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', @@ -99,7 +104,11 @@ export class FloorController { async addUserFloor(@Body() addUserFloorDto: AddUserFloorDto) { try { await this.floorService.addUserFloor(addUserFloorDto); - return { message: 'user floor added successfully' }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'user floor added successfully', + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', diff --git a/src/room/controllers/room.controller.ts b/src/room/controllers/room.controller.ts index f59300b..50d80e5 100644 --- a/src/room/controllers/room.controller.ts +++ b/src/room/controllers/room.controller.ts @@ -32,7 +32,12 @@ export class RoomController { async addRoom(@Body() addRoomDto: AddRoomDto) { try { const room = await this.roomService.addRoom(addRoomDto); - return { message: 'Room added successfully', uuid: room.uuid }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'Room added successfully', + data: room, + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', @@ -76,7 +81,11 @@ export class RoomController { async addUserRoom(@Body() addUserRoomDto: AddUserRoomDto) { try { await this.roomService.addUserRoom(addUserRoomDto); - return { message: 'user room added successfully' }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'user room added successfully', + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', diff --git a/src/unit/controllers/unit.controller.ts b/src/unit/controllers/unit.controller.ts index 89ca053..fa48784 100644 --- a/src/unit/controllers/unit.controller.ts +++ b/src/unit/controllers/unit.controller.ts @@ -34,7 +34,12 @@ export class UnitController { async addUnit(@Body() addUnitDto: AddUnitDto) { try { const unit = await this.unitService.addUnit(addUnitDto); - return { message: 'Unit added successfully', uuid: unit.uuid }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'Unit added successfully', + data: unit, + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', @@ -95,7 +100,11 @@ export class UnitController { async addUserUnit(@Body() addUserUnitDto: AddUserUnitDto) { try { await this.unitService.addUserUnit(addUserUnitDto); - return { message: 'user unit added successfully' }; + return { + statusCode: HttpStatus.CREATED, + success: true, + message: 'user unit added successfully', + }; } catch (error) { throw new HttpException( error.message || 'Internal server error', From 4ddc379bf7b3a9cd92d6418ca5b5a4ab8fc212c5 Mon Sep 17 00:00:00 2001 From: faris Aljohari <83524184+farisaljohari@users.noreply.github.com> Date: Sun, 19 May 2024 14:25:29 +0300 Subject: [PATCH 33/33] Add support for multiple permission types in DeviceService --- src/device/services/device.service.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/device/services/device.service.ts b/src/device/services/device.service.ts index 29bddca..5f59d59 100644 --- a/src/device/services/device.service.ts +++ b/src/device/services/device.service.ts @@ -27,6 +27,7 @@ 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'; +import { In } from 'typeorm'; @Injectable() export class DeviceService { @@ -57,7 +58,7 @@ export class DeviceService { permission: { userUuid, permissionType: { - type: PermissionType.READ || PermissionType.CONTROLLABLE, + type: In([PermissionType.READ, PermissionType.CONTROLLABLE]), }, }, }, @@ -81,6 +82,7 @@ export class DeviceService { } as GetDeviceDetailsInterface; }), ); + return devicesData; } catch (error) { // Handle the error here @@ -147,12 +149,11 @@ export class DeviceService { throw new Error('Product UUID is missing for the device.'); } - await this.deviceRepository.save({ + return await this.deviceRepository.save({ deviceTuyaUuid: addDeviceInRoomDto.deviceTuyaUuid, spaceDevice: { uuid: addDeviceInRoomDto.roomUuid }, productDevice: { uuid: device.productUuid }, }); - return { message: 'device added in room successfully' }; } catch (error) { if (error.code === '23505') { throw new HttpException(