oidc-fastapi-test/src/oidc_test/settings.py
phil fb433e27be
Some checks failed
/ build (push) Failing after 6s
/ test (push) Failing after 6s
Add postgres db (messy)
2025-02-17 02:42:38 +01:00

150 lines
4.1 KiB
Python

from os import environ
import string
import random
from typing import Type, Tuple
from pathlib import Path
from pydantic import BaseModel, computed_field, AnyUrl
from pydantic_settings import (
BaseSettings,
SettingsConfigDict,
PydanticBaseSettingsSource,
YamlConfigSettingsSource,
)
from starlette.requests import Request
class Resource(BaseModel):
"""A resource with an URL that can be accessed with an OAuth2 access token"""
resource_name: str
name: str
url: str
class AuthProviderSettings(BaseModel):
"""Auth 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] = []
account_url_template: str | None = None
info_url: str | None = (
None # Used eg. for Keycloak's public key (see https://stackoverflow.com/questions/54318633/getting-keycloaks-public-key)
)
public_key: str | None = None
public_key_url: str | None = None
signature_alg: str = "RS256"
resource_provider_scopes: list[str] = []
session_key: str = "sid"
skip_verify_signature: bool = True
disabled: bool = False
@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
def get_account_url(self, request: Request, user: dict) -> str | None:
if self.account_url_template:
if not (self.url.endswith("/") or self.account_url_template.startswith("/")):
sep = "/"
else:
sep = ""
return self.url + sep + self.account_url_template.format(request=request, user=user)
else:
return None
class ResourceProvider(BaseModel):
id: str
name: str
base_url: AnyUrl
resources: list[Resource] = []
class AuthSettings(BaseModel):
show_session_details: bool = False
providers: list[AuthProviderSettings] = []
swagger_provider: str = ""
class Insecure(BaseModel):
"""Warning: changing these defaults are only suitable for debugging"""
skip_verify_signature: bool = False
class DB(BaseModel):
host: str = "localhost"
port: int = 5432
db: str = "oidc-test"
user: str = "oidc-test"
password: str = "oidc-test"
debug: bool = False
pool_size: int = 10
max_overflow: int = 10
echo: bool = False
@property
def sqla_url(self):
return (
f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
)
def get_pg_url(self):
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.db}"
class Settings(BaseSettings):
"""Settings wil be read from an .env file"""
model_config = SettingsConfigDict(env_nested_delimiter="__")
auth: AuthSettings = AuthSettings()
resource_providers: list[ResourceProvider] = []
secret_key: str = "".join(random.choice(string.ascii_letters) for _ in range(16))
log: bool = False
insecure: Insecure = Insecure()
db: DB = DB()
cors_origins: list[str] = []
debug_token: bool = False
show_token: bool = False
@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()