feat(13-03): add cloud operations service and reconnect/health/test routes
- Create backend/services/cloud_operations.py as the single Phase 13 orchestration seam:
get_connection_health (D-12), test_connection (D-13), reconnect_connection (CONN-01/02/03,
D-14), disconnect_connection (D-16, explicit CloudItem cascade for SQLite compatibility)
- Add POST /connections/{id}/reconnect route (CONN-01/02/03, D-14)
- Add GET /connections/{id}/health route (D-12, T-13-02)
- Add POST /connections/{id}/test route (D-13, T-13-02)
- Update DELETE /connections/{id} to use service-layer disconnect with explicit
CloudItem deletion (D-16; covers FK-cascade-less environments like SQLite tests)
- Fix (Rule 2 - T-13-02): add _scrub_audit_metadata() to admin audit log API to
remove credential fields before returning audit rows to admin callers
- All 14 reconnect tests pass + 110 provider-contract tests pass
- Pre-existing failures in test_cloud_mutations and test_extractor are unrelated
This commit is contained in:
+21
-1
@@ -25,6 +25,26 @@ from storage.minio_backend import MinIOBackend
|
||||
router = APIRouter(prefix="/api/admin", tags=["audit"])
|
||||
|
||||
_VALID_EVENT_PREFIXES = frozenset({"auth", "document", "folder", "share", "admin", "cloud"})
|
||||
|
||||
# Fields that must never appear in admin audit log responses (T-13-02)
|
||||
_AUDIT_SCRUB_KEYS = frozenset({
|
||||
"access_token", "refresh_token", "credentials_enc",
|
||||
"client_secret", "client_id", "password",
|
||||
})
|
||||
|
||||
|
||||
def _scrub_audit_metadata(metadata: Optional[dict]) -> Optional[dict]:
|
||||
"""Remove credential fields from audit metadata before returning to admin (T-13-02).
|
||||
|
||||
The admin audit log must never surface raw tokens or credential fields even if
|
||||
a bug caused them to be written to metadata_. This scrub is a defence-in-depth
|
||||
gate applied to every audit row regardless of how the metadata was written.
|
||||
"""
|
||||
if not metadata or not isinstance(metadata, dict):
|
||||
return metadata
|
||||
return {k: v for k, v in metadata.items() if k not in _AUDIT_SCRUB_KEYS}
|
||||
|
||||
|
||||
_CSV_FIELDS = [
|
||||
"id",
|
||||
"event_type",
|
||||
@@ -48,7 +68,7 @@ def _audit_base_fields(entry: AuditLog) -> dict:
|
||||
"actor_id": str(entry.actor_id) if entry.actor_id else None,
|
||||
"resource_id": str(entry.resource_id) if entry.resource_id else None,
|
||||
"ip_address": str(entry.ip_address) if entry.ip_address else None,
|
||||
"metadata_": entry.metadata_,
|
||||
"metadata_": _scrub_audit_metadata(entry.metadata_), # T-13-02: credential scrub
|
||||
"created_at": entry.created_at.isoformat(),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user