Refactor backend configuration and authentication
- Add comprehensive documentation for backend development - Improve client IP detection with utility functions and tests - Update auth router with better error handling - Refactor config module with environment-based settings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ Follows pydantic-settings patterns: all values are prefixed with BANGUI_
|
||||
and validated at startup via the Settings singleton.
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import shlex
|
||||
from typing import Literal
|
||||
|
||||
@@ -180,6 +181,62 @@ class Settings(BaseSettings):
|
||||
"In production, leave unset (defaults to false) to avoid exposing API schema."
|
||||
),
|
||||
)
|
||||
trusted_proxies: str | list[str] = Field(
|
||||
default_factory=list,
|
||||
description=(
|
||||
"Comma-separated list of trusted reverse proxy IP addresses or CIDR ranges. "
|
||||
"Only requests from these IPs/ranges are allowed to set X-Forwarded-For and X-Real-IP headers. "
|
||||
"Examples: '192.168.1.1' or '10.0.0.0/8' or '192.168.1.1,10.0.0.0/8'. "
|
||||
"Leave empty to disable proxy header forwarding (default). "
|
||||
"This is critical for correct client IP extraction behind reverse proxies like nginx."
|
||||
),
|
||||
)
|
||||
|
||||
@field_validator("trusted_proxies", mode="before")
|
||||
@classmethod
|
||||
def _normalize_trusted_proxies(cls, value: str | list[str] | None) -> list[str]:
|
||||
"""Normalize trusted_proxies from comma-separated string to list.
|
||||
|
||||
Args:
|
||||
value: A comma-separated string or list of trusted proxy IPs/CIDRs.
|
||||
|
||||
Returns:
|
||||
A list of normalized proxy IP/CIDR strings.
|
||||
"""
|
||||
if value is None:
|
||||
return []
|
||||
if isinstance(value, str):
|
||||
return [proxy.strip() for proxy in value.split(",") if proxy.strip()]
|
||||
return value
|
||||
|
||||
@field_validator("trusted_proxies", mode="after")
|
||||
@classmethod
|
||||
def _validate_trusted_proxies(cls, value: list[str]) -> list[str]:
|
||||
"""Validate trusted_proxies as valid IPs or CIDR ranges.
|
||||
|
||||
Args:
|
||||
value: A list of proxy IP addresses or CIDR ranges.
|
||||
|
||||
Returns:
|
||||
The validated list.
|
||||
|
||||
Raises:
|
||||
ValueError: If any item is not a valid IP address or CIDR range.
|
||||
"""
|
||||
for proxy in value:
|
||||
try:
|
||||
# Try to parse as a CIDR network first
|
||||
ipaddress.ip_network(proxy, strict=False)
|
||||
except ValueError:
|
||||
try:
|
||||
# Fall back to parsing as a single IP address
|
||||
ipaddress.ip_address(proxy)
|
||||
except ValueError as exc:
|
||||
raise ValueError(
|
||||
f"Invalid IP address or CIDR range: {proxy!r}. "
|
||||
f"Expected format: '192.168.1.1' or '10.0.0.0/8'"
|
||||
) from exc
|
||||
return value
|
||||
|
||||
@field_validator("fail2ban_start_command", mode="after")
|
||||
@classmethod
|
||||
@@ -222,4 +279,4 @@ def get_settings() -> Settings:
|
||||
A validated :class:`Settings` object. Raises :class:`pydantic.ValidationError`
|
||||
if required keys are absent or values fail validation.
|
||||
"""
|
||||
return Settings() # pydantic-settings populates required fields from env vars
|
||||
return Settings() # type: ignore[call-arg] # pydantic-settings populates required fields from env vars
|
||||
|
||||
Reference in New Issue
Block a user