瀏覽代碼

feat(auth): 一些密码和权限问题

xiongzhu 1 年之前
父節點
當前提交
f373ff7db7

+ 2 - 0
src/sys-config/sys-config.admin.controller.ts

@@ -4,9 +4,11 @@ import { Public } from '../auth/public.decorator'
 import { SysConfigService } from './sys-config.service'
 import { PageRequest } from 'src/common/dto/page-request'
 import { SysConfig } from './entities/sys-config.entity'
+import { HasRoles } from 'src/auth/roles.decorator'
 
 @ApiTags('sys-config.admin')
 @Controller('/admin/sys-config')
+@HasRoles('admin')
 export class SysConfigAdminController {
     constructor(private readonly sysConfigService: SysConfigService) {}
 

+ 18 - 4
src/users/users.admin.controller.ts

@@ -9,14 +9,15 @@ import {
     Delete,
     BadRequestException,
     Req,
-    Post
+    Post,
+    ForbiddenException
 } from '@nestjs/common'
 import { UsersService } from './users.service'
 import { UserProfileDto } from './dto/user-profile.dto'
 import { UserUpdateDto } from './dto/user-update.dto'
 import { IUsers } from './interfaces/users.interface'
 import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'
-import { HasRoles } from '../auth/roles.decorator'
+import { HasAnyRoles, HasRoles } from '../auth/roles.decorator'
 import { Role } from '../model/role.enum'
 import { IPaginationOptions } from 'nestjs-typeorm-paginate'
 import { PageRequest } from 'src/common/dto/page-request'
@@ -30,8 +31,10 @@ import { OperationType } from '../operation-log/entities/operation-log.entity'
 @Controller('/admin/users')
 @ApiBearerAuth()
 export class UsersAdminController {
-    constructor(private readonly usersService: UsersService, private readonly operationLogService: OperationLogService) {
-    }
+    constructor(
+        private readonly usersService: UsersService,
+        private readonly operationLogService: OperationLogService
+    ) {}
 
     @Post()
     public async list(@Req() req, @Body() page: PageRequest<Users>) {
@@ -51,7 +54,18 @@ export class UsersAdminController {
     }
 
     @Put()
+    @HasAnyRoles('admin', 'api', 'superApi')
     public async create(@Req() req, @Body() user: UserCreateDto) {
+        if (user.roles) {
+            for (const role of user.roles) {
+                if (role === Role.Admin && !req.user.roles.includes((Role.Admin))) {
+                    throw new ForbiddenException('无权限')
+                }
+                if (role === Role.Api && !(req.user.roles.includes(Role.Admin) || req.user.roles.includes((Role.SuperApi)))) {
+                    throw new ForbiddenException('无权限')
+                }
+            }
+        }
         const users = await this.usersService.create(user)
         await this.operationLogService.create(req, users, 'Users', OperationType.INSERT, '新增用户')
         return users

+ 13 - 8
src/users/users.controller.ts

@@ -7,7 +7,9 @@ import {
     NotFoundException,
     BadRequestException,
     Req,
-    Post, InternalServerErrorException
+    Post,
+    InternalServerErrorException,
+    ForbiddenException
 } from '@nestjs/common'
 import { UsersService } from './users.service'
 import { UserProfileDto } from './dto/user-profile.dto'
@@ -17,13 +19,16 @@ import { Users } from './entities/users.entity'
 import { Public } from 'src/auth/public.decorator'
 import { OperationLogService } from '../operation-log/operation-log.service'
 import { OperationType } from '../operation-log/entities/operation-log.entity'
+import { Role } from 'src/model/role.enum'
 
 @ApiTags('users')
 @Controller('users')
 @ApiBearerAuth()
 export class UsersController {
-    constructor(private readonly usersService: UsersService, private readonly operationLogService: OperationLogService) {
-    }
+    constructor(
+        private readonly usersService: UsersService,
+        private readonly operationLogService: OperationLogService
+    ) {}
 
     @Get('/my')
     public async my(@Req() req) {
@@ -62,12 +67,12 @@ export class UsersController {
 
     @Post('/adminUpdatePassword')
     public async adminUpdatePassword(@Body() { userId, password }, @Req() req) {
-        console.log(req.user.roles)
-        if (req.user.roles.includes('user') || req.user.roles.includes('api')) {
-            throw new InternalServerErrorException('Permission denied!')
+        if (req.user.roles.includes(Role.Admin) || req.user.roles.includes(Role.SuperApi)) {
+            const users = await this.usersService.updatePassword(userId, password)
+            await this.operationLogService.create(req, users, 'Users', OperationType.UPDATE, '修改密码')
+        } else {
+            throw new ForbiddenException('无权限')
         }
-        const users = await this.usersService.updatePassword(userId, password)
-        await this.operationLogService.create(req, users, 'Users', OperationType.UPDATE, '修改密码')
     }
 
     @Get('/invites')

+ 28 - 18
src/users/users.service.ts

@@ -34,8 +34,7 @@ export class UsersService implements OnModuleInit {
         private readonly userRepository: Repository<Users>,
         private readonly hashingService: HashingService,
         private readonly smsService: SmsService
-    ) {
-    }
+    ) {}
 
     async onModuleInit() {
         if (!(await this.userRepository.findOneBy({ username: 'admin' }))) {
@@ -116,11 +115,11 @@ export class UsersService implements OnModuleInit {
     public async login(username: string, password: string, code: string): Promise<Users> {
         let user = await this.userRepository.findOneBy({ username })
         if (!user) {
-            throw new UnauthorizedException('Username and password doesn\'t match')
+            throw new UnauthorizedException("Username and password doesn't match")
         }
         const isMatch = await this.hashingService.compare(password, user.password)
         if (!isMatch) {
-            throw new UnauthorizedException('Username and password doesn\'t match')
+            throw new UnauthorizedException("Username and password doesn't match")
         }
         if (!user.roles.includes(Role.Admin)) {
             if (user.twoFactorCode === null || user.twoFactorCode === '') {
@@ -138,11 +137,11 @@ export class UsersService implements OnModuleInit {
     public async binding(username: string, password: string) {
         const users = await this.userRepository.findOneBy({ username })
         if (!users) {
-            throw new UnauthorizedException('Username and password doesn\'t match')
+            throw new UnauthorizedException("Username and password doesn't match")
         }
         const isMatch = await this.hashingService.compare(password, users.password)
         if (!isMatch) {
-            throw new UnauthorizedException('Username and password doesn\'t match')
+            throw new UnauthorizedException("Username and password doesn't match")
         }
         if (users.twoFactorCode) {
             return 'success'
@@ -158,11 +157,11 @@ export class UsersService implements OnModuleInit {
     public async handleConfirmBinding(username: string, password: string, bindingCode: string) {
         const users = await this.userRepository.findOneBy({ username })
         if (!users) {
-            throw new UnauthorizedException('Username and password doesn\'t match')
+            throw new UnauthorizedException("Username and password doesn't match")
         }
         const isMatch = await this.hashingService.compare(password, users.password)
         if (!isMatch) {
-            throw new UnauthorizedException('Username and password doesn\'t match')
+            throw new UnauthorizedException("Username and password doesn't match")
         }
         if (users.twoFactorCode === null || users.twoFactorCode === '') {
             throw new UnauthorizedException('请绑定谷歌验证器获取认证码.')
@@ -176,11 +175,19 @@ export class UsersService implements OnModuleInit {
         }
     }
 
+    isValidPassword(password: string) {
+        return /[a-z]/.test(password) && /[A-Z]/.test(password) && /[0-9]/.test(password) && password.length >= 8
+    }
+
     public async create(userDto: UserCreateDto): Promise<IUsers> {
-        try {
-            if (userDto.password) {
+        if (userDto.password) {
+            if (this.isValidPassword(userDto.password)) {
                 userDto.password = await this.hashingService.hash(userDto.password)
+            } else {
+                throw new BadRequestException('密码长度至少8位,且必须包含大小写字母和数字')
             }
+        }
+        try {
             return await this.userRepository.save(userDto)
         } catch (err) {
             throw new InternalServerErrorException(err.message)
@@ -210,13 +217,17 @@ export class UsersService implements OnModuleInit {
     }
 
     public async updatePassword(id: number, password: string): Promise<Users> {
-        try {
-            const user = await this.userRepository.findOneBy({ id })
-            user.password = await this.hashingService.hash(password)
-
-            return await this.userRepository.save(user)
-        } catch (err) {
-            throw new HttpException(err, HttpStatus.BAD_REQUEST)
+        if (this.isValidPassword(password)) {
+            try {
+                const user = await this.userRepository.findOneBy({ id })
+                user.password = await this.hashingService.hash(password)
+
+                return await this.userRepository.save(user)
+            } catch (err) {
+                throw new HttpException(err, HttpStatus.BAD_REQUEST)
+            }
+        } else {
+            throw new BadRequestException('密码长度至少8位,且必须包含大小写字母和数字')
         }
     }
 
@@ -341,5 +352,4 @@ export class UsersService implements OnModuleInit {
             hasInvite: !!user
         }
     }
-
 }