- 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)
9.9 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 05-cloud-storage-backends | 05 | api |
|
|
|
|
|
|
|
|
|
12min | 2026-05-29 |
Phase 5 Plan 05: Cloud Connection Management API Summary
OAuth initiate/callback for Google Drive and OneDrive, WebDAV/Nextcloud credential connect, connection list/delete, TTL-cached folder listing, default-storage PATCH, and cloud credential purge on account deletion — all 7 endpoints using get_regular_user dep with credentials_enc excluded from all responses
Performance
- Duration: 12 min
- Started: 2026-05-29T09:09:57Z
- Completed: 2026-05-29T09:21:57Z
- Tasks: 3
- Files modified: 3
Accomplishments
- Created
backend/api/cloud.pywith all 7 endpoints: OAuth initiate and callback (Google Drive + OneDrive), WebDAV/Nextcloud credential connect, connection list, connection delete, folder listing (TTL-cached), and default-storage update. All handlers useDepends(get_regular_user)— admin gets 403.CloudConnectionOutimported fromapi.adminso the credentials_enc exclusion whitelist is never duplicated. - Implemented
_call_cloud_ophelper for transparent token refresh: retries op_fn once ontoken_expired(decrypt → refresh via provider → encrypt → update DB → rebuild backend → retry), setsstatus="REQUIRES_REAUTH"and writes audit log oninvalid_grant. - Registered both routers in
main.py(Phase 5 section after audit router). All 6 cloud routes +/api/users/me/default-storagevisible inapp.routes. - Added cloud credential cleanup to
admin.py delete_user(SEC-09): queries allCloudConnectionrows, deletes cloud-stored documents viaget_storage_backend_for_document + delete_object(best-effort), explicitly deletesCloudConnectionrows withsession.flush(), writescloud.credentials_purgedaudit event with providers list.
Task Commits
- Task 1: Create cloud.py -
2424f52(feat) - Task 2: Register cloud routers in main.py -
f509c37(feat) - Task 3: Cloud cleanup on admin user deletion -
d85a097(feat)
Files Created/Modified
/Users/nik/Documents/Progamming/document_scanner/backend/api/cloud.py— All 7 cloud connection management endpoints +_call_cloud_ophelper +_upsert_cloud_connectionhelper/Users/nik/Documents/Progamming/document_scanner/backend/main.py—cloud_routerandcloud_users_routerregistered (Phase 5 section)/Users/nik/Documents/Progamming/document_scanner/backend/api/admin.py—CloudConnection+get_storage_backend_for_documentimported; cloud cleanup block added todelete_userbefore MinIO cleanup
Decisions Made
- OAuth callback endpoint does not require a JWT Bearer token. The OAuth redirect flow happens in the browser: the provider redirects back with
codeandstate, and the backend validates thestateagainst Redis to identify the user. Adding a JWT dep would break the flow (browser doesn't carry the Bearer header in redirect responses). The state token provides equivalent security: 256 bits of entropy, TTL 1800s, single-use deletion. - Cloud cleanup was added to
admin.py delete_user(notauth.py). Theauth.pymodule does not implement account self-deletion — there is noDELETE /api/users/meendpoint. The admin-initiated deletion (DELETE /api/admin/users/{id}) is the only account deletion code path. - Cloud object deletion runs before MinIO document deletion in
delete_userso thatget_storage_backend_for_documentcan decrypt credentials and build the correct backend. Aftersession.delete(conn)+session.flush(), the credentials would be gone.
Deviations from Plan
None — plan executed exactly as written. cloud.py was already partially written (untracked file) from a prior aborted attempt; it was inspected and verified complete, then committed. All three tasks executed as specified.
Issues Encountered
cloud.py already existed as an untracked file from a prior failed execution attempt. Verified it contained all required endpoints, routes, and security invariants before committing. No rework needed.
Known Stubs
None. All endpoints have real implementations. The test_cloud.py stubs remain xfail because they call pytest.xfail() unconditionally inside the test body — they are scaffolding tests that will be replaced by real integration tests in Plan 05-06 or later.
Threat Surface Scan
New network-facing endpoints introduced (all on router and users_router):
GET /api/cloud/oauth/initiate/{provider}— initiates OAuth redirect; all providers validated against allowlistGET /api/cloud/oauth/callback/{provider}— receives OAuth code from provider; state validated before any DB writePOST /api/cloud/connections/webdav— user-supplied URL validated viavalidate_cloud_url(SSRF) before any outbound callGET /api/cloud/connections— read-only, returns CloudConnectionOut (credentials_enc excluded)DELETE /api/cloud/connections/{connection_id}— returns 404 for wrong-owner (enumeration prevention)GET /api/cloud/folders/{provider}/{folder_id}— makes outbound call to cloud provider using decrypted credentials; TTL-cachedPATCH /api/users/me/default-storage— updates a single user column; no outbound calls
All STRIDE threats from the plan's threat model are mitigated as implemented:
- T-05-05-01:
secrets.token_urlsafe(32)state token in Redis — confirmed - T-05-05-02: Redis key deleted immediately on callback validation — confirmed
- T-05-05-03:
CloudConnectionOutimported from admin.py — same whitelist — confirmed - T-05-05-04: DELETE returns 404 for wrong-owner — confirmed
- T-05-05-05:
validate_cloud_urlcalled before WebDAV backend instantiation — confirmed - T-05-05-06:
Depends(get_regular_user)on all endpoints — confirmed - T-05-05-08: audit metadata =
{"provider": provider}only — confirmed
No threat flags raised beyond those already documented in the plan.
Next Phase Readiness
- All cloud connection management endpoints are live and importable. Plans 05-06 through 05-08 can begin building on top of the API layer.
_call_cloud_opis available for use in upload/download handlers in Plan 05-06._build_backendhelper incloud.pyreconstructs any backend from provider name + credentials dict.- 262 tests pass / 43 xfailed / 1 pre-existing failure (
test_extract_docx— python-docx not installed).
Self-Check: PASSED
Files verified present:
backend/api/cloud.py: FOUNDbackend/main.py: FOUND (cloud_router registered)backend/api/admin.py: FOUND (cloud cleanup present)
Commits verified:
2424f52: feat(05-05): implement cloud.py — FOUNDf509c37: feat(05-05): register cloud and users routers in main.py — FOUNDd85a097: feat(05-05): add cloud credential cleanup on admin user deletion — FOUND
Phase: 05-cloud-storage-backends Completed: 2026-05-29