Prepare glob-scoped callers and prove offsite ledger recovery
Some checks failed
Transactional mail acceptance / mail (push) Failing after 32s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 57s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a11fd9-caf6-7a63-b578-9daaaa134ff2
This commit is contained in:
tegwick 2026-10-09 11:43:11 +02:00
parent 6fb206b359
commit 93b6ba2aae
21 changed files with 1080 additions and 19 deletions

View file

@ -15,6 +15,7 @@ import re
import secrets
import time
from dataclasses import dataclass, field
from fnmatch import fnmatchcase
from http import HTTPStatus
from pathlib import Path
@ -63,20 +64,26 @@ class CallerPolicy:
burst: int
max_payload_bytes: int
adopt_legacy_verifications: bool = False
adopt_legacy_invitations: bool = False
legacy_invitation_binding: tuple[str, str] | None = None
binding_patterns: frozenset[tuple[str, str]] = frozenset()
def __post_init__(self):
if not isinstance(self.caller_id, str) or not IDENTIFIER.fullmatch(self.caller_id):
if not isinstance(self.caller_id, str) or not IDENTIFIER.fullmatch(
self.caller_id
):
raise ValueError("invalid_caller_policy")
if not isinstance(self.source, str) or not IDENTIFIER.fullmatch(self.source):
raise ValueError("invalid_caller_policy")
if not isinstance(self.token, str) or not re.fullmatch(r"[!-~]{16,512}", self.token):
if not isinstance(self.token, str) or not re.fullmatch(
r"[!-~]{16,512}", self.token
):
raise ValueError("invalid_caller_credential")
if (
not self.operations
or not self.operations <= set(OPERATIONS.values())
or not self.templates <= TEMPLATES
or not self.bindings
or not (self.bindings or self.binding_patterns)
):
raise ValueError("invalid_caller_policy")
for binding in self.bindings:
@ -84,6 +91,15 @@ class CallerPolicy:
not isinstance(v, str) or not IDENTIFIER.fullmatch(v) for v in binding
):
raise ValueError("invalid_caller_binding")
if len(self.bindings) + len(self.binding_patterns) > 128:
raise ValueError("too_many_caller_bindings")
for binding in self.binding_patterns:
if len(binding) != 2 or any(
not isinstance(v, str)
or not re.fullmatch(r"[A-Za-z0-9*?][A-Za-z0-9._:*?-]{0,127}", v)
for v in binding
):
raise ValueError("invalid_caller_binding_pattern")
for value, maximum in (
(self.rate_per_minute, 6000),
(self.burst, 600),
@ -91,17 +107,40 @@ class CallerPolicy:
):
if type(value) is not int or not 0 < value <= maximum:
raise ValueError("invalid_caller_limits")
if type(self.adopt_legacy_verifications) is not bool:
if any(
type(value) is not bool
for value in (
self.adopt_legacy_verifications,
self.adopt_legacy_invitations,
)
):
raise ValueError("invalid_legacy_admission")
if self.adopt_legacy_verifications or self.legacy_invitation_binding is not None:
if self.caller_id != "user-engine" or self.source != "user-engine":
raise ValueError("legacy_admission_requires_original_caller")
if (
self.adopt_legacy_verifications
or self.adopt_legacy_invitations
or self.legacy_invitation_binding is not None
) and (self.caller_id != "user-engine" or self.source != "user-engine"):
raise ValueError("legacy_admission_requires_original_caller")
if (
self.legacy_invitation_binding is not None
and self.legacy_invitation_binding not in self.bindings
):
raise ValueError("invalid_legacy_binding")
def __contains__(self, binding):
if (
not isinstance(binding, tuple)
or len(binding) != 2
or any(
not isinstance(v, str) or not IDENTIFIER.fullmatch(v) for v in binding
)
):
return False
return binding in self.bindings or any(
fnmatchcase(binding[0], tenant) and fnmatchcase(binding[1], client)
for tenant, client in self.binding_patterns
)
def load_callers(path):
"""Owner-controlled non-secret policy plus independently delivered token files."""
@ -127,6 +166,9 @@ def load_callers(path):
token = raw_token.decode("ascii").strip()
values["token"] = token
values["bindings"] = frozenset(tuple(v) for v in values["bindings"])
values["binding_patterns"] = frozenset(
tuple(v) for v in values.get("binding_patterns", [])
)
for key in ("operations", "templates"):
values[key] = frozenset(values[key])
if values.get("legacy_invitation_binding") is not None:
@ -161,7 +203,7 @@ class ScopedTransactionalApplication:
self.internal_token,
portal_url,
verification_namespace="" if p.adopt_legacy_verifications else p.caller_id,
verification_bindings=p.bindings,
verification_bindings=p,
)
for p in policies
}
@ -187,7 +229,7 @@ class ScopedTransactionalApplication:
or event_id.startswith("ec2_")
):
raise ValueError("invalid_event_id")
if policy.legacy_invitation_binding is not None:
if policy.adopt_legacy_invitations or policy.legacy_invitation_binding is not None:
return event_id
return (
"ec2_" + hashlib.sha256(json.dumps([policy.caller_id, event_id]).encode()).hexdigest()
@ -319,7 +361,7 @@ class ScopedTransactionalApplication:
binding = (payload.get("tenant"), payload.get("client_id"))
if binding == (None, None) and policy.legacy_invitation_binding is not None:
binding = policy.legacy_invitation_binding
if not all(isinstance(v, str) for v in binding) or binding not in policy.bindings:
if binding not in policy:
raise ValueError("caller_scope_denied")
data = payload.get("data")
if (
@ -332,9 +374,25 @@ class ScopedTransactionalApplication:
if not isinstance(recipient, str) or len(recipient) > 254:
raise ValueError("invalid_recipient")
payload = dict(payload, id=event_key, source="user-engine")
if policy.adopt_legacy_invitations:
# The original caller adds client_id at the transport boundary.
# Match an existing pre-transition fingerprint before removing
# that field; all other immutable fields still have to match.
legacy = dict(payload)
legacy.pop("client_id", None)
expected = hashlib.sha256(
json.dumps(legacy, sort_keys=True, separators=(",", ":")).encode()
).hexdigest()
with self.store.lock:
row = self.store.db.execute(
"SELECT fingerprint FROM send_attempts WHERE event_id=?",
(event_key,),
).fetchone()
if row and hmac.compare_digest(row[0], expected):
payload = legacy
if path == "/v1/registration-verifications":
binding = (payload.get("tenant"), payload.get("client_id"))
if not all(isinstance(v, str) for v in binding) or binding not in policy.bindings:
if binding not in policy:
raise ValueError("caller_scope_denied")
raw = json.dumps(payload).encode()
internal = dict(environ, CONTENT_LENGTH=str(len(raw)))