"""Authenticated, idempotent HTTP ingestion for user-engine outbox events.""" from __future__ import annotations import hashlib import hmac import json import os import sqlite3 from datetime import datetime from http import HTTPStatus from typing import Any from wsgiref.simple_server import make_server from audit_core.interface import AuditEvent MAX_BODY_BYTES = 256 * 1024 _SECRET_FRAGMENTS = ("password", "secret", "token", "credential", "private_key") class SQLiteEventStore: def __init__(self, path: str) -> None: self.db = sqlite3.connect(path, check_same_thread=False) self.db.execute(""" CREATE TABLE IF NOT EXISTS events ( event_id TEXT PRIMARY KEY, payload_hash TEXT NOT NULL, accepted_at TEXT NOT NULL, record TEXT NOT NULL ) """) self.db.commit() def accept(self, event: AuditEvent, payload_hash: str) -> tuple[bool, str]: existing = self.db.execute( "SELECT payload_hash FROM events WHERE event_id = ?", (event.event_id,) ).fetchone() if existing: if existing[0] != payload_hash: raise ValueError("event_id_conflict") return True, f"audit:{event.event_id}" with self.db: self.db.execute( "INSERT INTO events VALUES (?, ?, ?, ?)", (event.event_id, payload_hash, datetime.now().astimezone().isoformat(), json.dumps(event.as_record(), sort_keys=True)), ) return False, f"audit:{event.event_id}" class IngestionApplication: def __init__(self, store: SQLiteEventStore, bearer_token: str) -> None: if not bearer_token: raise ValueError("bearer token is required") self.store = store self.token = bearer_token def __call__(self, environ, start_response): path = environ.get("PATH_INFO", "") if path in ("/healthz", "/readyz"): return self._json(start_response, HTTPStatus.OK, {"status": "ok"}) if path != "/v1/events" or environ.get("REQUEST_METHOD") != "POST": return self._json(start_response, HTTPStatus.NOT_FOUND, {"error": "not_found"}) supplied = str(environ.get("HTTP_AUTHORIZATION", "")) if not hmac.compare_digest(supplied, f"Bearer {self.token}"): return self._json(start_response, HTTPStatus.UNAUTHORIZED, {"error": "unauthorized"}) try: length = int(environ.get("CONTENT_LENGTH") or 0) if length <= 0 or length > MAX_BODY_BYTES: raise ValueError("invalid_size") raw = environ["wsgi.input"].read(length) payload = json.loads(raw) event = normalize(payload, environ.get("HTTP_IDEMPOTENCY_KEY")) duplicate, reference = self.store.accept( event, hashlib.sha256(raw).hexdigest() ) except (ValueError, TypeError, KeyError, json.JSONDecodeError) as exc: return self._json(start_response, HTTPStatus.BAD_REQUEST, {"error": str(exc)}) return self._json(start_response, HTTPStatus.OK if duplicate else HTTPStatus.ACCEPTED, {"status": "duplicate" if duplicate else "accepted", "reference": reference}) @staticmethod def _json(start_response, status: HTTPStatus, payload: dict[str, Any]): body = json.dumps(payload).encode() start_response(f"{status.value} {status.phrase}", [ ("Content-Type", "application/json"), ("Content-Length", str(len(body))) ]) return [body] def normalize(payload: dict[str, Any], idempotency_key: str | None) -> AuditEvent: required = ("id", "type", "source", "subject", "tenant", "correlation_id", "occurred_at", "data") if not isinstance(payload, dict) or any(not payload.get(key) for key in required): raise ValueError("invalid_event") if idempotency_key != payload["id"]: raise ValueError("idempotency_key_mismatch") if payload["source"] != "user-engine": raise ValueError("source_not_allowed") datetime.fromisoformat(str(payload["occurred_at"]).replace("Z", "+00:00")) if _contains_secret(payload["data"]): raise ValueError("secret_shaped_field") return AuditEvent( event_id=str(payload["id"]), observed_at=str(payload["occurred_at"]), tenant=str(payload["tenant"]), scope="tenant", source="user-engine", action=str(payload["type"]), resource=str(payload["subject"]), outcome="recorded", actor=None, details={"correlation_id": str(payload["correlation_id"]), "data": payload["data"]}, ) def _contains_secret(value: Any) -> bool: if isinstance(value, dict): return any(any(fragment in str(key).lower() for fragment in _SECRET_FRAGMENTS) or _contains_secret(item) for key, item in value.items()) if isinstance(value, list): return any(_contains_secret(item) for item in value) return False def main() -> None: app = IngestionApplication( SQLiteEventStore(os.environ.get("AUDIT_CORE_DATABASE_PATH", "/data/audit-core.db")), os.environ["AUDIT_CORE_INGEST_TOKEN"].strip(), ) with make_server(os.environ.get("AUDIT_CORE_HOST", "0.0.0.0"), int(os.environ.get("AUDIT_CORE_HTTP_PORT", "8080")), app) as server: server.serve_forever()