Nginx already forwards X-Forwarded-For and X-Real-IP, but the backend read request.client.host directly — always returning 172.18.0.x. Added get_client_ip() helper to audit service; updated all 13 call sites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
36 lines
1.2 KiB
Python
36 lines
1.2 KiB
Python
import json
|
|
from fastapi import Request
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from app.models.audit_log import AuditLog
|
|
|
|
|
|
def get_client_ip(request: Request) -> str:
|
|
"""Extract the real client IP from proxy headers, falling back to direct connection."""
|
|
forwarded_for = request.headers.get("x-forwarded-for")
|
|
if forwarded_for:
|
|
# X-Forwarded-For can be a comma-separated list; first entry is the original client
|
|
return forwarded_for.split(",")[0].strip()
|
|
real_ip = request.headers.get("x-real-ip")
|
|
if real_ip:
|
|
return real_ip.strip()
|
|
return request.client.host if request.client else "unknown"
|
|
|
|
|
|
async def log_audit_event(
|
|
db: AsyncSession,
|
|
action: str,
|
|
actor_id: int | None = None,
|
|
target_id: int | None = None,
|
|
detail: dict | None = None,
|
|
ip: str | None = None,
|
|
) -> None:
|
|
"""Record an action in the audit log. Does NOT commit — caller handles transaction."""
|
|
entry = AuditLog(
|
|
actor_user_id=actor_id,
|
|
target_user_id=target_id,
|
|
action=action,
|
|
detail=json.dumps(detail) if detail else None,
|
|
ip_address=ip[:45] if ip else None,
|
|
)
|
|
db.add(entry)
|