Как настраивать права доступа в контроллерах Django
Что проверяют
Знаете ли вы встроенную систему прав Django и отличаете ли проверку «кто вы» от проверки «что вам можно с этой записью». Следом почти всегда спрашивают, как запретить пользователю редактировать чужой заказ.
Как отвечать
Контроллер в Django это view, проверки вешаются на него:
- функции:
@login_requiredи@permission_required("orders.change_order", raise_exception=True); - классы:
LoginRequiredMixin,PermissionRequiredMixinс атрибутомpermission_required,UserPassesTestMixinс методомtest_funcдля произвольного условия. Миксины ставятся левее базового view; - сами права: для каждой модели создаются
add,change,delete,view, свои добавляются вMeta.permissions. Выдаются напрямую или черезGroup, проверяютсяuser.has_perm("app.codename"). У суперпользователя есть все права, у неактивного нет ни одного; - права на объект: стандартный
ModelBackendих не проверяет. Либо фильтр вget_queryset()по владельцу (чужая запись даёт 404), либо django-guardian или django-rules; - DRF:
permission_classes = [IsAuthenticated, IsOwner], в своём классе методыhas_permissionиhas_object_permission. Второй вызывается изget_object(), в собственномAPIViewего запускают черезcheck_object_permissions; - закрыть весь сайт: в Django 5.1 появился
LoginRequiredMiddleware, открытые view помечаются@login_not_required.
В шаблоне права проверяются через переменную perms: {% if perms.orders.change_order %}. Скрытая кнопка не защита, проверка во view обязательна.
Где спотыкаются
Пишут в шаблоне user.has_perm('...'). Шаблонизатор Django не вызывает методы с аргументами, для этого и есть perms.
Ставят @permission_required без raise_exception=True и удивляются, что залогиненного пользователя без права отправляет на страницу входа вместо 403.
Проверяют только права на модель. change_order разрешает менять любые заказы, и без фильтра по владельцу пользователь правит чужие, просто подставив id в URL.