150 lines
4.1 KiB
Python
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()
|