Files
g0v0-server/app/router/private/password.py
2025-10-07 13:07:14 +00:00

54 lines
1.8 KiB
Python

from typing import Annotated
from app.auth import (
authenticate_user,
get_password_hash,
validate_password,
)
from app.database.auth import OAuthToken
from app.database.verification import LoginSession, TrustedDevice
from app.dependencies.database import Database
from app.dependencies.user import ClientUser
from app.log import log
from .router import router
from fastapi import Depends, Form, HTTPException
from fastapi_limiter.depends import RateLimiter
from sqlmodel import col, delete
logger = log("Auth")
@router.post(
"/password/change",
name="更改密码",
tags=["验证", "g0v0 API"],
status_code=204,
dependencies=[Depends(RateLimiter(times=3, minutes=5))],
)
async def change_password(
current_user: ClientUser,
session: Database,
current_password: Annotated[str, Form(..., description="当前密码")],
new_password: Annotated[str, Form(..., description="新密码")],
):
"""更改用户密码
同时删除所有的已登录会话和信任设备
速率限制: 5 分钟内最多 3 次
"""
if not await authenticate_user(session, current_user.username, current_password):
raise HTTPException(status_code=403, detail="Password incorrect")
if errors := validate_password(new_password):
raise HTTPException(status_code=400, detail="; ".join(errors))
async with session.begin():
current_user.pw_bcrypt = get_password_hash(new_password)
await session.execute(delete(TrustedDevice).where(col(TrustedDevice.user_id) == current_user.id))
await session.execute(delete(LoginSession).where(col(LoginSession.user_id) == current_user.id))
await session.execute(delete(OAuthToken).where(col(OAuthToken.user_id) == current_user.id))
logger.info(f"User {current_user.id} changed password and sessions revoked")