feat: Implement TOTP-based Two-Factor Authentication
- Add django-otp and qrcode dependencies - Create comprehensive 2FA views and templates in German - Add 2FA setup, verification, and management interfaces - Implement backup token system with 10 recovery codes - Add TwoFactorMiddleware for session enforcement - Integrate 2FA controls into user navigation menu - Support QR code generation for authenticator apps - Add forms for secure 2FA operations with validation - Configure OTP settings and admin site integration Features: - Optional 2FA (users can enable/disable) - TOTP compatible with Google Authenticator, Authy, etc. - Backup codes for emergency access - German language interface - Session-based 2FA enforcement - Password confirmation for sensitive operations - Production-ready with HTTPS support
This commit is contained in:
@@ -8,7 +8,10 @@ import threading
|
||||
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
||||
from django.db.models.signals import post_delete, post_save, pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import reverse
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
|
||||
from stiftung.audit import get_client_ip, log_action, track_model_changes
|
||||
|
||||
@@ -36,6 +39,40 @@ class AuditMiddleware(MiddlewareMixin):
|
||||
return response
|
||||
|
||||
|
||||
class TwoFactorMiddleware(MiddlewareMixin):
|
||||
"""
|
||||
Middleware that enforces 2FA verification for users with 2FA enabled
|
||||
"""
|
||||
|
||||
def process_request(self, request):
|
||||
"""Check if user needs 2FA verification"""
|
||||
# Skip if user is not authenticated
|
||||
if not request.user.is_authenticated:
|
||||
return None
|
||||
|
||||
# Skip for admin URLs and 2FA URLs themselves
|
||||
if (request.path.startswith('/admin/') or
|
||||
request.path.startswith('/two-factor/') or
|
||||
request.path.startswith('/auth/2fa/') or
|
||||
request.path == '/logout/' or
|
||||
request.path.startswith('/static/') or
|
||||
request.path.startswith('/media/')):
|
||||
return None
|
||||
|
||||
# Check if user has 2FA enabled
|
||||
has_2fa = TOTPDevice.objects.filter(user=request.user, confirmed=True).exists()
|
||||
|
||||
if has_2fa:
|
||||
# Check if user has completed 2FA verification in this session
|
||||
if not request.session.get('2fa_verified', False):
|
||||
# Redirect to 2FA verification page
|
||||
return HttpResponseRedirect(
|
||||
reverse('stiftung:two_factor_verify') + f'?next={request.path}'
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_current_request():
|
||||
"""Get the current request from thread-local storage"""
|
||||
return getattr(_local, "request", None)
|
||||
|
||||
Reference in New Issue
Block a user