Модуль 30

Permissions та throttling в DRF

Ось готовий урок, створений спеціально для тебе в стилі CS50. Вмикай уяву, ми починаємо!


🎓 Урок: Permissions (Дозволи) та Throttling (Обмеження) в DRF

Привіт, друзі! Це CS50... тобто, це ваш гід по Django REST Framework! 👋

Сьогодні ми не просто пишемо код. Ми стаємо охоронцями вашої цифрової фортеці. Ми поговоримо про те, як сказати користувачеві "Тобі сюди не можна" і "Гей, полегше, не так швидко!".


1. 🔥 Вступ: Коли все йде шкереберть

Уявіть ситуацію. Ви створили чудовий API для соціальної мережі. Все працює ідеально. Але раптом...

Ви прокидаєтеся вранці, а на головній сторінці замість новин — реклама підозрілих казино, опублікована від імені адміністратора. А ще сервер ледве дихає, тому що якийсь бот надсилає 1000 запитів на секунду, намагаючись підібрати паролі.

Питання до вас: 1. Чи хотіли б ви, щоб будь-хто в інтернеті міг видалити ваш акаунт просто надіславши DELETE запит? 2. Чи справедливо, що один нахабний скрипт забирає всі ресурси сервера, а ваші реальні користувачі бачать помилку 500?

Звісно, ні! Без Permissions (Дозволів) ваш API — це прохідний двір, де кожен робить що хоче. А без Throttling (Тротлінгу) — це шведський стіл, де один гість з'їдає всю їжу за хвилину.

Аналогія проста: * Authentication (Автентифікація): Це як фейс-контроль у клубі. Ви показуєте паспорт. Охоронець каже: "Ок, я знаю, хто ти". * Permissions (Дозволи): Це VIP-зони всередині клубу. Те, що вас пустили в клуб, не означає, що ви можете зайти в кабінет директора і відкрити сейф. * Throttling (Тротлінг): Це бармен. Навіть якщо ви VIP, він не наллє вам 50 коктейлів за одну хвилину. Він скаже: "Друже, почекай трохи".


2. 🧠 Теоретична база: Як це працює під капотом

Давайте заглянемо під капот DRF.

Коли запит (request) прилітає на ваш сервер, він проходить через серію перевірок перед тим, як запуститься код вашого view (функції або класу, що обробляє логіку).

🔐 Permissions (Хто має право?)

DRF перевіряє права доступу. Якщо перевірка не пройдена, view навіть не запуститься, а користувач миттєво отримає помилку 403 Forbidden.

Що треба запам'ятати залізно: * IsAuthenticated: Пускаємо тільки тих, хто залогінився. * IsAdminUser: Пускаємо тільки персонал (is_staff=True). * AllowAny: Пускаємо всіх (небезпечно, але іноді треба для реєстрації). * Object Level Permissions: Це вищий пілотаж. Це коли ми перевіряємо право не просто "зайти на сторінку редагування", а "редагувати саме цей конкретний пост".

⏳ Throttling (Не поспішай)

Це механізм захисту від DDoS-атак та скрапінгу. Він рахує кількість запитів від одного користувача (або IP) за певний час. Якщо ліміт перевищено — помилка 429 Too Many Requests.

Інтуїтивно це виглядає так: DRF має маленький блокнотик (зазвичай у кеші), де ставить "галочку" кожного разу, коли ви робите запит. * Запит 1... ✅ * Запит 2... ✅ * Запит 101... 🛑 "Стоп, приходь через хвилину".


3. 🧪 Приклади: Від простого до реального

Приклад 1: "Тільки для своїх" (Базовий рівень)

Уявіть, що у нас є список секретних документів. Ми хочемо, щоб його бачили тільки авторизовані юзери.

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response

class SecretDocumentView(APIView):
    permission_classes = [IsAuthenticated]  # <--- Вся магія тут!

    def get(self, request):
        return Response({"secret": "Код від сейфа: 1234"})

Питання: Що побачить анонімний користувач, якщо спробує зробити сюди GET-запит? (Відповідь: Він отримає 403 Forbidden і повідомлення про те, що облікові дані не надані).


Приклад 2: "Дивитися можна всім, чіпати — тільки своє" (Реальний проєкт)

Це класика для блогів чи Instagram. Всі можуть читати пости, але редагувати пост може лише його автор. DRF не має такого вбудованого класу, тому ми пишемо свій!

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Дозволяє читати (GET, HEAD, OPTIONS) всім.
    Але писати/змінювати (POST, PUT, DELETE) — тільки власнику об'єкта.
    """
    def has_object_permission(self, request, view, obj):
        # 1. Якщо метод безпечний (читання) — дозволяємо завжди
        if request.method in permissions.SAFE_METHODS:
            return True

        # 2. Якщо це запит на зміну — перевіряємо, чи юзер є автором
        # Припускаємо, що у моделі є поле `author`
        return obj.author == request.user

Як це застосувати?

class PostDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

Логіка: Спочатку DRF перевірить, чи юзер взагалі залогінений (IsAuthenticated). Якщо так — запустить нашу кастомну перевірку IsOwnerOrReadOnly.


Приклад 3: "Заспокойся, ковбой" (Throttling)

Налаштуємо так, щоб аноніми могли робити 10 запитів на хвилину, а залогінені юзери — 100.

У settings.py:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '10/min',  # Аноніми
        'user': '100/min'  # Юзери
    }
}

Тепер це працює глобально для всього API! Вам навіть не треба нічого писати у views. DRF сам подивиться на IP аноніма або ID юзера і порахує їхні запити.


4. 🛠 Практична частина

Час забруднити руки кодом! Виконайте ці завдання:

🔹 Завдання 1: Повторення Створіть View, яка повертає рядок "Привіт, адмін!". Додайте permission, щоб цей рядок міг бачити тільки суперюзер (is_superuser=True). Спробуйте зайти звичайним юзером.

🔹 Завдання 2: Виправити помилку Студент написав код, але скаржиться, що будь-хто може видалити дані. Знайдіть, чого не вистачає.

class UserProfileView(APIView):
    def delete(self, request, pk):
        profile = get_object_or_404(Profile, pk=pk)
        profile.delete()
        return Response(status=204)

🔹 Завдання 3: Кастомний Permission (Міні-кейс) Напишіть пермішен IsGmailUser. Він має дозволяти доступ тільки тим користувачам, чия пошта закінчується на @gmail.com. Застосуйте його до будь-якої в'юшки.

🔹 Завдання 4: Scoped Throttling У вас є ендпоінт для відправки SMS. Це дорого. Зробіть так, щоб конкретно на цей ендпоінт можна було звертатися лише 1 раз на хвилину. Підказка: Використовуйте ScopedRateThrottle і атрибут throttle_scope у view.

🔹 Завдання 5: Питання "А що, якщо..." Що станеться, якщо у вас у списку permission_classes стоїть [AllowAny, IsAuthenticated]? (Подумайте, як DRF перевіряє список: це "І" чи "АБО"? Або він перевіряє послідовно?)


5. 💡 Мислення як у розробника

Ось вам кілька порад, як не наступити на граблі, на які наступали ми всі.

  1. Порядок має значення (іноді). У Permissions DRF зазвичай перевіряє умови. Якщо хоч одна умова поверне False, доступ закрито (логічне І). Але будьте обережні з налаштуваннями за замовчуванням у settings.py.

  2. Не довіряйте клієнту. Новачки часто думають: "Я просто приховаю кнопку 'Видалити' на фронтенді, і ніхто не видалить пост". Помилка! Будь-хто може відкрити Postman або cURL і надіслати запит напряму. Без перевірки на бекенді ваша система незахищена.

  3. Object Permissions — це дорого. Перевірка прав доступу до конкретного об'єкта вимагає запиту до бази даних. Якщо ви виводите список із 100 об'єктів і для кожного перевіряєте права — це може сповільнити систему. Думайте про оптимізацію.

  4. Throttling — це не панацея. Це захист від дурня або перевантаження. Справжню DDoS-атаку тротлінг на рівні Django/Python не зупинить (сервер впаде раніше). Для цього потрібні рішення типу Nginx або Cloudflare.


6. 🧩 Підсумок

Отже, що ми сьогодні здобули?

  • Ви навчилися бути охоронцем свого API. Ви знаєте, як пускати тільки "своїх" (Permissions).
  • Ви навчилися бути регулювальником трафіку. Ви не дасте покласти базу даних занадто частими запитами (Throttling).
  • Ви зрозуміли різницю між перевіркою "хто ти" (Auth) і "чи можна тобі" (Perm).

Тепер ви вмієте: ✅ Захищати ендпоінти від анонімів. ✅ Писати власні правила доступу (наприклад, тільки для власників). ✅ Обмежувати частоту запитів, щоб ваш сервер спав спокійно.

🚀 Тизер наступного уроку: Ок, ми захистили дані. Але що робити, якщо даних занадто багато? Як знайти потрібний пост серед мільйона? На наступному уроці ми розберемо Filtering (Фільтрацію) та Pagination (Пагінацію). Зробимо наш API зручним для пошуку!

А поки що — це був CS50... тобто DRF Permissions! Кодьте безпечно! 🛡️💻