"""Bounded, owner-private recovery journal; never an authorization source. An unresolved write intent is retained indefinitely and blocks further writes. Only the directory owner may reconcile it using independently obtained evidence. """ from __future__ import annotations import json from contextlib import contextmanager import os from pathlib import Path import sqlite3 import stat from typing import Any, Protocol from urllib.parse import quote class GroupJournalStore(Protocol): """Owner-injected persistence only; grants no caller or native authority.""" def check(self) -> None: ... def reserve(self, key: str, binding: str) -> tuple[dict[str, Any], bool]: ... def get(self, key: str, binding: str) -> dict[str, Any]: ... def claim_revoke(self, key: str, binding: str, delete_if_empty: bool) -> dict[str, Any]: ... def save(self, receipt: dict[str, Any]) -> None: ... class GroupProvenanceUnavailable(RuntimeError): def __init__(self): super().__init__("group_provenance_unavailable") class GroupProvenance: def __init__(self, path: str, *, capacity: int = 256, initialize: bool = False) -> None: target = Path(path) if not target.is_absolute() or target.parent.resolve() != target.parent or capacity < 1 or capacity > 4096: raise ValueError("private absolute provenance path and bounded capacity required") parent = target.parent.lstat() if not stat.S_ISDIR(parent.st_mode) or parent.st_uid != os.geteuid() or parent.st_mode & 0o077: raise ValueError("provenance directory must be owner-private") # Service startup never creates a replacement for missing durable state. flags = os.O_RDWR | os.O_NOFOLLOW if initialize: flags |= os.O_CREAT | os.O_EXCL fd = os.open(target, flags, 0o600) try: info = os.fstat(fd) if not stat.S_ISREG(info.st_mode) or info.st_uid != os.geteuid() or info.st_mode & 0o077 or info.st_nlink != 1: raise ValueError("provenance file must be owner-private and regular") finally: os.close(fd) self.path = str(target) self._device, self._inode = info.st_dev, info.st_ino self.capacity = capacity with self._db() as db: if initialize: db.execute("CREATE TABLE group_receipts (key TEXT PRIMARY KEY, binding TEXT NOT NULL, state TEXT NOT NULL, active INTEGER UNIQUE, body TEXT NOT NULL)") db.execute("PRAGMA user_version=1") if db.execute("PRAGMA user_version").fetchone()[0] != 1: raise RuntimeError("unrecognized group provenance schema") db.execute("SELECT key, binding, state, active, body FROM group_receipts LIMIT 0") def _check_file(self): try: target = Path(self.path) parent = target.parent.lstat() info = target.lstat() if target.parent.resolve() != target.parent or not stat.S_ISDIR(parent.st_mode) or parent.st_uid != os.geteuid() or parent.st_mode & 0o077: raise GroupProvenanceUnavailable() if not stat.S_ISREG(info.st_mode) or info.st_uid != os.geteuid() or info.st_mode & 0o077 or info.st_nlink != 1: raise GroupProvenanceUnavailable() if (info.st_dev, info.st_ino) != (self._device, self._inode): raise GroupProvenanceUnavailable() except OSError: raise GroupProvenanceUnavailable() from None def check(self): with self._db() as db: if db.execute("PRAGMA user_version").fetchone()[0] != 1: raise GroupProvenanceUnavailable() db.execute("SELECT key FROM group_receipts LIMIT 0") @staticmethod def _load(body, key, binding): try: if not isinstance(body, str) or len(body) > 1048576: raise GroupProvenanceUnavailable() value = json.loads(body) if not isinstance(value, dict) or value.get("key") != key or value.get("binding") != binding: raise GroupProvenanceUnavailable() if value.get("state") not in {"pending", "ensured", "revoking", "revoked", "uncertain", "refused"}: raise GroupProvenanceUnavailable() if not isinstance(value.get("groups"), dict) or len(value["groups"]) > 16: raise GroupProvenanceUnavailable() for field, limit in (("created_groups", 16), ("created_memberships", 256)): if not isinstance(value.get(field), list) or len(value[field]) > limit or any(not isinstance(item, dict) for item in value[field]): raise GroupProvenanceUnavailable() if "intent" not in value or value["intent"] is not None and not isinstance(value["intent"], dict): raise GroupProvenanceUnavailable() for group in value["groups"].values(): if not isinstance(group, dict) or "id" not in group or not all(isinstance(group.get(f), list) and all(isinstance(u, str) for u in group[f]) for f in ("before", "after")): raise GroupProvenanceUnavailable() if value["state"] in {"ensured", "revoking", "revoked"} and ( not isinstance(value.get("requested_groups"), list) or not isinstance(value.get("ensure_correlation_id"), str) ): raise GroupProvenanceUnavailable() return value except (TypeError, ValueError): raise GroupProvenanceUnavailable() from None @contextmanager def _db(self): db = None try: self._check_file() db = sqlite3.connect("file:" + quote(self.path, safe="/") + "?mode=rw", uri=True, timeout=5) self._check_file() db.execute("PRAGMA synchronous=FULL") with db: yield db except sqlite3.Error: raise GroupProvenanceUnavailable() from None finally: if db is not None: db.close() def reserve(self, key: str, binding: str) -> tuple[dict[str, Any], bool]: with self._db() as db: db.execute("BEGIN IMMEDIATE") row = db.execute("SELECT binding, body FROM group_receipts WHERE key=?", (key,)).fetchone() if row: if row[0] != binding: raise ValueError("ensure idempotency key is bound to another request") return self._load(row[1], key, binding), False if db.execute("SELECT COUNT(*) FROM group_receipts").fetchone()[0] >= self.capacity: raise RuntimeError("provenance capacity reached; retained records require owner disposition") receipt = {"key": key, "binding": binding, "state": "pending", "intent": None, "groups": {}, "created_groups": [], "created_memberships": []} try: db.execute("INSERT INTO group_receipts VALUES (?, ?, ?, 1, ?)", (key, binding, "pending", json.dumps(receipt, sort_keys=True))) except sqlite3.IntegrityError: raise RuntimeError("another group operation is unresolved") from None return receipt, True def get(self, key: str, binding: str) -> dict[str, Any]: with self._db() as db: row = db.execute("SELECT binding, body FROM group_receipts WHERE key=?", (key,)).fetchone() if not row or row[0] != binding: raise ValueError("unknown ensure receipt or request binding mismatch") return self._load(row[1], key, binding) def claim_revoke(self, key: str, binding: str, delete_if_empty: bool) -> dict[str, Any]: with self._db() as db: db.execute("BEGIN IMMEDIATE") row = db.execute("SELECT binding, body FROM group_receipts WHERE key=?", (key,)).fetchone() if not row or row[0] != binding: raise ValueError("unknown ensure receipt or request binding mismatch") receipt = self._load(row[1], key, binding) if receipt["state"] == "revoked": if receipt["delete_if_empty"] != delete_if_empty: raise ValueError("completed rollback options cannot change") return receipt if receipt["state"] != "ensured": raise RuntimeError("group provenance unresolved; independent owner recovery required") receipt.update(state="revoking", delete_if_empty=delete_if_empty) try: db.execute("UPDATE group_receipts SET state='revoking', active=1, body=? WHERE key=?", (json.dumps(receipt, sort_keys=True), key)) except sqlite3.IntegrityError: raise RuntimeError("another group operation is unresolved") from None return receipt def save(self, receipt: dict[str, Any]) -> None: active = 1 if receipt["state"] in {"pending", "revoking", "uncertain"} else None with self._db() as db: written = db.execute("UPDATE group_receipts SET state=?, active=?, body=? WHERE key=? AND binding=?", (receipt["state"], active, json.dumps(receipt, sort_keys=True), receipt["key"], receipt["binding"])) if written.rowcount != 1: raise GroupProvenanceUnavailable()