* Used absolute paths constructed from `app.root_path` (BASE_DIR)
* Modified `config.py` to ensure `sqlite:///{path}` works across Docker and Host Linux systems
180 lines
6.7 KiB
Python
180 lines
6.7 KiB
Python
# ==============================================================================
|
|
# Formeleon - Multi-tenant Form Generator
|
|
# Author: Björn Runden <br@runden.net>
|
|
# Module: config.py
|
|
# Description: Application configuration with environment variable support.
|
|
# Handles development mode, reCAPTCHA, Zitadel OIDC, and database settings.
|
|
# ==============================================================================
|
|
|
|
"""
|
|
Application configuration module for Formeleon.
|
|
|
|
This module centralizes all configuration settings for the Flask application,
|
|
loading from environment variables with sensible defaults for development.
|
|
|
|
Environment Variables:
|
|
- DEV_MODE: Enable development mode (default: False)
|
|
- SECRET_KEY: Flask secret key for session management
|
|
- SQLALCHEMY_DATABASE_URI: Database connection string
|
|
- SQLALCHEMY_TRACK_MODIFICATIONS: Track modifications (default: False)
|
|
|
|
- ZITADEL_ISSUER: OIDC issuer URL for Zitadel
|
|
- ZITADEL_CLIENT_ID: OIDC client ID
|
|
- ZITADEL_CLIENT_SECRET: OIDC client secret
|
|
- ZITADEL_REDIRECT_URI: OIDC redirect URI
|
|
|
|
- RECAPTCHA_SECRET_KEY: reCAPTCHA v3 secret key
|
|
- RECAPTCHA_SITE_KEY: reCAPTCHA v3 site key
|
|
- RECAPTCHA_THRESHOLD: Minimum score threshold (default: 0.5)
|
|
- MOCK_RECAPTCHA_SCORE: Mock score for dev mode (default: 0.9)
|
|
|
|
- DEV_USER_ROLE: Role for dev mode user (default: 'admin')
|
|
- DEV_TENANT_ID: Tenant ID for dev mode (default: 'tenant_dev_123')
|
|
- DEV_USER_NAME: Username for dev mode (default: 'Dev Admin')
|
|
- DEV_USER_EMAIL: Email for dev mode (default: 'dev@formeleon.local')
|
|
"""
|
|
|
|
import os
|
|
from typing import Optional
|
|
|
|
|
|
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
class Config:
|
|
"""
|
|
Base configuration class with default values.
|
|
|
|
All configuration values can be overridden via environment variables.
|
|
"""
|
|
|
|
# Flask Core Settings
|
|
SECRET_KEY: str = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')
|
|
DEBUG: bool = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true'
|
|
TESTING: bool = os.environ.get('FLASK_TESTING', 'false').lower() == 'true'
|
|
|
|
# Development Mode
|
|
DEV_MODE: bool = os.environ.get('DEV_MODE', 'false').lower() == 'true'
|
|
|
|
# Dev User Settings
|
|
DEV_USER_ROLE: str = os.environ.get('DEV_USER_ROLE', 'admin')
|
|
DEV_TENANT_ID: str = os.environ.get('DEV_TENANT_ID', 'tenant_dev_123')
|
|
DEV_USER_NAME: str = os.environ.get('DEV_USER_NAME', 'Dev Admin')
|
|
DEV_USER_EMAIL: str = os.environ.get('DEV_USER_EMAIL', 'dev@formeleon.local')
|
|
|
|
# Database Configuration
|
|
if 'SQLALCHEMY_DATABASE_URI' in os.environ:
|
|
SQLALCHEMY_DATABASE_URI: str = os.environ['SQLALCHEMY_DATABASE_URI']
|
|
else:
|
|
SQLALCHEMY_DATABASE_URI: str = f'sqlite:///{os.path.join(BASE_DIR, "instance", "formeleon.db")}'
|
|
|
|
SQLALCHEMY_TRACK_MODIFICATIONS: bool = False
|
|
SQLALCHEMY_ECHO: bool = os.environ.get('SQLALCHEMY_ECHO', 'false').lower() == 'true'
|
|
|
|
# Zitadel OIDC Configuration
|
|
ZITADEL_ISSUER: Optional[str] = os.environ.get('ZITADEL_ISSUER')
|
|
ZITADEL_CLIENT_ID: Optional[str] = os.environ.get('ZITADEL_CLIENT_ID')
|
|
ZITADEL_CLIENT_SECRET: Optional[str] = os.environ.get('ZITADEL_CLIENT_SECRET')
|
|
ZITADEL_REDIRECT_URI: Optional[str] = os.environ.get(
|
|
'ZITADEL_REDIRECT_URI',
|
|
'http://localhost:5000/auth/callback'
|
|
)
|
|
ZITADEL_SCOPES: list = ['openid', 'profile', 'email']
|
|
|
|
# reCAPTCHA v3 Configuration
|
|
RECAPTCHA_SECRET_KEY: Optional[str] = os.environ.get('RECAPTCHA_SECRET_KEY')
|
|
RECAPTCHA_SITE_KEY: Optional[str] = os.environ.get('RECAPTCHA_SITE_KEY')
|
|
RECAPTCHA_THRESHOLD: float = float(os.environ.get('RECAPTCHA_THRESHOLD', '0.5'))
|
|
MOCK_RECAPTCHA_SCORE: float = float(os.environ.get('MOCK_RECAPTCHA_SCORE', '0.9'))
|
|
RECAPTCHA_VERIFY_URL: str = 'https://www.google.com/recaptcha/api/siteverify'
|
|
|
|
# Application Settings
|
|
DEFAULT_PAGE_SIZE: int = 20
|
|
MAX_PAGE_SIZE: int = 100
|
|
|
|
# Form Submission Settings
|
|
ALLOWED_ORIGINS: list = os.environ.get('ALLOWED_ORIGINS', '*').split(',')
|
|
ENABLE_CORS: bool = os.environ.get('ENABLE_CORS', 'true').lower() == 'true'
|
|
|
|
# File Upload Settings
|
|
MAX_CONTENT_LENGTH: int = 16 * 1024 * 1024 # 16MB
|
|
UPLOAD_FOLDER: str = os.environ.get('UPLOAD_FOLDER', '/tmp/formeleon_uploads')
|
|
ALLOWED_EXTENSIONS: set = {'png', 'jpg', 'jpeg', 'gif', 'pdf', 'txt'}
|
|
|
|
# Session Configuration
|
|
SESSION_COOKIE_SECURE: bool = os.environ.get('SESSION_COOKIE_SECURE', 'false').lower() == 'true'
|
|
SESSION_COOKIE_HTTPONLY: bool = True
|
|
SESSION_COOKIE_SAMESITE: str = os.environ.get('SESSION_COOKIE_SAMESITE', 'Lax')
|
|
PERMANENT_SESSION_LIFETIME: int = 3600 # 1 hour in seconds
|
|
|
|
def is_dev_mode(self) -> bool:
|
|
"""
|
|
Check if application is running in development mode.
|
|
|
|
:return: True if DEV_MODE is enabled, False otherwise.
|
|
"""
|
|
return self.DEV_MODE
|
|
|
|
def is_recaptcha_enabled(self) -> bool:
|
|
"""
|
|
Check if reCAPTCHA is properly configured.
|
|
|
|
:return: True if both secret and site keys are configured, False otherwise.
|
|
"""
|
|
return (self.RECAPTCHA_SECRET_KEY is not None and
|
|
self.RECAPTCHA_SITE_KEY is not None)
|
|
|
|
def is_zitadel_enabled(self) -> bool:
|
|
"""
|
|
Check if Zitadel OIDC is properly configured.
|
|
|
|
:return: True if all required Zitadel settings are present, False otherwise.
|
|
"""
|
|
return (self.ZITADEL_ISSUER is not None and
|
|
self.ZITADEL_CLIENT_ID is not None and
|
|
self.ZITADEL_CLIENT_SECRET is not None)
|
|
|
|
|
|
class DevelopmentConfig(Config):
|
|
"""
|
|
Development-specific configuration.
|
|
|
|
Overrides base configuration with development-friendly settings.
|
|
"""
|
|
DEBUG: bool = True
|
|
DEV_MODE: bool = True
|
|
SQLALCHEMY_ECHO: bool = True
|
|
SESSION_COOKIE_SECURE: bool = False
|
|
|
|
|
|
class ProductionConfig(Config):
|
|
"""
|
|
Production-specific configuration.
|
|
|
|
Overrides base configuration with production-safe settings.
|
|
"""
|
|
DEBUG: bool = False
|
|
DEV_MODE: bool = False
|
|
SQLALCHEMY_ECHO: bool = False
|
|
SESSION_COOKIE_SECURE: bool = True
|
|
SESSION_COOKIE_SAMESITE: str = 'Strict'
|
|
|
|
|
|
class TestingConfig(Config):
|
|
"""
|
|
Testing-specific configuration.
|
|
|
|
Overrides base configuration with test-friendly settings.
|
|
"""
|
|
TESTING: bool = True
|
|
SQLALCHEMY_DATABASE_URI: str = 'sqlite:///:memory:'
|
|
WTF_CSRF_ENABLED: bool = False
|
|
|
|
|
|
# Configuration mapping for easy access
|
|
config_map: dict = {
|
|
'development': DevelopmentConfig,
|
|
'production': ProductionConfig,
|
|
'testing': TestingConfig,
|
|
'default': DevelopmentConfig if os.environ.get('FLASK_ENV', 'development') == 'development' else ProductionConfig
|
|
}
|