feat(phase-4): Task 1 — audit log backfill in auth.py and documents.py (D-13)
- Add write_audit_log import to auth.py and documents.py - auth.py: login success (auth.login), login failure (auth.login_failed, no PII), logout (auth.logout), logout-all (auth.sign_out_all), change-password (auth.password_changed), TOTP enable (auth.totp_enrolled), TOTP disable (auth.totp_revoked), backup code used (auth.backup_code_used) - documents.py: upload confirm (document.uploaded, size+backend only), document delete (document.deleted, size only — no filename/extracted_text) - Add request: Request param to change_password, disable_totp, confirm_upload, delete_document
This commit is contained in:
@@ -30,6 +30,7 @@ from db.models import Document, Quota, Share, User
|
||||
from deps.auth import get_regular_user
|
||||
from deps.db import get_db
|
||||
from services import classifier, storage
|
||||
from services.audit import write_audit_log
|
||||
from storage import get_storage_backend
|
||||
from tasks.document_tasks import extract_and_classify
|
||||
|
||||
@@ -95,6 +96,7 @@ async def request_upload_url(
|
||||
@router.post("/{doc_id}/confirm")
|
||||
async def confirm_upload(
|
||||
doc_id: str,
|
||||
request: Request,
|
||||
session: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_regular_user),
|
||||
):
|
||||
@@ -173,6 +175,17 @@ async def confirm_upload(
|
||||
used_bytes = row.used_bytes
|
||||
|
||||
doc.status = "uploaded"
|
||||
# D-13: document uploaded event — size_bytes + storage_backend only, NO filename, NO extracted_text (T-04-07-02)
|
||||
_ip = request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None)
|
||||
await write_audit_log(
|
||||
session,
|
||||
event_type="document.uploaded",
|
||||
user_id=current_user.id,
|
||||
actor_id=current_user.id,
|
||||
resource_id=doc.id,
|
||||
ip_address=_ip,
|
||||
metadata_={"size_bytes": size, "storage_backend": "minio"},
|
||||
)
|
||||
await session.commit()
|
||||
extract_and_classify.delay(str(doc.id))
|
||||
|
||||
@@ -340,6 +353,7 @@ async def get_document(
|
||||
@router.delete("/{doc_id}")
|
||||
async def delete_document(
|
||||
doc_id: str,
|
||||
request: Request,
|
||||
session: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_regular_user),
|
||||
):
|
||||
@@ -360,9 +374,27 @@ async def delete_document(
|
||||
if doc is None or doc.user_id != current_user.id:
|
||||
raise HTTPException(404, "Document not found")
|
||||
|
||||
# Capture audit metadata before delete removes the row
|
||||
_doc_size = doc.size_bytes
|
||||
_doc_id = doc.id
|
||||
_ip = request.headers.get("X-Forwarded-For") or (request.client.host if request.client else None)
|
||||
|
||||
ok = await storage.delete_document(session, doc_id)
|
||||
if not ok:
|
||||
raise HTTPException(404, "Document not found")
|
||||
|
||||
# D-13: document deleted event — written AFTER successful delete, size_bytes only (T-04-07-02)
|
||||
await write_audit_log(
|
||||
session,
|
||||
event_type="document.deleted",
|
||||
user_id=current_user.id,
|
||||
actor_id=current_user.id,
|
||||
resource_id=_doc_id,
|
||||
ip_address=_ip,
|
||||
metadata_={"size_bytes": _doc_size},
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
return {"success": True}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user