fix(upload): honor no-login mode after account setup (#6528)

* fix(upload): honor no-login mode after account setup

* ci: refresh checks against the current dev base

* test(upload): add regression tests for unconfigured auth and no-login POST

* fix(upload): fail closed on missing auth_manager and preserve cross-user isolation

Enforce strict cross-user isolation so that authenticated users cannot access
files owned by other accounts when app.state.auth_manager is absent or None.
Fail closed with HTTP 503 for non-loopback requests when AUTH_ENABLED=true and
authentication infrastructure is unexpectedly unavailable, while preserving
intended single-user mode (AUTH_ENABLED=false) and intentional first-run setup
mode (auth_manager.is_configured=False).

* fix(upload): eliminate fail-open paths for unconfigured manager and loopback transport

- Eliminate early return on unconfigured auth_manager (Finding A): fail closed with HTTP 401 Setup required
- Eliminate loopback transport bypass when auth_manager is missing (Finding B): fail closed with HTTP 503
- Enforce strict ownership isolation: non-owners and legacy ownerless uploads require verified administrator access
- Add regression suites in tests/test_upload_no_login.py covering Finding A, Finding B, bearer tokens, and ownerless files
- Explicitly set AUTH_ENABLED=false for unauthenticated tests in tests/test_upload_routes_owner_scope.py

* fix(upload): prevent delegated API tokens from inheriting admin privileges and enforce upload scopes

- Prevent delegated bearer API credentials from inheriting interactive administrator privileges over other users' files or legacy ownerless files.
- Enforce granular upload scopes for delegated credentials: chat and documents:write for upload creation and vision OCR editing; chat, documents:read, and documents:write for downloads, thumbnails, and vision OCR reads.
- Reject requests with missing token owners or reserved sentinel identities (api, demo, system, internal-tool) with 403 Forbidden.
- Add comprehensive regression tests in tests/test_upload_no_login.py verifying delegated token scope isolation, sentinel rejection, owner isolation, and untouched OCR state on rejected edits.

---------

Co-authored-by: PR #6029 Reviewer <reviewer@odysseus-review.local>
This commit is contained in:
Robert Vind-Gardoș
2026-10-15 11:08:58 +01:00
committed by GitHub
co-authored by PR #6029 Reviewer
parent b2e717e28f
commit c74f475679
3 changed files with 966 additions and 25 deletions
+2
View File
@@ -318,6 +318,7 @@ def test_put_vision_text_allows_same_owner_to_write_cache(tmp_path, monkeypatch)
def test_download_file_survives_corrupted_uploads_json(tmp_path, monkeypatch):
# A truncated/corrupt uploads.json must not 500 the download endpoint —
# metadata simply becomes unavailable and the file is still served.
monkeypatch.setenv("AUTH_ENABLED", "false")
handler, alice_id, _bob_id, upload_dir = _make_upload_store(tmp_path, monkeypatch)
download_file = _upload_endpoints(handler, monkeypatch)["download_file"]
(upload_dir / "uploads.json").write_text('{"alice:h1": {', encoding="utf-8")
@@ -332,6 +333,7 @@ def test_download_file_survives_corrupted_uploads_json(tmp_path, monkeypatch):
def test_put_vision_text_returns_400_on_malformed_json(tmp_path, monkeypatch):
# A non-JSON request body must yield 400, not an unhandled JSONDecodeError -> 500.
monkeypatch.setenv("AUTH_ENABLED", "false")
handler, alice_id, _bob_id, _upload_dir = _make_upload_store(tmp_path, monkeypatch)
put_vision_text = _upload_endpoints(handler, monkeypatch)["put_vision_text"]