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.
98 lines
3.5 KiB
98 lines
3.5 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
|
|
import bcrypt
|
|
|
|
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"])
|
|
|
|
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 bcrypt.checkpw(req.password.encode('utf-8'), user.password_hash.encode('utf-8')):
|
|
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 bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|