400ce3ddcb
OTP首次绑定: - 新增统一 OTP 路由 /auth/otp-* (otp.py + router.py) - 坐席端 OTP 绑定面板 (OtpBindPanel.vue) - 管理端 OTP 管理列表 (MfaManage.vue) - agent_login 签发半认证 token 支持首次绑定流程 三端登录修复: - 坐席/管理端去掉'返回扫码登录'按钮 - 管理端改为二维码始终可见+轮询扫码状态 - 员工端 /itdesk/ 改为 alias 直接服务 H5 (不再301重定向) - docker-compose 添加 h5 volume 挂载 管理端权限修复: - 扫码登录改用 get_user_roles() 替代写死 roles=['agent'] - get_user_roles() 增加 agents.role 回退 - 新增 GET /admin/roles/user-roles 端点 - 角色管理页加载用户角色分配数据 文档更新: - OTP PRD + 系统设计文档 - 故障排查手册 v1.1 (新增6案例) - nginx 生产基准配置
134 lines
4.7 KiB
Python
134 lines
4.7 KiB
Python
# =============================================================================
|
|
# 企微IT智能服务台 — MFA 二次认证 Pydantic Schema
|
|
# =============================================================================
|
|
# 说明:定义 MFA TOTP 服务相关的请求/响应数据结构
|
|
# Phase 2.1 task #17: pyotp TOTP 服务 + User MFA 字段
|
|
# Schema 仅做字段校验,不涉及业务逻辑(业务逻辑在 mfa_service + mfa API)
|
|
# =============================================================================
|
|
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# MFA 状态查询响应
|
|
# --------------------------------------------------------------------------
|
|
class MFAStatusResponse(BaseModel):
|
|
"""GET /api/mfa/status 响应。
|
|
|
|
Attributes:
|
|
bound: 是否已绑定(已生成 secret 且首次验证通过)
|
|
enabled: 是否已启用(与 bound 等价,保留双字段便于前端路由守卫判断)
|
|
last_verified_at: 最近一次验证成功时间(可空)
|
|
"""
|
|
|
|
bound: bool = Field(..., description="是否已绑定 MFA")
|
|
enabled: bool = Field(..., description="是否已启用 MFA")
|
|
last_verified_at: Optional[datetime] = Field(
|
|
None, description="最近一次验证成功时间"
|
|
)
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# MFA 绑定启动响应
|
|
# --------------------------------------------------------------------------
|
|
class MFABindStartResponse(BaseModel):
|
|
"""POST /api/mfa/bind/start 响应。
|
|
|
|
Attributes:
|
|
secret: TOTP 共享密钥(base32),用户可手动输入到 Authenticator
|
|
otpauth_url: otpauth:// URI,可生成二维码
|
|
qr_code_base64: 二维码 PNG 的 base64(data URL 已剥离,前端自行拼接)
|
|
"""
|
|
|
|
secret: str = Field(..., description="TOTP 共享密钥(base32)")
|
|
otpauth_url: str = Field(..., description="otpauth:// 格式 URI")
|
|
qr_code_base64: str = Field(..., description="二维码 PNG base64(不含 data: 前缀)")
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# MFA 绑定确认请求
|
|
# --------------------------------------------------------------------------
|
|
class MFABindConfirmRequest(BaseModel):
|
|
"""POST /api/mfa/bind/confirm 请求体。
|
|
|
|
Attributes:
|
|
otp_code: 用户输入的 6 位 OTP 码
|
|
"""
|
|
|
|
otp_code: str = Field(..., min_length=6, max_length=6, description="6 位 OTP 动态码")
|
|
|
|
|
|
class MFABindConfirmResponse(BaseModel):
|
|
"""POST /api/mfa/bind/confirm 响应。
|
|
|
|
Attributes:
|
|
success: 绑定是否成功
|
|
"""
|
|
|
|
success: bool = Field(..., description="绑定是否成功")
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# MFA 验证请求/响应
|
|
# --------------------------------------------------------------------------
|
|
class MFAVerifyRequest(BaseModel):
|
|
"""POST /api/mfa/verify 请求体。
|
|
|
|
Attributes:
|
|
otp_code: 用户输入的 6 位 OTP 码
|
|
"""
|
|
|
|
otp_code: str = Field(..., min_length=6, max_length=6, description="6 位 OTP 动态码")
|
|
|
|
|
|
class MFAVerifyResponse(BaseModel):
|
|
"""POST /api/mfa/verify 响应。
|
|
|
|
Attributes:
|
|
verified: 验证是否通过
|
|
expires_in: 验证状态在 Redis 里的剩余秒数(1800s 滑动窗口)
|
|
token: 首次绑定时签发的登录 token(可选,仅首次绑定场景返回)
|
|
"""
|
|
|
|
verified: bool = Field(..., description="验证是否通过")
|
|
expires_in: int = Field(..., description="Redis 验证标记剩余秒数(秒)")
|
|
token: Optional[str] = Field(None, description="首次绑定成功后签发的登录 token")
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# MFA 关闭请求/响应
|
|
# --------------------------------------------------------------------------
|
|
class MFADisableRequest(BaseModel):
|
|
"""POST /api/mfa/disable 请求体。
|
|
|
|
Attributes:
|
|
otp_code: 用户输入的 6 位 OTP 码(防止误操作)
|
|
"""
|
|
|
|
otp_code: str = Field(..., min_length=6, max_length=6, description="6 位 OTP 动态码")
|
|
|
|
|
|
class MFADisableResponse(BaseModel):
|
|
"""POST /api/mfa/disable 响应。
|
|
|
|
Attributes:
|
|
success: 关闭是否成功
|
|
"""
|
|
|
|
success: bool = Field(..., description="关闭是否成功")
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 管理员重置 MFA 响应
|
|
# --------------------------------------------------------------------------
|
|
class MFAAdminResetResponse(BaseModel):
|
|
"""POST /api/admin/mfa/reset/{employee_id} 响应。
|
|
|
|
Attributes:
|
|
success: 重置是否成功
|
|
"""
|
|
|
|
success: bool = Field(..., description="重置是否成功") |