Backend
This commit is contained in:
@@ -21,7 +21,7 @@ from app.core.access import (
|
||||
require_permission,
|
||||
)
|
||||
from app.core.config import settings
|
||||
from app.core.security import issue_token
|
||||
from app.core.security import hash_password, issue_token, verify_password
|
||||
from app.db.session import get_db
|
||||
from app.models.access import Permission, Role, User
|
||||
|
||||
@@ -129,6 +129,61 @@ def read_my_permissions(user: User = Depends(get_current_user)):
|
||||
return sorted(get_user_permissions(user))
|
||||
|
||||
|
||||
class UpdateMeRequest(BaseModel):
|
||||
name: str | None = None
|
||||
email: str | None = None
|
||||
current_password: str | None = None
|
||||
new_password: str | None = None
|
||||
|
||||
|
||||
@router.patch("/me", response_model=UserSession)
|
||||
def update_me(
|
||||
payload: UpdateMeRequest,
|
||||
db: Session = Depends(get_db),
|
||||
user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Allow an internal user to update their own name, email, or password."""
|
||||
if payload.new_password:
|
||||
# Require current password verification before allowing a password change.
|
||||
# Users who have never set a personal password must supply the shared
|
||||
# admin password as the current credential.
|
||||
current_ok = (
|
||||
verify_password(payload.current_password or "", user.password_hash)
|
||||
if user.password_hash
|
||||
else (payload.current_password or "") == settings.admin_password
|
||||
)
|
||||
if not current_ok:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Current password is incorrect",
|
||||
)
|
||||
if len(payload.new_password) < 8:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="New password must be at least 8 characters",
|
||||
)
|
||||
user.password_hash = hash_password(payload.new_password)
|
||||
|
||||
if payload.name is not None:
|
||||
name = payload.name.strip()
|
||||
if not name:
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Name cannot be empty")
|
||||
user.name = name
|
||||
|
||||
if payload.email is not None:
|
||||
email = payload.email.strip().lower()
|
||||
if not email or "@" not in email:
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Invalid email address")
|
||||
existing = db.scalar(select(User).where(User.email == email, User.id != user.id))
|
||||
if existing:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Email is already in use")
|
||||
user.email = email
|
||||
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return _serialize_session(user, include_token=True)
|
||||
|
||||
|
||||
# Permission-enforced administrative endpoints. Route bodies should not check
|
||||
# role names — every gate is the require_permission(...) dependency.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user