Files
calminer/config/settings.py

131 lines
4.4 KiB
Python

from __future__ import annotations
import os
from dataclasses import dataclass
from datetime import timedelta
from functools import lru_cache
from typing import Optional
from services.security import JWTSettings
@dataclass(frozen=True, slots=True)
class SessionSettings:
"""Cookie and header configuration for session token transport."""
access_cookie_name: str
refresh_cookie_name: str
cookie_secure: bool
cookie_domain: Optional[str]
cookie_path: str
header_name: str
header_prefix: str
allow_header_fallback: bool
@dataclass(frozen=True, slots=True)
class Settings:
"""Application configuration sourced from environment variables."""
jwt_secret_key: str = "change-me"
jwt_algorithm: str = "HS256"
jwt_access_token_minutes: int = 15
jwt_refresh_token_days: int = 7
session_access_cookie_name: str = "calminer_access_token"
session_refresh_cookie_name: str = "calminer_refresh_token"
session_cookie_secure: bool = False
session_cookie_domain: Optional[str] = None
session_cookie_path: str = "/"
session_header_name: str = "Authorization"
session_header_prefix: str = "Bearer"
session_allow_header_fallback: bool = True
@classmethod
def from_environment(cls) -> "Settings":
"""Construct settings from environment variables."""
return cls(
jwt_secret_key=os.getenv("CALMINER_JWT_SECRET", "change-me"),
jwt_algorithm=os.getenv("CALMINER_JWT_ALGORITHM", "HS256"),
jwt_access_token_minutes=cls._int_from_env(
"CALMINER_JWT_ACCESS_MINUTES", 15
),
jwt_refresh_token_days=cls._int_from_env(
"CALMINER_JWT_REFRESH_DAYS", 7
),
session_access_cookie_name=os.getenv(
"CALMINER_SESSION_ACCESS_COOKIE", "calminer_access_token"
),
session_refresh_cookie_name=os.getenv(
"CALMINER_SESSION_REFRESH_COOKIE", "calminer_refresh_token"
),
session_cookie_secure=cls._bool_from_env(
"CALMINER_SESSION_COOKIE_SECURE", False
),
session_cookie_domain=os.getenv("CALMINER_SESSION_COOKIE_DOMAIN"),
session_cookie_path=os.getenv("CALMINER_SESSION_COOKIE_PATH", "/"),
session_header_name=os.getenv(
"CALMINER_SESSION_HEADER_NAME", "Authorization"
),
session_header_prefix=os.getenv(
"CALMINER_SESSION_HEADER_PREFIX", "Bearer"
),
session_allow_header_fallback=cls._bool_from_env(
"CALMINER_SESSION_ALLOW_HEADER_FALLBACK", True
),
)
@staticmethod
def _int_from_env(name: str, default: int) -> int:
raw_value = os.getenv(name)
if raw_value is None:
return default
try:
return int(raw_value)
except ValueError:
return default
@staticmethod
def _bool_from_env(name: str, default: bool) -> bool:
raw_value = os.getenv(name)
if raw_value is None:
return default
lowered = raw_value.strip().lower()
if lowered in {"1", "true", "yes", "on"}:
return True
if lowered in {"0", "false", "no", "off"}:
return False
return default
def jwt_settings(self) -> JWTSettings:
"""Build runtime JWT settings compatible with token helpers."""
return JWTSettings(
secret_key=self.jwt_secret_key,
algorithm=self.jwt_algorithm,
access_token_ttl=timedelta(minutes=self.jwt_access_token_minutes),
refresh_token_ttl=timedelta(days=self.jwt_refresh_token_days),
)
def session_settings(self) -> SessionSettings:
"""Provide transport configuration for session tokens."""
return SessionSettings(
access_cookie_name=self.session_access_cookie_name,
refresh_cookie_name=self.session_refresh_cookie_name,
cookie_secure=self.session_cookie_secure,
cookie_domain=self.session_cookie_domain,
cookie_path=self.session_cookie_path,
header_name=self.session_header_name,
header_prefix=self.session_header_prefix,
allow_header_fallback=self.session_allow_header_fallback,
)
@lru_cache(maxsize=1)
def get_settings() -> Settings:
"""Return cached application settings."""
return Settings.from_environment()