- Создана заглушка для calendarEventDragDrop.js с экспортом initCalendarEventDragDrop - Закомментирован импорт несуществующего database.js в emoji-picker/index.js - Исправлена ошибка 'does not provide an export named initCalendarEventDragDrop' - Исправлена ошибка 404 для emoji-picker/database.js
7.4 KiB
Исправление прав доступа для глобальных календарей
Дата: 23 февраля 2026 г. Статус: ✅ Выполнено
Проблема
Обычные пользователи не могли создавать события в глобальных календарях (новая архитектура), даже если у них были соответствующие права через подписку (can_edit=True) или если календарь имел default_can_edit=True.
Причина
Метод get_permissions() в CalendarEventsViewSet проверял права только для legacy логики (department/employee), но не учитывал новую архитектуру с calendar_id:
# Старая логика (ОШИБКА):
def get_permissions(self):
emp = self._employee_id(required=False)
dep = self._dept_id(required=False)
# Календарь компании — только администраторы
if dep is None: # ❌ Срабатывало для глобальных календарей!
return [IsAdminUser()]
Когда пользователь создавал событие в глобальном календаре:
calendar_idпередавался в теле запросаget_permissions()НЕ виделcalendar_id(смотрел только URL/query параметры)- Видел
dep = None,emp = None - Считал это legacy календарём компании
- Блокировал всех, кроме администраторов
Решение
Переписан метод get_permissions() с поддержкой новой архитектуры календарей:
Новая логика проверки прав
- Приоритет новой архитектуры: Сначала проверяется наличие
calendar_id - Проверка через модель Calendar: Используется метод
calendar.can_user_edit(user) - Fallback на legacy: Если
calendar_idне указан, используется старая логика
Класс CalendarEditPermission
class CalendarEditPermission(BasePermission):
"""Проверка прав редактирования через календари."""
def has_permission(self, request, view):
# Ищем calendar_id в:
# 1. Теле запроса (create)
# 2. Query параметрах
# 3. Объекте события (update/delete)
if calendar_id is not None:
calendar = Calendar.objects.get(id=int(calendar_id))
return calendar.can_user_edit(request.user)
return None # Переход к legacy логике
Метод calendar.can_user_edit()
Проверяет права в следующем порядке:
- Админы/staff → ✅ Всегда могут
- Владелец календаря → ✅ Всегда может
- Подписка с
can_edit=True→ ✅ Может - Иначе → ❌ Не может
Изменённые файлы
backend/api/v1/calendar/views.py- Метод
CalendarEventsViewSet.get_permissions()— добавлена проверка новой архитектуры - Класс
CalendarEditPermission— новый permission для календарей
- Метод
Как это работает теперь
Сценарий 1: Глобальный календарь с default_can_edit=True
# Создание календаря админом
calendar = Calendar.objects.create(
title="Общие мероприятия",
visibility=CalendarVisibility.PUBLIC,
default_can_edit=True # ✅ Все могут редактировать
)
# Обычный пользователь создаёт событие
POST /api/v1/calendar/events/
{
"calendar_id": 1,
"title": "Корпоратив",
"start_date": "2026-03-01",
"all_day": true
}
# ✅ Успех! Проверка через calendar.can_user_edit()
Сценарий 2: Глобальный календарь с подпиской
# Админ приглашает пользователя
calendar.subscriptions.create(
user=user,
can_edit=True # ✅ Даём право редактировать
)
# Пользователь создаёт событие
POST /api/v1/calendar/events/
{
"calendar_id": 1,
"title": "Важная встреча",
"start_date": "2026-03-05",
"all_day": true
}
# ✅ Успех! can_edit=True в подписке
Сценарий 3: Legacy календарь компании
# Без calendar_id — старая логика
POST /api/v1/calendar/events/
{
"title": "Собрание компании",
"start_date": "2026-03-10",
"all_day": true
}
# ❌ Требуется IsAdminUser (как и раньше)
Обратная совместимость
✅ Полностью сохранена:
- Legacy календари (без
calendar_id) работают по-прежнему - Календари отделов требуют
MANAGE_CALENDARpermission - Личные календари доступны только владельцу
Тестирование
Рекомендуемые тесты:
-
Глобальный календарь + default_can_edit=True:
- Обычный пользователь создаёт событие → ✅ Успех
-
Глобальный календарь + подписка с can_edit=True:
- Подписанный пользователь создаёт событие → ✅ Успех
- Неподписанный пользователь → ❌ Отказ
-
Глобальный календарь + подписка с can_edit=False:
- Подписанный пользователь создаёт событие → ❌ Отказ
-
Legacy календарь компании:
- Обычный пользователь → ❌ Отказ
- Админ → ✅ Успех
-
Update/Delete:
- Проверка прав через
event.calendar.can_user_edit()
- Проверка прав через
Дополнительные улучшения
Возможные расширения:
- Кеширование проверок прав (если календарей много)
- Логирование отказов для аудита
- Rate limiting для создания событий
- Webhook уведомления о создании событий
Связанные файлы
backend/calendar_app/models.py— моделиCalendar,CalendarSubscriptionbackend/api/v1/calendar/serializers.py— сериализаторы календарейbackend/api/v1/calendar/views.py— ViewSet для событий и календарей
Примечания
- Проверка прав происходит до валидации сериализатора
- Для update/delete используется тот же механизм через
get_object() - Админы и staff всегда имеют полный доступ
Автор: GitHub Copilot Проверено: Требуется тестирование