Prepare glob-scoped callers and prove offsite ledger recovery
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a11fd9-caf6-7a63-b578-9daaaa134ff2
This commit is contained in:
parent
6fb206b359
commit
93b6ba2aae
21 changed files with 1080 additions and 19 deletions
|
|
@ -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)))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue