chore(release): 0.0.7
Squash-merge lib-standardisation: adopt repo standards across packaging, tooling, docs, and CI. - standardize pyproject.toml (canonical packaging, lint config, extras, GSDFC pydoclint) - add py.typed marker, modernize type hints and Google-style docstrings - nest generated docs under docs/lib and docs/mcp; add hand-written wiki - adopt standard Type A PyPI CI pipeline (tag==version guard + quality gate) - promote generated CHANGELOG.md; bump version to 0.0.7
This commit is contained in:
@@ -1,17 +1,21 @@
|
||||
"""
|
||||
# Summary
|
||||
|
||||
OpenAPI security scheme parsing and auto-generated auth dependencies.
|
||||
|
||||
Reads `securitySchemes` and per-operation `security` from an OpenAPI spec,
|
||||
resolves `{ENV_VAR}` placeholders in `x-` extension fields, and generates
|
||||
FastAPI dependencies for token validation (e.g., Bearer JWT introspection).
|
||||
This module reads `securitySchemes` and per-operation `security` from an
|
||||
OpenAPI spec, resolves `{ENV_VAR}` placeholders in `x-` extension fields,
|
||||
and generates FastAPI dependencies for token validation (e.g., Bearer JWT
|
||||
introspection).
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from typing import Any, Callable
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from fastapi import Depends, Request, HTTPException
|
||||
from fastapi import Depends, HTTPException, Request
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
_env_pattern = re.compile(r"\{(\w+)\}")
|
||||
@@ -19,14 +23,16 @@ _env_pattern = re.compile(r"\{(\w+)\}")
|
||||
|
||||
def _resolve_env(value: str) -> str:
|
||||
"""Replace {ENV_VAR} placeholders with values from os.environ."""
|
||||
def _replace(m: re.Match) -> str:
|
||||
|
||||
def _replace(m: re.Match[str]) -> str:
|
||||
return os.environ.get(m.group(1), "")
|
||||
|
||||
return _env_pattern.sub(_replace, value)
|
||||
|
||||
|
||||
def _resolve_scheme(scheme: dict) -> dict:
|
||||
def _resolve_scheme(scheme: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Recursively resolve env vars in all string-valued fields of a scheme."""
|
||||
resolved = {}
|
||||
resolved: dict[str, Any] = {}
|
||||
for key, value in scheme.items():
|
||||
if isinstance(value, str):
|
||||
resolved[key] = _resolve_env(value)
|
||||
@@ -37,13 +43,13 @@ def _resolve_scheme(scheme: dict) -> dict:
|
||||
return resolved
|
||||
|
||||
|
||||
def parse_security_schemes(spec: dict) -> dict[str, dict]:
|
||||
def parse_security_schemes(spec: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
"""Extract and resolve environment variables in security schemes."""
|
||||
raw = spec.get("components", {}).get("securitySchemes", {})
|
||||
return {name: _resolve_scheme(scheme) for name, scheme in raw.items()}
|
||||
|
||||
|
||||
def _make_bearer_dependency(introspect_url: str | None) -> Callable:
|
||||
def _make_bearer_dependency(introspect_url: str | None) -> Callable[..., Any]:
|
||||
"""
|
||||
Create a FastAPI dependency that validates a Bearer JWT.
|
||||
|
||||
@@ -76,7 +82,7 @@ def _make_bearer_dependency(introspect_url: str | None) -> Callable:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="Authentication service unavailable",
|
||||
)
|
||||
) from None
|
||||
|
||||
if resp.status_code != 200:
|
||||
raise HTTPException(status_code=401, detail="Invalid or expired token")
|
||||
@@ -92,7 +98,9 @@ def _make_bearer_dependency(introspect_url: str | None) -> Callable:
|
||||
return _bearer_dep
|
||||
|
||||
|
||||
def _build_dependency(scheme_name: str, scheme: dict) -> Callable | None:
|
||||
def _build_dependency(
|
||||
scheme_name: str, scheme: dict[str, Any]
|
||||
) -> Callable[..., Any] | None:
|
||||
"""Return a FastAPI dependency callable for *scheme*, or ``None``."""
|
||||
scheme_type = scheme.get("type")
|
||||
|
||||
@@ -106,8 +114,8 @@ def _build_dependency(scheme_name: str, scheme: dict) -> Callable | None:
|
||||
|
||||
|
||||
def make_security_dependencies(
|
||||
spec: dict,
|
||||
security_schemes: dict[str, dict],
|
||||
spec: dict[str, Any],
|
||||
security_schemes: dict[str, dict[str, Any]],
|
||||
) -> dict[str, list[Any]]:
|
||||
"""
|
||||
Build a mapping of ``METHOD:/path`` → list of ``Depends(...)``.
|
||||
|
||||
Reference in New Issue
Block a user