"""Durable alert acknowledgments behind owner-supplied identity and PDP adapters. This module deliberately has no network entrypoint or trusted-header fallback. The package must bind verified browser sessions and fresh authorization decisions. """ from dataclasses import dataclass, field from contextlib import contextmanager from datetime import datetime, timezone import hashlib import html import hmac import json import os from pathlib import Path import re import sqlite3 import stat import time from urllib.parse import parse_qs, urlencode import uuid from receiver import instant, strict_json ROLE = 'railiance-admin' TENANT = 'tenant:platform' SOURCE = 'railiance-telemetry' def encoded(value): return json.dumps(value, sort_keys=True, separators=(',', ':'), allow_nan=False) def occurrence(fingerprint, starts_at): if not isinstance(fingerprint, str) or not re.fullmatch('[0-9a-f]{16}', fingerprint): raise ValueError('invalid fingerprint') # Preserve nanosecond precision in the wire timestamp; Python datetime only # keeps microseconds. Alertmanager template and webhook use the same format. if not isinstance(starts_at, str) or len(starts_at) > 40: raise ValueError('invalid start time') instant(starts_at) return str(uuid.uuid5(uuid.NAMESPACE_URL, SOURCE + ':' + fingerprint + ':' + starts_at)) @dataclass(frozen=True) class Actor: """Only constructed by the package's verified server-side session adapter.""" issuer: str subject: str tenant: str roles: tuple expires_at: float csrf: str principal_type: str = 'human' assurance: dict = field(default_factory=dict) groups: tuple = () tenant_source: str = '' class Store: def __init__(self, path): path = Path(path) parent = path.parent.lstat() if (not stat.S_ISDIR(parent.st_mode) or parent.st_uid != os.getuid() or parent.st_mode & 0o077): raise ValueError('private owned state directory required') fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600) try: info = os.fstat(fd) if (not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_nlink != 1 or info.st_mode & 0o077): raise ValueError('private owned database required') finally: os.close(fd) self.path = str(path) with self.connect() as db: db.executescript(''' CREATE TABLE IF NOT EXISTS alerts ( id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, starts_at TEXT NOT NULL, alertname TEXT NOT NULL, labels_digest TEXT NOT NULL, received_at TEXT NOT NULL); CREATE TABLE IF NOT EXISTS acknowledgments ( alert_id TEXT PRIMARY KEY REFERENCES alerts(id), event_id TEXT UNIQUE NOT NULL, issuer TEXT NOT NULL, subject TEXT NOT NULL, occurred_at TEXT NOT NULL, decision_id TEXT NOT NULL); CREATE TABLE IF NOT EXISTS outbox ( event_id TEXT PRIMARY KEY, body TEXT NOT NULL, reference TEXT, status TEXT NOT NULL DEFAULT 'pending'); CREATE TRIGGER IF NOT EXISTS ack_immutable_update BEFORE UPDATE ON acknowledgments BEGIN SELECT RAISE(ABORT, 'immutable acknowledgment'); END; CREATE TRIGGER IF NOT EXISTS ack_immutable_delete BEFORE DELETE ON acknowledgments BEGIN SELECT RAISE(ABORT, 'immutable acknowledgment'); END; CREATE TRIGGER IF NOT EXISTS outbox_body_immutable BEFORE UPDATE OF body, event_id ON outbox BEGIN SELECT RAISE(ABORT, 'immutable audit event'); END; CREATE TRIGGER IF NOT EXISTS outbox_immutable_delete BEFORE DELETE ON outbox BEGIN SELECT RAISE(ABORT, 'immutable audit event'); END; CREATE TRIGGER IF NOT EXISTS alert_immutable_update BEFORE UPDATE ON alerts BEGIN SELECT RAISE(ABORT, 'immutable alert'); END; CREATE TRIGGER IF NOT EXISTS alert_immutable_delete BEFORE DELETE ON alerts BEGIN SELECT RAISE(ABORT, 'immutable alert'); END; ''') @contextmanager def connect(self): db = sqlite3.connect(self.path, timeout=10) db.row_factory = sqlite3.Row db.execute('PRAGMA foreign_keys=ON') db.execute('PRAGMA synchronous=FULL') try: with db: yield db finally: db.close() def receive(self, payload, now): if (payload.get('version') != '4' or payload.get('receiver') != 'railiance-admin-email' or payload.get('truncatedAlerts', 0) != 0 or not isinstance(payload.get('alerts'), list) or not 1 <= len(payload['alerts']) <= 100): raise ValueError('invalid webhook') rows = [] for alert in payload['alerts']: labels = alert['labels'] if (alert['status'] not in ('firing', 'resolved') or not isinstance(labels, dict) or not 1 <= len(labels) <= 32 or not all(isinstance(k, str) and isinstance(v, str) and len(k) <= 100 and len(v) <= 256 for k, v in labels.items()) or not re.fullmatch('[A-Za-z_:][A-Za-z0-9_:]{0,99}', labels.get('alertname', '')) or labels.get('owner') != SOURCE): raise ValueError('invalid alert scope') identity = occurrence(alert['fingerprint'], alert['startsAt']) if instant(alert['startsAt']).timestamp() > now: raise ValueError('future alert') digest = hashlib.sha256(encoded(labels).encode()).hexdigest() rows.append((identity, alert['fingerprint'], alert['startsAt'], labels['alertname'], digest, datetime.fromtimestamp(now, timezone.utc).isoformat())) with self.connect() as db: db.execute('BEGIN IMMEDIATE') for row in rows: old = db.execute('SELECT labels_digest FROM alerts WHERE id=?', (row[0],)).fetchone() if old and old['labels_digest'] != row[4]: raise ValueError('alert identity collision') db.execute('INSERT OR IGNORE INTO alerts VALUES (?,?,?,?,?,?)', row) return [r[0] for r in rows] def get(self, identity): with self.connect() as db: row = db.execute('''SELECT a.*, k.subject, k.occurred_at, o.reference, o.status AS audit_status FROM alerts a LEFT JOIN acknowledgments k ON a.id=k.alert_id LEFT JOIN outbox o ON k.event_id=o.event_id WHERE a.id=?''', (identity,)).fetchone() return dict(row) if row else None def acknowledge(self, identity, actor, decision_id, now): event_id = str(uuid.uuid4()) at = datetime.fromtimestamp(now, timezone.utc).isoformat() with self.connect() as db: db.execute('BEGIN IMMEDIATE') alert = db.execute('SELECT * FROM alerts WHERE id=?', (identity,)).fetchone() if alert is None: raise ValueError('unknown alert') old = db.execute('SELECT event_id FROM acknowledgments WHERE alert_id=?', (identity,)).fetchone() if old: return old['event_id'] event = dict(id=event_id, type='telemetry.alert.acknowledged', source=SOURCE, subject='alert:' + identity, tenant=TENANT, correlation_id=identity, occurred_at=at, data=dict(actor_issuer=actor.issuer, actor_subject=actor.subject, role=ROLE, decision_id=decision_id, fingerprint=alert['fingerprint'], starts_at=alert['starts_at'], alertname=alert['alertname'], labels_sha256=alert['labels_digest'], meaning='receipt acknowledged; not resolved or silenced')) db.execute('INSERT INTO acknowledgments VALUES (?,?,?,?,?,?)', (identity, event_id, actor.issuer, actor.subject, at, decision_id)) db.execute('INSERT INTO outbox(event_id,body) VALUES (?,?)', (event_id, encoded(event))) return event_id def drain(self, send): """send(event) returns (HTTP status, decoded body); transport owns custody. Concurrent drains may replay identical events. The receiver's idempotency contract makes that safe. No success without the exact receiver reference. """ with self.connect() as db: rows = db.execute("SELECT event_id,body FROM outbox WHERE status='pending' LIMIT 20").fetchall() for row in rows: try: status, body = send(json.loads(row['body'])) except (OSError, TimeoutError): continue if not isinstance(body, dict): continue accepted = (status, body.get('status')) in ((202, 'accepted'), (200, 'duplicate')) reference = 'audit:' + row['event_id'] with self.connect() as db: if accepted and body.get('reference') == reference: db.execute("UPDATE outbox SET status='delivered',reference=? WHERE event_id=?", (reference, row['event_id'])) elif status in (400, 401, 403, 409, 422): db.execute("UPDATE outbox SET status='blocked' WHERE event_id=? AND status='pending'", (row['event_id'],)) def requeue(self, event_id): """Explicit maintenance after fixing a receiver refusal; preserve bytes.""" with self.connect() as db: return db.execute("UPDATE outbox SET status='pending' WHERE event_id=? AND status='blocked'", (event_id,)).rowcount == 1 def audit_debt(self): with self.connect() as db: return {row['status']: row['n'] for row in db.execute( "SELECT status,COUNT(*) AS n FROM outbox WHERE status!='delivered' GROUP BY status")} class Application: """WSGI adapter. authenticate and authorize are required, never default-allow. authenticate(environ) returns a verified Actor or None. authorize(actor, action, resource) returns a fresh, binding-checked PDP decision receipt with effect/id/expires_at; the package adapter must validate the native envelope. """ def __init__(self, store, origin, webhook_token, authenticate, authorize, clock=time.time): from urllib.parse import urlsplit parsed = urlsplit(origin) if (parsed.scheme != 'https' or not parsed.hostname or parsed.path or parsed.query or parsed.fragment or parsed.username or parsed.password): raise ValueError('fixed HTTPS origin required') if not isinstance(webhook_token, str) or len(webhook_token) < 32: raise ValueError('dedicated webhook credential required') if not callable(authenticate) or not callable(authorize): raise ValueError('identity and policy adapters required') self.store, self.origin, self.webhook_token = store, origin, webhook_token self.authenticate, self.authorize, self.clock = authenticate, authorize, clock def __call__(self, env, start): def respond(code, body): headers = [('Content-Type', 'text/html; charset=utf-8'), ('Cache-Control', 'no-store'), ('Content-Security-Policy', "default-src 'none'; form-action 'self'; frame-ancestors 'none'"), ('X-Content-Type-Options', 'nosniff'), ('Referrer-Policy', 'same-origin')] start(code, headers) return [body.encode()] try: method, path = env.get('REQUEST_METHOD'), env.get('PATH_INFO') if path == '/webhook' and method == 'POST': if not hmac.compare_digest(env.get('HTTP_AUTHORIZATION', ''), 'Bearer ' + self.webhook_token): return respond('401 Unauthorized', 'Authentication required.') raw = self.body(env) self.store.receive(strict_json(raw), self.clock()) return respond('200 OK', 'Recorded.') if path != '/ack/alerts' or method not in ('GET', 'POST'): return respond('404 Not Found', 'Not found.') actor = self.authenticate(env) now = self.clock() if (not isinstance(actor, Actor) or actor.expires_at <= now or not actor.issuer or not actor.subject or len(actor.csrf) < 32): return respond('401 Unauthorized', 'Sign in through the admitted identity provider.') if actor.tenant != TENANT or actor.principal_type != 'human' or ROLE not in actor.roles: return respond('403 Forbidden', 'Railiance admin role required.') params = parse_qs(env.get('QUERY_STRING', ''), strict_parsing=True, max_num_fields=2) if set(params) != {'fingerprint', 'starts_at'} or any(len(v) != 1 for v in params.values()): raise ValueError('invalid link') identity = occurrence(params['fingerprint'][0], params['starts_at'][0]) decision = self.authorize(actor, 'acknowledge' if method == 'POST' else 'read', 'alert:' + identity) if (decision.get('effect') != 'allow' or not decision.get('id') or decision.get('expires_at', 0) <= self.clock()): return respond('403 Forbidden', 'Permission unavailable or refused.') alert = self.store.get(identity) if alert is None: return respond('404 Not Found', 'Alert not received yet. Retry shortly.') if method == 'POST': if env.get('HTTP_ORIGIN') != self.origin: return respond('403 Forbidden', 'Invalid origin.') form = parse_qs(self.body(env).decode(), strict_parsing=True, max_num_fields=1) if (set(form) != {'csrf'} or len(form['csrf']) != 1 or not hmac.compare_digest(form['csrf'][0], actor.csrf)): return respond('403 Forbidden', 'Invalid confirmation.') if actor.expires_at <= self.clock() or decision['expires_at'] <= self.clock(): return respond('403 Forbidden', 'Session or permission expired.') self.store.acknowledge(identity, actor, decision['id'], self.clock()) alert = self.store.get(identity) title = html.escape(alert['alertname']) if alert['occurred_at']: status = 'Audit record archived.' if alert['audit_status'] == 'delivered' else 'Audit delivery pending.' return respond('200 OK', f'
{title}
{status}
This does not resolve or silence the alert.
') query = html.escape(urlencode({k: v[0] for k, v in params.items()}), quote=True) csrf = html.escape(actor.csrf, quote=True) return respond('200 OK', f'Started {html.escape(alert["starts_at"])}
' 'Confirm that you received this alert. This does not resolve or silence it.
' f'') except (ValueError, KeyError, TypeError, AttributeError, UnicodeError): return respond('400 Bad Request', 'Invalid request.') except (OSError, sqlite3.Error): return respond('503 Service Unavailable', 'Service unavailable. Retry later.') @staticmethod def body(env): length = int(env.get('CONTENT_LENGTH', '0')) if not 0 < length <= 32768: raise ValueError('body size') raw = env['wsgi.input'].read(length) if len(raw) != length: raise ValueError('incomplete body') return raw