from fastapi import Depends, HTTPException, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from models.database import get_db from models.models import User from .jwt_utils import decode_access_token from .permissions import ( ACCOUNTS_COOKIE, ACCOUNTS_CREATE, ACCOUNTS_DELETE, ACCOUNTS_START, ACCOUNTS_STOP, ACCOUNTS_UPDATE, ACCOUNTS_WRITE, ACCOUNTS_WRITE_GRANULAR, LINK_CARDS_WRITE, MESSAGES_WRITE, ORDERS_CREATE, ROLES_MANAGE, RULES_WRITE, SETTINGS_DATABASE, SYSTEM_LOGS_CLEAR, USERS_MANAGE, WRITE_PERMISSIONS, ) from .roles import can_write, has_permission, is_admin bearer_scheme = HTTPBearer(auto_error=False) async def get_current_user( credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme), db: AsyncSession = Depends(get_db), ) -> User: if not credentials or not credentials.credentials: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="未登录或令牌缺失") payload = decode_access_token(credentials.credentials) if not payload or not payload.get("sub"): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="登录已过期,请重新登录") try: user_id = int(payload["sub"]) except (TypeError, ValueError): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效令牌") result = await db.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() if not user or not user.is_active: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在或已禁用") return user def require_permission(permission: str): """FastAPI dependency factory that checks a single permission code.""" async def _checker(user: User = Depends(get_current_user)) -> User: if has_permission(user.role, permission): return user raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=f"缺少权限:{permission}", ) return _checker async def require_admin(user: User = Depends(get_current_user)) -> User: if not is_admin(user.role): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="需要管理员权限") return user async def require_write(user: User = Depends(get_current_user)) -> User: """Any write-capable permission (accounts/messages/rules) or legacy can_write.""" if is_admin(user.role) or can_write(user.role): return user if any(has_permission(user.role, code) for code in WRITE_PERMISSIONS): return user raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="当前角色只读,无法执行此操作", ) def _require_any(*codes: str, detail: str): async def _checker(user: User = Depends(get_current_user)) -> User: if any(has_permission(user.role, code) for code in codes): return user raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=detail) return _checker # Backward-compatible: any account write capability. require_accounts_write = _require_any( ACCOUNTS_WRITE, *ACCOUNTS_WRITE_GRANULAR, detail="缺少账号写权限", ) require_accounts_create = require_permission(ACCOUNTS_CREATE) require_accounts_update = require_permission(ACCOUNTS_UPDATE) require_accounts_delete = require_permission(ACCOUNTS_DELETE) require_accounts_start = require_permission(ACCOUNTS_START) require_accounts_stop = require_permission(ACCOUNTS_STOP) require_accounts_cookie = require_permission(ACCOUNTS_COOKIE) require_messages_write = require_permission(MESSAGES_WRITE) require_rules_write = require_permission(RULES_WRITE) require_link_cards_write = require_permission(LINK_CARDS_WRITE) require_system_logs_clear = require_permission(SYSTEM_LOGS_CLEAR) require_settings_database = require_permission(SETTINGS_DATABASE) require_orders_create = require_permission(ORDERS_CREATE) async def require_user_manager(user: User = Depends(get_current_user)) -> User: if has_permission(user.role, USERS_MANAGE): return user raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="需要用户管理权限") async def require_role_manager(user: User = Depends(get_current_user)) -> User: if has_permission(user.role, ROLES_MANAGE): return user raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="需要角色管理权限")