# -*- coding: utf-8 -*- """ 权限中间件 —— 基于 FastAPI 依赖注入的权限检查。 提供两个依赖工厂: - require_permission(*codes):检查用户 status=approved 且拥有指定权限 - require_approved():仅检查用户 status=approved(不检查具体权限) 用法: @router.get("/finance") async def get_finance( user: CurrentUser = Depends(require_permission("view_board_finance")) ): ... @router.get("/tasks") async def get_tasks( user: CurrentUser = Depends(require_approved()) ): ... """ from __future__ import annotations import logging from fastapi import Depends, HTTPException, status from app.auth.dependencies import CurrentUser, get_current_user from app.database import get_connection from app.services.role import get_user_permissions logger = logging.getLogger(__name__) def _get_user_status(user_id: int) -> str | None: """从数据库查询用户当前 status。返回 None 表示用户不存在。""" conn = get_connection() try: with conn.cursor() as cur: cur.execute( "SELECT status FROM auth.users WHERE id = %s", (user_id,), ) row = cur.fetchone() finally: conn.close() return row[0] if row else None def require_permission(*permission_codes: str): """ 权限依赖工厂:要求用户 status=approved 且拥有全部指定权限。 流程: 1. 通过 get_current_user 从 JWT 提取 user_id + site_id 2. 查询 auth.users.status —— 非 approved 则 403 3. 查询 user_site_roles + role_permissions 获取权限列表 4. 检查所需权限是否全部在列表中 —— 缺失则 403 5. 返回 CurrentUser 对象 """ async def _dependency( user: CurrentUser = Depends(get_current_user), ) -> CurrentUser: # 查询数据库中的实时 status db_status = _get_user_status(user.user_id) if db_status is None: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="用户不存在", ) if db_status != "approved": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="用户未通过审核,无法访问此资源", ) # 检查具体权限 if permission_codes: user_perms = await get_user_permissions(user.user_id, user.site_id) missing = set(permission_codes) - set(user_perms) if missing: logger.warning( "用户 %s 在 site_id=%s 下缺少权限: %s", user.user_id, user.site_id, missing, ) raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="权限不足", ) return user return _dependency def require_approved(): """ 审核状态依赖工厂:仅检查用户 status=approved,不检查具体权限。 用于通用的已认证端点,只需确认用户已通过审核即可访问。 """ async def _dependency( user: CurrentUser = Depends(get_current_user), ) -> CurrentUser: db_status = _get_user_status(user.user_id) if db_status is None: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="用户不存在", ) if db_status != "approved": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="用户未通过审核,无法访问此资源", ) return user return _dependency