280 Commits

Author SHA1 Message Date
curo1305 b7df9719c2 feat(6.1-01): add second_auth_user fixture to conftest.py
- Add @pytest_asyncio.fixture second_auth_user with handle prefix 'user2_'
- Creates User + Quota row following the same pattern as auth_user
- Returns {user, token, headers} dict shape for use in sharing tests
2026-05-30 23:09:39 +02:00
curo1305 838698e715 docs(06): capture phase context — performance & production hardening
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 22:41:14 +02:00
curo1305 767c5234de docs(milestone): update v1.0 audit — Phase 1 Nyquist upgraded to compliant
Phase 1 moved from partial_phases to compliant_phases after
gsd-validate-phase 1 closed all 3 gaps. Audit status remains
gaps_found (3 blockers unchanged: SHARE-02/DOC-01, STORE-06/SEC-09,
ADMIN-06). Nyquist overall: partial (phases 3, 4 still draft; phase 2
missing).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 21:34:09 +02:00
curo1305 a2ece9ee7d docs(phase-1): mark VALIDATION.md Nyquist-compliant — all 6 rows green
Audit 2026-05-30: 3 gaps closed (STORE-07 concurrent test added,
test_confirm_endpoint unblocked, alembic tests moved to manual-only).
nyquist_compliant: true, status: compliant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:57:07 +02:00
curo1305 bf7d86184d fix(documents): normalize UUID to undashed hex in raw SQL quota UPDATE
str(uuid) returns dashed format (xxxx-xxxx-…) which mismatches SQLite's
CHAR(32) storage (undashed hex). Replace with .replace('-', '') so the
WHERE clause matches in both SQLite (tests) and PostgreSQL (production).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:57:02 +02:00
curo1305 bd765f69bf test(phase-1): add Nyquist validation tests — STORE-07 concurrent put, fix confirm UUID
- Add test_concurrent_put_objects to test_storage.py (STORE-07: verifies no
  per-instance lock blocks concurrent MinIO workers via asyncio.gather)
- Remove @pytest.mark.xfail from test_confirm_endpoint; test now passes on
  SQLite after uuid format fix in api/documents.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:56:58 +02:00
curo1305 33e5efe846 docs(v1.0): add milestone audit — 48/54 requirements satisfied, 3 blockers
Audit findings: share recipient doc-metadata 404 (SHARE-02/DOC-01),
cloud document delete corrupts MinIO quota (STORE-06/SEC-09), admin
CSV export returns 403 (ADMIN-06). 6 tech debt warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:44:16 +02:00
curo1305 710e535411 docs(phase-5): mark VALIDATION.md Nyquist-compliant — all 13 rows green
All 117 cloud tests pass; 13/13 validation map requirements COVERED.
Updated status, frontmatter, sign-off, and added audit trail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:24:12 +02:00
curo1305 cafdceef10 docs(phase-5): add security threat verification
56/56 threats verified CLOSED across all 12 plans. 14 accepted risks documented. Unregistered flag (GET /connections/{id}/config) reviewed and confirmed fully mitigated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:18:22 +02:00
curo1305 1a6fa08a34 docs(05): add code review and verification reports for phase 5
REVIEW.md: 3 critical findings fixed (HTTPException passthrough,
Redis pre-flight ordering, CLOUD_CREDS_KEY in celery-worker env)
VERIFICATION.md: 7/7 must-haves verified; 6 human-verification items
require live cloud credentials (Google Drive, OneDrive, Nextcloud/WebDAV)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:07:42 +02:00
curo1305 b1a136b5be fix(05-12): resolve 3 critical code review findings
CR-01: add `except HTTPException: raise` before broad except in
stream_document_content — prevents 503 (reconnect prompt) from being
swallowed and replaced with misleading 502

CR-02: move pre-flight credential checks BEFORE Redis setex in
oauth_initiate — no orphan state tokens written for unconfigured providers;
also adds onedrive_tenant_id to OneDrive pre-flight condition (WR-02)

CR-03: add CLOUD_CREDS_KEY to celery-worker environment in docker-compose.yml
— worker cannot decrypt cloud credentials without this key; every cloud
document task was silently failing at runtime

WR-03: assert Redis store empty after 400 pre-flight responses in both
new tests — confirms no token leak on misconfigured-provider requests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:04:09 +02:00
curo1305 12dd692f00 docs(05): mark phase 5 complete — 12/12 plans done, all UAT gaps resolved
Update STATE.md and ROADMAP.md to reflect plan 05-12 completion and Phase 5
as fully complete. All UAT gaps (OneDrive 500 → 400, cloud stream 500 → 502,
upload hint) resolved. 293 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 17:56:18 +02:00
curo1305 10175ee4b5 fix(05-12): close 3 UAT gaps — OAuth 400 preflight, 502 cloud fallback, upload hint
- oauth_initiate: pre-flight check returns 400 with env-var hint when
  GOOGLE_CLIENT_ID/SECRET or ONEDRIVE_CLIENT_ID/SECRET are not configured,
  preventing opaque MSAL/OAuth library 500 errors on misconfigured servers
- stream_document_content: broad except-clause catches non-CloudConnectionError
  exceptions and returns 502 with user-friendly message (was raw 500)
- docker-compose.yml: add volumes: - ./backend:/app to celery-worker so code
  changes are picked up by docker compose restart without a rebuild
- CloudStorageView: upload hint paragraph directs users to navigate into a
  cloud folder; no DropZone added (no folder context at overview level)
- 3 new backend tests pass; 2 existing tests patched with credential monkeypatch;
  full suite: 293 passed, 0 new failures, 1 pre-existing (test_extract_docx)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 17:55:08 +02:00
curo1305 f4f340545b chore: add .DS_Store, node_modules/, screenshots/ to .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:58:51 +02:00
curo1305 5250895587 feat(05): cloud folder browser views, routing, and sidebar nav
Add CloudStorageView (/cloud) and CloudFolderView (/cloud/:provider/:folderId).
Tree items filter to directories only (is_dir) to hide files in the nav tree.
CloudProviderTreeItem root click navigates to /cloud/{provider}/root instead
of /settings. AppSidebar Cloud Storage link upgraded to router-link with
active-class highlighting. Router registers both cloud routes with requiresAuth.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:58:08 +02:00
curo1305 54ef3357ba fix(05): cloud API path param, root sentinel, webdav creds in list, upload path
cloud.py: list_connections now decrypts and surfaces server_url +
connection_username for nextcloud/webdav providers; folder route uses
{folder_id:path} to handle slashes; translates "root" sentinel to "".
nextcloud_backend.py: skip parent directory entry in PROPFIND Depth:1 results.
webdav_backend.py: add cloud_folder + original_filename params to
upload_object so files land in the user's chosen folder with their real name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:58:01 +02:00
curo1305 67edc19a36 docs(05): add UAT, UI-SPEC, deferred items, debug notes; refine plans 09-11
Plan refinements: Vitest tests added to 09/10 must-haves, explicit
mock_flow two-tuple pattern in 10, test_admin_api.py fixture usage in 11.
New artifacts: UAT checklist, UI-SPEC, deferred-items, debug investigation
for cloud-doc-operations-fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:57:54 +02:00
curo1305 34f012b4e8 fix(05): resolve 5 critical code review findings
CR-01: add Field(min_length=1) to UserDeleteConfirm.admin_password
CR-02: add folder ownership check in PATCH /documents/{id} — prevents IDOR
        when folder_id belongs to another user
CR-03: add min_length=1, max_length=255, and path-separator validator to
        DocumentPatch.filename — prevents empty and path-traversal filenames
CR-04: fetchDocumentContent now throws on non-ok responses instead of
        silently returning the error Response
CR-05: object URL revoke in DocumentView uses pagehide + load events with
        120s fallback instead of unreliable 60s blind timer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:51:54 +02:00
curo1305 9935c06aab docs(05): add code review report — 5 critical, 6 warning, 3 info findings 2026-05-30 11:49:43 +02:00
curo1305 aafd552a1e fix(05-09): set storage_backend='minio' in test_celery_task_uses_user_provider
Cloud-aware routing added in 05-09 checks doc.storage_backend; MagicMock
attribute is truthy and != 'minio', so the test was entering the cloud branch
without any mock for get_storage_backend_for_document. Regression: test passed
before 05-09 when _run() had no cloud routing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 11:43:48 +02:00
curo1305 02ef11c432 chore: merge executor worktree (05-11 admin hard-delete) 2026-05-30 11:40:40 +02:00
curo1305 3180e759de docs(05-11): complete admin hard-delete with password confirmation plan
- UserDeleteConfirm Pydantic model + Argon2 password verification in delete_user
- adminDeleteUser(id, adminPassword) exported from client.js
- AdminUsersTab inline delete confirmation panel with password field
- Three new tests pass: 204/403/422 scenarios
- Full 21-test admin suite green; frontend build clean
2026-05-30 11:40:14 +02:00
curo1305 72687212a1 feat(05-11): add adminDeleteUser API function + inline delete confirmation panel
- Export adminDeleteUser(id, adminPassword) from client.js — sends JSON body to DELETE /api/admin/users/{id}
- AdminUsersTab: add confirmDelete, deletePassword, deleteError state refs
- AdminUsersTab: add startDelete, cancelDelete, confirmDoDelete functions (mutually exclusive with deactivate panel)
- AdminUsersTab: Delete button added to active and deactivated user rows
- AdminUsersTab: inline password confirmation panel with Argon2 verification via backend
2026-05-30 11:39:10 +02:00
curo1305 390a693ec6 feat(05-11): add UserDeleteConfirm model + admin password verification in delete_user
- Import verify_password from services.auth
- Add UserDeleteConfirm Pydantic model (admin_password field)
- delete_user handler now requires body; fails fast with 403 on wrong password
- All existing SEC-09 cloud/MinIO purge logic and audit log unchanged
- Three new tests pass: 204 on correct pw, 403 on wrong pw, 422 on no body
2026-05-30 11:37:59 +02:00
curo1305 8727592bff test(05-11): add failing tests for delete_user password verification
- test_delete_user_correct_password: 204 on correct admin password
- test_delete_user_wrong_password: 403 on wrong password, user survives
- test_delete_user_no_body: 422 when no body provided (Pydantic validation)
2026-05-30 11:37:12 +02:00
curo1305 bd3b637d30 chore: merge executor worktree (05-10 OAuth fix + cloud UI gaps) 2026-05-30 11:33:57 +02:00
curo1305 f5ea2103b3 docs(05-10): complete OAuth initiate fix + cloud UI gap closure plan 2026-05-30 11:31:42 +02:00
curo1305 87de148a59 feat(05-10): OAuth fetch + Nextcloud edit fix + Edit on ERROR + text overflow
- client.js: add initiateOAuth() and getConnectionConfig() helpers
- SettingsCloudTab: replace window.location.href with initiateOAuth() + fetch/JWT
- SettingsCloudTab: add Edit button to ACTIVE and ERROR blocks for non-OAuth providers
- SettingsCloudTab: wrap ConfirmBlock in w-full overflow-hidden div
- CloudCredentialModal: add existing prop, edit-mode pre-population via /config endpoint
- CloudCredentialModal: add showAdvanced + customEndpoint for Nextcloud custom paths
- ConfirmBlock: add break-words class to message paragraph
- cloud.py: add GET /api/cloud/connections/{id}/config endpoint (non-secret fields)
2026-05-30 11:30:13 +02:00
curo1305 e2e499b8b1 feat(05-10): oauth_initiate returns 200 JSON {url} instead of 302 redirect
- Remove response_class=RedirectResponse from @router.get decorator
- Replace both RedirectResponse(status_code=302) returns with JSONResponse({url})
- Frontend can now inject Bearer header before navigating to OAuth URL (T-05-10-01)
- Update test_connect_google_drive to expect 200 JSON (regression fix)
2026-05-30 11:24:33 +02:00
curo1305 9b6d3f91d4 test(05-10): add failing tests for OAuth initiate JSON URL return 2026-05-30 11:23:38 +02:00
curo1305 dc475aaaa2 chore: merge executor worktree (05-09 cloud doc access) 2026-05-30 11:20:41 +02:00
curo1305 7534f679f3 docs(05-09): complete cloud document access fixes plan — PATCH endpoint, cloud-aware re-analyze, authenticated preview 2026-05-30 11:19:33 +02:00
curo1305 4a42ccee5a feat(05-09): authenticated document preview via fetch + Blob URL
- Add fetchDocumentContent() to client.js: fetch with Bearer auth, 401 refresh
  retry pattern, returns raw Response (not parsed JSON) for blob() calls
- Replace iframe :src=proxyUrl (unauthenticated) in DocumentPreviewModal.vue
  with authenticated fetch → blob → URL.createObjectURL; loading/error states;
  URL.revokeObjectURL on unmount to prevent memory leaks
- Replace window.open(rawUrl) in DocumentView.vue openPdf() with
  fetchDocumentContent → blob → objectUrl → window.open; 60s auto-revoke
- Frontend build exits 0 with zero errors
- Closes T-05-09-04: no persistent unauthenticated content exposure
2026-05-30 11:18:01 +02:00
curo1305 6d094d17f0 feat(05-09): PATCH /documents/{id} endpoint + cloud-aware Celery re-analyze
- Add DocumentPatch Pydantic model with filename and folder_id optional fields
- Add PATCH /api/documents/{doc_id} endpoint: ownership guard, model_fields_set
  to distinguish absent vs null folder_id, returns updated metadata dict
- Update _run() in document_tasks.py to use get_storage_backend_for_document
  for non-MinIO backends instead of hardcoded MinIO path
- CloudConnectionError caught in cloud path: returns extract_failed status
- Update test to use pure unit mocks (no PostgreSQL) for _run() cloud routing
- All 3 plan tests pass; 23 test_cloud.py tests pass
2026-05-30 11:16:01 +02:00
curo1305 9bc056100c test(05-09): add failing tests for PATCH /documents/{id} and cloud-aware re-analyze
- test_patch_document_filename: expects 200 with updated filename (PATCH endpoint missing → 405)
- test_patch_document_wrong_owner: expects 404 for non-owner (PATCH endpoint missing → 405)
- test_reanalyze_cloud_document_routes_to_cloud_backend: expects cloud backend called for nextcloud docs
2026-05-30 11:13:31 +02:00
curo1305 f006c00d49 docs(05): create UAT gap closure plans 09-11
Three new plans address all 6 diagnosed gaps from 05-UAT.md:

- 05-09: cloud document open (fetch+Blob URL), re-analyze (cloud-aware
  Celery task), and edit (PATCH /api/documents/{id})
- 05-10: OAuth initiate JSON response fix, Nextcloud custom endpoint
  edit round-trip, Edit button on ERROR rows, confirmation text overflow
- 05-11: admin hard-delete with admin-password confirmation (backend
  UserDeleteConfirm model + frontend inline panel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:39:47 +02:00
curo1305 7691477c6d docs(05): mark Phase 5 complete — all 8 plans executed, security gates passed, human checkpoint approved
- ROADMAP.md: all 05-01..05-08 plans marked [x], phase gates [x], Progress Table row updated to Complete 2026-05-29
- STATE.md: status→complete, completed_phases→5, percent→100, session continuity entry added
2026-05-29 09:16:45 +02:00
curo1305 f1a7f52616 fix(security): bump python-multipart>=0.0.27 and PyMuPDF>=1.26.7 — pip-audit findings 2026-05-29 09:14:27 +02:00
curo1305 c6a97b6a89 docs(05-08): complete cloud sidebar tree plan — awaiting human checkpoint 2026-05-29 08:34:42 +02:00
curo1305 98576ac298 feat(05-08): add Cloud Storage collapsible section to AppSidebar
- Import CloudProviderTreeItem and useCloudConnectionsStore
- Add cloudExpanded ref (default true) and activeCloudConnections/loadingCloudConnections computed
- Insert Cloud Storage section between Folders and Topics sections
- Fetch connections on mount; render one CloudProviderTreeItem per ACTIVE connection
- Empty state: 'No cloud storage connected'; loading state: 'Loading...'
2026-05-29 08:33:33 +02:00
curo1305 34b0593782 feat(05-08): add cloud tree components and getCloudFolders API function
- Add getCloudFolders(provider, folderId) to api/client.js (GET /api/cloud/folders/{provider}/{folderId})
- Create CloudProviderTreeItem.vue: lazy-load folder tree per connection, providerIconColor computed, expand/collapse arrow, loading/error states
- Create CloudFolderTreeItem.vue: recursive folder tree node with is_dir expand arrow, lazy-load children, depth padding
2026-05-29 08:32:19 +02:00
curo1305 ec0c69fb4e docs(05-07): complete cloud storage frontend UI plan — SUMMARY and STATE
- useCloudConnectionsStore, 3-tab SettingsView, SettingsCloudTab, CloudCredentialModal
- 61 Vitest tests passing, Vite build exits 0
- Fixed pre-existing build failure (top-level await) via build.target=esnext
2026-05-29 08:18:48 +02:00
curo1305 63a68296a5 feat(05-07): 3-tab SettingsView, SettingsCloudTab, CloudCredentialModal
- Convert SettingsView to 3-tab layout (Preferences/AI/Cloud) matching AdminView pattern
- Extract SettingsPreferencesTab.vue and SettingsAiTab.vue from original SettingsView
- Create SettingsCloudTab.vue with all 4 providers, status badges, action buttons
- Create CloudCredentialModal.vue for WebDAV/Nextcloud credential input
- Handle OAuth callback query params (cloud_connected/cloud_error) in SettingsView.onMounted
- Add success toast (auto-dismiss 5s) and persistent error banner for OAuth results
- Fix pre-existing build failure: add build.target=esnext to vite.config.js for top-level await support
- 2 SettingsCloudTab mount tests passing (W4 — CLAUDE.md)
2026-05-29 08:12:36 +02:00
curo1305 612d542c06 feat(05-07): cloud connections Pinia store + API client functions
- Create useCloudConnectionsStore with connections/loading/error refs
- fetchConnections, disconnect(id), disconnectAll() actions
- Append listCloudConnections, disconnectCloud, connectWebDav, updateDefaultStorage to api/client.js
- Add vitest test script to package.json
- 4 unit tests passing (W4 — CLAUDE.md)
2026-05-29 08:05:59 +02:00
curo1305 c44e861271 docs(05-06): complete cloud upload/test integration plan — SUMMARY and STATE
- Create 05-06-SUMMARY.md: documents.py cloud extension + 20 passing cloud tests
- Update STATE.md: plan 5→6 of 8, session notes, next action → 05-07
- Update ROADMAP.md: mark 05-06 as complete [x]
2026-05-29 07:58:03 +02:00
curo1305 d84e38acca test(05-06): promote 11 integration test stubs to real passing tests
- test_connect_google_drive: OAuth initiate redirects to Google (Redis mocked)
- test_oauth_callback_valid_state: valid state + mocked Flow.fetch_token → 302 (CLOUD-01)
- test_oauth_callback_invalid_state: invalid state → error redirect (CLOUD-01)
- test_webdav_connect_validates: localhost URL → 422 (D-17 SSRF)
- test_credentials_enc_not_exposed: credentials_enc absent from response (CLOUD-02, SEC-08)
- test_cloud_upload_no_presigned: cloud upload returns no upload_url (CLOUD-03)
- test_connection_status_display: ACTIVE status in list response (CLOUD-04)
- test_invalid_grant_sets_requires_reauth: 503 on invalid_grant (CLOUD-05)
- test_disconnect_deletes_credentials: DELETE 204 + DB row gone (CLOUD-06)
- test_admin_cannot_see_credentials: admin gets 403 (SEC-08 IDOR)
- test_cross_user_idor: wrong-owner delete → 404 (SEC-08 IDOR)

Also fix CloudConnectionOut.id field validator to accept UUID objects from ORM
(Rule 1: Bug - UUID id caused pydantic validation error on list_connections)

All 20 cloud tests PASSED; full suite: 282 passed, 1 pre-existing failure
2026-05-29 07:51:02 +02:00
curo1305 096bb48116 test(05-06): promote 4 unit test stubs to real passing tests
- test_credential_round_trip: verifies HKDF AES-256-GCM round-trip (CLOUD-02)
- test_ssrf_validation: parametrized over 6 URLs (5 blocked, 1 public) (D-17)
- test_ssrf_link_local: explicit link-local 169.254.x.x check (D-17)
- test_factory_returns_correct_backend: mock-based factory test (CLOUD-07)
All 4 tests PASSED; no xfail decorators remaining on these tests
2026-05-29 07:47:33 +02:00
curo1305 d7d6382d49 feat(05-06): extend upload and content-proxy endpoints for cloud backends
- Add POST /api/documents/upload multipart endpoint with target_backend form field
- Cloud backends (google_drive, onedrive, nextcloud, webdav) use direct put_object()
- MinIO path generates presigned PUT URL (unchanged flow)
- Cloud uploads do NOT touch quota (D-11: separate backend)
- GET /api/documents/{id}/content now uses get_storage_backend_for_document
- CloudConnectionError from any cloud op raises HTTPException(503) with safe message
- target_backend validated against _CLOUD_PROVIDERS allowlist (T-05-06-01)
- Import CloudConnectionError with fallback stub for envs without google-auth deps
2026-05-29 07:45:28 +02:00
curo1305 be6ff5a71f docs(05-05): complete cloud API endpoints plan — SUMMARY and STATE
- Created 05-05-SUMMARY.md: cloud.py (7 endpoints), main.py (router registration), admin.py (SEC-09 cleanup)
- Updated STATE.md: plan advanced to 5/8, session log updated, decisions recorded
- Updated ROADMAP.md: 05-03, 05-04, 05-05 marked complete
- Updated REQUIREMENTS.md: SEC-09 marked complete (cloud credential purge on account deletion)
2026-05-29 07:34:22 +02:00
curo1305 d85a09719e feat(05-05): add cloud credential cleanup on admin user deletion (SEC-09)
- Import CloudConnection and get_storage_backend_for_document into admin.py
- In delete_user: query all CloudConnection rows for the target user before MinIO cleanup
- For each connection: query documents with matching storage_backend, call delete_object
  best-effort (catch + ignore exceptions — same pattern as MinIO cleanup)
- Explicit session.delete(conn) for each CloudConnection row before user row deletion
- session.flush() after connection deletes to order SQL before user DELETE
- write_audit_log(event_type="cloud.credentials_purged") with providers list metadata
- Cloud cleanup runs BEFORE existing MinIO cleanup: credentials still available to build
  cloud backend instances for delete_object calls (SEC-09)
- No orphaned credentials_enc rows after account deletion (SEC-09 satisfied)
2026-05-29 00:59:10 +02:00