Разрешения фреймворка Django REST в Python
В этом руководстве вы изучите разрешения Django REST Framework и узнаете, как определить пользовательский класс разрешений для проекта списка задач в Python.
- Что такое разрешения Django REST Framework
- Разрешение Project-level
- Разрешения View-level
- Пользовательские разрешения
Что такое разрешения 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 с возможностью просмотра, используя обычного пользователя 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 «Запрещено»:
Чтобы выйти из системы пользователя 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. Это должно работать так, как и ожидалось.