✨ New Features: - AI-powered habit creation with natural language processing - HuggingFace transformers integration for sentiment analysis (tracked via Git LFS) - Advanced predictive analytics and behavioral insights - Voice & image input capabilities for hands-free habit tracking - Real-time notifications and community features - Plugin system with extensible architecture 🔧 Technical Improvements: - Comprehensive FastAPI backend with 30+ endpoints - React frontend with PWA capabilities - Advanced authentication with 2FA support - RBAC authorization system - Comprehensive security features (CSRF, rate limiting, audit logging) - Database migrations and health monitoring - Docker containerization support - Git LFS configured for large AI model files (2+ GB) 📚 Documentation & DevOps: - Complete deployment guides for multiple platforms - Professional README with feature highlights - GitHub Actions CI/CD workflows - Comprehensive API documentation - Security audit roadmap and compliance framework - Setup scripts for development environment 🧪 Testing & Quality: - Comprehensive test suite with 20+ test modules - Setup verification scripts - Working development environment with both backend and frontend - Health checks and monitoring systems 🌟 Ready for: - Portfolio showcasing - Community contributions - Production deployment - Professional presentation
286 lines
11 KiB
Python
286 lines
11 KiB
Python
"""
|
|
Development Environment Configuration and Security Controls
|
|
|
|
This module provides separate configurations for development and production
|
|
environments, implementing security controls appropriate for each context.
|
|
"""
|
|
|
|
import os
|
|
from typing import Dict, Any
|
|
from config import settings
|
|
|
|
|
|
class DevelopmentSecurityConfig:
|
|
"""Security configuration specific to development environments"""
|
|
|
|
def __init__(self):
|
|
self.is_development = self._detect_development_mode()
|
|
self.dev_overrides = self._get_development_overrides()
|
|
|
|
def _detect_development_mode(self) -> bool:
|
|
"""Detect if running in development mode"""
|
|
# Check various indicators of development environment
|
|
indicators = [
|
|
os.getenv('ENVIRONMENT') == 'development',
|
|
os.getenv('ENV') == 'dev',
|
|
os.getenv('DEBUG') == 'true',
|
|
os.getenv('FLASK_ENV') == 'development',
|
|
os.getenv('NODE_ENV') == 'development',
|
|
'dev' in os.getcwd().lower(),
|
|
os.path.exists('.env.development'),
|
|
not settings.FORCE_HTTPS, # Likely dev if not forcing HTTPS
|
|
'localhost' in str(settings.FRONTEND_ORIGINS)
|
|
]
|
|
return any(indicators)
|
|
|
|
def _get_development_overrides(self) -> Dict[str, Any]:
|
|
"""Get security setting overrides for development"""
|
|
if not self.is_development:
|
|
return {}
|
|
|
|
return {
|
|
# Logging configuration
|
|
'LOG_LEVEL': 'DEBUG',
|
|
'DETAILED_ERRORS': True,
|
|
'LOG_SQL_QUERIES': True,
|
|
|
|
# CORS configuration (more permissive for dev)
|
|
'CORS_ALLOW_CREDENTIALS': True,
|
|
'CORS_ALLOWED_ORIGINS': [
|
|
'http://localhost:3000',
|
|
'http://localhost:5173', # Vite default
|
|
'http://127.0.0.1:3000',
|
|
'http://127.0.0.1:5173'
|
|
],
|
|
|
|
# Security headers (relaxed for testing)
|
|
'CSP_REPORT_ONLY': True, # Report violations but don't block
|
|
'HSTS_ENABLE': False, # No HSTS in development
|
|
|
|
# Rate limiting (more lenient)
|
|
'RATE_LIMIT_PER_MINUTE': 1000,
|
|
'RATE_LIMIT_BURST': 100,
|
|
|
|
# Session configuration
|
|
'SESSION_SECURE': False, # Allow HTTP in development
|
|
'SESSION_HTTPONLY': True, # Still protect from XSS
|
|
|
|
# Database security
|
|
'DB_SSL_REQUIRE': False,
|
|
'DB_CONNECTION_POOL_SIZE': 5,
|
|
|
|
# Development-specific features
|
|
'ENABLE_API_DOCS': True,
|
|
'ENABLE_DEBUG_TOOLBAR': True,
|
|
'ENABLE_HOT_RELOAD': True,
|
|
|
|
# Testing support
|
|
'ALLOW_TEST_ROUTES': True,
|
|
'MOCK_EXTERNAL_SERVICES': True
|
|
}
|
|
|
|
def get_security_headers(self) -> Dict[str, str]:
|
|
"""Get security headers appropriate for development"""
|
|
if not self.is_development:
|
|
# Use production headers
|
|
return self._get_production_headers()
|
|
|
|
# Development headers - more permissive for testing
|
|
return {
|
|
"Content-Security-Policy-Report-Only": (
|
|
"default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; "
|
|
"connect-src 'self' ws: wss: http: https:; "
|
|
"font-src 'self' data: https:; "
|
|
"img-src 'self' data: blob: https:; "
|
|
"media-src 'self' blob: https:; "
|
|
"object-src 'none'; "
|
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; "
|
|
"style-src 'self' 'unsafe-inline' https:; "
|
|
"report-uri /api/csp-report"
|
|
),
|
|
"X-Content-Type-Options": "nosniff",
|
|
"X-Frame-Options": "SAMEORIGIN", # Less strict for dev tools
|
|
"X-XSS-Protection": "1; mode=block",
|
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
|
"X-Development-Mode": "true",
|
|
"Cache-Control": "no-cache, no-store, must-revalidate"
|
|
}
|
|
|
|
def _get_production_headers(self) -> Dict[str, str]:
|
|
"""Get strict production security headers"""
|
|
return {
|
|
"Content-Security-Policy": settings.csp_header(),
|
|
"X-Content-Type-Options": "nosniff",
|
|
"X-Frame-Options": "DENY",
|
|
"X-XSS-Protection": "1; mode=block",
|
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
|
"Strict-Transport-Security": (
|
|
"max-age=31536000; includeSubDomains; preload"
|
|
),
|
|
"Permissions-Policy": (
|
|
"camera=(), microphone=(), geolocation=(), payment=()"
|
|
)
|
|
}
|
|
|
|
def get_cors_config(self) -> Dict[str, Any]:
|
|
"""Get CORS configuration for current environment"""
|
|
if self.is_development:
|
|
return {
|
|
"allow_origins": self.dev_overrides['CORS_ALLOWED_ORIGINS'],
|
|
"allow_credentials": True,
|
|
"allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
"allow_headers": ["*"],
|
|
"expose_headers": ["X-Request-ID", "X-API-Version"]
|
|
}
|
|
else:
|
|
# Production CORS - more restrictive
|
|
return {
|
|
"allow_origins": settings.FRONTEND_ORIGINS,
|
|
"allow_credentials": True,
|
|
"allow_methods": ["GET", "POST", "PUT", "DELETE"],
|
|
"allow_headers": [
|
|
"Authorization",
|
|
"Content-Type",
|
|
"X-CSRF-Token",
|
|
"X-API-Key"
|
|
],
|
|
"expose_headers": ["X-Request-ID"]
|
|
}
|
|
|
|
def get_rate_limit_config(self) -> Dict[str, int]:
|
|
"""Get rate limiting configuration"""
|
|
if self.is_development:
|
|
return {
|
|
"requests_per_minute": self.dev_overrides[
|
|
'RATE_LIMIT_PER_MINUTE'],
|
|
"burst_limit": self.dev_overrides['RATE_LIMIT_BURST']
|
|
}
|
|
else:
|
|
return {
|
|
"requests_per_minute": 60,
|
|
"burst_limit": 20
|
|
}
|
|
|
|
def should_log_sql(self) -> bool:
|
|
"""Whether to log SQL queries"""
|
|
return (self.is_development and
|
|
self.dev_overrides.get('LOG_SQL_QUERIES', False))
|
|
|
|
def should_enable_debug_routes(self) -> bool:
|
|
"""Whether to enable debug/test routes"""
|
|
return (self.is_development and
|
|
self.dev_overrides.get('ALLOW_TEST_ROUTES', False))
|
|
|
|
def get_session_config(self) -> Dict[str, Any]:
|
|
"""Get session configuration for current environment"""
|
|
if self.is_development:
|
|
return {
|
|
"secure": self.dev_overrides['SESSION_SECURE'],
|
|
"httponly": self.dev_overrides['SESSION_HTTPONLY'],
|
|
"samesite": "lax",
|
|
"max_age": 3600 * 24 # 24 hours for development
|
|
}
|
|
else:
|
|
return {
|
|
"secure": True,
|
|
"httponly": True,
|
|
"samesite": "strict",
|
|
"max_age": 3600 * 8 # 8 hours for production
|
|
}
|
|
|
|
def get_logging_config(self) -> Dict[str, Any]:
|
|
"""Get logging configuration"""
|
|
if self.is_development:
|
|
return {
|
|
"level": "DEBUG",
|
|
"format": (
|
|
"%(asctime)s - %(name)s - %(levelname)s - "
|
|
"%(filename)s:%(lineno)d - %(message)s"
|
|
),
|
|
"include_trace": True,
|
|
"log_sql": True,
|
|
"log_requests": True
|
|
}
|
|
else:
|
|
return {
|
|
"level": "INFO",
|
|
"format": (
|
|
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
),
|
|
"include_trace": False,
|
|
"log_sql": False,
|
|
"log_requests": False
|
|
}
|
|
|
|
def validate_development_security(self) -> Dict[str, Any]:
|
|
"""Validate that development environment is properly secured"""
|
|
warnings = []
|
|
recommendations = []
|
|
|
|
if self.is_development:
|
|
# Check for potential security issues in development
|
|
if os.getenv('SECRET_KEY') == 'dev-secret-key':
|
|
warnings.append("Using default development secret key")
|
|
recommendations.append(
|
|
"Set unique SECRET_KEY even in development")
|
|
|
|
db_url = os.getenv('DATABASE_URL', '')
|
|
if not db_url.startswith('sqlite'):
|
|
if 'localhost' not in db_url:
|
|
warnings.append("Database not on localhost in development")
|
|
recommendations.append(
|
|
"Use local database for development")
|
|
|
|
redis_url = os.getenv('REDIS_URL', '')
|
|
if redis_url and 'localhost' not in redis_url:
|
|
warnings.append("Redis not on localhost in development")
|
|
recommendations.append(
|
|
"Use local Redis instance for development")
|
|
|
|
# Check for production data in development
|
|
if 'prod' in os.getcwd().lower():
|
|
warnings.append(
|
|
"Development mode detected in production-like path")
|
|
recommendations.append(
|
|
"Ensure separate development environment")
|
|
|
|
return {
|
|
"is_development": self.is_development,
|
|
"warnings": warnings,
|
|
"recommendations": recommendations,
|
|
"config_overrides": len(self.dev_overrides),
|
|
"environment_secure": len(warnings) == 0
|
|
}
|
|
|
|
|
|
# Global instance
|
|
dev_config = DevelopmentSecurityConfig()
|
|
|
|
|
|
def get_environment_config() -> DevelopmentSecurityConfig:
|
|
"""Get the development configuration instance"""
|
|
return dev_config
|
|
|
|
|
|
def is_development_mode() -> bool:
|
|
"""Quick check if running in development mode"""
|
|
return dev_config.is_development
|
|
|
|
|
|
def get_security_config_for_environment() -> Dict[str, Any]:
|
|
"""Get complete security configuration for current environment"""
|
|
env_name = ("development" if dev_config.is_development
|
|
else "production")
|
|
|
|
config = {
|
|
"environment": env_name,
|
|
"security_headers": dev_config.get_security_headers(),
|
|
"cors_config": dev_config.get_cors_config(),
|
|
"rate_limit_config": dev_config.get_rate_limit_config(),
|
|
"session_config": dev_config.get_session_config(),
|
|
"logging_config": dev_config.get_logging_config(),
|
|
"validation": dev_config.validate_development_security()
|
|
}
|
|
|
|
return config
|