131 lines
4.4 KiB
Python
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()
|