uchill/backend/apps/users/middleware/email_verification.py

154 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Middleware для проверки подтверждения email пользователя.
Блокирует все запросы, кроме связанных с подтверждением email.
"""
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
class EmailVerificationMiddleware(MiddlewareMixin):
"""
Middleware для проверки подтверждения email.
Разрешает доступ к следующим endpoints без подтверждения email:
- /api/users/auth/register/
- /api/users/auth/login/
- /api/users/auth/telegram/
- /api/users/auth/telegram/bot-info/
- /api/users/auth/token/refresh/
- /api/users/auth/verify-email/
- /api/users/auth/resend-verification/
- /api/users/auth/password-reset/
- /api/users/auth/password-reset-confirm/
- /api/users/auth/logout/
- /api/docs/ (Swagger документация)
- /api/schema/ (API схема)
"""
# Список путей, которые разрешены без подтверждения email
ALLOWED_PATHS = [
# Аутентификация и регистрация
'/api/auth/register/',
'/api/auth/login/',
'/api/auth/telegram/',
'/api/auth/telegram/bot-info/',
'/api/auth/token/refresh/',
'/api/auth/verify-email/',
'/api/auth/resend-verification/',
'/api/auth/change-password/',
'/api/auth/password-reset/',
'/api/auth/password-reset-confirm/',
'/api/auth/logout/',
# API документация
'/api/swagger/',
'/api/swagger.json',
'/api/swagger.yaml',
'/api/redoc/',
'/api/schema/',
# Статические файлы и медиа
'/static/',
'/media/',
# Admin панель
'/admin/',
# Health check
'/health/',
]
def process_request(self, request):
"""
Обработка запроса перед передачей в view.
"""
# Проверяем, является ли путь разрешенным
if self._is_allowed_path(request.path):
return None # Разрешаем запрос
# Проверяем, есть ли токен авторизации
if not self._has_auth_token(request):
return None # Если нет токена, пусть другие middleware обрабатывают
# Получаем пользователя из токена
user = self._get_user_from_token(request)
if user is None:
return None # Если не удалось получить пользователя, пусть другие middleware обрабатывают
# Проверяем подтверждение email
if not user.email_verified:
return JsonResponse(
{
'success': False,
'error': 'email_not_verified',
'message': 'Необходимо подтвердить email адрес для доступа к платформе',
'email': user.email,
},
status=403
)
return None # Разрешаем запрос
def _is_allowed_path(self, path: str) -> bool:
"""
Проверяет, является ли путь разрешенным.
"""
# Проверяем точное совпадение
if path in self.ALLOWED_PATHS:
return True
# Проверяем, начинается ли путь с разрешенного
for allowed_path in self.ALLOWED_PATHS:
if path.startswith(allowed_path):
return True
# Разрешаем доступ к статическим файлам и медиа
if path.startswith('/static/') or path.startswith('/media/'):
return True
# Разрешаем доступ к admin панели
if path.startswith('/admin/'):
return True
return False
def _has_auth_token(self, request) -> bool:
"""
Проверяет наличие токена авторизации в запросе.
"""
# Проверяем заголовок Authorization
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
if auth_header.startswith('Bearer '):
return True
# Проверяем токен в cookies (если используется)
if 'access_token' in request.COOKIES:
return True
return False
def _get_user_from_token(self, request):
"""
Получает пользователя из JWT токена.
"""
try:
# Используем JWTAuthentication для получения пользователя
jwt_auth = JWTAuthentication()
header = jwt_auth.get_header(request)
if header is None:
return None
raw_token = jwt_auth.get_raw_token(header)
if raw_token is None:
return None
validated_token = jwt_auth.get_validated_token(raw_token)
user = jwt_auth.get_user(validated_token)
return user
except (InvalidToken, TokenError, AttributeError, TypeError, ValueError):
# Если токен невалиден или отсутствует, возвращаем None
return None
except Exception:
# Для любых других ошибок также возвращаем None
return None