feat: embed permission claims in JWT and add captcha verification
- Add GroupClaims model for JWT permission snapshots - Add JWTPayload model for typed JWT decoding - Refactor auth middleware: jwt_required (no DB) -> admin_required (no DB) -> auth_required (DB) - Add UserBanStore for instant ban enforcement via Redis + memory fallback - Fix status check bug: StrEnum is always truthy, use explicit != ACTIVE - Shorten access_token expiry from 3h to 1h - Add CaptchaScene enum and verify_captcha_if_needed service - Add require_captcha dependency injection factory - Add CLA document and new default settings - Update all tests for new JWT API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from .user import (
|
||||
BatchDeleteRequest,
|
||||
JWTPayload,
|
||||
LoginRequest,
|
||||
RefreshTokenRequest,
|
||||
RegisterRequest,
|
||||
@@ -37,7 +38,7 @@ from .node import (
|
||||
NodeType,
|
||||
)
|
||||
from .group import (
|
||||
Group, GroupBase, GroupOptions, GroupOptionsBase, GroupAllOptionsBase, GroupResponse,
|
||||
Group, GroupBase, GroupClaims, GroupOptions, GroupOptionsBase, GroupAllOptionsBase, GroupResponse,
|
||||
# 管理员DTO
|
||||
GroupCreateRequest, GroupUpdateRequest, GroupDetailResponse, GroupListResponse,
|
||||
)
|
||||
|
||||
@@ -188,6 +188,28 @@ class GroupListResponse(SQLModelBase):
|
||||
"""总数"""
|
||||
|
||||
|
||||
class GroupClaims(GroupCoreBase, GroupAllOptionsBase):
|
||||
"""
|
||||
JWT 中的用户组权限快照。
|
||||
|
||||
复用 GroupCoreBase(id, name, max_storage, share_enabled, web_dav_enabled, admin, speed_limit)
|
||||
和 GroupAllOptionsBase(share_download, share_free, ... 共 11 个功能开关)。
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_group(cls, group: "Group") -> "GroupClaims":
|
||||
"""
|
||||
从 Group ORM 对象(需预加载 options 关系)构建权限快照。
|
||||
|
||||
:param group: 已加载 options 的 Group 对象
|
||||
"""
|
||||
opts = group.options
|
||||
return cls(
|
||||
**GroupCoreBase.model_validate(group, from_attributes=True).model_dump(),
|
||||
**(GroupAllOptionsBase.model_validate(opts, from_attributes=True).model_dump() if opts else {}),
|
||||
)
|
||||
|
||||
|
||||
class GroupResponse(GroupBase, GroupOptionsBase):
|
||||
"""用户组响应 DTO"""
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ default_settings: list[Setting] = [
|
||||
Setting(name="siteKeywords", value="网盘,网盘", type=SettingsType.BASIC),
|
||||
Setting(name="siteDes", value="DiskNext", type=SettingsType.BASIC),
|
||||
Setting(name="siteTitle", value="云星启智", type=SettingsType.BASIC),
|
||||
Setting(name="site_notice", value="", type=SettingsType.BASIC),
|
||||
Setting(name="footer_code", value="", type=SettingsType.BASIC),
|
||||
Setting(name="tos_url", value="", type=SettingsType.BASIC),
|
||||
Setting(name="privacy_url", value="", type=SettingsType.BASIC),
|
||||
Setting(name="fromName", value="DiskNext", type=SettingsType.MAIL),
|
||||
Setting(name="mail_keepalive", value="30", type=SettingsType.MAIL),
|
||||
Setting(name="fromAdress", value="no-reply@yxqi.cn", type=SettingsType.MAIL),
|
||||
|
||||
@@ -99,7 +99,7 @@ class LoginRequest(SQLModelBase):
|
||||
captcha: str | None = None
|
||||
"""验证码"""
|
||||
|
||||
two_fa_code: int | None = Field(min_length=6, max_length=6)
|
||||
two_fa_code: int | None = Field(default=None, min_length=6, max_length=6)
|
||||
"""两步验证代码"""
|
||||
|
||||
|
||||
@@ -151,6 +151,22 @@ class WebAuthnInfo(SQLModelBase):
|
||||
transports: list[str]
|
||||
"""支持的传输方式"""
|
||||
|
||||
class JWTPayload(SQLModelBase):
|
||||
"""JWT 访问令牌解析后的 claims"""
|
||||
|
||||
sub: UUID
|
||||
"""用户 ID"""
|
||||
|
||||
jti: UUID
|
||||
"""令牌唯一标识符"""
|
||||
|
||||
status: UserStatus
|
||||
"""用户状态"""
|
||||
|
||||
group: "GroupClaims"
|
||||
"""用户组权限快照"""
|
||||
|
||||
|
||||
class AccessTokenBase(BaseModel):
|
||||
"""访问令牌响应 DTO"""
|
||||
|
||||
@@ -370,10 +386,11 @@ class UserAdminDetailResponse(UserPublic):
|
||||
|
||||
|
||||
# 前向引用导入
|
||||
from .group import GroupResponse # noqa: E402
|
||||
from .group import GroupClaims, GroupResponse # noqa: E402
|
||||
from .user_authn import AuthnResponse # noqa: E402
|
||||
|
||||
# 更新前向引用
|
||||
JWTPayload.model_rebuild()
|
||||
UserResponse.model_rebuild()
|
||||
UserSettingResponse.model_rebuild()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user