50 lines
1.3 KiB
Python
Executable File
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"}
|