frontpage/frontpage/config.py
2022-09-03 14:49:13 +10:00

87 lines
1.9 KiB
Python

"""Configuration classes and utilities."""
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional
import toml
import dacite
from flask import current_app
@dataclass
class CoreConfig:
"""Core application configuration."""
debug: bool = False
port: int = 5000
@dataclass
class OidcConfig:
"""OIDC client configuration."""
issuer: str
client_id: str
client_secret: Optional[str] = None
client_secret_file: Optional[str] = None
scopes: List[str] = field(default_factory=list)
@dataclass
class AppConfig:
"""App link configuration."""
url: str
name: Optional[str] = None
image: Optional[str] = None
description: Optional[str] = None
groups: List[str] = field(default_factory=list)
@dataclass
class Config:
"""Top-level configuration."""
core: CoreConfig
oidc: OidcConfig
apps: Dict[str, AppConfig] = field(default_factory=dict)
class ConfigError(Exception):
pass
def _validate(config: Config) -> None:
oidc = config.oidc
if oidc.client_secret is None and oidc.client_secret_file is None:
raise ConfigError(
"exactly one of oidc.client_secret or oidc.client_secret_file is required"
)
if oidc.client_secret is not None and oidc.client_secret_file is not None:
raise ConfigError(
"exactly one of oidc.client_secret or oidc.client_secret_file is required"
)
def load(file: str) -> Config:
"""
Load the configuration from a `file` path containing a TOML configuration.
:param file: a file path to a TOML config
:return: the configuration object
"""
path = Path(file)
cfg = dacite.from_dict(data_class=Config, data=toml.load(path))
_validate(cfg)
return cfg
def current_config() -> Config:
"""
Load the configuration from the current Flask app.
:return: the configuration object
"""
return current_app.config["user_config"]