feat(auth): 新增手机号与 DCAuth 设备认证登录
- 登录链路改为用户名 + 密码 + 短信验证码 + DCAuth v2 设备签名 - 新增 login_sms_codes / user_devices / device_challenges 三张表,迁移 0063/0064 - 设备绑定只比对 deviceId + publicKey,每账号一个 active 设备,管理员可重置 - 保留未绑定手机号账号的密码登录兜底,绑定后强制安全登录 - 登录页与用户管理页支持验证码、DCAuth 轮询、手机号绑定与设备重置
This commit is contained in:
@@ -9,35 +9,44 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..deps import require_permission
|
||||
from ..models import Account, AuditLog, HuyaAccount, User
|
||||
from ..permissions import PERMISSIONS, get_user_permissions
|
||||
from ..models import Account, AuditLog, HuyaAccount, User, UserDevice
|
||||
from ..permissions import PERMISSIONS
|
||||
from ..schemas import UserCreate, UserInfo, UserRename, UserUpdate
|
||||
from ..security import hash_password
|
||||
from ..services.user_profile_service import to_user_info
|
||||
|
||||
router = APIRouter(prefix="/api/users", tags=["用户管理"])
|
||||
|
||||
|
||||
def _active_device(db: Session, user_id: int) -> UserDevice | None:
|
||||
return (
|
||||
db.query(UserDevice)
|
||||
.filter(UserDevice.user_id == user_id, UserDevice.status == "active")
|
||||
.order_by(UserDevice.id.desc())
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=list[UserInfo])
|
||||
def list_users(
|
||||
db: Session = Depends(get_db),
|
||||
_: User = Depends(require_permission("user:view")),
|
||||
):
|
||||
users = db.query(User).filter(User.deleted_at.is_(None)).order_by(User.id).all()
|
||||
result = []
|
||||
for u in users:
|
||||
result.append(
|
||||
UserInfo(
|
||||
id=u.id,
|
||||
username=u.username,
|
||||
role=u.role,
|
||||
is_active=u.is_active,
|
||||
remark=u.remark or "",
|
||||
created_at=u.created_at,
|
||||
permissions=get_user_permissions(u),
|
||||
custom_permissions=u.custom_permissions,
|
||||
devices: dict[int, UserDevice] = {}
|
||||
if users:
|
||||
device_rows = (
|
||||
db.query(UserDevice)
|
||||
.filter(
|
||||
UserDevice.user_id.in_([user.id for user in users]),
|
||||
UserDevice.status == "active",
|
||||
)
|
||||
.order_by(UserDevice.id.desc())
|
||||
.all()
|
||||
)
|
||||
return result
|
||||
for device in device_rows:
|
||||
devices.setdefault(device.user_id, device)
|
||||
return [to_user_info(user, devices.get(user.id)) for user in users]
|
||||
|
||||
|
||||
@router.post("", response_model=UserInfo)
|
||||
@@ -70,15 +79,7 @@ def create_user(
|
||||
)
|
||||
db.commit()
|
||||
|
||||
return UserInfo(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
role=user.role,
|
||||
is_active=user.is_active,
|
||||
remark=user.remark or "",
|
||||
permissions=get_user_permissions(user),
|
||||
custom_permissions=user.custom_permissions,
|
||||
)
|
||||
return to_user_info(user, _active_device(db, user.id))
|
||||
|
||||
|
||||
@router.put("/{user_id}", response_model=UserInfo)
|
||||
@@ -118,15 +119,7 @@ def update_user(
|
||||
)
|
||||
db.commit()
|
||||
|
||||
return UserInfo(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
role=user.role,
|
||||
is_active=user.is_active,
|
||||
remark=user.remark or "",
|
||||
permissions=get_user_permissions(user),
|
||||
custom_permissions=user.custom_permissions,
|
||||
)
|
||||
return to_user_info(user, _active_device(db, user.id))
|
||||
|
||||
|
||||
@router.patch("/{user_id}/username", response_model=UserInfo)
|
||||
@@ -145,15 +138,7 @@ def rename_user(
|
||||
if len(username) < 2:
|
||||
raise HTTPException(status_code=400, detail="用户名至少 2 个字符")
|
||||
if username == user.username:
|
||||
return UserInfo(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
role=user.role,
|
||||
is_active=user.is_active,
|
||||
remark=user.remark or "",
|
||||
permissions=get_user_permissions(user),
|
||||
custom_permissions=user.custom_permissions,
|
||||
)
|
||||
return to_user_info(user, _active_device(db, user.id))
|
||||
if db.query(User.id).filter(User.username == username).first():
|
||||
raise HTTPException(status_code=400, detail="用户名已存在")
|
||||
|
||||
@@ -173,15 +158,7 @@ def rename_user(
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=400, detail="用户名已存在") from exc
|
||||
db.refresh(user)
|
||||
return UserInfo(
|
||||
id=user.id,
|
||||
username=user.username,
|
||||
role=user.role,
|
||||
is_active=user.is_active,
|
||||
remark=user.remark or "",
|
||||
permissions=get_user_permissions(user),
|
||||
custom_permissions=user.custom_permissions,
|
||||
)
|
||||
return to_user_info(user, _active_device(db, user.id))
|
||||
|
||||
|
||||
@router.delete("/{user_id}")
|
||||
|
||||
Reference in New Issue
Block a user