feat(audit): record user_id on log events
This commit is contained in:
@@ -41,6 +41,21 @@ from sqlalchemy.exc import IntegrityError
|
||||
from cyclone.inbox_state import apply_999_rejections
|
||||
from cyclone.inbox_state_277ca import apply_277ca_rejections
|
||||
from cyclone.audit_log import AuditEvent, append_event, verify_chain
|
||||
|
||||
|
||||
def _actor_user_id(request: Request) -> int | None:
|
||||
"""Return the acting user's id from ``request.state.user``, or None.
|
||||
|
||||
``get_current_user``/``matrix_gate`` populate ``request.state.user``
|
||||
for both the authenticated path and the AUTH_DISABLED escape hatch.
|
||||
Returns None when the state hasn't been set (e.g. background jobs
|
||||
or unit tests that bypass auth). Used to stamp ``user_id`` onto
|
||||
audit events without crashing the request.
|
||||
"""
|
||||
user = getattr(request.state, "user", None)
|
||||
if user is None:
|
||||
return None
|
||||
return getattr(user, "id", None)
|
||||
from cyclone.parsers.exceptions import CycloneParseError
|
||||
from cyclone.parsers.models import BatchSummary, ClaimOutput, Envelope, ParseResult
|
||||
from cyclone.parsers.models_270 import (
|
||||
@@ -766,6 +781,7 @@ async def parse_999_endpoint(
|
||||
entity_id=cid,
|
||||
payload={"source_batch_id": synthetic_id, "ack_id": row.id},
|
||||
actor="999-parser",
|
||||
user_id=_actor_user_id(request),
|
||||
))
|
||||
audit_s.commit()
|
||||
|
||||
@@ -989,6 +1005,7 @@ async def parse_277ca_endpoint(
|
||||
"277ca_id": row.id,
|
||||
},
|
||||
actor="277ca-parser",
|
||||
user_id=_actor_user_id(request),
|
||||
))
|
||||
audit_s.commit()
|
||||
if apply_result.orphans:
|
||||
@@ -2526,7 +2543,7 @@ def get_clearhouse():
|
||||
|
||||
|
||||
@app.post("/api/clearhouse/submit")
|
||||
def submit_to_clearhouse(body: dict):
|
||||
def submit_to_clearhouse(request: Request, body: dict):
|
||||
"""Submit a batch of claims to the clearhouse (SFTP). SP9: stub.
|
||||
|
||||
Body: ``{"claim_ids": [...], "payer_id": "CO_TXIX"}``
|
||||
@@ -2654,6 +2671,7 @@ def submit_to_clearhouse(body: dict):
|
||||
"stub": ch.sftp_block.stub,
|
||||
},
|
||||
actor="clearhouse-submit",
|
||||
user_id=_actor_user_id(request),
|
||||
))
|
||||
audit_s.commit()
|
||||
return {"ok": True, "submitted": results, "stub": ch.sftp_block.stub}
|
||||
|
||||
@@ -103,6 +103,12 @@ class AuditEvent:
|
||||
computed hash. Payload must be JSON-serializable; the audit_log
|
||||
module handles the encoding so callers don't need to think about
|
||||
canonical form.
|
||||
|
||||
``user_id`` is the authenticated actor for this event, when known
|
||||
(e.g. a parse-999 call made by user 7). It's stored on the row but
|
||||
is NOT part of the hash chain — the chain hashes only the fields
|
||||
that existed pre-SP-auth so verify_chain stays compatible with
|
||||
pre-auth rows.
|
||||
"""
|
||||
|
||||
event_type: str
|
||||
@@ -111,6 +117,7 @@ class AuditEvent:
|
||||
payload: dict[str, Any] = field(default_factory=dict)
|
||||
actor: str = "system"
|
||||
created_at: datetime | None = None
|
||||
user_id: int | None = None
|
||||
|
||||
|
||||
def append_event(
|
||||
@@ -155,6 +162,7 @@ def append_event(
|
||||
created_at=created_at,
|
||||
prev_hash=prev_hash,
|
||||
hash=GENESIS_PREV_HASH, # placeholder; updated below
|
||||
user_id=event.user_id,
|
||||
)
|
||||
session.add(row)
|
||||
session.flush() # populate row.id
|
||||
|
||||
@@ -669,6 +669,11 @@ class AuditLog(Base):
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
prev_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
# SP-auth: which authenticated user performed this action. Nullable
|
||||
# so existing (pre-auth) rows and system-initiated events stay valid.
|
||||
# NOT part of the hash chain — verify_chain must continue to work on
|
||||
# legacy rows that pre-date this column.
|
||||
user_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, index=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_audit_log_entity", "entity_type", "entity_id"),
|
||||
|
||||
Reference in New Issue
Block a user