fester/backend/policy/api.py

50 lines
1.3 KiB
Python
Executable File

"""Async wrapper around the singleton PolicyEngine.
F-15 remediation: the global ``POLICY`` reference is now guarded by a
lock so concurrent FastAPI handlers can't race on init.
"""
from __future__ import annotations
import logging
import threading
from typing import Any, Optional
from backend.events.emitter import emit_policy
log = logging.getLogger(__name__)
_POLICY: Optional[Any] = None
_policy_lock = threading.Lock()
def init(policy_engine: Any) -> None:
"""Register the singleton policy engine."""
global _POLICY
with _policy_lock:
_POLICY = policy_engine
def _get() -> Any:
"""Return the singleton, raising a clear error if init() was never called."""
with _policy_lock:
if _POLICY is None:
raise RuntimeError("policy engine not initialised — call policy.api.init() first")
return _POLICY
async def set_override(data: dict) -> dict:
"""Set ``data['key'] = data['value']`` on the singleton policy engine."""
eng = _get()
eng.set_override(data["key"], data["value"])
await emit_policy(data["key"], data["value"])
return {"status": "ok"}
async def clear_override(data: dict) -> dict:
"""Clear ``data['key']`` from the singleton policy engine."""
eng = _get()
eng.clear_override(data["key"])
await emit_policy(data["key"], None)
return {"status": "cleared"}