From 446d59f0faf98064aeacb0674f5b569225136cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Fri, 28 Nov 2025 08:04:09 +0300 Subject: [PATCH] =?UTF-8?q?Fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=B0=20?= =?UTF-8?q?=D0=BA=20=D1=87=D0=B0=D1=82=D1=83=20=D0=BF=D1=80=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B5=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена поддержка разных типов чатов (private, global, department, group) - Для личных чатов проверяется участие через participants - Для чатов отдела проверяется принадлежность к отделу - Для глобального чата проверяется только is_active - Для групповых/канальных чатов используется ChatMembership Настройки CSRF: - CSRF_COOKIE_HTTPONLY = False - CSRF_COOKIE_SAMESITE = 'Lax' - CSRF_USE_SESSIONS = False --- backend/api/v1/communications/views.py | 58 ++++++++++++++----- backend/eusrr_backend/settings.py | 5 ++ .../templates/communications/chat_detail.html | 7 +++ 3 files changed, 56 insertions(+), 14 deletions(-) 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/', {