You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

100 lines
3.6 KiB

import uuid
from datetime import datetime, timedelta
import jwt
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from passlib.context import CryptContext
from database import get_db
from models import User, UserRole, Role, RolePermission, Permission
from schemas import LoginRequest, TokenResponse, UserOut, RoleOut
from config import settings
router = APIRouter(prefix="/api/auth", tags=["auth"])
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
async def get_permission_codes(db: AsyncSession, role_ids: list[uuid.UUID]) -> list[str]:
result = await db.execute(
select(Permission.code)
.join(RolePermission)
.where(RolePermission.role_id.in_(role_ids))
)
return list(set(result.scalars().all()))
async def get_user_roles(db: AsyncSession, user_id: uuid.UUID) -> list[RoleOut]:
result = await db.execute(
select(Role).join(UserRole).where(UserRole.user_id == user_id)
)
roles = result.scalars().all()
out = []
for role in roles:
rp_result = await db.execute(
select(Permission.code)
.join(RolePermission)
.where(RolePermission.role_id == role.id)
)
perms = list(rp_result.scalars().all())
out.append(RoleOut(
id=role.id,
name=role.name,
code=role.code,
description=role.description,
is_system=role.is_system,
data_scope=role.data_scope,
permissions=perms,
))
return out
@router.post("/login", response_model=TokenResponse)
async def login(req: LoginRequest, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.username == req.username))
user = result.scalar_one_or_none()
if not user or not pwd_context.verify(req.password, user.password_hash):
raise HTTPException(401, "用户名或密码错误")
if user.status != "active":
raise HTTPException(403, "账户已被禁用")
roles = await get_user_roles(db, user.id)
expire = datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
token = jwt.encode(
{"sub": str(user.id), "username": user.username, "exp": expire},
settings.JWT_SECRET,
algorithm=settings.JWT_ALGORITHM,
)
return TokenResponse(
access_token=token,
user=UserOut(
id=user.id, username=user.username, display_name=user.display_name,
email=user.email, phone=user.phone, wecom_user_id=user.wecom_user_id,
department_id=user.department_id, position=user.position,
manager_id=user.manager_id, status=user.status,
roles=roles, created_at=user.created_at,
),
)
@router.get("/me", response_model=UserOut)
async def get_me(request: Request, db: AsyncSession = Depends(get_db)):
user_ctx = request.state.user
result = await db.execute(select(User).where(User.id == user_ctx["id"]))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(404, "用户不存在")
roles = await get_user_roles(db, user.id)
return UserOut(
id=user.id, username=user.username, display_name=user.display_name,
email=user.email, phone=user.phone, wecom_user_id=user.wecom_user_id,
department_id=user.department_id, position=user.position,
manager_id=user.manager_id, status=user.status,
roles=roles, created_at=user.created_at,
)
def hash_password(password: str) -> str:
return pwd_context.hash(password)