This commit is contained in:
Iliyan Angelov
2025-09-19 11:58:53 +03:00
parent 306b20e24a
commit 6b247e5b9f
11423 changed files with 1500615 additions and 778 deletions

View File

@@ -0,0 +1,289 @@
"""
Zero Trust Middleware
Automatically applies Zero Trust principles to all requests
"""
import logging
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin
from django.contrib.auth import get_user_model
from django.utils import timezone
from ..services.zero_trust import zero_trust_service
logger = logging.getLogger(__name__)
User = get_user_model()
class ZeroTrustMiddleware(MiddlewareMixin):
"""
Middleware that applies Zero Trust principles to all requests
"""
def __init__(self, get_response):
self.get_response = get_response
super().__init__(get_response)
def process_request(self, request):
"""Process incoming request for Zero Trust assessment"""
# Skip Zero Trust for certain paths
skip_paths = [
'/admin/',
'/static/',
'/media/',
'/api/auth/login/',
'/api/auth/logout/',
'/health/',
'/metrics/',
]
if any(request.path.startswith(path) for path in skip_paths):
return None
# Only apply to authenticated users
if not request.user.is_authenticated:
return None
# Skip for API endpoints that don't require Zero Trust
if request.path.startswith('/api/') and self._is_public_endpoint(request.path):
return None
try:
# Collect request context
request_context = self._collect_request_context(request)
# Perform Zero Trust assessment
assessment_result = zero_trust_service.assess_access_request(
request.user,
request_context
)
# Handle assessment result
if not assessment_result.get('access_granted', False):
return self._handle_access_denied(request, assessment_result)
# Store assessment result in request for use in views
request.zero_trust_assessment = assessment_result
# Update behavior profile
zero_trust_service.update_behavior_profile(request.user, request_context)
except Exception as e:
logger.error(f"Zero Trust middleware error: {e}")
# In case of error, allow access but log the issue
request.zero_trust_assessment = {
'access_granted': True,
'reason': 'Assessment error - defaulting to allow',
'risk_level': 'UNKNOWN',
'error': str(e)
}
return None
def _collect_request_context(self, request) -> dict:
"""Collect context data from request"""
# Get client IP
ip_address = self._get_client_ip(request)
# Get device ID from headers or session
device_id = (
request.headers.get('X-Device-ID') or
request.session.get('device_id') or
self._generate_device_fingerprint(request)
)
return {
'ip_address': ip_address,
'user_agent': request.META.get('HTTP_USER_AGENT', ''),
'device_id': device_id,
'timestamp': timezone.now(),
'request_method': request.method,
'request_path': request.path,
'referer': request.META.get('HTTP_REFERER', ''),
'accept_language': request.META.get('HTTP_ACCEPT_LANGUAGE', ''),
'session_id': request.session.session_key,
}
def _get_client_ip(self, request) -> str:
"""Get client IP address from request"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0].strip()
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def _generate_device_fingerprint(self, request) -> str:
"""Generate a device fingerprint for tracking"""
import hashlib
# Create fingerprint from user agent and IP
fingerprint_data = f"{request.META.get('HTTP_USER_AGENT', '')}{self._get_client_ip(request)}"
return hashlib.sha256(fingerprint_data.encode()).hexdigest()[:16]
def _is_public_endpoint(self, path: str) -> bool:
"""Check if endpoint is public and doesn't require Zero Trust"""
public_endpoints = [
'/api/auth/',
'/api/health/',
'/api/status/',
'/api/docs/',
]
return any(path.startswith(endpoint) for endpoint in public_endpoints)
def _handle_access_denied(self, request, assessment_result: dict) -> JsonResponse:
"""Handle access denied based on Zero Trust assessment"""
required_actions = assessment_result.get('required_actions', [])
risk_level = assessment_result.get('risk_level', 'UNKNOWN')
reason = assessment_result.get('reason', 'Access denied')
# Determine response based on required actions
if 'MANUAL_REVIEW' in required_actions:
return JsonResponse({
'error': 'Access requires manual review',
'reason': reason,
'risk_level': risk_level,
'required_actions': required_actions,
'support_contact': 'security@company.com'
}, status=423) # 423 Locked
elif 'STEP_UP_AUTH' in required_actions:
return JsonResponse({
'error': 'Additional authentication required',
'reason': reason,
'risk_level': risk_level,
'required_actions': required_actions,
'auth_url': '/api/auth/step-up/'
}, status=401) # 401 Unauthorized
elif 'DEVICE_REGISTRATION' in required_actions:
return JsonResponse({
'error': 'Device registration required',
'reason': reason,
'risk_level': risk_level,
'required_actions': required_actions,
'registration_url': '/api/security/register-device/'
}, status=403) # 403 Forbidden
elif 'ADDITIONAL_MFA' in required_actions:
return JsonResponse({
'error': 'Additional MFA required',
'reason': reason,
'risk_level': risk_level,
'required_actions': required_actions,
'mfa_url': '/api/auth/mfa/'
}, status=401) # 401 Unauthorized
else:
# Generic access denied
return JsonResponse({
'error': 'Access denied',
'reason': reason,
'risk_level': risk_level,
'required_actions': required_actions
}, status=403) # 403 Forbidden
class DeviceRegistrationMiddleware(MiddlewareMixin):
"""
Middleware to handle device registration for Zero Trust
"""
def process_request(self, request):
"""Process device registration requests"""
if request.path == '/api/security/register-device/' and request.method == 'POST':
return self._handle_device_registration(request)
return None
def _handle_device_registration(self, request):
"""Handle device registration"""
try:
if not request.user.is_authenticated:
return JsonResponse({'error': 'Authentication required'}, status=401)
device_data = request.json if hasattr(request, 'json') else {}
# Register device
device_posture = zero_trust_service.register_device(request.user, device_data)
return JsonResponse({
'success': True,
'device_id': device_posture.device_id,
'trust_level': device_posture.trust_level,
'risk_score': device_posture.risk_score,
'is_compliant': device_posture.is_compliant,
'message': 'Device registered successfully'
})
except Exception as e:
logger.error(f"Device registration failed: {e}")
return JsonResponse({
'error': 'Device registration failed',
'details': str(e)
}, status=400)
class RiskBasedRateLimitMiddleware(MiddlewareMixin):
"""
Middleware for risk-based rate limiting
"""
def __init__(self, get_response):
self.get_response = get_response
super().__init__(get_response)
self.request_counts = {} # In production, use Redis or database
def process_request(self, request):
"""Apply risk-based rate limiting"""
if not request.user.is_authenticated:
return None
# Get user's risk level from Zero Trust assessment
assessment = getattr(request, 'zero_trust_assessment', None)
if not assessment:
return None
risk_level = assessment.get('risk_level', 'LOW')
user_id = str(request.user.id)
current_time = timezone.now()
# Define rate limits based on risk level
rate_limits = {
'LOW': {'requests': 1000, 'window': 3600}, # 1000 requests per hour
'MEDIUM': {'requests': 500, 'window': 3600}, # 500 requests per hour
'HIGH': {'requests': 100, 'window': 3600}, # 100 requests per hour
'CRITICAL': {'requests': 10, 'window': 3600}, # 10 requests per hour
}
limit = rate_limits.get(risk_level, rate_limits['LOW'])
# Check rate limit
if self._is_rate_limited(user_id, limit, current_time):
return JsonResponse({
'error': 'Rate limit exceeded',
'reason': f'Too many requests for risk level {risk_level}',
'retry_after': limit['window']
}, status=429) # 429 Too Many Requests
return None
def _is_rate_limited(self, user_id: str, limit: dict, current_time) -> bool:
"""Check if user has exceeded rate limit"""
# Simplified rate limiting (in production, use proper rate limiting library)
window_start = current_time.timestamp() - limit['window']
# Clean old entries
if user_id in self.request_counts:
self.request_counts[user_id] = [
timestamp for timestamp in self.request_counts[user_id]
if timestamp > window_start
]
else:
self.request_counts[user_id] = []
# Check if limit exceeded
if len(self.request_counts[user_id]) >= limit['requests']:
return True
# Add current request
self.request_counts[user_id].append(current_time.timestamp())
return False