File size: 7,774 Bytes
f6278c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bcc5440
 
 
 
f6278c5
 
 
 
 
6ec566b
 
 
 
 
 
 
f6278c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ec566b
f6278c5
 
 
 
 
bcc5440
 
 
 
 
f6278c5
 
 
 
 
 
 
 
 
 
 
 
 
 
bcc5440
 
f6278c5
 
 
 
 
 
bcc5440
f77d8aa
 
 
 
 
 
 
 
 
 
 
 
bcc5440
 
5c85005
 
 
 
 
020f362
 
 
 
 
 
 
 
 
bcc5440
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f6278c5
 
 
 
 
 
 
 
 
 
 
 
bcc5440
f6278c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
"""
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()