feat(sp11): tamper-evident hash-chained audit_log
- New audit_log table (migration 0009, user_version=9):
* id, event_type, entity_type, entity_id, actor, payload_json,
created_at, prev_hash, hash
* Indexes on (entity_type, entity_id), event_type, created_at
- New cyclone.audit_log module:
* append_event(session, AuditEvent) — appends one chained row
* verify_chain(session) — walks the chain, returns first bad id
* SHA-256 hash over canonical row form (unit-separator delimited)
* Genesis prev_hash = 64 zeros (Bitcoin-style sentinel)
- New AuditLog ORM model
- New admin API endpoints:
* GET /api/admin/audit-log (paginated, filterable)
* GET /api/admin/audit-log/verify (returns ok/first_bad_id/reason)
- Hooked into existing endpoints to append events:
* /api/parse-999 → 'claim.rejected' per matched claim
* /api/parse-277ca → 'claim.payer_rejected' per matched claim
* /api/clearhouse/submit → 'clearhouse.submitted' per claim
- HIPAA §164.316(b)(2) compliance note in docs
Tests: 688 -> 705 (9 audit + 8 audit-API). All 705 backend tests pass.
This commit is contained in:
@@ -51,17 +51,18 @@ def test_migration_0002_creates_acks_table():
|
||||
|
||||
def test_migration_latest_idempotent_on_fresh_db():
|
||||
"""Re-running the migration on the same DB must be a no-op (PRAGMA
|
||||
user_version already at the latest version — currently 8 after
|
||||
user_version already at the latest version — currently 9 after
|
||||
0004-0006 line_reconciliation, 0005 ta1_acks, SP9's 0007
|
||||
providers/payers/clearhouse, and SP10's 0008 payer_rejected)."""
|
||||
providers/payers/clearhouse, SP10's 0008 payer_rejected, and
|
||||
SP11's 0009 audit_log)."""
|
||||
with db.engine().begin() as c:
|
||||
v1 = c.exec_driver_sql("PRAGMA user_version").scalar() or 0
|
||||
assert v1 == 8
|
||||
assert v1 == 9
|
||||
# A second run should not raise and should not bump the version.
|
||||
db_migrate.run(db.engine())
|
||||
with db.engine().begin() as c:
|
||||
v2 = c.exec_driver_sql("PRAGMA user_version").scalar() or 0
|
||||
assert v2 == 8
|
||||
assert v2 == 9
|
||||
|
||||
|
||||
def test_add_ack_persists_row():
|
||||
|
||||
Reference in New Issue
Block a user