# ============================================================================= # 企微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 滑动窗口) """ verified: bool = Field(..., description="验证是否通过") expires_in: int = Field(..., description="Redis 验证标记剩余秒数(秒)") # -------------------------------------------------------------------------- # 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="重置是否成功")