Add shared automation and WebUI editing primitives
This commit is contained in:
@@ -2,8 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
from functools import lru_cache
|
||||
from typing import Protocol, runtime_checkable
|
||||
from typing import Any, Mapping, Protocol, runtime_checkable
|
||||
|
||||
from cryptography.fernet import Fernet, InvalidToken
|
||||
|
||||
@@ -18,6 +19,10 @@ class SecretDecryptionError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class TransientPayloadError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
CAPABILITY_SECURITY_SECRET_PROVIDER = "security.secretProvider" # noqa: S105 # nosec B105 - capability identifier.
|
||||
|
||||
|
||||
@@ -72,3 +77,29 @@ def decrypt_secret(value: str | None) -> str | None:
|
||||
return _fernet().decrypt(value.encode("utf-8")).decode("utf-8")
|
||||
except InvalidToken as exc:
|
||||
raise SecretDecryptionError("Stored secret cannot be decrypted with the configured master key") from exc
|
||||
|
||||
|
||||
def seal_transient_payload(payload: Mapping[str, Any]) -> str:
|
||||
"""Seal a short-lived JSON object without persisting server-side state."""
|
||||
|
||||
encoded = json.dumps(
|
||||
dict(payload),
|
||||
separators=(",", ":"),
|
||||
sort_keys=True,
|
||||
).encode("utf-8")
|
||||
return _fernet().encrypt(encoded).decode("utf-8")
|
||||
|
||||
|
||||
def open_transient_payload(token: str, *, ttl_seconds: int) -> dict[str, Any]:
|
||||
"""Open a sealed JSON object and enforce its maximum age."""
|
||||
|
||||
if ttl_seconds <= 0:
|
||||
raise ValueError("Transient payload TTL must be positive")
|
||||
try:
|
||||
encoded = _fernet().decrypt(token.encode("utf-8"), ttl=ttl_seconds)
|
||||
payload = json.loads(encoded.decode("utf-8"))
|
||||
except (InvalidToken, UnicodeDecodeError, json.JSONDecodeError, TypeError, ValueError) as exc:
|
||||
raise TransientPayloadError("Transient payload is invalid or expired") from exc
|
||||
if not isinstance(payload, dict):
|
||||
raise TransientPayloadError("Transient payload must contain a JSON object")
|
||||
return payload
|
||||
|
||||
Reference in New Issue
Block a user