Встроенные классы прав в Django REST Framework
Серия "Права доступа в Django REST Framework":
- Права доступа в Django REST Framework
- Встроенные классы разрешений в Django REST Framework (эта статья!)
- Пользовательские классы прав доступа в Django REST Framework
Цели
К концу этой статьи вы должны уметь:
- Объясните различия между семью встроенными классами разрешений в DRF
- Установка разрешений на конкретную модель и объект
- Использование встроенных классов разрешений для установки глобальной политики разрешений
Встроенные классы
Хотя вы можете создавать свои собственные классы разрешений, DRF поставляется с семью встроенными классами, призванными облегчить вам жизнь:
- AllowAny
- IsAuthenticated
- IsAuthenticatedOrReadOnly
- IsAdminUser
- DjangoModelPermissions
- DjangoModelPermissionsOrAnonReadOnly
- 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-запроса:
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
Теперь неаутентифицированный пользователь получает отказ в доступе:
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
Неавторизованный пользователь может просмотреть сообщение, опубликованное авторизованным пользователем, но не может ничего с ним сделать или добавить свое:
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
:
В то же время для аутентифицированного пользователя, не имеющего доступа администратора, возникает исключение PermissionDenied
:
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
В отличие от других разрешений, на этом установка разрешения не заканчивается. Необходимо установить разрешения для конкретного пользователя:
Если вы посмотрите на одиночный вид сообщения, вы увидите, что этот конкретный пользователь может редактировать сообщение, но не может его удалить:
DjangoModelPermissions
не обязательно должен быть назначен одному пользователю. Его можно использовать и для разрешений группы. Вот группа, которой разрешено удалить сообщение:
А здесь видно, что член этой группы может удалить сообщение:
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
Анонимные пользователи могут видеть объекты, но не могут взаимодействовать с ними:
DjangoObjectPermissions
В то время как DjangoModelPermissions
ограничивает права пользователя на взаимодействие с моделью (всеми экземплярами), DjangoObjectPermissions
ограничивает взаимодействие одним экземпляром модели (объектом). Для использования DjangoObjectPermissions
вам понадобится бэкенд разрешений, поддерживающий разрешения на уровне объектов. Мы рассмотрим django-guardian.
Хотя существует довольно много пакетов, которые охватывают разрешения Django, DRF явно упоминает django-guardian, поэтому мы работаем с ним в этой статье.
Другие пакеты, работающие с разрешениями на уровне объектов:
Установка 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". При нажатии на нее открывается панель разрешений объекта:
Использование DjangoObjectPermissions
Теперь знание разницы между has_permission
и has_object_permission
пригодится. Вкратце, DRF сначала проверяет, есть ли у запроса разрешение на доступ к модели. Если нет, то DRF не заботится о разрешениях на уровне объекта.
Это означает, что пользователь должен иметь разрешение модели, если вы хотите, чтобы проверялось разрешение на уровне объекта. Хорошим вариантом использования разрешений на уровне объектов является разрешение только владельцу объекта изменять или удалять его. Вот представление, которое позволяет удалять объект только его создателю:
# 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)
Как видите, есть два важных изменения:
- Во-первых, класс разрешения
DjangoObjectPermissions
. - Далее мы подключились к созданию экземпляра модели. Мы не можем назначить разрешение несуществующему объекту, поэтому сначала нужно создать экземпляр, а затем назначить ему объектное разрешение с помощью ярлыка assign_perm из django-guardian.
assign_perm
- это функция django-guardian, используемая для назначения разрешений определенным пользователям или группам. Она принимает три аргумента:
- разрешение:
delete_message
- пользователь_или_группа:
self.request.user
- объект (по умолчанию
None
):instance
Опять же, чтобы объектное разрешение работало, пользователь должен иметь разрешение на уровне модели для соответствующей модели. Допустим, у вас есть два пользователя с одинаковыми правами на модель:
И вы используете приведенный выше код, чтобы назначить разрешение только создателю. пользователь_1 - создатель объекта - может удалить его, но пользователь_2 не может удалить его, хотя у него есть разрешения на уровне модели:
Если мы удалим разрешение на удаление любого объекта в модели для пользователя_1:
Они не могут удалить свое сообщение:
Даже если у них есть разрешение на удаление этого конкретного объекта:
Глобальные разрешения
Вы можете легко установить глобальные разрешения в файле 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":
- Права доступа в Django REST Framework
- Встроенные классы разрешений в Django REST Framework (эта статья!)
- Пользовательские классы прав доступа в Django REST Framework