From d959e0cf42604d37b18a3867d35acc55236314e5 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Mon, 22 Jun 2026 19:29:46 +0200 Subject: [PATCH] feat(13-06): emit metadata-only audit rows on authoritative upload success - Write cloud.file_uploaded audit row in same transaction as reconciliation - Audit payload is metadata-only: filename, size_bytes, connection_id, provider_item_id, parent_ref - No provider URLs, tokens, bytes, or document text in audit metadata (T-13-02) - Non-success paths (conflict, offline, reauth_required) bypass audit write (T-13-21) - Import get_client_ip from deps.utils and write_audit_log from services.audit --- backend/api/cloud/operations.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/backend/api/cloud/operations.py b/backend/api/cloud/operations.py index 5a82287..cb1e904 100644 --- a/backend/api/cloud/operations.py +++ b/backend/api/cloud/operations.py @@ -45,6 +45,8 @@ from config import settings from db.models import User from deps.auth import get_regular_user from deps.db import get_db +from deps.utils import get_client_ip +from services.audit import write_audit_log from services.cloud_items import ConnectionNotFound from services.rate_limiting import account_limiter from storage.cloud_base import ( @@ -683,6 +685,28 @@ async def upload_cloud_file( error_message="Folder contents changed by upload — re-listing required.", ) + # Write metadata-only audit row in the same transaction (Plan 06, Task 2: T-13-20). + # Only authoritative upload success reaches this path. Non-success outcomes + # (conflict, offline, reauth_required) are handled in separate branches below + # and never call write_audit_log — preserving T-13-21 (no false success events). + # The audit payload is metadata-only: no provider URLs, tokens, bytes, or + # document text are included (T-13-02). + await write_audit_log( + session, + event_type="cloud.file_uploaded", + user_id=current_user.id, + actor_id=current_user.id, + resource_id=None, + ip_address=get_client_ip(request), + metadata_={ + "connection_id": str(connection_id), + "provider_item_id": provider_item_id, + "filename": resolved_name, + "size_bytes": resolved_size, + "parent_ref": resolved_parent_ref, + }, + ) + await session.commit() return {