Для початку давайте подумаємо, чому стандартних дозволів може бути недостатньо. Розглянемо пару прикладів:
- Роль автора: ви хочете, щоб тільки користувач, який створив статтю, міг її редагувати або видаляти.
- Специфічна логіка доступу: тільки користувачі з активною підпискою можуть залишати коментарі.
- Динамічні умови: доступ до ресурсу дозволено тільки в разі, якщо дата створення об'єкта не старше місяця.
У таких випадках простіше і логічніше написати власний дозвіл, ніж намагатися об'єднати стандартні.
Як пишуться кастомні дозволи?
У DRF дозвіл — це клас, який наслідується від BasePermission і реалізує метод has_permission() або/і метод has_object_permission(). Давай розберемо їх по черзі:
has_permission(self, request, view)— перевіряє, чи може користувач взагалі отримати доступ до ендпоінта (в рамках всього представлення).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()]
Поширені помилки при створенні кастомних дозволів
Забули обробити
objуhas_object_permission()
DRF при доступі до об'єктів викликає методhas_object_permission()тільки якщо він визначений. Якщо ти обмежуєшсяhas_permission(), перевірка прав на рівні об'єктів буде проігнорована.Не повертають
True/Falseявно
Методи дозволів завжди повинні повертати строгоTrueабоFalse, інакше виникнуть несподівані помилки.Помилки у логіці складних дозволів
Наприклад, забули врахувати різні HTTP-методи. Завжди перевіряй свою логіку через тести!
Коли кастомізація — це зло?
Іноді спокуса написати складні кастомні дозволи може зіграти злий жарт. Якщо ваші дозволи стають надмірно комплексними, це ускладнює подальшу підтримку проєкту. У таких випадках варто переглянути архітектуру проєкту або задуматися про використання ролевої моделі (IsInGroup), яка дозволяє абстрагуватися від прямих перевірок.
Практичне завдання
Напишіть кастомний дозвіл, який дозволить:
- Користувачам із групи "Адміністратори" видаляти записи.
- Іншим користувачам тільки переглядати записи (GET).
- Вимкніть доступ на запис (POST, PUT) для всіх інших.
Застосуйте цей дозвіл до моделі
Postу вашому проєкті. Напишіть кілька тестів за допомогою DRF тест-клієнта, щоб перевірити різну поведінку.
Тепер ми не просто знаємо, як використовувати вбудовані можливості DRF, але й можемо створювати власні кастомні рішення, адаптуючи систему безпеки наших API до найскладніших сценаріїв. Хто тепер зупинить вас від створення світового панування за допомогою кастомних дозволів? Ніхто! 🙂
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ