uchill/backend/apps/subscriptions/permissions.py

116 lines
6.0 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.

"""
Permissions для subscriptions модуля.
"""
from rest_framework import permissions
from rest_framework.exceptions import PermissionDenied
import logging
logger = logging.getLogger(__name__)
class IsSubscriptionOwner(permissions.BasePermission):
"""
Проверка что пользователь - владелец подписки.
"""
message = 'Только владелец подписки может выполнить это действие.'
def has_object_permission(self, request, view, obj):
"""Проверка доступа к объекту."""
return obj.user == request.user
class RequiresActiveSubscription(permissions.BasePermission):
"""
Проверка наличия активной подписки.
Блокирует POST, PUT, PATCH, DELETE запросы, если подписка не активна.
"""
message = 'Требуется активная подписка'
def has_permission(self, request, view):
"""Проверка разрешения на уровне запроса."""
# Разрешаем GET запросы (чтение)
if request.method in permissions.SAFE_METHODS:
return True
# Проверяем только для менторов
if not hasattr(request.user, 'role') or request.user.role != 'mentor':
return True
# Получаем активную подписку
from .services import SubscriptionService
from .models import Subscription
from django.utils import timezone
# Получаем все подписки пользователя
all_subscriptions = Subscription.objects.filter(
user=request.user,
status__in=['trial', 'active']
).order_by('-end_date')
# Также проверяем истекшие подписки для диагностики
expired_subscriptions = Subscription.objects.filter(
user=request.user,
status__in=['expired', 'past_due', 'cancelled']
).order_by('-end_date')
# Проверяем, есть ли действительно активная подписка
active_subscription = None
for sub in all_subscriptions:
if sub.is_active():
active_subscription = sub
break
# Логируем для отладки
logger.error(
f"RequiresActiveSubscription: {request.method} {request.path}, "
f"user={request.user.id}, has_active={active_subscription is not None}"
)
# Если нет активной подписки, блокируем запрос
if not active_subscription:
# Получаем информацию о подписке для сообщения об ошибке
from apps.users.models import Client
current_clients_count = Client.objects.filter(mentors=request.user).count()
# Если подписка есть, но истекла
expired_subscription = expired_subscriptions.first() if expired_subscriptions.exists() else None
if expired_subscription:
logger.error(
f"❌ BLOCKING: {request.method} {request.path}, "
f"user={request.user.id} - subscription expired (id={expired_subscription.id})"
)
# Используем стандартный формат DRF для PermissionDenied
# PermissionDenied принимает detail как строку, дополнительные данные добавляем через атрибуты
error = PermissionDenied(
detail='Ваша подписка истекла. Для продолжения работы необходимо продлить подписку.'
)
# Добавляем дополнительные данные в error для обработки в exception handler
error.detail_dict = {
'error': 'Подписка истекла',
'detail': 'Ваша подписка истекла. Для продолжения работы необходимо продлить подписку.',
'subscription_id': expired_subscription.id,
'end_date': expired_subscription.end_date.strftime('%Y-%m-%d') if expired_subscription.end_date else None,
'current_students': current_clients_count,
'paid_students': expired_subscription.student_count if expired_subscription.plan.subscription_type == 'per_student' else None,
'requires_renewal': True
}
raise error
else:
logger.error(
f"❌ BLOCKING: {request.method} {request.path}, "
f"user={request.user.id} - no active subscription"
)
error = PermissionDenied(
detail='Для использования платформы необходимо оформить подписку. Пожалуйста, перейдите на страницу подписок и выберите подходящий тариф.'
)
error.detail_dict = {
'error': 'Требуется активная подписка',
'detail': 'Для использования платформы необходимо оформить подписку. Пожалуйста, перейдите на страницу подписок и выберите подходящий тариф.'
}
raise error
return True