Встроенные классы прав в Django REST Framework

Серия "Права доступа в Django REST Framework":

  1. Права доступа в Django REST Framework
  2. Встроенные классы разрешений в Django REST Framework (эта статья!)
  3. Пользовательские классы прав доступа в Django REST Framework

Цели

К концу этой статьи вы должны уметь:

  1. Объясните различия между семью встроенными классами разрешений в DRF
  2. Установка разрешений на конкретную модель и объект
  3. Использование встроенных классов разрешений для установки глобальной политики разрешений

Встроенные классы

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

  1. AllowAny
  2. IsAuthenticated
  3. IsAuthenticatedOrReadOnly
  4. IsAdminUser
  5. DjangoModelPermissions
  6. DjangoModelPermissionsOrAnonReadOnly
  7. DjangoObjectPermissions

Использовать их так же просто, как включить класс в список permission_classes определенного API View. Они могут быть разными: от полностью открытых (AllowAny) до доступа, предоставляемого только пользователям-администраторам (IsAdminUser). Не прилагая особых усилий, вы можете использовать их для реализации тонкого контроля доступа - как на уровне модели, так и на уровне объекта. Вы также можете установить разрешения глобально, для всех конечных точек API.

Все эти классы, кроме последнего, DjangoObjectPermissions, переопределяют только метод has_permission и наследуют has_object_permission от класса BasePermission. has_object_permission в классе BasePermission всегда возвращает True, поэтому он не влияет на ограничение доступа на уровне объекта:

Permission class has_permission has_object_permission
AllowAny
IsAuthenticated
IsAuthenticatedOrReadOnly
IsAdminUser
DjangoModelPermissions
DjangoModelPermissionsOrAnonReadOnly
DjangoObjectPermissions by extending DjangoModelPermissions

Для получения дополнительной информации о has_permission в сравнении с has_object_permission, обязательно ознакомьтесь с первой статьей из этого цикла, Разрешения в Django REST Framework.

AllowAny

Самое открытое разрешение из всех - AllowAny. Методы has_permission и has_object_permission на AllowAny всегда возвращают True, ничего не проверяя. Использовать его необязательно (не задавая класс разрешения, вы неявно задаете этот класс), но все же стоит, поскольку это делает намерение явным и помогает поддерживать последовательность во всем приложении.

Вы указываете его, включая permission_classes в представление:

from rest_framework import viewsets
from rest_framework.permissions import AllowAny

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [AllowAny] # built-in permission class used

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

Любой, даже неаутентифицированный пользователь, может получить доступ к конечной точке API, используя любой метод HTTP-запроса:

DRF Permissions Execution

IsAuthenticated

IsAuthenticated проверяет, есть ли в запросе пользователь и аутентифицирован ли он. Установка значения permission_classes в IsAuthenticated означает, что только аутентифицированные пользователи смогут получить доступ к конечной точке API с помощью любого из методов запроса.

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [IsAuthenticated] # permission class changed

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

Теперь неаутентифицированный пользователь получает отказ в доступе:

DRF Permissions Execution

IsAuthenticatedOrReadOnly

Когда разрешения установлены на IsAuthenticatedOrReadOnly, запрос должен либо иметь аутентифицированного пользователя, либо использовать один из безопасных/ограниченных только чтением методов HTTP-запросов (GET, HEAD, OPTIONS). Это означает, что каждый пользователь сможет просмотреть все сообщения, но только авторизованные пользователи смогут добавлять, изменять или удалять объекты.

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [IsAuthenticatedOrReadOnly] # ReadOnly added

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

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

DRF Permissions Execution

IsAdminUser

Разрешения, установленные на IsAdminUser, означают, что запрос должен иметь пользователя, и этот пользователь должен иметь is_staff, установленный на True. Это означает, что только пользователи-администраторы могут видеть, добавлять, изменять или удалять объекты.

from rest_framework import viewsets
from rest_framework.permissions import IsAdminUser

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [IsAdminUser] # only for admin users

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

Интересным моментом здесь является то, что неаутентифицированные пользователи и аутентифицированные пользователи без доступа администратора получат разные ошибки.

Для неаутентифицированных пользователей возникает исключение NotAuthenticated:

DRF Permissions Execution

В то же время для аутентифицированного пользователя, не имеющего доступа администратора, возникает исключение PermissionDenied:

DRF Permissions Execution

DjangoModelPermissions

DjangoModelPermissions позволяет установить любую комбинацию разрешений для каждого из пользователей в отдельности. Затем разрешение проверяет, аутентифицирован ли пользователь и имеет ли он add, change или delete пользовательские разрешения на модель.

from rest_framework import viewsets
from rest_framework.permissions import DjangoModelPermissions

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [DjangoModelPermissions]

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

В отличие от других разрешений, на этом установка разрешения не заканчивается. Необходимо установить разрешения для конкретного пользователя:

Django Admin

Если вы посмотрите на одиночный вид сообщения, вы увидите, что этот конкретный пользователь может редактировать сообщение, но не может его удалить:

DRF Permissions Execution

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

Django Admin

А здесь видно, что член этой группы может удалить сообщение:

DRF Permissions Execution

DjangoModelPermissions можно применять только к представлениям, имеющим queryset свойство или get_queryset() метод.

Например, общее представление CreateAPIView не требует набора запросов, поэтому если вы установите DjangoModelPermissions на него, то получите ошибку утверждения. Однако, если вы выполните запрос, даже не используя кверисет, DjangoModelPermissions будет работать:

class NewMessage(generics.CreateAPIView):
    queryset = Message.objects.all()
    permission_classes = [DjangoModelPermissions]
    serializer_class = MessageSerializer

DjangoModelPermissionsOrAnonReadOnly

DjangoModelPermissionsOrAnonReadOnly расширяет DjangoModelPermissions и изменяет только одно: он устанавливает authenticated_users_only в False.

from rest_framework import viewsets
from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [DjangoModelPermissionsOrAnonReadOnly]

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

Анонимные пользователи могут видеть объекты, но не могут взаимодействовать с ними:

DRF Permissions Execution

DjangoObjectPermissions

В то время как DjangoModelPermissions ограничивает права пользователя на взаимодействие с моделью (всеми экземплярами), DjangoObjectPermissions ограничивает взаимодействие одним экземпляром модели (объектом). Для использования DjangoObjectPermissions вам понадобится бэкенд разрешений, поддерживающий разрешения на уровне объектов. Мы рассмотрим django-guardian.

Хотя существует довольно много пакетов, которые охватывают разрешения Django, DRF явно упоминает django-guardian, поэтому мы работаем с ним в этой статье.

Другие пакеты, работающие с разрешениями на уровне объектов:

  1. drf-extensions
  2. django-oso
  3. django-rules
  4. django-role-permissions

Установка django-guardian

Чтобы использовать django-guardian, вам сначала нужно установить его:

$ pip install django-guardian

Затем добавьте его к INSTALLED_APPS и AUTHENTICATION_BACKENDS:

# settings.py

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'guardian',
]

# ...

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'guardian.backends.ObjectPermissionBackend',
)

Наконец, запустите миграции:

(venv)$ python manage.py migrate

Если вы хотите просмотреть разрешения для одного объекта, вам нужно использовать GuardedModelAdmin вместо ModelAdmin. Это можно сделать в файле admin.py следующим образом:

# admin.py

from django.contrib import admin
from guardian.admin import GuardedModelAdmin
from tutorial.models import Message

class MessageAdmin(GuardedModelAdmin):
    pass

admin.site.register(Message, MessageAdmin)

Теперь, если вы откроете отдельный объект в панели администратора, справа вверху появится новая кнопка "OBJECT PERMISSIONS". При нажатии на нее открывается панель разрешений объекта:

Django Admin

Использование DjangoObjectPermissions

Теперь знание разницы между has_permission и has_object_permission пригодится. Вкратце, DRF сначала проверяет, есть ли у запроса разрешение на доступ к модели. Если нет, то DRF не заботится о разрешениях на уровне объекта.

DRF Permissions Execution

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

# views.py

from guardian.shortcuts import assign_perm
from rest_framework import viewsets
from rest_framework.permissions import DjangoObjectPermissions

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):

    permission_classes = [DjangoObjectPermissions] # class changed

    queryset = Message.objects.all()
    serializer_class = MessageSerializer

    def perform_create(self, serializer): # new function
        instance = serializer.save()
        assign_perm("delete_message", self.request.user, instance)

Как видите, есть два важных изменения:

  1. Во-первых, класс разрешения DjangoObjectPermissions.
  2. Далее мы подключились к созданию экземпляра модели. Мы не можем назначить разрешение несуществующему объекту, поэтому сначала нужно создать экземпляр, а затем назначить ему объектное разрешение с помощью ярлыка assign_perm из django-guardian.

assign_perm - это функция django-guardian, используемая для назначения разрешений определенным пользователям или группам. Она принимает три аргумента:

  1. разрешение: delete_message
  2. пользователь_или_группа: self.request.user
  3. объект (по умолчанию None): instance

Опять же, чтобы объектное разрешение работало, пользователь должен иметь разрешение на уровне модели для соответствующей модели. Допустим, у вас есть два пользователя с одинаковыми правами на модель:

Django Admin

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

DRF Permissions Execution

Если мы удалим разрешение на удаление любого объекта в модели для пользователя_1:

Django Admin

Они не могут удалить свое сообщение:

DRF Permissions Execution

Даже если у них есть разрешение на удаление этого конкретного объекта:

Django Admin

Глобальные разрешения

Вы можете легко установить глобальные разрешения в файле settings.py, используя встроенные классы разрешений. Например:

# settings.py

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

DEFAULT_PERMISSION_CLASSES будет работать только для представлений или объектов, для которых не установлены явные разрешения.

Здесь не обязательно использовать встроенные классы. Вы можете использовать и свои собственные пользовательские классы.

Заключение

Вот и все. Теперь вы должны знать, как использовать семь встроенных классов разрешений Django REST Framework. Они варьируются от полностью открытых (AllowAny) до преимущественно закрытых (IsAdminUser).

Вы можете установить разрешения глобально, на модель (DjangoModelPermissions) или на отдельный объект (DjangoObjectPermissions). Существуют также классы, позволяющие ограничить "небезопасные" HTTP-методы, но разрешить все безопасные (IsAuthenticatedOrReadOnly, DjangoModelPermissionsOrAnonReadOnly).

Если у вас нет особых требований, встроенных классов будет достаточно для большинства случаев. С другой стороны, если у вас есть какие-то особые требования, вам следует создать свой собственный класс разрешения.

Серия "Права доступа в Django REST Framework":

  1. Права доступа в Django REST Framework
  2. Встроенные классы разрешений в Django REST Framework (эта статья!)
  3. Пользовательские классы прав доступа в Django REST Framework
Вернуться на верх