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, })