JavaRush /Курси /Модуль 3: Django /Створення кастомних дозволів

Створення кастомних дозволів

Модуль 3: Django
Рівень 21 , Лекція 3
Відкрита

Для початку давайте подумаємо, чому стандартних дозволів може бути недостатньо. Розглянемо пару прикладів:

  1. Роль автора: ви хочете, щоб тільки користувач, який створив статтю, міг її редагувати або видаляти.
  2. Специфічна логіка доступу: тільки користувачі з активною підпискою можуть залишати коментарі.
  3. Динамічні умови: доступ до ресурсу дозволено тільки в разі, якщо дата створення об'єкта не старше місяця.

У таких випадках простіше і логічніше написати власний дозвіл, ніж намагатися об'єднати стандартні.

Як пишуться кастомні дозволи?

У DRF дозвіл — це клас, який наслідується від BasePermission і реалізує метод has_permission() або/і метод has_object_permission(). Давай розберемо їх по черзі:

  1. has_permission(self, request, view) — перевіряє, чи може користувач взагалі отримати доступ до ендпоінта (в рамках всього представлення).
  2. has_object_permission(self, request, view, obj) — перевіряє права доступу до конкретного об'єкта (наприклад, до певної статті).

Приклад 1: дозвіл тільки для авторів об'єкта

Припустимо, ти хочеш дозволити редагувати записи тільки тим користувачам, які їх створили.

Код дозволу буде виглядати так:

from rest_framework.permissions import BasePermission

class IsAuthor(BasePermission):
    """
    Дозволяє доступ тільки авторам об'єкта.
    """

    def has_object_permission(self, request, view, obj):
        # Перевіряємо, що користувач є автором об'єкта
        return obj.author == request.user

Тепер застосуємо його в нашому ViewSet:

from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from .permissions import IsAuthor
from .models import Post
from .serializers import PostSerializer

class PostViewSet(ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    # Комбінація дозволів: користувач має бути аутентифікованим і бути автором об'єкта.
    permission_classes = [IsAuthenticated, IsAuthor]

Такий підхід дозволить виділити авторів серед усіх аутентифікованих користувачів.

Приклад 2: дозвіл на основі групи

Тепер створимо дозвіл, який дозволяє доступ тільки користувачам, що належать до певної групи, наприклад, "Модератори".

from rest_framework.permissions import BasePermission

class IsInModeratorGroup(BasePermission):
    """
    Дозволяє доступ тільки для користувачів з групи "Модератори".
    """

    def has_permission(self, request, view):
        # Перевіряємо, що користувач аутентифікований і входить до групи "Модератори"
        return request.user.is_authenticated and request.user.groups.filter(name="Модератори").exists()

У цьому випадку ми перевіряємо доступ на рівні ендпоінта, а не конкретного об'єкта.

Застосовуємо дозвіл так само, як у попередньому прикладі:

permission_classes = [IsAuthenticated, IsInModeratorGroup]

Логіка і порядок застосування дозволів

DRF дозволяє задавати кілька дозволів одночасно, комбінуючи їх у список permission_classes. При цьому всі дозволи зі списку перевіряються; якщо хоча б один повертає False — доступ буде заборонений.

Приклад з комбінуванням:

permission_classes = [IsAuthenticated, IsAuthor]

Щоб запит пройшов, користувач повинен бути аутентифікованим і бути автором об'єкта.

Приклад 3: умова для GET і POST запитів

Тепер давайте створимо кастомний дозвіл, який:

  • Дозволяє GET-запити всім.
  • Вимагає аутентифікації для POST-запитів.
from rest_framework.permissions import BasePermission, SAFE_METHODS

class ReadOnlyOrIsAuthenticated(BasePermission):
    """
    Дозволяє доступ для всіх на читання (GET), але вимагає аутентифікації для запису (POST).
    """

    def has_permission(self, request, view):
        if request.method in SAFE_METHODS:  # SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
            return True
        return request.user.is_authenticated

Ось так ми можемо одночасно підтримувати відкритий доступ для читання даних і захищений доступ для їх створення.

Інтеграція кастомних дозволів у реальному проєкті

Припустимо, ми працюємо над блогом, де:

  • Модератори можуть видаляти будь-які коментарі.
  • Автори коментарів можуть редагувати тільки свої коментарі.

Створимо два кастомні дозволи: IsModerator і IsCommentAuthor.

from rest_framework.permissions import BasePermission

class IsModerator(BasePermission):
    """
    Перевіряє, чи є користувач Модератором.
    """
    def has_permission(self, request, view):
        return request.user.groups.filter(name="Модератори").exists()

class IsCommentAuthor(BasePermission):
    """
    Перевіряє, чи є користувач автором коментаря.
    """
    def has_object_permission(self, request, view, obj):
        return obj.author == request.user

Тепер налаштовуємо дозволи у ViewSet:

from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated, SAFE_METHODS
from .permissions import IsModerator, IsCommentAuthor
from .models import Comment
from .serializers import CommentSerializer

class CommentViewSet(ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer

    def get_permissions(self):
        # Відкриваємо доступ тільки на читання для всіх
        if self.request.method in SAFE_METHODS:
            return [permission() for permission in []]

        # Модератори можуть видаляти будь-які коментарі
        if self.request.method == 'DELETE':
            return [IsModerator()]

        # Будь-які інші дії (наприклад, PUT) доступні тільки автору
        return [IsAuthenticated(), IsCommentAuthor()]

Поширені помилки при створенні кастомних дозволів

  1. Забули обробити obj у has_object_permission()
    DRF при доступі до об'єктів викликає метод has_object_permission() тільки якщо він визначений. Якщо ти обмежуєшся has_permission(), перевірка прав на рівні об'єктів буде проігнорована.

  2. Не повертають True/False явно
    Методи дозволів завжди повинні повертати строго True або False, інакше виникнуть несподівані помилки.

  3. Помилки у логіці складних дозволів
    Наприклад, забули врахувати різні HTTP-методи. Завжди перевіряй свою логіку через тести!

Коли кастомізація — це зло?

Іноді спокуса написати складні кастомні дозволи може зіграти злий жарт. Якщо ваші дозволи стають надмірно комплексними, це ускладнює подальшу підтримку проєкту. У таких випадках варто переглянути архітектуру проєкту або задуматися про використання ролевої моделі (IsInGroup), яка дозволяє абстрагуватися від прямих перевірок.

Практичне завдання

  1. Напишіть кастомний дозвіл, який дозволить:

    • Користувачам із групи "Адміністратори" видаляти записи.
    • Іншим користувачам тільки переглядати записи (GET).
    • Вимкніть доступ на запис (POST, PUT) для всіх інших.
  2. Застосуйте цей дозвіл до моделі Post у вашому проєкті. Напишіть кілька тестів за допомогою DRF тест-клієнта, щоб перевірити різну поведінку.

Тепер ми не просто знаємо, як використовувати вбудовані можливості DRF, але й можемо створювати власні кастомні рішення, адаптуючи систему безпеки наших API до найскладніших сценаріїв. Хто тепер зупинить вас від створення світового панування за допомогою кастомних дозволів? Ніхто! 🙂

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ