Files
qiji/backend/app/services/auth.py
T

107 lines
3.7 KiB
Python

from uuid import uuid4
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import httpx
from app.config import settings
from app.models.user import User
from app.utils.security import create_access_token
async def get_or_create_user_from_casdoor(
db: AsyncSession, casdoor_id: str, name: str,
role: str = "manager", department: str = ""
) -> User:
"""Find existing user by casdoor_id, or create a new one."""
result = await db.execute(select(User).where(User.casdoor_id == casdoor_id))
user = result.scalar_one_or_none()
if user is None:
user = User(
casdoor_id=casdoor_id,
name=name,
role=role,
department=department,
)
db.add(user)
await db.commit()
await db.refresh(user)
else:
# Update name/department if changed
if user.name != name or user.department != department:
user.name = name
user.department = department
await db.commit()
await db.refresh(user)
return user
async def exchange_casdoor_code(code: str) -> dict | None:
"""Exchange Casdoor OIDC authorization code for user info."""
import logging
logger = logging.getLogger("uvicorn")
token_url = f"{settings.CASDOOR_ENDPOINT}/api/login/oauth/access_token"
async with httpx.AsyncClient() as client:
try:
resp = await client.post(token_url, data={
"grant_type": "authorization_code",
"client_id": settings.CASDOOR_CLIENT_ID,
"client_secret": settings.CASDOOR_CLIENT_SECRET,
"code": code,
}, timeout=10)
if resp.status_code != 200:
logger.error("[Casdoor] token exchange failed: status=%s", resp.status_code)
return None
token_data = resp.json()
access_token = token_data.get("access_token", "")
if not access_token:
logger.error("[Casdoor] token exchange response did not contain an access token")
return None
except Exception as e:
logger.error(f"[Casdoor] token exchange exception: {e}")
return None
userinfo_url = f"{settings.CASDOOR_ENDPOINT}/api/userinfo"
async with httpx.AsyncClient() as client:
try:
resp = await client.get(userinfo_url, headers={
"Authorization": f"Bearer {access_token}"
}, timeout=10)
if resp.status_code != 200:
logger.error("[Casdoor] userinfo failed: status=%s", resp.status_code)
return None
return resp.json()
except Exception as e:
logger.error(f"[Casdoor] userinfo exception: {e}")
return None
return None
async def bind_wecom_user(db: AsyncSession, casdoor_id: str, wecom_userid: str) -> User | None:
"""Bind a WeChat Work userid to a Casdoor user."""
result = await db.execute(select(User).where(User.casdoor_id == casdoor_id))
user = result.scalar_one_or_none()
if user is None:
return None
user.wecom_userid = wecom_userid
await db.commit()
await db.refresh(user)
return user
async def get_user_by_wecom_id(db: AsyncSession, wecom_userid: str) -> User | None:
"""Find user by wecom_userid."""
result = await db.execute(select(User).where(User.wecom_userid == wecom_userid))
return result.scalar_one_or_none()
def build_token_for_user(user: User) -> str:
"""Build a JWT token for the given user."""
return create_access_token(data={
"sub": str(user.id),
"user_id": str(user.id),
"casdoor_id": user.casdoor_id,
"name": user.name,
"role": user.role,
"department": user.department,
})