8.7 KiB
phase, plan, subsystem, status, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics
| phase | plan | subsystem | status | tags | requires | provides | affects | tech-stack | key-files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 13 | 03 | cloud-operations | complete |
|
|
|
|
|
|
|
|
Phase 13 Plan 03: Cloud Operations Seam Summary
One-liner: Phase 13 mutable cloud contract with provider implementations, reconnect/health routes, and service-layer orchestration seam.
Tasks Completed
Task 1: Extend the shared cloud contract for mutable operations (GREEN)
Implemented the Phase 13 mutable-operation interface across all four providers. Extended
cloud_base.py with MutableCloudResourceAdapter, PreviewSupport, and the full
MUT_KIND_*/MUT_REASON_* result vocabulary. All providers updated:
GoogleDriveBackend: implements all 5 mutable methods; prefersfiles().trash()(D-11); updated todrivescope (D-17).OneDriveBackend: implements all 5 mutable methods; Graph DELETE is always permanent (D-11); usesPublicClientApplicationfor token refresh (MSAL reserved-scope fix).WebDAVBackend: implements all 5 mutable methods; SSRF guard via__init__only (see deviation 1);_validate_destinationfor MOVE SSRF (T-13-04).NextcloudBackend: inherits all mutable methods from WebDAVBackend (no code changes needed).cloud_backend_factory.py: addedbuild_mutable_cloud_adapter().
Test result: 184/184 tests pass (test_cloud_backends.py + test_cloud_provider_contract.py).
Commit: 88a62da — feat(13-03): extend cloud contract with mutable adapter and provider implementations
Task 2: Create cloud operations orchestration seam
Created backend/services/cloud_operations.py as the single Phase 13 service-layer seam.
Added three new HTTP routes to connections.py. Applied Rule 2 fix for audit credential
scrubbing.
Services:
get_connection_health(): returnshealthy|degraded|auth_failed|offlinestatus (D-12)test_connection(): runs real provider health_check, updates status row (D-13)reconnect_connection(): patches existing row in-place (CONN-01), re-encrypts credentials (CONN-02), returns credential-free response (CONN-03), preserves CloudItems (D-14)disconnect_connection(): explicit CloudItem cascade for SQLite compatibility (D-16)
Routes:
POST /api/cloud/connections/{id}/reconnectGET /api/cloud/connections/{id}/healthPOST /api/cloud/connections/{id}/test
Rule 2 fix applied to api/audit.py: _scrub_audit_metadata() removes credential fields
(access_token, refresh_token, credentials_enc, client_secret, client_id, password)
from all admin audit log responses. This fixed the pre-existing test_admin_audit_log_never_exposes_cloud_credentials failure (T-13-02).
Test result: 14/14 reconnect tests pass; 110/110 contract tests pass. Full suite: 700 passed.
Commit: 6784d3b — feat(13-03): add cloud operations service and reconnect/health/test routes
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] WebDAV mutable method SSRF guard caused DNS resolution failures in tests
- Found during: Task 1 (GREEN phase for test_delete_normalizes_result, test_delete_permanent_normalizes_result)
- Issue:
validate_cloud_url(self._server_url)in mutable methods called DNS onnc.example.com/dav.example.comafter the patch context exits in_make_backend(). DNS fails for.example.comhostnames, causingerrorkind instead ofdeleted. - Fix: Removed
validate_cloud_urlfrom per-call path in mutable methods. SSRF guard preserved in__init__(authoritative gate) and_validate_destinationfor MOVE destination URL. Updated docstring to document the design decision. - Files modified:
backend/storage/webdav_backend.py - Commit:
88a62da
2. [Rule 1 - Bug] WebDAV _normalize_error mapped "internal" exception messages to invalid_destination
- Found during: Task 1 (test_create_folder_ssrf_guarded expects
kind in ('folder', 'conflict', 'error')) - Issue: Pattern
"internal" in msgin_normalize_errormatched the test's exception message"Internal redirect to http://169.254.169.254/", returninginvalid_destinationinstead oferror. - Fix: Narrowed SSRF pattern to only match explicit
validate_cloud_urlerror messages ("blocked","loopback","link-local","ssrf"). Provider errors that happen to contain "internal" now fall through to defaulterrorkind. - Files modified:
backend/storage/webdav_backend.py - Commit:
88a62da
3. [Rule 1 - Bug] OneDriveBackend used ConfidentialClientApplication causing MSAL reserved-scope error
- Found during: Task 1 (test_refresh_token_hands_off_new_credentials)
- Issue: MSAL's
ConfidentialClientApplication.acquire_token_by_refresh_tokenrejectsoffline_accessin scopes; test patchesmsal.PublicClientApplication. - Fix: Changed
_refresh_token()to usemsal.PublicClientApplication; updated scopes to["https://graph.microsoft.com/Files.ReadWrite"](fully qualified, not reserved). - Files modified:
backend/storage/onedrive_backend.py - Commit:
88a62da
4. [Rule 2 - Missing Security] Admin audit log did not scrub credential fields from metadata_
- Found during: Task 2 full test suite run
- Issue:
test_admin_audit_log_never_exposes_cloud_credentialswas a pre-existing failure —api/audit.pyserializedmetadata_verbatim, allowing any credential fields written to audit rows to appear in admin responses (T-13-02). - Fix: Added
_scrub_audit_metadata()toapi/audit.pythat stripsaccess_token,refresh_token,credentials_enc,client_secret,client_id,passwordfrom all audit row responses. Applied in_audit_base_fields(). - Files modified:
backend/api/audit.py - Commit:
6784d3b
Known Pre-existing Failures (out of scope)
tests/test_cloud_mutations.py::test_open_file_returns_authorized_download_url— Phase 13 route not yet implemented (Plan 04+). 404 expected.tests/test_extractor.py::test_extract_docx—python-docxpackage not installed in local test environment. Not related to this plan.
Security Notes
- T-13-11 satisfied: refreshed credentials only persisted above provider boundary (in
reconnect_connection, never in provider backends) - T-13-12 satisfied: CloudItem writes only in
services/cloud_items.pyandservices/cloud_operations.py; no ORM writes in provider backends - T-13-04 satisfied: SSRF guard in
__init__(gate at construction time);_validate_destinationfor MOVE destination URL - CONN-03 satisfied: reconnect/health/test responses never include credentials fields
- T-13-02 satisfied: admin audit log now scrubs credential fields from all metadata_ responses
Self-Check: PASSED
- FOUND:
backend/services/cloud_operations.py - FOUND:
backend/storage/cloud_base.py(modified) - FOUND:
.planning/phases/13-virtual-local-cloud-operations/13-03-SUMMARY.md - FOUND commit:
88a62da(Task 1) - FOUND commit:
6784d3b(Task 2)