"""Security helpers for the inference registry API. Ensures auth_secret_ref values are NEVER exposed in API responses. Replaces the actual secret reference with a status string. Requirements: 3.6 """ from __future__ import annotations from typing import Any from services.inference_registry.schemas import ( BindingResponse, DeploymentResponse, EndpointListResponse, EndpointResponse, ) def redact_endpoint(endpoint_dict: dict[str, Any]) -> EndpointResponse: """Convert a raw endpoint dict to an EndpointResponse with secrets redacted. The auth_secret_ref value is replaced with a status string: - "configured" if a secret reference exists - "not_configured" if no secret reference is set The actual secret ref value is NEVER included in the response. """ auth_secret_ref = endpoint_dict.get("auth_secret_ref") auth_secret_status = "configured" if auth_secret_ref else "not_configured" return EndpointResponse( id=endpoint_dict["id"], name=endpoint_dict["name"], protocol=endpoint_dict["protocol"], base_url=endpoint_dict["base_url"], auth_secret_status=auth_secret_status, auth_scheme=endpoint_dict.get("auth_scheme", "bearer"), default_headers=endpoint_dict.get("default_headers") or {}, health_path=endpoint_dict.get("health_path"), enabled=endpoint_dict.get("enabled", True), revision=endpoint_dict.get("revision", 1), created_at=endpoint_dict.get("created_at"), updated_at=endpoint_dict.get("updated_at"), ) def redact_endpoint_for_list(endpoint_dict: dict[str, Any]) -> EndpointListResponse: """Convert a raw endpoint dict to a list response with secrets redacted.""" auth_secret_ref = endpoint_dict.get("auth_secret_ref") auth_secret_status = "configured" if auth_secret_ref else "not_configured" return EndpointListResponse( id=endpoint_dict["id"], name=endpoint_dict["name"], protocol=endpoint_dict["protocol"], base_url=endpoint_dict["base_url"], auth_secret_status=auth_secret_status, enabled=endpoint_dict.get("enabled", True), revision=endpoint_dict.get("revision", 1), created_at=endpoint_dict.get("created_at"), updated_at=endpoint_dict.get("updated_at"), ) def redact_deployment(deployment_dict: dict[str, Any]) -> DeploymentResponse: """Convert a raw deployment dict to a DeploymentResponse.""" return DeploymentResponse( id=deployment_dict["id"], endpoint_id=deployment_dict["endpoint_id"], served_model_name=deployment_dict["served_model_name"], display_name=deployment_dict["display_name"], capabilities=deployment_dict.get("capabilities") or {}, context_window=deployment_dict.get("context_window"), max_output_tokens=deployment_dict.get("max_output_tokens"), quantization=deployment_dict.get("quantization"), runtime_metadata=deployment_dict.get("runtime_metadata") or {}, enabled=deployment_dict.get("enabled", True), revision=deployment_dict.get("revision", 1), ) def redact_binding(binding_dict: dict[str, Any]) -> BindingResponse: """Convert a raw binding dict to a BindingResponse.""" return BindingResponse( id=binding_dict["id"], agent_id=binding_dict["agent_id"], stage=binding_dict["stage"], model_deployment_id=binding_dict.get("model_deployment_id"), route_order=binding_dict.get("route_order", 0), routing_config=binding_dict.get("routing_config") or {}, is_active=binding_dict.get("is_active", True), revision=binding_dict.get("revision", 1), ) def is_external_endpoint(base_url: str) -> bool: """Determine if an endpoint URL points to an external (non-cluster) service. External endpoints require egress confirmation before enablement. Local/cluster endpoints match: - localhost / 127.0.0.1 - *.svc.cluster.local (Kubernetes internal) - 10.x.x.x / 192.168.x.x (private network) """ from urllib.parse import urlparse parsed = urlparse(base_url) hostname = parsed.hostname or "" # Cluster-local patterns if hostname in ("localhost", "127.0.0.1", "::1"): return False if hostname.endswith(".svc.cluster.local"): return False if hostname.startswith("10.") or hostname.startswith("192.168."): return False # Additional private ranges if hostname.startswith("172."): parts = hostname.split(".") if len(parts) >= 2: try: second = int(parts[1]) if 16 <= second <= 31: return False except ValueError: pass return True