"""Common Configurations for the Gateway Server.""" import asyncio import os import threading from typing import Optional import fastapi from httpx import HTTPStatusError from common.guardrails import Guardrail, GuardrailAction, GuardrailRuleSet from common.authorization import extract_authorization_from_headers def extract_policy_from_headers(request: Optional[fastapi.Request]) -> Optional[str]: """ Extracts the guardrailing policy from the request headers if present. Returns 'None' if no such header is present. """ if request is None: return None policy = request.headers.get("Invariant-Guardrails") # undo unicode_escape if policy: # interpret as bytes then decode policy = policy.encode("utf-8").decode("unicode_escape") return policy class GatewayConfig: """Common configurations for the Gateway Server.""" def __init__(self): self.guardrails_from_file = self._load_guardrails_from_file() def _load_guardrails_from_file(self) -> str: """ Loads the guardrails from the file specified in GUARDRAILS_FILE_PATH. Returns the guardrails file content as a string. """ from integrations.guardrails import _preload guardrails_file = os.getenv("GUARDRAILS_FILE_PATH", "") if not guardrails_file: print("[warning: GUARDRAILS_FILE_PATH is not set. Using empty guardrails]") return "" invariant_api_key = os.getenv("INVARIANT_API_KEY", "") if not invariant_api_key: raise ValueError( "Error: INVARIANT_API_KEY is not set." "It is required to validate guardrails file content." ) try: with open(guardrails_file, "r", encoding="utf-8") as f: guardrails_file_content = f.read() asyncio.run( _preload(guardrails_file_content, "Bearer " + invariant_api_key) ) return guardrails_file_content except (FileNotFoundError, PermissionError, OSError) as e: raise ValueError( f"Unable to read guardrails file ({guardrails_file}): {e}" ) from e except HTTPStatusError as e: raise ValueError(f"Cannot load guardrails, {e}, {e.response.text}") from e def __repr__(self) -> str: return f"GatewayConfig(guardrails_from_file={repr(self.guardrails_from_file)})" class GatewayConfigManager: """Manager for Gateway Configuration.""" _config_instance = None _lock = threading.Lock() @classmethod def get_config(cls, request: fastapi.Request = None) -> GatewayConfig: """Initializes and returns the gateway configuration using double-checked locking.""" local_config = cls._config_instance if local_config is None: with cls._lock: local_config = cls._config_instance if local_config is None: local_config = GatewayConfig() cls._config_instance = local_config return local_config async def GuardrailsInHeader(request: fastapi.Request) -> Optional[GuardrailRuleSet]: """ Extracts Invariant-Guardrails from the request header if provided, and returns a corresponding GuardrailRuleSet. If no guardrails are provided, returns None. """ # if provided in header, use custom guardrailing policy if guardrails := extract_policy_from_headers(request): guardrails = [ Guardrail( id="guardrails-from-header", name="guardrails from request header", content=guardrails, action=GuardrailAction.BLOCK, ) ] return GuardrailRuleSet( blocking_guardrails=guardrails, logging_guardrails=[], )