"""Closed, server-derived tenant scope for the conditional WP10 foundation.""" from __future__ import annotations import re from collections.abc import Mapping from dataclasses import dataclass from enum import StrEnum class TenantContextError(ValueError): """A tenant boundary was absent, unapproved, or contradicted by input.""" class DeliveryMode(StrEnum): PRIVATE_SINGLE_TENANT = "private_single_tenant" SHARED_CONTROL_PLANE = "shared_control_plane" SAAS = "saas" _TENANT_ID = re.compile(r"^[a-z][a-z0-9-]{0,62}$") _HOST = re.compile(r"^[a-z0-9][a-z0-9.-]{0,252}$") _EVENT_TYPE = re.compile(r"^[a-z][a-z0-9_.-]{0,80}$") _DIGEST = re.compile(r"^[0-9a-f]{64}$") def _tenant_id(value: str) -> str: if not isinstance(value, str) or not _TENANT_ID.fullmatch(value): raise TenantContextError("tenant_scope_denied") return value @dataclass(frozen=True) class TenantScope: tenant_id: str mode: DeliveryMode version: int = 1 def __post_init__(self) -> None: _tenant_id(self.tenant_id) if self.version != 1: raise TenantContextError("tenant_context_version_denied") def require_background_scope(self, declared_tenant_id: str | None) -> str: if declared_tenant_id is None: raise TenantContextError("tenant_scope_required") if _tenant_id(declared_tenant_id) != self.tenant_id: raise TenantContextError("tenant_scope_denied") return self.tenant_id def require_admin_scope(self, declared_tenant_id: str | None) -> str: return self.require_background_scope(declared_tenant_id) TenantContext = TenantScope def validate_tenant_event_envelope(scope: TenantScope, envelope: Mapping[str, object]) -> dict[str, object]: """Reject task/event dispatch unless its scope is explicit and server-matched.""" required = {"schema_version", "tenant_id", "event_type", "payload_digest"} if set(envelope) - required: raise TenantContextError("tenant_event_closed") tenant_id = envelope.get("tenant_id") if tenant_id is None: raise TenantContextError("tenant_scope_required") if set(envelope) != required: raise TenantContextError("tenant_event_closed") if not isinstance(tenant_id, str) or _tenant_id(tenant_id) != scope.tenant_id: raise TenantContextError("tenant_scope_denied") if envelope.get("schema_version") != 1 or not isinstance(envelope.get("event_type"), str) or not _EVENT_TYPE.fullmatch(envelope["event_type"]): raise TenantContextError("tenant_event_closed") if not isinstance(envelope.get("payload_digest"), str) or not _DIGEST.fullmatch(envelope["payload_digest"]): raise TenantContextError("tenant_event_closed") return dict(envelope) class TenantContextResolver: """Resolve scope only from server-held delivery configuration and host mapping.""" def __init__( self, *, mode: DeliveryMode, approved_default_tenant: str | None, route_tenants: Mapping[str, str], activated_modes: frozenset[DeliveryMode] | None = None, ) -> None: self.mode = DeliveryMode(mode) self.approved_default_tenant = ( _tenant_id(approved_default_tenant) if approved_default_tenant is not None else None ) self.route_tenants = { self._host(host): _tenant_id(tenant_id) for host, tenant_id in route_tenants.items() } self.activated_modes = activated_modes or frozenset( {DeliveryMode.PRIVATE_SINGLE_TENANT} ) @staticmethod def _host(host: str) -> str: if not isinstance(host, str) or not _HOST.fullmatch(host.lower()): raise TenantContextError("tenant_route_denied") return host.lower() def resolve_request( self, *, host: str, claimed_tenant_id: str | None, principal_id: str, ) -> TenantContext: if not isinstance(principal_id, str) or not principal_id: raise TenantContextError("tenant_principal_required") if self.mode not in self.activated_modes: raise TenantContextError("delivery_mode_not_approved") self._host(host) if self.mode is DeliveryMode.PRIVATE_SINGLE_TENANT: if self.approved_default_tenant is None: raise TenantContextError("tenant_default_not_approved") # Client claim is intentionally ignored: it never selects a tenant. return TenantContext(self.approved_default_tenant, self.mode) resolved = self.route_tenants.get(self._host(host)) if resolved is None: raise TenantContextError("tenant_route_denied") if claimed_tenant_id is not None and _tenant_id(claimed_tenant_id) != resolved: raise TenantContextError("tenant_spoof_denied") return TenantContext(resolved, self.mode)