from os import environ import string import random from typing import Type, Tuple from pathlib import Path from pydantic import BaseModel, computed_field from pydantic_settings import ( BaseSettings, SettingsConfigDict, PydanticBaseSettingsSource, YamlConfigSettingsSource, ) class Resource(BaseModel): """A resource with an URL that can be accessed with an OAuth2 access token""" id: str name: str url: str class OIDCProvider(BaseModel): """OIDC provider, can also be a resource server""" id: str name: str url: str client_id: str client_secret: str = "" # For PKCE (not implemented yet) code_challenge_method: str | None = None hint: str = "No hint" resources: list[Resource] = [] @computed_field @property def openid_configuration(self) -> str: return self.url + "/.well-known/openid-configuration" @computed_field @property def token_url(self) -> str: return "auth/" + self.id class ResourceProvider(BaseModel): id: str name: str resources: list[Resource] = [] class OIDCSettings(BaseModel): show_session_details: bool = False providers: list[OIDCProvider] = [] swagger_provider: str = "" class Settings(BaseSettings): """Settings wil be read from an .env file""" oidc: OIDCSettings = OIDCSettings() secret_key: str = "".join(random.choice(string.ascii_letters) for _ in range(16)) log: bool = False model_config = SettingsConfigDict(env_nested_delimiter="__") @classmethod def settings_customise_sources( cls, settings_cls: Type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, ) -> Tuple[PydanticBaseSettingsSource, ...]: return ( init_settings, env_settings, file_secret_settings, YamlConfigSettingsSource( settings_cls, Path( Path( environ.get( "OIDC_TEST_SETTINGS_FILE", Path.cwd() / "settings.yaml" ), ) ), ), dotenv_settings, ) settings = Settings()