Я Python Developer в компании Нетрика. В данной статье расскажу, как устроены права в Django и django-rest-framework и как мы используем их на одном из проектов. Django В Django есть модели User, которая представляет пользователей вашей системы, и Group, которая представляет наборы пользователей. Также Django поставляется со встроенной системой прав. Она предоставляет возможность назначать права пользователям и группам пользователей. Например, она используется в админке. Django админка использует разрешения следующим образом: Доступ к просмотру объектов определённого типа есть у тех пользователей, у которых есть право на просмотр и изменение. Доступ к добавлению объектов определённого типа есть у тех пользователей, у которых есть право на добавление. Доступ к просмотру истории изменений и изменению объектов определённого типа есть у пользователей, у которых есть право на изменение. Доступ к удалению объектов определённого типа есть у пользователей, у которых есть право на удаление. К слову, права можно устанавливать не только по типу объекта, но так же и по конкретным объектам. Для этого можно переопределить методы has_view_permission, has_add_permission, has_change_permission и has_delete_permission класса ModelAdmin. В моделе User есть many-to-many поля groups и user_permissions. Когда django.contrib.auth представлен в настройке INSTALLED_APPS, то при вызове ./manage.py migrate создаются права для всех зарегистрированных поделей. Функциональность, которая создаёт разрешения, находится в сигнале post_migrate. Предположим, что у вас есть приложение foo и модель Bar в нём, проверить наличие у пользователя прав для этой модели можно следующим образом: add: user.has_perm("foo.add_bar") change: user.has_perm(foo.change_bar") delete: user.hes_perm("foo.delete_bar") view: user.has_perm("foo.view_bar") Модель Group предназначена для категоризации пользователей, так вы можете предоставлять права или что-нибудь ещё какой-то группе пользователей. Пользователь может принадлежать произвольному количеству групп. Пользователь получает все права, которые назначены его группе. Так же через группы можно дать набору пользователей какой-нибудь ярлык или расширенную функциональность. Например, можно отправлять email только пользователям из какой-то группы. django-rest-framework Права в django-rest-framework определяют, следует ли удовлетворить или отклонить запрос к различным частям вашего API. Например, права могут проверять, что пользователь аутентифицирован (IsAuthenticated). Они всегда запускаются перед любым кодом вашего представления. Где определяются права Права в django-rest-framework всегда определяются как список классов прав. Каждое право из списка проверяется перед запуском тела вашего представления. Если хотя бы одно из них  не проходит, то одно из исключений (exceptions.PermissionDenied или exceptions.NotAuthenticated) выбрасывается и код вашего представления не запускатеся. Возвращается 403 или 401 соответственно статус ответа согласно следующим правилам: Запрос был аутентифицирован, но право не предоставлено - 403 Forbidden. Запрос не был аутентифицирован и класс для аутентификации с наивысшим приоритетом не использует WWW-Authenticate заголовок - 403 Forbidden. Запрос не был аутентифицирован и заголовок используется - 401 Unauthorized. Права можно задать глобально, используя настройку DEFAULT_PERMISSION_CLASSES: Права можно задать для вашего представления используя permission_classes атрибут или get_permissions метод: Когда права определяются на уровне представления, то права в settings.py игнорируются. Все права наследуются от BasePermission и могут быть объеденены в цепочки, используя побитовые операции (| (or), & (and), ~(not)). Встроенные права AllowAny Это право предоставляет неограниченный доступ, не зависимо от того является ли запрос аутентифицированным или нет. IsAuthenticated Это право предоставляет доступ только аутентифицированным пользователя, иначе отклоняет запрос. IsAdminUser Это право предоставляет доступ только администраторам, т.е. пользователям, у которых поле is_staff равно True. IsAuthenticatedOrReadOnly Это право позволяет аутентифицированным пользователям выполнять любой тип запроса. Иначе запрос выполнится только в том случае, если его метод "безопасный" (GET, HEAD или OPTIONS). DjangoModelPermissions Это право связано со стандартными разрешениями django, расположенными в django.contrib.auth. Для его использования в вашем представлении должно быть объявлено либо .queryset свойство, либо .get_queryset() метод. Запрос будет разрешён только в том случае, если пользователь аутентифицирован и имеет соответствующее джанговское право для модели. Модель определяется по .get_queryset().model или .queryset.model. POST запросы требуют, чтобы у пользователя было add право на модель. PUT и PATCH запросы требуют, чтобы у пользователя было change право на модель. DELETE запросы требуют, чтобы у пользователя было delete право на модель. Также можно добавить свои джанговские права, переопределив право DjangoModelPermissions. Например, можно добавить право на просмотр модели (view) для GET-запросов. Для этого нужно переопределить .perms_map свойство. DjangoModelPermissionsOrAnonReadOnly Схоже с предыдущим, только неаутентфицированные пользователи получают доступ на чтение. DjangoObjectPermissions Как и DjangoModelPermissions это право связано со стандартными джанговскими правами. Оно позволяет работать с правами на уровне объектов. Но, чтобы его использовать, нужно добавить бэкенд, которых поддерживает права на уровне объектов (например, django-guardian). Для view права можно использовать DjangoObjectPermissionsFilter класс из django-guardian, который возвращает только объекты, для которых у пользователя есть соответствующее право. Пользовательские разрешения Для того, чтобы реализовать своё право, нужно переопределить BasePermission и реализовать все (или один) из методов: .has_permission(self, request, view) .has_object_permission(self, request, view, obj) Эти методы должны возвращать True, если запрос разрешён, иначе - False. Если в вашем праве, нужно проверять является ли запрос "безопасным", можно использовать константу SAFE_METHODS, которая является кортежем, содержащим 'GET', 'OPTIONS' и 'HEAD'. Например: Метод has_object_permission вызывается, если метод has_permission пройдёт успешно. Отметим, что generic представления проверяют права для объекта (has_object_permission) только тогда, когда запрашивается один объект. Если необходимо фильтровать список объектов, нужно фильтровать queryset отдельно в методе .get_queryset() вашего представления. Опыт использования прав на одном из проектов Ниже расскажу про использования прав на одном из проектов. Но немного про сам проект. Мы разрабатываем комплексное решение для автоматизации проектной деятельности, сам продукт - это по сути отечественный аналог MS Project с адаптацией под потребности российских компаний. Особенность в том, что в продукте работают все участники проектной деятельности от топ-менеджмента до непосредственных исполнителей + смежные подразделения компаний + администратор системы. Нужно было создать достаточно сложную модель прав с учетом всей функциональности продукта и предусмотреть отдельные права для каждой роли пользователя в разных сценариях: ведение проектов, программ и портфелей проектов, работа с инициативами для всех участников отдельный кабинет для топ-менеджмента с контролем реализации проектов и верхнеуровневой аналитикой работа с запросами на изменения и многоступенчатыми согласованиями с участием пользователей из смежных подразделений Модели User и UserGroup Мы не используем стандартные джанговские права. Вместо этого права представлены следующим образом: И храняться в модели как кастомное поле ChoiceArrayField: Также мы не используем стандартную модель для групп, вместо этого у нас своя - UserGroup. Группы в моделе User хранятся как поле groups - many-to-many поле на модель UserGroup. Права и группы можно выбрать в админке на странице редактирования пользователя. Чтобы была возможность проверять права у пользователя через метод .has_perm() (user.has_perm(USER_PERMISSION.project_add)), был добавлен свой бэкенд: Представления В представлениях определяем атрибут permissions_classes с нашими правами: Модели и миксин PermissionModelMixin Миксин для работы требует наличие атрибута authorization_class и определяет метод .can(): Модели наследуются от PermissionModelMixin и определяют атрибут authorization_class - класс, который наследуется от класса ModelAuthorization. Эти классы так же определяют метод .can(): Права Все наши права наследуются от базового класса ModelAuthorizationPermission, который в свою очередь наследуется от BasePermission, определённого в django-rest-framework. Метод .can() определён в миксине PermissionModelMixin, от которого наследуются наши модели. Каждое наше право определяет аттрибут-словарь authorization_action_mapping, который представляет собой отображение view action на права в системе. Класс ModelAuthorization и наследники Эти классы и занимаются проверкой прав. Класс ModelAuthorization определяет метод .can(): А его наследники - допустимые права и по два метода для каждого из них: Всё вместе При определении модели наследуем её от PermissionModelMixin. Определяем для неё класс, наследуемый от класса ModelAuthorization. В нём для каждого права определяем два метода is_custom_permission_allowed и has_custom_permission_access. В этих методах определяем логику проверки у пользователя прав на совершение данного действия. Определяем класс, наследуемый от класса ModelAuthorizationPermission - наше право. В нём задаём словарь - отображение action из ViewSet - на право в системе. Добавляем этот класс в соответствующий ViewSet. Какие плюсы у данной реализации Стандартные джанговские права позволяют проверять права на CRUD операции. Наша же реализация позволяет в ModelAuthorization проверять права отличные от CRUD.  Python developer at Netrika