Разрешения фреймворка Django REST в Python

В этом руководстве вы изучите разрешения Django REST Framework и узнаете, как определить пользовательский класс разрешений для проекта списка задач в Python.

Содержание

Что такое разрешения Django REST Framework

Django REST Framework имеет некоторые встроенные настройки разрешений, которые можно использовать для защиты API. Django REST Framework позволяет устанавливать разрешения на трех уровнях:

  • Project-level
  • View-level
  • Model-level

Разрешение Project-level

Разрешения на уровне проекта задаются в единственной настройке Django под названием REST_FRAMEWORK в файле settings.py проекта Django.

По умолчанию Django REST Framework разрешает неограниченный доступ к API. Это эквивалентно следующему:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

Помимо классов разрешений AllowAny, Django REST Framework также предлагает другие встроенные разрешения на уровне проекта:

  • IsAuthenticated: доступ имеют только аутентифицированные пользователи.
  • IsAdminUser: доступ имеют только администраторы/суперпользователи.
  • IsAuthenticatedOrReadOnly: неаутентифицированные пользователи могут вызывать любой API, но только аутентифицированные пользователи могут создавать, обновлять и удалять привилегии.

Давайте воспользуемся классом разрешений IsAuthenticated:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

Если вы выйдете из системы как суперпользователь и получите доступ к конечной точке API, которая показывает все задачи /api/v1/todos/, вы получите следующее сообщение об ошибке HTTP 403 «Запрещено»:

HTTP 403 Forbidden
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "detail": "Authentication credentials were not provided."
}

Причина в том, что API требует аутентификации пользователя.

Давайте создадим обычного пользователя с именем testapi, используя сайт администратора http://localhost:8000/admin/1. Обратите внимание, что обычный пользователь не является суперпользователем.

Чтобы разрешить обычному пользователю testapi входить и выходить из системы, необходимо обновить URLconf на уровне проекта, чтобы создать представления входа/выхода:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('api.urls')),
    path("auth/", include("rest_framework.urls")), # added
]

После установки rest_framework.urls вы можете получить доступ к конечной точке API /api/v1/todos/. Небольшое изменение — ссылка для входа на правой стороне:

Пример получения доступа к конечной точке API /api/v1/todos/

Если вы нажмете ссылку «Войти», вы увидите следующую форму входа:

Окно при нажатии ссылки

Войдите в API с возможностью просмотра, используя обычного пользователя testapi и пароль. После успешного входа с пользователем testapi вы увидите следующий ответ:

Успешный вход пользователя testapi

Разрешения View-level

Django REST Framework позволяет добавлять разрешения на уровне представления для более детального контроля.

Например, вы можете обновить представление TodoDetail так, чтобы его могли просматривать только администраторы, а обычные пользователи не имели к нему доступа.

class TodoDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes =(permissions.IsAdminUser,) # added
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

Представление TodoDetail имеет permission_classes, который инициализируется как permissions.IsAdminUser.

Благодаря этому Django REST Framework позволяет только администратору получать доступ к представлению TodoDetail, которое сопоставляется с конечной точкой API /api/v1/todos/id.

Если вы используете пользователя testapi (обычного пользователя) для доступа к API Todo Detail, вы получите ошибку HTTP 403 «Запрещено»:

Пример разрешения View-level для обычного пользователя

Чтобы выйти из системы пользователя testapi, щелкните ссылку с именем пользователя и выберите ссылку «Нажмите кнопку выхода»:

Выход из системы пользователя testapi

И войдите в API с возможностью просмотра, используя супер-администратора (Джон).

Поскольку представление TodoDetail позволяет суперадминистратору получить доступ, вы можете увидеть ответ API:

Вход суперадминистратора

Пользовательские разрешения

В проекте Todo вы можете ограничить доступ так, что только владельцы todo могут просматривать, редактировать, обновлять и удалять. Но они не могут получить доступ к todo других пользователей. Кроме того, суперпользователь будет иметь доступ к своему собственному списку todo, а также управлять todo других пользователей.

Чтобы удовлетворить эти требования к разрешениям, вам необходимо использовать специальные разрешения Django REST Framework.

Чтобы определить пользовательское разрешение, вы используете класс BasePermission Django REST Framework. Вот исходный код класса BasePermission:

class BasePermission(metaclass=BasePermissionMetaclass):
    """
    A base class from which all permission classes should inherit.
    """

    def has_permission(self, request, view):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

    def has_object_permission(self, request, view, obj):
        """
        Return `True` if permission is granted, `False` otherwise.
        """
        return True

BasePermission имеет два метода:

  • has_permission(self, request, view) – Возвращает True, если разрешение предоставлено, или False в противном случае. Представления списка вызовут метод has_permission для проверки разрешений.
  • has_object_permission(self, requests, view, obj) – возвращает True, если разрешение предоставлено, или False в противном случае. Метод подробного представления сначала вызовет has_permission(). Если все проходит, он вызовет метод has_object_permission(), чтобы проверить разрешения.

Хорошей практикой является явное переопределение обоих методов, чтобы избежать нежелательных настроек по умолчанию базового класса.

Ниже показаны шаги по определению и использованию пользовательского класса разрешений в Django REST Framework:

  • Сначала определите класс IsOwnerOnly, который расширяет класс BasePermission в файле permissions.py:
from rest_framework import permissions


class IsOwnerOnly(permissions.BasePermission):
    def has_permission(self, request, view):
        return True

    def has_object_permission(self, request, view, obj):
        if request.user.is_superuser:
            return True

        return obj.user == request.user

has_permission всегда возвращает True. Это означает, что любой пользователь может получить доступ к представлению TodoList, чтобы получить все задачи и создать новую задачу. Обратите внимание, что для ограничения доступа к задачам текущего пользователя мы сделаем это позже в представлении TodoList.

Метод has_object_permission возвращает True, если текущий пользователь является суперпользователем или владельцем задачи.

  • Во-вторых, обновите классы представлений в файле views.py приложения API, чтобы использовать пользовательский класс разрешений IsOwnerOnly:
from rest_framework import generics
from .serializers import TodoSerializer
from .permissions import IsOwnerOnly
from todo.models import Todo


class TodoList(generics.ListCreateAPIView):
    permission_classes =(IsOwnerOnly,)  # added
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    # added
    def filter_queryset(self, queryset):
        queryset = queryset.filter(user=self.request.user)
        return super().filter_queryset(queryset)


class TodoDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes =(IsOwnerOnly,)  # added
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

Оба класса TodoList и TodoDetail используют класс IsOwnerOnly:

permission_classes =(IsOwnerOnly,)  # added

Также добавьте метод filter_queryset в класс TodoList, чтобы возвращать только задачи текущего аутентифицированного пользователя:

def filter_queryset(self, queryset):
    queryset = queryset.filter(user=self.request.user)
    return super().filter_queryset(queryset)

Если вы войдете в API с возможностью просмотра, используя учетную запись суперпользователя, вы сможете просматривать, создавать, редактировать, обновлять и удалять задачи. Но если вы войдете в систему, используя обычного пользователя(testapi), вы увидите, что /api/v1/todos/ возвращает пустой список.

Кроме того, доступ к конечной точке API /api/v1/todos/1/ приведет к ошибке HTTP 403 «Запрещено», поскольку у пользователя testapi нет доступа к задаче, которая не принадлежит этому пользователю:

Задача, которая не принадлежит пользователю

Теперь вы можете использовать пользователя testapi для создания новой задачи со следующей информацией:

{
    "title": "Test API",
    "completed": false
}

Вот ответ:

HTTP 201 Created
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 5,
    "title": "Test API",
    "completed": false,
    "user": "testapi"
}

Сделайте HTTP-запрос GET к конечной точке /api/v1/todos/, и вы получите список задач, созданный пользователем testapi:

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "id": 5,
        "title": "Test API",
        "completed": false,
        "user": "testapi"
    }
]

Также вы можете протестировать просмотр, обновление и удаление todo с идентификатором 5, используя пользователя testapi. Это должно работать так, как и ожидалось.

Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *