Security:
- DEBT-008: remove query-string token auth; header-only Bearer now enforced
- DEBT-013: add regex constraint ^[a-z0-9\-]{1,64}$ on decky_name path param
- DEBT-015: stop leaking raw exception detail to API clients; log server-side
- DEBT-016: validate search (max_length=512) and datetime params with regex
Reliability:
- DEBT-014: wrap SSE event_generator in try/except; yield error frame on failure
- DEBT-017: emit log.warning/error on DB init retry; silent failures now visible
Observability / Docs:
- DEBT-020: add 401/422 response declarations to all route decorators
Infrastructure:
- DEBT-018: add HEALTHCHECK to all 24 template Dockerfiles
- DEBT-019: add USER decnet + setcap cap_net_bind_service to all 24 Dockerfiles
- DEBT-024: bump Redis template version 7.0.12 → 7.2.7
Config:
- DEBT-012: validate DECNET_API_PORT and DECNET_WEB_PORT range (1-65535)
Code quality:
- DEBT-010: delete 22 duplicate decnet_logging.py copies; deployer injects canonical
- DEBT-022: closed as false positive (print only in module docstring)
- DEBT-009: closed as false positive (templates already use structured syslog_line)
Build:
- DEBT-025: generate requirements.lock via pip freeze
Testing:
- DEBT-005/006/007: comprehensive test suite added across tests/api/
- conftest: in-memory SQLite + StaticPool + monkeypatched session_factory
- fuzz mark added; default run excludes fuzz; -n logical parallelism
DEBT.md updated: 23/25 items closed; DEBT-011 (Alembic) and DEBT-023 (digest pinning) remain
28 lines
1.1 KiB
Python
28 lines
1.1 KiB
Python
from typing import Any, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
|
|
from decnet.web.auth import get_password_hash, verify_password
|
|
from decnet.web.dependencies import get_current_user, repo
|
|
from decnet.web.db.models import ChangePasswordRequest
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post(
|
|
"/auth/change-password",
|
|
tags=["Authentication"],
|
|
responses={401: {"description": "Invalid or expired token / wrong old password"}, 422: {"description": "Validation error"}},
|
|
)
|
|
async def change_password(request: ChangePasswordRequest, current_user: str = Depends(get_current_user)) -> dict[str, str]:
|
|
_user: Optional[dict[str, Any]] = await repo.get_user_by_uuid(current_user)
|
|
if not _user or not verify_password(request.old_password, _user["password_hash"]):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Incorrect old password",
|
|
)
|
|
|
|
_new_hash: str = get_password_hash(request.new_password)
|
|
await repo.update_user_password(current_user, _new_hash, must_change_password=False)
|
|
return {"message": "Password updated successfully"}
|