13 KiB
13 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12 | 02 | execute | 2 |
|
|
true |
|
|
Scope fence: capability reporting is active, but create/rename/move/delete/open/preview implementations remain Phase 13; file byte hydration and cache accounting remain Phase 14.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/phases/12-cloud-resource-foundation/12-01-SUMMARY.md @.planning/phases/12-cloud-resource-foundation/12-CONTEXT.md @.planning/phases/12-cloud-resource-foundation/12-RESEARCH.md @.planning/phases/12-cloud-resource-foundation/12-PATTERNS.md @.planning/phases/12-cloud-resource-foundation/12-VALIDATION.md Task 1: Normalize provider listings and capability evidence backend/storage/cloud_backend_factory.py, backend/storage/google_drive_backend.py, backend/storage/onedrive_backend.py, backend/storage/nextcloud_backend.py, backend/storage/webdav_backend.py, backend/tests/test_cloud_backends.py - backend/storage/cloud_base.py — contract created by Plan 12-01 - backend/storage/cloud_backend_factory.py — existing lazy provider construction - backend/api/cloud.py — current inline Google/OneDrive listing implementations to relocate - backend/storage/nextcloud_backend.py — current N+1 listing implementation and SSRF guard - backend/tests/test_cloud_backends.py — provider contract test conventions Make each provider expose `CloudResourceAdapter` behavior and normalize all pages into `CloudListing`. Google requests explicit files fields for id, parents, name, mimeType, size, modifiedTime, version/etag-equivalent fields, and item capabilities; follows `nextPageToken`; represents native Google documents with nullable/zero size without treating them as folders; and reports `insufficient_scope` when current `drive.file` credentials cannot browse intended content. OneDrive follows `@odata.nextLink` and normalizes id, drive/parent reference, file/folder facet, size, modified time, eTag/cTag, and effective permission state. Nextcloud/WebDAV use SSRF-validated non-mutating PROPFIND/OPTIONS metadata, normalize getetag/getlastmodified/getcontentlength/getcontenttype/resourcetype, and treat absent properties as unknown rather than unsupported. Factory adds `build_cloud_resource_adapter`. Fixture tests assert equivalent normalized results, complete pagination, safe capability evidence, and zero byte-download/mutation calls. - all supported providers return the same normalized resource and capability schema - Google and OneDrive pagination is followed before `complete=true` - every WebDAV/Nextcloud outbound request retains `validate_cloud_url()` protection - provider tests verify list/capability flows never call get_object/put/delete/move/rename - `pytest -q tests/test_cloud_backends.py` exits 0 cd backend && pytest -q tests/test_cloud_backends.py All four providers pass the normalized metadata/capability and complete-pagination contract without byte or mutation calls. Task 2: Decompose cloud API and add connection-ID browse schemas backend/api/cloud/__init__.py, backend/api/cloud/connections.py, backend/api/cloud/browse.py, backend/api/cloud/schemas.py, backend/api/cloud.py, backend/main.py, backend/tests/test_cloud.py - backend/api/cloud.py — preserve all connection/OAuth/WebDAV/default-storage behavior while splitting - backend/api/documents/__init__.py — no-prefix sub-router aggregator pattern and FastAPI empty-path warning - backend/api/schemas.py — explicit response whitelist pattern - backend/deps/utils.py — canonical `parse_uuid` and `get_client_ip` - backend/services/cloud_items.py — owner-scoped service API Convert `api/cloud.py` into an `api/cloud/` package with one parent router owning `/api/cloud`; sub-routers carry no prefix. Preserve existing endpoint URLs as compatibility routes where unambiguous, but introduce canonical `GET /api/cloud/connections/{connection_id}/items` with optional encoded parent reference and owner-scoped `PATCH /api/cloud/connections/{connection_id}` accepting only a validated nonblank `display_name` field. Parse UUIDs with `deps.utils.parse_uuid`, resolve the connection by current owner, and return explicit schemas for connection root, items, merged capabilities, freshness state, and last refresh timestamp. List connections keeps every same-provider account and never returns credentials. For an indexed folder, return durable rows and set/schedule refresh; for an empty first visit, perform one bounded metadata fetch so the user receives content. Map service/provider errors to controlled router errors. Admin remains blocked by `get_regular_user`. Add tests for renaming one connection without affecting another, duplicate providers, foreign connection/item IDs, admin access, credentials/token/password exclusion, malformed UUID, mass-assignment rejection, and compatibility endpoint behavior. - cloud package aggregator is the only router with `/api/cloud` prefix - canonical browse endpoint uses connection UUID and cannot select by provider alone - response schemas cannot serialize `credentials_enc` or decrypted credential fields - two active Google Drive connections for one user are independently browsable and independently renameable - focused cloud API tests pass cd backend && pytest -q tests/test_cloud.py -k "connection or browse or credential or admin" Canonical connection-ID browse and display-name APIs preserve existing connection flows and pass ownership/credential negatives. Task 3: Add durable metadata refresh task, retry behavior, and plan documentation backend/tasks/cloud_tasks.py, backend/celery_app.py, backend/tests/test_cloud_items.py, backend/tests/test_cloud.py, backend/main.py, frontend/package.json, frontend/package-lock.json, AGENTS.md, README.md - backend/tasks/document_tasks.py — sync Celery entry point bridging async service with bounded retry - backend/celery_app.py — JSON-only task registration and queue routing - backend/services/cloud_items.py — idempotent reconciliation/folder-state functions - AGENTS.md — documentation, version, testing, and security protocol - README.md — cloud feature and API documentation sections Add `refresh_cloud_folder(user_id, connection_id, parent_ref)` as a JSON-only Celery task on the documents queue. The task opens its own async DB session, revalidates owner/connection, decrypts credentials inside the worker (never broker payload), marks refreshing, obtains a complete normalized listing, reconciles idempotently, and marks fresh. Retry only transient provider/network failures using bounded increasing countdowns with jitter or the established explicit retry harness; terminal auth/scope errors become warning state with safe reason/remedy. Update task registration. Add tests for idempotency, retry countdown bounds, failed refresh retaining rows/last-success time, and no provider byte calls/quota changes. Apply project protocol for this user-visible/API plan: bump backend/frontend/package-lock patch version from 0.1.4 to 0.1.5, update AGENTS current state/shared module map if new canonical modules were added, and document the connection-ID browse/capability foundation in README. - task payload contains IDs/references only and no credential or provider byte content - transient retries are bounded; auth/scope failures do not retry forever - warning state preserves cached rows and last successful refresh timestamp - browse/refresh tests assert no `get_object`, MinIO put, or quota update - versions are exactly 0.1.5 in backend main, package.json, and package-lock root/package entry - focused backend tests pass cd backend && pytest -q tests/test_cloud.py tests/test_cloud_backends.py tests/test_cloud_items.py && cd ../frontend && npm run build Durable refresh retries safely, retains cached metadata on failure, performs no byte/quota work, and plan documentation/version 0.1.5 is complete.<threat_model>
| Threat ID | Threat | Mitigation and evidence |
|---|---|---|
| T-12-01 | Connection/item IDOR | get_regular_user plus owner-scoped connection UUID lookup; foreign/admin negative tests |
| T-12-03 | Credential disclosure | Explicit schemas and broker payload tests exclude credentials/tokens/passwords |
| T-12-04 | WebDAV SSRF | Preserve URL validation before every request and redirect; regression tests |
| T-12-05 | Cache collision | Connection UUID replaces provider in route/task/durable identity |
| T-12-07 | Data loss from partial pagination | complete=true only after all pages; reconciliation test |
| T-12-08 | Provider error XSS/leak | Stable reason codes and controlled messages; no raw credential-bearing exception response |
| T-12-09 | Byte/quota side effects | Negative mocks assert metadata browse never invokes byte or quota APIs |
| </threat_model> |
<success_criteria>
- Every provider supplies normalized metadata and capabilities.
- Browsing is keyed by owned connection UUID and supports duplicate provider accounts.
- Durable cached rows return without provider-byte downloads and refresh safely in background.
- Failure states preserve navigation, retry boundedly, and expose safe actionable reasons. </success_criteria>
Create .planning/phases/12-cloud-resource-foundation/12-02-SUMMARY.md when complete.