Corp/backend/api/v1/schedule/permissions.py
igor_nadein 8e66ac9b65 feat(employees): переход на Service Layer и перенос birthday логики из calendar_app
- Интегрирован django-service-objects 0.7.1 для Service Layer Pattern
- Перенесена вся логика дней рождения из calendar_app → employees:
  * UpsertBirthdayEventService - создание/обновление событий ДР
  * DeleteBirthdayEventService - удаление событий ДР
  * BulkSyncBirthdaysService - массовая синхронизация
  * signals_birthday.py - автосинхронизация через post_save/post_delete
  * management command sync_birthdays - CLI команда синхронизации
  * тесты (11 tests, 6 passing)

- Обновлена миграция 0013_migrate_to_django_scheduler:
  * Добавлен ШАГ 5: автоматическая синхронизация дней рождения (131 событие)
  * Исключены старые события ДР из миграции (source.endswith(':birthday'))
  * Исправлены имена сотрудников (f'{last_name} {first_name}' вместо get_full_name())
  * Добавлен цвет #FFC107 для событий дней рождения

- Очищен calendar_app от birthday сервисов:
  * Удалены calendar_app/services/birthday_events.py
  * Удалены calendar_app/signals_scheduler.py
  * Удалены calendar_app/management/commands/sync_birthdays.py
  * Удалены calendar_app/tests/test_services.py
  * Обновлен apps.py - удалены ссылки на signals_scheduler

- API улучшения:
  * Добавлен api/v1/schedule/permissions.py (IsCalendarOwnerOrAdmin)
  * Обновлен views.py с правильными permissions

- Документация:
  * DJANGO_SERVICE_OBJECTS_INTEGRATION.md - руководство по Service Layer
  * CALENDAR_MIGRATION_ANALYSIS.md - анализ миграции
  * MODERNIZATION_ANALYSIS.md - анализ модернизации кодовой базы

Цель: подготовка к будущему удалению calendar_app, централизация
логики сотрудников в employees приложении.

Refs: #birthday-refactoring
2026-02-27 23:49:09 +03:00

103 lines
4.0 KiB
Python

# backend/api/v1/schedule/permissions.py
"""
Права доступа для django-scheduler API.
"""
from rest_framework import permissions
from django.contrib.contenttypes.models import ContentType
from schedule.models import CalendarRelation
class IsOwnerOfCalendar(permissions.BasePermission):
"""
Только owner календаря может изменять/удалять календарь.
Viewer и editor могут только читать календарь.
"""
def has_object_permission(self, request, view, obj):
"""Проверка прав на календарь."""
# SAFE_METHODS (GET, HEAD, OPTIONS) доступны всем участникам
if request.method in permissions.SAFE_METHODS:
return True
# Только owner может изменять/удалять календарь
return self._is_owner(obj, request.user)
@staticmethod
def _is_owner(calendar, user):
"""Проверка, является ли пользователь владельцем календаря."""
if user.is_staff or user.is_superuser:
return True
from django.contrib.auth import get_user_model
User = get_user_model()
ct = ContentType.objects.get_for_model(User)
return CalendarRelation.objects.filter(
calendar=calendar,
content_type=ct,
object_id=user.id,
distinction='owner'
).exists()
class CanEditCalendar(permissions.BasePermission):
"""
Права для событий в календаре:
- owner: полный доступ (create, read, update, delete)
- editor: может создавать/изменять/удалять события (create, read, update, delete)
- viewer: только чтение (read)
"""
def has_permission(self, request, view):
"""Проверка прав на создание события."""
# Для создания события нужно быть owner или editor календаря
if view.action == 'create':
calendar_id = request.data.get('calendar')
if not calendar_id:
return False
from schedule.models import Calendar
try:
calendar = Calendar.objects.get(id=calendar_id)
except Calendar.DoesNotExist:
return False
return self._can_edit(calendar, request.user)
return True
def has_object_permission(self, request, view, obj):
"""Проверка прав на событие."""
# Event object
event = obj
calendar = event.calendar
# SAFE_METHODS (GET, HEAD, OPTIONS) доступны всем участникам
if request.method in permissions.SAFE_METHODS:
return True
# Для изменения/удаления нужны права editor или owner
return self._can_edit(calendar, request.user)
@staticmethod
def _can_edit(calendar, user):
"""Проверка, может ли пользователь редактировать события в календаре."""
if user.is_staff or user.is_superuser:
return True
from django.contrib.auth import get_user_model
User = get_user_model()
ct = ContentType.objects.get_for_model(User)
# Проверяем роль пользователя в календаре
try:
relation = CalendarRelation.objects.get(
calendar=calendar,
content_type=ct,
object_id=user.id
)
# Owner и editor могут редактировать, viewer - нет
return relation.distinction in ['owner', 'editor']
except CalendarRelation.DoesNotExist:
return False