Merge pull request #32 from SyncrowIOT/SP-196-be-handle-super-admin-migration

Sp 196 be handle super admin migration
This commit is contained in:
faris Aljohari
2024-05-18 20:40:33 +03:00
committed by GitHub
14 changed files with 158 additions and 20 deletions

View File

@ -1,2 +1,3 @@
import emailConfig from './email.config'; import emailConfig from './email.config';
export default [emailConfig]; import superAdminConfig from './super.admin.config';
export default [emailConfig, superAdminConfig];

View File

@ -0,0 +1,9 @@
import { registerAs } from '@nestjs/config';
export default registerAs(
'super-admin',
(): Record<string, any> => ({
SUPER_ADMIN_EMAIL: process.env.SUPER_ADMIN_EMAIL,
SUPER_ADMIN_PASSWORD: process.env.SUPER_ADMIN_PASSWORD,
}),
);

View File

@ -1,4 +1,5 @@
export enum RoleType { export enum RoleType {
USER = 'USER', SUPER_ADMIN = 'SUPER_ADMIN',
ADMIN = 'ADMIN', ADMIN = 'ADMIN',
USER = 'USER',
} }

View File

@ -1,11 +1,30 @@
import { Global, Module } from '@nestjs/common'; import { Global, Module } from '@nestjs/common';
import { HelperHashService } from './services'; 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() @Global()
@Module({ @Module({
providers: [HelperHashService], providers: [
exports: [HelperHashService], HelperHashService,
SuperAdminService,
UserRepository,
UserRoleRepository,
RoleTypeRepository,
],
exports: [HelperHashService, SuperAdminService],
controllers: [], controllers: [],
imports: [], imports: [
ConfigModule.forRoot(),
UserRepositoryModule,
UserRoleRepositoryModule,
RoleTypeRepositoryModule,
],
}) })
export class HelperModule {} export class HelperModule {}

View File

@ -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<void> {
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<void> {
const salt = this.helperHashService.randomSalt(10); // Hash the password using bcrypt
const hashedPassword = await this.helperHashService.bcrypt(
this.configService.get<string>('super-admin.SUPER_ADMIN_PASSWORD'),
salt,
);
try {
const user = await this.userRepository.save({
email: this.configService.get<string>('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;
}
}
}

View File

@ -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 { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { RoleTypeDto } from '../dtos/role.type.dto'; import { RoleTypeDto } from '../dtos/role.type.dto';
import { RoleType } from '@app/common/constants/role.type.enum'; import { RoleType } from '@app/common/constants/role.type.enum';
import { UserRoleEntity } from '../../user-role/entities'; import { UserRoleEntity } from '../../user-role/entities';
@Entity({ name: 'role-type' }) @Entity({ name: 'role-type' })
@Unique(['type'])
export class RoleTypeEntity extends AbstractEntity<RoleTypeDto> { export class RoleTypeEntity extends AbstractEntity<RoleTypeDto> {
@Column({ @Column({
nullable: false, nullable: false,

View File

@ -1,10 +1,11 @@
import { Entity, ManyToOne } from 'typeorm'; import { Entity, ManyToOne, Unique } from 'typeorm';
import { AbstractEntity } from '../../abstract/entities/abstract.entity'; import { AbstractEntity } from '../../abstract/entities/abstract.entity';
import { UserRoleDto } from '../dtos'; import { UserRoleDto } from '../dtos';
import { UserEntity } from '../../user/entities'; import { UserEntity } from '../../user/entities';
import { RoleTypeEntity } from '../../role-type/entities'; import { RoleTypeEntity } from '../../role-type/entities';
@Entity({ name: 'user-role' }) @Entity({ name: 'user-role' })
@Unique(['user', 'roleType'])
export class UserRoleEntity extends AbstractEntity<UserRoleDto> { export class UserRoleEntity extends AbstractEntity<UserRoleDto> {
@ManyToOne(() => UserEntity, (user) => user.role, { @ManyToOne(() => UserEntity, (user) => user.role, {
nullable: false, nullable: false,

View File

@ -16,7 +16,7 @@ import { ResponseMessage } from '../../../libs/common/src/response/response.deco
import { UserLoginDto } from '../dtos/user-login.dto'; import { UserLoginDto } from '../dtos/user-login.dto';
import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos'; import { ForgetPasswordDto, UserOtpDto, VerifyOtpDto } from '../dtos';
import { RefreshTokenGuard } from '@app/common/guards/jwt-refresh.auth.guard'; 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({ @Controller({
version: '1', version: '1',
@ -52,7 +52,7 @@ export class UserAuthController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(AdminRoleGuard) @UseGuards(SuperAdminRoleGuard)
@Delete('user/delete/:id') @Delete('user/delete/:id')
async userDelete(@Param('id') id: string) { async userDelete(@Param('id') id: string) {
await this.userAuthService.deleteUser(id); await this.userAuthService.deleteUser(id);
@ -98,7 +98,7 @@ export class UserAuthController {
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(AdminRoleGuard) @UseGuards(SuperAdminRoleGuard)
@Get('user/list') @Get('user/list')
async userList() { async userList() {
const userList = await this.userAuthService.userList(); const userList = await this.userAuthService.userList();

View File

@ -4,10 +4,13 @@ import { AuthGuard } from '@nestjs/passport';
export class AdminRoleGuard extends AuthGuard('jwt') { export class AdminRoleGuard extends AuthGuard('jwt') {
handleRequest(err, user) { handleRequest(err, user) {
const isAdmin = user.roles.some((role) => role.type === RoleType.ADMIN);
if (err || !user) { if (err || !user) {
throw err || new UnauthorizedException(); throw err || new UnauthorizedException();
} else { } else {
const isAdmin = user.roles.some(
(role) =>
role.type === RoleType.SUPER_ADMIN || role.type === RoleType.ADMIN,
);
if (!isAdmin) { if (!isAdmin) {
throw new BadRequestException('Only admin role can access this route'); throw new BadRequestException('Only admin role can access this route');
} }

View File

@ -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;
}
}

View File

@ -4,12 +4,15 @@ import { AuthGuard } from '@nestjs/passport';
export class UserRoleGuard extends AuthGuard('jwt') { export class UserRoleGuard extends AuthGuard('jwt') {
handleRequest(err, user) { handleRequest(err, user) {
const isUserOrAdmin = user.roles.some(
(role) => role.type === RoleType.ADMIN || role.type === RoleType.USER,
);
if (err || !user) { if (err || !user) {
throw err || new UnauthorizedException(); throw err || new UnauthorizedException();
} else { } else {
const isUserOrAdmin = user.roles.some(
(role) =>
role.type === RoleType.SUPER_ADMIN ||
role.type === RoleType.ADMIN ||
role.type === RoleType.USER,
);
if (!isUserOrAdmin) { if (!isUserOrAdmin) {
throw new BadRequestException( throw new BadRequestException(
'Only admin or user role can access this route', 'Only admin or user role can access this route',

View File

@ -4,6 +4,7 @@ import rateLimit from 'express-rate-limit';
import helmet from 'helmet'; import helmet from 'helmet';
import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils'; import { setupSwaggerAuthentication } from '../libs/common/src/util/user-auth.swagger.utils';
import { ValidationPipe } from '@nestjs/common'; import { ValidationPipe } from '@nestjs/common';
import { SuperAdminService } from '@app/common/helper/services/super.admin.sarvice';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AuthModule); 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); await app.listen(process.env.PORT || 4000);
} }

View File

@ -11,7 +11,7 @@ import {
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { RoleService } from '../services/role.service'; import { RoleService } from '../services/role.service';
import { UserRoleEditDto } from '../dtos'; import { UserRoleEditDto } from '../dtos';
import { AdminRoleGuard } from 'src/guards/admin.role.guard'; import { SuperAdminRoleGuard } from 'src/guards/super.admin.role.guard';
@ApiTags('Role Module') @ApiTags('Role Module')
@Controller({ @Controller({
@ -21,7 +21,7 @@ import { AdminRoleGuard } from 'src/guards/admin.role.guard';
export class RoleController { export class RoleController {
constructor(private readonly roleService: RoleService) {} constructor(private readonly roleService: RoleService) {}
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(AdminRoleGuard) @UseGuards(SuperAdminRoleGuard)
@Get('types') @Get('types')
async fetchRoleTypes() { async fetchRoleTypes() {
try { try {
@ -36,7 +36,7 @@ export class RoleController {
} }
} }
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(AdminRoleGuard) @UseGuards(SuperAdminRoleGuard)
@Put('edit/user/:userUuid') @Put('edit/user/:userUuid')
async editUserRoleType( async editUserRoleType(
@Param('userUuid') userUuid: string, @Param('userUuid') userUuid: string,

View File

@ -1,13 +1,16 @@
import { RoleType } from '@app/common/constants/role.type.enum'; import { RoleType } from '@app/common/constants/role.type.enum';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsEnum } from 'class-validator'; import { IsEnum, IsIn } from 'class-validator';
export class UserRoleEditDto { export class UserRoleEditDto {
@ApiProperty({ @ApiProperty({
description: 'role type', description: 'Role type (USER or ADMIN)',
enum: RoleType, enum: [RoleType.USER, RoleType.ADMIN],
required: true, required: true,
}) })
@IsEnum(RoleType) @IsEnum(RoleType)
@IsIn([RoleType.USER, RoleType.ADMIN], {
message: 'roleType must be one of the following values: USER, ADMIN',
})
roleType: RoleType; roleType: RoleType;
} }