Chatty / config.py
findEthics
Fix anonymous sessions and clean up codebase
f77d8aa
Raw
History Blame Contribute Delete
7.77 kB
"""
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()