""" 鉴权依赖注入 JWT 验证、当前用户、角色检查。 """ from enum import Enum from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from ..database import get_db from ..dependencies.tenant import get_tenant_id from ..models import User, UserStatus # noqa: E402 from ..services.auth import AuthService, has_any_role oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login") async def get_current_user( token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db), ) -> User: """从 JWT 解析并返回当前认证用户""" credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) username = AuthService.verify_token(token) if username is None: raise credentials_exception result = await db.execute(select(User).where(User.username == username)) user = result.scalar_one_or_none() if user is None: raise credentials_exception return user async def get_current_active_user( current_user: User = Depends(get_current_user), ) -> User: """当前活跃用户(排除 AWAY / OFFLINE)""" if (not getattr(current_user, "is_active", True)) or current_user.status != UserStatus.ACTIVE: raise HTTPException(status_code=403, detail="User is not active") return current_user def require_roles(*roles: str): """依赖工厂:要求用户具备指定角色之一""" async def role_checker( current_user: User = Depends(get_current_user), ) -> User: user_role = ( current_user.role.value if isinstance(current_user.role, Enum) else current_user.role ) if not has_any_role(user_role, list(roles)): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=f"Access denied. Required roles: {', '.join(roles)}", ) return current_user return role_checker async def get_current_tenant_id( current_user: User = Depends(get_current_user), header_tenant_id: int = Depends(get_tenant_id), ) -> int: """兼容旧接口:优先取 JWT 中 tenant_id,没有则回落 Header。""" try: tenant_id = int(getattr(current_user, "tenant_id", 0) or 0) if tenant_id > 0: return tenant_id except (TypeError, ValueError): pass return int(header_tenant_id or 1)