""" Configuration settings for Chatty application Handles different environments (development, production, testing) """ import os import secrets from datetime import timedelta from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() class Config: """Base configuration class""" # Security Configuration SECRET_KEY = os.environ.get('SECRET_KEY') or secrets.token_hex(32) # MongoDB Configuration MONGODB_URL = os.environ.get('MONGODB_URL') or os.environ.get('MONGODB_URI') MONGODB_DATABASE = os.environ.get('MONGODB_DATABASE', 'Atlas') # Session Configuration PERMANENT_SESSION_LIFETIME = timedelta(hours=int(os.environ.get('SESSION_LIFETIME_HOURS', 24))) SESSION_COOKIE_SECURE = os.environ.get('SESSION_COOKIE_SECURE', 'False').lower() == 'true' SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' # CSRF Configuration WTF_CSRF_TIME_LIMIT = int(os.environ.get('CSRF_TIME_LIMIT', 3600)) # 1 hour WTF_CSRF_SSL_STRICT = os.environ.get('WTF_CSRF_SSL_STRICT', 'False').lower() == 'true' WTF_CSRF_ENABLED = os.environ.get('WTF_CSRF_ENABLED', 'True').lower() == 'true' # Additional CSRF settings for proxy environments (like Hugging Face Spaces) WTF_CSRF_CHECK_DEFAULT = os.environ.get('WTF_CSRF_CHECK_DEFAULT', 'True').lower() == 'true' # Rate Limiting Configuration MAX_LOGIN_ATTEMPTS = int(os.environ.get('MAX_LOGIN_ATTEMPTS', 5)) RATE_LIMIT_WINDOW = int(os.environ.get('RATE_LIMIT_WINDOW', 900)) # 15 minutes # Anonymous User Configuration ANONYMOUS_ENABLED = os.environ.get('ANONYMOUS_ENABLED', 'True').lower() == 'true' ANONYMOUS_RATE_LIMIT = int(os.environ.get('ANONYMOUS_RATE_LIMIT', 20)) # messages per hour ANONYMOUS_RATE_LIMIT_WINDOW = int(os.environ.get('ANONYMOUS_RATE_LIMIT_WINDOW', 3600)) # 1 hour ANONYMOUS_SESSION_TIMEOUT = int(os.environ.get('ANONYMOUS_SESSION_TIMEOUT', 3600)) # 1 hour ANONYMOUS_MAX_MESSAGE_LENGTH = int(os.environ.get('ANONYMOUS_MAX_MESSAGE_LENGTH', 2000)) # characters # API Configuration API_URL = os.environ.get('API_URL', 'https://findEthics-Atlas.hf.space/chat') API_TIMEOUT = int(os.environ.get('API_TIMEOUT', 30)) # Logging Configuration LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO') LOG_FILE = os.environ.get('LOG_FILE') # Application Configuration DEBUG = False TESTING = False @staticmethod def validate_config(): """Validate required configuration values""" errors = [] if not Config.MONGODB_URL: errors.append("MONGODB_URL environment variable is required") if not Config.SECRET_KEY or Config.SECRET_KEY == 'dev-secret-key-change-in-production': errors.append("SECRET_KEY environment variable must be set to a secure random value") if len(Config.SECRET_KEY) < 32: errors.append("SECRET_KEY should be at least 32 characters long") return errors class DevelopmentConfig(Config): """Development configuration""" DEBUG = True SESSION_COOKIE_SECURE = False WTF_CSRF_SSL_STRICT = False WTF_CSRF_ENABLED = False # Disable CSRF for testing LOG_LEVEL = 'DEBUG' class ProductionConfig(Config): """Production configuration""" DEBUG = False # Hugging Face Spaces compatibility # HF Spaces runs behind a proxy, so we need to be more flexible with CSRF/session settings SESSION_COOKIE_SECURE = os.environ.get('SESSION_COOKIE_SECURE', 'False').lower() == 'true' WTF_CSRF_SSL_STRICT = os.environ.get('WTF_CSRF_SSL_STRICT', 'False').lower() == 'true' # Override with production-specific values MAX_LOGIN_ATTEMPTS = int(os.environ.get('MAX_LOGIN_ATTEMPTS', 3)) # Stricter in production RATE_LIMIT_WINDOW = int(os.environ.get('RATE_LIMIT_WINDOW', 1800)) # 30 minutes @staticmethod def validate_config(): """Additional production-specific validation""" errors = Config.validate_config() # Production-specific checks if not os.environ.get('SECRET_KEY'): errors.append("SECRET_KEY environment variable must be explicitly set in production") # Relaxed validation for Hugging Face Spaces compatibility # SESSION_COOKIE_SECURE and WTF_CSRF_SSL_STRICT are now optional in production if ProductionConfig.DEBUG: errors.append("DEBUG must be False in production") return errors class HuggingFaceConfig(Config): """Hugging Face Spaces specific configuration Security Note: CSRF protection is disabled for Hugging Face Spaces due to: 1. HF Spaces runs applications in iframes which breaks CSRF token validation 2. Cross-origin restrictions prevent proper CSRF token exchange 3. HF Spaces provides its own security layer at the platform level Trade-offs: - Reduced protection against CSRF attacks - Mitigated by: HF Spaces platform security, rate limiting, and authentication requirements """ DEBUG = False # Hugging Face Spaces runs in iframes with complex proxy setup # Sessions and CSRF are problematic in this environment SESSION_COOKIE_SECURE = False WTF_CSRF_SSL_STRICT = False WTF_CSRF_ENABLED = False # Disable CSRF for HF Spaces due to iframe issues # Very permissive session settings for iframe compatibility SESSION_COOKIE_SAMESITE = None # Most permissive setting SESSION_COOKIE_HTTPONLY = False # Allow JavaScript access SESSION_COOKIE_DOMAIN = None # Don't restrict domain SESSION_COOKIE_PATH = '/' # Ensure cookies work across all paths # Extend session lifetime to help with iframe issues PERMANENT_SESSION_LIFETIME = timedelta(hours=int(os.environ.get('SESSION_LIFETIME_HOURS', 48))) # Production-level security for other settings MAX_LOGIN_ATTEMPTS = int(os.environ.get('MAX_LOGIN_ATTEMPTS', 3)) RATE_LIMIT_WINDOW = int(os.environ.get('RATE_LIMIT_WINDOW', 1800)) @staticmethod def validate_config(): """Validation for Hugging Face Spaces""" errors = Config.validate_config() if not os.environ.get('SECRET_KEY'): errors.append("SECRET_KEY environment variable must be explicitly set") return errors class TestingConfig(Config): """Testing configuration""" TESTING = True DEBUG = True SESSION_COOKIE_SECURE = False WTF_CSRF_ENABLED = False # Disable CSRF for testing MONGODB_DATABASE = os.environ.get('TEST_MONGODB_DATABASE', 'Atlas_test') # Configuration mapping config = { 'development': DevelopmentConfig, 'production': ProductionConfig, 'huggingface': HuggingFaceConfig, 'testing': TestingConfig, 'default': DevelopmentConfig } def get_config(config_name=None): """Get configuration class based on environment""" if config_name is None: config_name = os.environ.get('FLASK_ENV', 'development') return config.get(config_name, config['default']) def validate_environment(): """Validate the current environment configuration""" config_name = os.environ.get('FLASK_ENV', 'development') config_class = get_config(config_name) errors = config_class.validate_config() if errors: print(f"Configuration errors for {config_name} environment:") for error in errors: print(f" - {error}") return False print(f"✓ Configuration validation passed for {config_name} environment") return True if __name__ == "__main__": # Validate configuration when run directly validate_environment()