# ============================================================================== # Formeleon - Multi-tenant Form Generator # Author: Björn Runden # 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 }