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
This commit is contained in:
curo1305
2026-06-22 19:29:46 +02:00
parent 33f0498503
commit d959e0cf42
+24
View File
@@ -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 {