Files
ctms/backend/app/core/deps.py
T

118 lines
3.9 KiB
Python

from typing import Annotated, AsyncGenerator, Callable, Iterable
import uuid
from fastapi import Depends, HTTPException, status
from pydantic import ValidationError
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.exceptions import AppException
from app.core.security import decode_token, oauth2_scheme
from app.crud import user as user_crud
from app.crud import member as member_crud
from app.db.session import SessionLocal
from app.schemas.user import TokenPayload
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
async with SessionLocal() as session:
yield session
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[AsyncSession, Depends(get_db_session)],
):
payload = decode_token(token)
try:
token_data = TokenPayload(**payload)
except ValidationError as exc:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无法验证登录凭据",
headers={"WWW-Authenticate": "Bearer"},
) from exc
user = await user_crud.get_by_id(db, uuid.UUID(str(token_data.sub)))
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="账号不存在",
headers={"WWW-Authenticate": "Bearer"},
)
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="账号已停用",
)
return user
def require_roles(roles: Iterable[str]) -> Callable:
roles_set = set(roles)
async def dependency(current_user=Depends(get_current_user)):
current_role = current_user.role.value if hasattr(current_user.role, "value") else str(current_user.role)
if current_role not in roles_set:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="权限不足",
)
return current_user
return dependency
async def get_study_member(
study_id: uuid.UUID,
current_user=Depends(get_current_user),
db: AsyncSession = Depends(get_db_session),
):
role_value = current_user.role.value if hasattr(current_user.role, "value") else current_user.role
if role_value == "ADMIN":
return None
return await member_crud.get_member(db, study_id, current_user.id)
def require_study_member():
async def dependency(
study_id: uuid.UUID,
current_user=Depends(get_current_user),
db: AsyncSession = Depends(get_db_session),
):
role_value = current_user.role.value if hasattr(current_user.role, "value") else current_user.role
if role_value == "ADMIN":
return current_user
membership = await member_crud.get_member(db, study_id, current_user.id)
if not membership or not membership.is_active:
raise AppException(
code="FORBIDDEN",
message="不是该项目成员",
status_code=status.HTTP_403_FORBIDDEN,
)
return current_user
return dependency
def require_study_roles(roles: Iterable[str]):
roles_set = set(roles)
async def dependency(
study_id: uuid.UUID,
current_user=Depends(get_current_user),
db: AsyncSession = Depends(get_db_session),
):
role_value = current_user.role.value if hasattr(current_user.role, "value") else current_user.role
if role_value == "ADMIN":
return current_user
membership = await member_crud.get_member(db, study_id, current_user.id)
if not membership or not membership.is_active or membership.role_in_study not in roles_set:
raise AppException(
code="FORBIDDEN",
message="项目权限不足",
status_code=status.HTTP_403_FORBIDDEN,
)
return current_user
return dependency