Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
"""Private SQLite storage for negotiated hosted-room links.
|
||||
|
||||
Route metadata and its scoped grant share the gateway's private root
|
||||
``state.db``. SQLite WAL plus ``BEGIN IMMEDIATE`` owns concurrency; grants are
|
||||
never included in reprs, status payloads, or exception messages.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any, Mapping
|
||||
|
||||
from gateway import hosted_rooms
|
||||
from gateway.hosted_room_peer import (
|
||||
GatewayRoomCatalog,
|
||||
HostedRoomPeerError,
|
||||
TransportSecurity,
|
||||
validate_room_link_url,
|
||||
)
|
||||
|
||||
|
||||
MAX_LINKS = 512
|
||||
MAX_GRANT_CHARS = 16 * 1024
|
||||
_LEGACY_FIELDS = {
|
||||
"room_id",
|
||||
"member_id",
|
||||
"target_url",
|
||||
"target_profile",
|
||||
"grant",
|
||||
"catalog",
|
||||
"cancellation_scope_id",
|
||||
"trace_id",
|
||||
"updated_at",
|
||||
}
|
||||
_STATUSES = {"ready", "unavailable", "needs_reauthorization"}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StoredRoomLink:
|
||||
room_id: str
|
||||
member_id: str
|
||||
target_url: str
|
||||
target_profile: str
|
||||
grant: str = field(repr=False)
|
||||
catalog: GatewayRoomCatalog
|
||||
cancellation_scope_id: str
|
||||
trace_id: str
|
||||
transport_security: TransportSecurity
|
||||
status: str
|
||||
updated_at: float
|
||||
|
||||
@classmethod
|
||||
def from_mapping(cls, value: Mapping[str, Any]) -> "StoredRoomLink":
|
||||
allowed = _LEGACY_FIELDS | {"transport_security", "status"}
|
||||
if set(value) - allowed or not _LEGACY_FIELDS.issubset(value):
|
||||
raise HostedRoomPeerError("stored room link fields are invalid")
|
||||
room_id = _short_string(value["room_id"], "room_id")
|
||||
member_id = _short_string(value["member_id"], "member_id")
|
||||
target_profile = _short_string(value["target_profile"], "target_profile")
|
||||
target_url, detected_security = validate_room_link_url(value["target_url"])
|
||||
transport_security = str(value.get("transport_security") or detected_security)
|
||||
if transport_security != detected_security:
|
||||
raise HostedRoomPeerError("transport_security does not match target_url")
|
||||
grant = str(value["grant"] or "")
|
||||
if not grant or len(grant) > MAX_GRANT_CHARS:
|
||||
raise HostedRoomPeerError("room grant is missing or too large")
|
||||
status = str(value.get("status") or "ready")
|
||||
if status not in _STATUSES:
|
||||
raise HostedRoomPeerError("stored room link status is invalid")
|
||||
updated_at = float(value["updated_at"])
|
||||
if not updated_at > 0:
|
||||
raise HostedRoomPeerError("updated_at must be positive")
|
||||
return cls(
|
||||
room_id=room_id,
|
||||
member_id=member_id,
|
||||
target_url=target_url,
|
||||
target_profile=target_profile,
|
||||
grant=grant,
|
||||
catalog=GatewayRoomCatalog.from_mapping(value["catalog"]),
|
||||
cancellation_scope_id=_short_string(
|
||||
value["cancellation_scope_id"], "cancellation_scope_id"
|
||||
),
|
||||
trace_id=_short_string(value["trace_id"], "trace_id"),
|
||||
transport_security=transport_security, # type: ignore[arg-type]
|
||||
status=status,
|
||||
updated_at=updated_at,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_record(cls, value: Mapping[str, Any]) -> "StoredRoomLink":
|
||||
try:
|
||||
catalog = json.loads(str(value["catalog_json"]))
|
||||
except Exception as exc:
|
||||
raise HostedRoomPeerError("stored room link catalog is unreadable") from exc
|
||||
return cls.from_mapping({
|
||||
"room_id": value["room_id"],
|
||||
"member_id": value["member_id"],
|
||||
"target_url": value["target_url"],
|
||||
"target_profile": value["target_profile"],
|
||||
"grant": value["grant"],
|
||||
"catalog": catalog,
|
||||
"cancellation_scope_id": value["cancellation_scope_id"],
|
||||
"trace_id": value["trace_id"],
|
||||
"transport_security": value["transport_security"],
|
||||
"status": value["status"],
|
||||
"updated_at": value["updated_at"],
|
||||
})
|
||||
|
||||
def catalog_mapping(self) -> dict[str, Any]:
|
||||
value = {
|
||||
"installation_id": self.catalog.installation_id,
|
||||
"protocol_versions": list(self.catalog.protocol_versions),
|
||||
"link_modes": list(self.catalog.link_modes),
|
||||
"persistent_process": self.catalog.persistent_process,
|
||||
"text": self.catalog.text,
|
||||
"attachments": self.catalog.attachments,
|
||||
"execution_policy": self.catalog.execution_policy.as_mapping(),
|
||||
"catalog_digest": self.catalog.catalog_digest,
|
||||
}
|
||||
if (
|
||||
self.catalog.endpoint_url is not None
|
||||
or self.catalog.endpoint_reason is not None
|
||||
):
|
||||
value["endpoint"] = self.catalog.endpoint_mapping()
|
||||
return value
|
||||
|
||||
def as_record(self) -> dict[str, Any]:
|
||||
return {
|
||||
"room_id": self.room_id,
|
||||
"member_id": self.member_id,
|
||||
"target_url": self.target_url,
|
||||
"target_profile": self.target_profile,
|
||||
"grant": self.grant,
|
||||
"catalog_json": json.dumps(
|
||||
self.catalog_mapping(), sort_keys=True, separators=(",", ":")
|
||||
),
|
||||
"cancellation_scope_id": self.cancellation_scope_id,
|
||||
"trace_id": self.trace_id,
|
||||
"transport_security": self.transport_security,
|
||||
"status": self.status,
|
||||
"updated_at": self.updated_at,
|
||||
}
|
||||
|
||||
|
||||
def _short_string(value: Any, field: str) -> str:
|
||||
normalized = str(value or "").strip()
|
||||
if not normalized or len(normalized) > 256:
|
||||
raise HostedRoomPeerError(f"{field} is invalid")
|
||||
return normalized
|
||||
|
||||
|
||||
def load_room_links(db_path: Path | str) -> tuple[StoredRoomLink, ...]:
|
||||
rows = hosted_rooms.list_room_link_records(db_path)
|
||||
if len(rows) > MAX_LINKS:
|
||||
raise HostedRoomPeerError("stored room link list is invalid")
|
||||
return tuple(StoredRoomLink.from_record(row) for row in rows)
|
||||
|
||||
|
||||
def load_room_links_tolerant(
|
||||
db_path: Path | str,
|
||||
) -> tuple[tuple[StoredRoomLink, ...], tuple[str, ...]]:
|
||||
"""Load healthy routes while quarantining malformed rows by identity."""
|
||||
rows = hosted_rooms.list_room_link_records(db_path)
|
||||
if len(rows) > MAX_LINKS:
|
||||
raise HostedRoomPeerError("stored room link list is invalid")
|
||||
links = []
|
||||
errors = []
|
||||
for row in rows:
|
||||
try:
|
||||
links.append(StoredRoomLink.from_record(row))
|
||||
except Exception:
|
||||
room = str(row.get("room_id") or "unknown")
|
||||
member = str(row.get("member_id") or "unknown")
|
||||
errors.append(f"{room}:{member}:invalid")
|
||||
return tuple(links), tuple(errors)
|
||||
|
||||
|
||||
def save_room_link(db_path: Path | str, link: StoredRoomLink) -> None:
|
||||
hosted_rooms.upsert_room_link_record(
|
||||
db_path, record=link.as_record(), max_links=MAX_LINKS
|
||||
)
|
||||
if os.name == "posix":
|
||||
try:
|
||||
Path(db_path).chmod(0o600)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def mark_room_link_status(
|
||||
db_path: Path | str,
|
||||
*,
|
||||
room_id: str,
|
||||
member_id: str,
|
||||
status: str,
|
||||
) -> bool:
|
||||
if status not in _STATUSES:
|
||||
raise HostedRoomPeerError("stored room link status is invalid")
|
||||
return hosted_rooms.update_room_link_status(
|
||||
db_path,
|
||||
room_id=_short_string(room_id, "room_id"),
|
||||
member_id=_short_string(member_id, "member_id"),
|
||||
status=status,
|
||||
)
|
||||
|
||||
|
||||
def make_stored_link(
|
||||
*,
|
||||
room_id: str,
|
||||
member_id: str,
|
||||
target_url: str,
|
||||
target_profile: str,
|
||||
grant: str,
|
||||
catalog: GatewayRoomCatalog,
|
||||
cancellation_scope_id: str,
|
||||
trace_id: str,
|
||||
) -> StoredRoomLink:
|
||||
target_url, transport_security = validate_room_link_url(target_url)
|
||||
return StoredRoomLink.from_mapping({
|
||||
"room_id": room_id,
|
||||
"member_id": member_id,
|
||||
"target_url": target_url,
|
||||
"target_profile": target_profile,
|
||||
"grant": grant,
|
||||
"catalog": {
|
||||
"installation_id": catalog.installation_id,
|
||||
"protocol_versions": list(catalog.protocol_versions),
|
||||
"link_modes": list(catalog.link_modes),
|
||||
"persistent_process": catalog.persistent_process,
|
||||
"text": catalog.text,
|
||||
"attachments": catalog.attachments,
|
||||
"execution_policy": catalog.execution_policy.as_mapping(),
|
||||
"catalog_digest": catalog.catalog_digest,
|
||||
**(
|
||||
{"endpoint": catalog.endpoint_mapping()}
|
||||
if catalog.endpoint_url is not None
|
||||
or catalog.endpoint_reason is not None
|
||||
else {}
|
||||
),
|
||||
},
|
||||
"cancellation_scope_id": cancellation_scope_id,
|
||||
"trace_id": trace_id,
|
||||
"transport_security": transport_security,
|
||||
"status": "ready",
|
||||
"updated_at": time.time(),
|
||||
})
|
||||
Reference in New Issue
Block a user