feat: add PATCH /user/settings/password endpoint for changing password
Register the fixed /password route before the wildcard /{option} to
prevent FastAPI from matching it as a path parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ from sqlmodels import (
|
|||||||
BUILTIN_DEFAULT_COLORS, ThemePreset, UserThemeUpdateRequest,
|
BUILTIN_DEFAULT_COLORS, ThemePreset, UserThemeUpdateRequest,
|
||||||
SettingOption, UserSettingUpdateRequest,
|
SettingOption, UserSettingUpdateRequest,
|
||||||
AuthIdentity, AuthIdentityResponse, AuthProviderType, BindIdentityRequest,
|
AuthIdentity, AuthIdentityResponse, AuthProviderType, BindIdentityRequest,
|
||||||
|
ChangePasswordRequest,
|
||||||
AuthnDetailResponse, AuthnRenameRequest,
|
AuthnDetailResponse, AuthnRenameRequest,
|
||||||
)
|
)
|
||||||
from sqlmodels.color import ThemeColorsBase
|
from sqlmodels.color import ThemeColorsBase
|
||||||
@@ -226,6 +227,43 @@ async def router_user_settings_theme(
|
|||||||
await user.save(session)
|
await user.save(session)
|
||||||
|
|
||||||
|
|
||||||
|
@user_settings_router.patch(
|
||||||
|
path='/password',
|
||||||
|
summary='修改密码',
|
||||||
|
status_code=status.HTTP_204_NO_CONTENT,
|
||||||
|
)
|
||||||
|
async def router_user_settings_change_password(
|
||||||
|
session: SessionDep,
|
||||||
|
user: Annotated[sqlmodels.user.User, Depends(auth_required)],
|
||||||
|
request: ChangePasswordRequest,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
修改当前用户密码
|
||||||
|
|
||||||
|
请求体:
|
||||||
|
- old_password: 当前密码
|
||||||
|
- new_password: 新密码(至少 8 位)
|
||||||
|
|
||||||
|
错误处理:
|
||||||
|
- 400: 用户没有邮箱密码认证身份
|
||||||
|
- 403: 当前密码错误
|
||||||
|
"""
|
||||||
|
email_identity: AuthIdentity | None = await AuthIdentity.get(
|
||||||
|
session,
|
||||||
|
(AuthIdentity.user_id == user.id)
|
||||||
|
& (AuthIdentity.provider == AuthProviderType.EMAIL_PASSWORD),
|
||||||
|
)
|
||||||
|
if not email_identity or not email_identity.credential:
|
||||||
|
http_exceptions.raise_bad_request("未找到邮箱密码认证身份")
|
||||||
|
|
||||||
|
verify_result = Password.verify(email_identity.credential, request.old_password)
|
||||||
|
if verify_result == PasswordStatus.INVALID:
|
||||||
|
http_exceptions.raise_forbidden("当前密码错误")
|
||||||
|
|
||||||
|
email_identity.credential = Password.hash(request.new_password)
|
||||||
|
await email_identity.save(session)
|
||||||
|
|
||||||
|
|
||||||
@user_settings_router.patch(
|
@user_settings_router.patch(
|
||||||
path='/{option}',
|
path='/{option}',
|
||||||
summary='更新用户设定',
|
summary='更新用户设定',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from .auth_identity import (
|
|||||||
AuthIdentityResponse,
|
AuthIdentityResponse,
|
||||||
AuthProviderType,
|
AuthProviderType,
|
||||||
BindIdentityRequest,
|
BindIdentityRequest,
|
||||||
|
ChangePasswordRequest,
|
||||||
)
|
)
|
||||||
from .user import (
|
from .user import (
|
||||||
BatchDeleteRequest,
|
BatchDeleteRequest,
|
||||||
|
|||||||
@@ -81,6 +81,16 @@ class BindIdentityRequest(SQLModelBase):
|
|||||||
"""OAuth 回调地址"""
|
"""OAuth 回调地址"""
|
||||||
|
|
||||||
|
|
||||||
|
class ChangePasswordRequest(SQLModelBase):
|
||||||
|
"""修改密码请求 DTO"""
|
||||||
|
|
||||||
|
old_password: str = Field(min_length=1)
|
||||||
|
"""当前密码"""
|
||||||
|
|
||||||
|
new_password: str = Field(min_length=8, max_length=128)
|
||||||
|
"""新密码(至少 8 位)"""
|
||||||
|
|
||||||
|
|
||||||
# ==================== 数据库模型 ====================
|
# ==================== 数据库模型 ====================
|
||||||
|
|
||||||
class AuthIdentity(SQLModelBase, UUIDTableBaseMixin):
|
class AuthIdentity(SQLModelBase, UUIDTableBaseMixin):
|
||||||
|
|||||||
Reference in New Issue
Block a user