github-actions[bot]
Deploy from GitHub Actions: c10f027367a836ed5d107682de3b67c4ba00b590
7f29c67
Raw History Blame Contribute Delete
4.16 kB
from __future__ import annotations
import os
from dataclasses import dataclass
from typing import Any, Mapping
from urllib.parse import urlsplit
import httpx
from fastapi import Header, HTTPException
@dataclass(frozen=True, slots=True)
class RuntimeEvidencePrincipal:
"""Authenticated Panorama principal allowed to inspect runtime evidence."""
user_id: str
def _unauthorized() -> HTTPException:
return HTTPException(
status_code=401,
detail="Authentication required.",
headers={"WWW-Authenticate": "Bearer"},
)
def _service_unavailable(detail: str) -> HTTPException:
return HTTPException(
status_code=503,
detail=detail,
)
def _resolve_auth_service_url() -> str:
"""Resolve Panorama auth without requiring HF environment changes.
An explicitly configured AUTH_SERVICE_URL is authoritative.
On legacy/uncontrolled deployments where it is absent, derive the
Panorama public API origin from PANORAMA_GATEWAY_URL. For example:
https://api.panoramablock.com/database
-> https://api.panoramablock.com
"""
explicit = (os.getenv("AUTH_SERVICE_URL") or "").strip()
if explicit:
return explicit.rstrip("/")
gateway_url = (os.getenv("PANORAMA_GATEWAY_URL") or "").strip()
if not gateway_url:
raise _service_unavailable(
"Authentication service is not configured."
)
parsed = urlsplit(gateway_url)
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
raise _service_unavailable(
"Authentication service is not configured."
)
return f"{parsed.scheme}://{parsed.netloc}"
def _parse_bearer_token(authorization: str | None) -> str:
if not authorization:
raise _unauthorized()
scheme, separator, token = authorization.partition(" ")
if (
not separator
or scheme.lower() != "bearer"
or not token.strip()
):
raise _unauthorized()
return token.strip()
def _extract_principal(payload: Mapping[str, Any]) -> RuntimeEvidencePrincipal:
if payload.get("isValid") is not True:
raise HTTPException(
status_code=401,
detail="Invalid authentication token.",
headers={"WWW-Authenticate": "Bearer"},
)
auth_payload = payload.get("payload")
if not isinstance(auth_payload, Mapping):
raise _service_unavailable(
"Authentication service returned an invalid response."
)
address = auth_payload.get("address")
if not isinstance(address, str) or not address.strip():
raise _service_unavailable(
"Authentication service returned an invalid response."
)
return RuntimeEvidencePrincipal(
user_id=address.strip().lower(),
)
def require_runtime_evidence_principal(
authorization: str | None = Header(default=None),
) -> RuntimeEvidencePrincipal:
"""Authenticate access to the runtime-evidence migration endpoint."""
token = _parse_bearer_token(authorization)
auth_service_url = _resolve_auth_service_url()
try:
response = httpx.post(
f"{auth_service_url}/auth/validate",
json={"token": token},
timeout=10.0,
)
except httpx.RequestError as exc:
raise _service_unavailable(
"Authentication service is unavailable."
) from exc
if response.status_code == 401:
raise HTTPException(
status_code=401,
detail="Invalid authentication token.",
headers={"WWW-Authenticate": "Bearer"},
)
if response.status_code != 200:
raise _service_unavailable(
"Authentication service is unavailable."
)
try:
payload = response.json()
except Exception as exc:
raise _service_unavailable(
"Authentication service returned an invalid response."
) from exc
if not isinstance(payload, Mapping):
raise _service_unavailable(
"Authentication service returned an invalid response."
)
return _extract_principal(payload)