diff --git a/backend/api/v1/communications/views.py b/backend/api/v1/communications/views.py index 13f09a2e..2accf370 100644 --- a/backend/api/v1/communications/views.py +++ b/backend/api/v1/communications/views.py @@ -5,7 +5,7 @@ from django.contrib.auth.decorators import login_required from django.db import transaction from django.http import JsonResponse from django.shortcuts import get_object_or_404 -from django.views.decorators.csrf import ensure_csrf_cookie +from django.views.decorators.csrf import csrf_protect from django.views.decorators.http import require_POST from communications.models import ( @@ -16,9 +16,9 @@ from communications.models import ( ) +@csrf_protect @login_required @require_POST -@ensure_csrf_cookie def upload_message_with_attachments(request): """Отправка сообщения с вложениями""" chat_id = request.POST.get('chat_id') @@ -33,18 +33,48 @@ def upload_message_with_attachments(request): chat = get_object_or_404(Chat, pk=chat_id) # Проверка доступа к чату - membership = ChatMembership.objects.filter( - chat=chat, - user=request.user, - is_active=True, - can_send_messages=True - ).first() - - if not membership: - return JsonResponse( - {'ok': False, 'error': 'Cannot send messages to this chat'}, - status=403 - ) + # Для групповых, канальных и объявлений проверяем ChatMembership + if chat.type in ['group', 'channel', 'announcement']: + membership = ChatMembership.objects.filter( + chat=chat, + user=request.user, + is_active=True, + can_send_messages=True + ).first() + + if not membership: + return JsonResponse( + {'ok': False, 'error': 'Cannot send messages to this chat'}, + status=403 + ) + # Для личных чатов проверяем, что пользователь - участник + elif chat.type == 'private': + if not chat.participants.filter(id=request.user.id).exists(): + return JsonResponse( + {'ok': False, 'error': 'You are not a participant'}, + status=403 + ) + # Для чатов отдела проверяем принадлежность к отделу + elif chat.type == 'department': + if chat.department: + from employees.models import EmployeeDepartment + is_member = EmployeeDepartment.objects.filter( + department=chat.department, + employee=request.user, + is_active=True + ).exists() + if not is_member and chat.department.head_id != request.user.id: + return JsonResponse( + {'ok': False, 'error': 'Not a department member'}, + status=403 + ) + # Глобальный чат доступен всем активным пользователям + elif chat.type == 'global': + if not request.user.is_active: + return JsonResponse( + {'ok': False, 'error': 'User is not active'}, + status=403 + ) # Если нет ни текста, ни файлов if not content and not request.FILES: diff --git a/backend/eusrr_backend/settings.py b/backend/eusrr_backend/settings.py index 26130fb2..fb247032 100644 --- a/backend/eusrr_backend/settings.py +++ b/backend/eusrr_backend/settings.py @@ -191,6 +191,11 @@ CSRF_TRUSTED_ORIGINS = _split_env_list( os.getenv("CSRF_TRUSTED_ORIGINS", "https://*.sytes.net") ) +# Настройки CSRF для AJAX запросов +CSRF_COOKIE_HTTPONLY = False # Позволяет JavaScript читать cookie +CSRF_COOKIE_SAMESITE = 'Lax' +CSRF_USE_SESSIONS = False + # ----------------------------------------------------------------------------- # EMAIL # ----------------------------------------------------------------------------- diff --git a/backend/templates/communications/chat_detail.html b/backend/templates/communications/chat_detail.html index de1a0acf..ec6ceb92 100644 --- a/backend/templates/communications/chat_detail.html +++ b/backend/templates/communications/chat_detail.html @@ -360,6 +360,13 @@ document.addEventListener('DOMContentLoaded', function() { formData.append('chat_id', '{{ chat.id }}'); const csrftoken = getCookie('csrftoken'); + console.log('CSRF token:', csrftoken); + + if (!csrftoken) { + console.error('CSRF token not found in cookies!'); + alert('Ошибка: CSRF токен не найден. Обновите страницу.'); + return; + } try { const response = await fetch('/api/v1/communications/upload-message/', {