mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-29 16:12:06 -04:00
fix(notes): fail closed when an unauthenticated request reaches owner-scoped routes (#4062)
* fix(notes): fail closed when an unauthenticated request reaches owner-scoped routes The notes CRUD routes resolved the acting user with bare get_current_user(). A request that reached them with no identity (auth-middleware regression, SSRF from a sibling service) came through as user=None — which every query treats as the single-user mode: list all accounts' notes, read/update/ delete/pin/archive any row, reorder globally. Resolve the owner through require_user() instead, which already encodes the right policy: 401 when auth is configured, while the documented anonymous modes (AUTH_ENABLED=false, LOCALHOST_BYPASS on loopback, unconfigured first-run) still resolve to the single-user path. fire-reminder in the same file already gated this way; the CRUD routes now match, and the inline require_user import there is folded into the module import. Extracted from #2940 (stabilization slice). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(notes): drive fail-closed test via ASGITransport, not sync TestClient The focused fail-closed test hung at `TestClient(app).get(...)` on some environments. Starlette's sync TestClient runs the app in a background event-loop thread (anyio blocking portal) and then dispatches each sync endpoint onto a second worker thread; that handshake deadlocks on certain anyio/httpx/platform combos. The identity injection also used BaseHTTPMiddleware (@app.middleware("http")), the other known TestClient deadlock source. Switch to the repo's existing httpx.ASGITransport + AsyncClient idiom so the whole request runs on the test's own event loop (no portal thread, no BaseHTTPMiddleware). Identity now comes from a pure-ASGI shim that writes the same request.state fields the real auth middleware sets, and a non-loopback client peer keeps require_user's loopback fall-throughs out of the picture. Same assertions and coverage; production code unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+12
-4
@@ -10,7 +10,7 @@ from fastapi import APIRouter, HTTPException, Request
|
||||
from pydantic import BaseModel
|
||||
|
||||
from core.database import SessionLocal, Note
|
||||
from src.auth_helpers import get_current_user
|
||||
from src.auth_helpers import require_user
|
||||
from src.constants import DATA_DIR
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
|
||||
@@ -570,7 +570,16 @@ def setup_note_routes(task_scheduler=None):
|
||||
router = APIRouter(prefix="/api/notes", tags=["notes"])
|
||||
|
||||
def _owner(request: Request) -> Optional[str]:
|
||||
return get_current_user(request)
|
||||
# require_user, not bare get_current_user: a request that reaches
|
||||
# these owner-scoped routes with NO identity (auth-middleware
|
||||
# regression, SSRF from a sibling service) must fail closed (401)
|
||||
# when auth is configured — not be treated as the single-user mode
|
||||
# and handed blanket access to every account's notes. The documented
|
||||
# anonymous modes (AUTH_ENABLED=false, LOCALHOST_BYPASS on loopback,
|
||||
# unconfigured first-run) still resolve to None, the single-user
|
||||
# path. fire_reminder below already gated this way; the CRUD routes
|
||||
# did not.
|
||||
return require_user(request) or None
|
||||
|
||||
def _is_admin_or_single_user(request: Request, user: str | None) -> bool:
|
||||
if user == "internal-tool":
|
||||
@@ -805,8 +814,7 @@ def setup_note_routes(task_scheduler=None):
|
||||
Returns {synthesis, email_sent}.
|
||||
"""
|
||||
# Gate against anonymous callers — LLM synthesis can burn tokens.
|
||||
from src.auth_helpers import require_user as _ru
|
||||
user = _ru(request)
|
||||
user = require_user(request)
|
||||
body = await request.json()
|
||||
note_id = str(body.get("note_id") or "").strip()
|
||||
if not note_id:
|
||||
|
||||
Reference in New Issue
Block a user