白名单管理页可给单个账号设明文登录口令(users.login_password,可空):
设置后该账号用户名直登(/login/username)须带匹配 ?password=,且优先于全局共享口令门;
未设口令的账号行为不变。前端登录 URL 已支持透传 ?password=,无需改登录页。
- 后端:UserRow/User 新增 login_password + 仓储映射;/login/username 账号级口令闸门
(_lookup_user_by_username 仅查不建);新管理员接口 PUT /users/{id}/login-password;
UserListItem 暴露明文(仅 admin 列表);migration 20260625_04 + 幂等补列 SQL。
- 前端:白名单页新增「登录口令」列(设置/修改/清除 + 复制登录链接);
core/auth api+hooks 增 setUserLoginPassword/useSetUserLoginPassword(含 Y-Log)。
- 文档:根 + 后端 CLAUDE.md 同步。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
44 lines
2.0 KiB
Python
44 lines
2.0 KiB
Python
"""User Pydantic models for authentication."""
|
|
|
|
from datetime import UTC, datetime
|
|
from typing import Literal
|
|
from uuid import UUID, uuid4
|
|
|
|
from pydantic import BaseModel, ConfigDict, EmailStr, Field
|
|
|
|
|
|
def _utc_now() -> datetime:
|
|
"""Return current UTC time (timezone-aware)."""
|
|
return datetime.now(UTC)
|
|
|
|
|
|
class User(BaseModel):
|
|
"""Internal user representation."""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: UUID = Field(default_factory=uuid4, description="Primary key")
|
|
email: EmailStr = Field(..., description="Unique email address")
|
|
password_hash: str | None = Field(None, description="bcrypt hash, nullable for OAuth users")
|
|
system_role: Literal["admin", "user"] = Field(default="user")
|
|
created_at: datetime = Field(default_factory=_utc_now)
|
|
|
|
# OAuth linkage (optional)
|
|
oauth_provider: str | None = Field(None, description="e.g. 'github', 'google'")
|
|
oauth_id: str | None = Field(None, description="User ID from OAuth provider")
|
|
|
|
# Auth lifecycle
|
|
needs_setup: bool = Field(default=False, description="True for auto-created admin until setup completes")
|
|
token_version: int = Field(default=0, description="Incremented on password change to invalidate old JWTs")
|
|
approved: bool = Field(default=True, description="登录白名单放行标记(黑名单模式);仅当系统设置 login_whitelist.enabled 打开时拦截被取消放行的用户。新账号默认放行(True),管理员可「取消放行」(False)封禁个别用户。")
|
|
login_password: str | None = Field(default=None, description="管理员在白名单管理页给该账号设置的「登录口令」(明文,仅 admin 可见)。非空 → 该账号用户名直登必须带匹配的 ?password=;为空 → 走原有共享口令门/免口令逻辑。")
|
|
|
|
|
|
class UserResponse(BaseModel):
|
|
"""Response model for user info endpoint."""
|
|
|
|
id: str
|
|
email: str
|
|
system_role: Literal["admin", "user"]
|
|
needs_setup: bool = False
|