fix permissions and teacher attendance workflows

This commit is contained in:
2026-07-10 20:40:52 +08:00
parent 247879f276
commit 8ed1682b90
95 changed files with 4745 additions and 1237 deletions

View File

@@ -2,18 +2,19 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
import { PERMISSION_KEY } from '../decorators/permission.decorator';
import { AUTHENTICATED_KEY } from '../decorators/authenticated.decorator';
/**
* 权限守卫 — 默认放行策略(⚠️ 安全关键)
* 权限守卫 — 默认拒绝策略(安全关键)
*
* 当 handler/controller 上不存在 @RequirePermission 时,守卫放行(仅需登录即可访问
* 这是有意的设计选择:所有敏感路由必须显式标注 @RequirePermission
* 当 handler/controller 上不存在 @RequirePermission、@Authenticated 且未标记 @Public 时,守卫拒绝访问。
* 所有路由必须显式声明公开、仅登录或所需权限
*
* ⚠️ 新增路由时务必添加 @RequirePermission,否则该路由对所有已认证用户开放!
* ⚠️ 新增路由时务必添加 @RequirePermission、@Authenticated 或 @Public。
* 建议配合 lint 规则确保无遗漏。
*/
@Injectable()
export class PermissionGuard implements CanActivate {
@Injectable()
export class PermissionGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
@@ -24,20 +25,28 @@ import { PERMISSION_KEY } from '../decorators/permission.decorator';
]);
if (isPublic) return true;
// 2. 获取所需权限getAllAndMerge 合并 handler+class 层的所有 metadata
const request = context.switchToHttp().getRequest();
const user = request.user;
// 2. @Authenticated() 只要求已登录,具体 JWT 有效性由 JwtAuthGuard 负责。
const authenticatedOnly = this.reflector.getAllAndOverride<boolean>(AUTHENTICATED_KEY, [
context.getHandler(),
context.getClass(),
]);
if (authenticatedOnly) return !!user;
// 3. 获取所需权限getAllAndMerge 合并 handler+class 层的所有 metadata
const requiredPermissions = this.reflector.getAllAndMerge<string[]>(PERMISSION_KEY, [
context.getHandler(),
context.getClass(),
]);
// 无装饰器 = 仅需登录即可,放行
if (!requiredPermissions || requiredPermissions.length === 0) return true;
// 无权限声明且非 @Public/@Authenticated默认拒绝避免新增接口意外裸奔
if (!requiredPermissions || requiredPermissions.length === 0) return false;
// 3. 从 JWT payload 获取用户权限
const request = context.switchToHttp().getRequest();
const user = request.user;
// 4. 从 JWT payload 获取用户权限
if (!user || !user.permissions || !Array.isArray(user.permissions)) return false;
// 4. OR 匹配:用户拥有 requiredPermissions 中任一权限即可通过
// 5. OR 匹配:用户拥有 requiredPermissions 中任一权限即可通过
return requiredPermissions.some((p) => user.permissions.includes(p));
}
}