Права доступа в Django

Django поставляется с мощной системой разрешений, готовой к использованию.

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

Цели

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

  1. Объяснить, как работают разрешения и группы Django
  2. Использовать возможности встроенной системы разрешений Django

Аутентификация против авторизации

Эта статья посвящена авторизации.

  • Аутентификация - это процесс подтверждения наличия у пользователя доступа к системе. Как правило, для аутентификации пользователя используются имя пользователя/адрес электронной почты и пароль.
  • Авторизация: относится к тому, что "аутентифицированный" пользователь может делать в системе.

Иными словами, аутентификация отвечает на вопрос "кто вы?", а авторизация - на вопрос "что вы можете сделать?'.

Разрешения на уровне пользователя

Когда django.contrib.auth добавляется к параметру INSTALLED_APPS в settings.py файл, Django автоматически создает add, change, delete и view разрешений для каждой создаваемой модели Django.

Разрешения в Django следуют следующей последовательности присвоения имен:

{app}.{action}_{model_name}

Примечания:

  • app это название приложения Django, в котором находится соответствующая модель
  • action: является add, change, delete, или view
  • model_name: это название модели в нижнем регистре

Давайте предположим, что у нас есть следующая модель в приложении под названием "блог":

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=400)
    body = models.TextField()

По умолчанию Django создаст следующие разрешения:

  1. blog.add_post
  2. blog.change_post
  3. blog.delete_post
  4. blog.view_post

Затем вы можете проверить, есть ли у пользователя (через объект пользователя Django) разрешения, с помощью метода has_perm():

from django.contrib.auth import get_user_model
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType

from blog.models import Post

content_type = ContentType.objects.get_for_model(Post)
post_permission = Permission.objects.filter(content_type=content_type)
print([perm.codename for perm in post_permission])
# => ['add_post', 'change_post', 'delete_post', 'view_post']

user = User.objects.create_user(username="test", password="test", email="test@user.com")

# Check if the user has permissions already
print(user.has_perm("blog.view_post"))
# => False

# To add permissions
for perm in post_permission:
    user.user_permissions.add(perm)

print(user.has_perm("blog.view_post"))
# => False
# Why? This is because Django's permissions do not take
# effect until you allocate a new instance of the user.

user = get_user_model().objects.get(email="test@user.com")
print(user.has_perm("blog.view_post"))
# => True

Для суперпользователей всегда будет установлено разрешение True, даже если это разрешение не существует:

from django.contrib.auth.models import User

superuser = User.objects.create_superuser(
    username="super", password="test", email="super@test.com"
)

# Output will be true
print(superuser.has_perm("blog.view_post"))

# Output will be true even if the permission does not exists
print(superuser.has_perm("foo.add_bar"))

Суперпользователь - это тип пользователя в Django, который обладает всеми разрешениями в системе. Будь то пользовательские разрешения или разрешения, созданные в Django, суперпользователи имеют доступ ко всем разрешениям.

Пользователь staff ничем не отличается от любого другого пользователя в вашей системе, но с дополнительным преимуществом - возможностью доступа к интерфейсу администратора Django. Интерфейс администратора Django доступен только для суперпользователей и штатных пользователей.

Разрешения на уровне группы

Необходимость каждый раз назначать разрешения пользователям утомительна и не поддается масштабированию. В некоторых случаях может потребоваться добавить новые разрешения для группы пользователей. Вот тут-то и вступают в игру группы Django.

Что такое группа?

  • Определение на английском языке: Группа - это набор объектов, которые классифицируются вместе.
  • Определение Django: Групповые модели - это общий способ категоризации пользователей, позволяющий применять к ним разрешения или какой-либо другой ярлык. Пользователь может принадлежать к любому количеству групп.

С помощью Django вы можете создавать группы для классификации пользователей и назначать разрешения каждой группе, поэтому при создании пользователей вы можете просто назначить пользователя группе, и, в свою очередь, пользователь получит все разрешения от этой группы.

Чтобы создать группу, вам понадобится модель Group из django.contrib.auth.models.

Давайте создадим группы для следующих ролей:

  • Author: Можно просматривать и добавлять записи
  • Editor: Может просматривать, добавлять и редактировать записи
  • Publisher: Может просматривать, добавлять, редактировать и удалять записи

Код:

from django.contrib.auth.models import Group, User, Permission
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404

from blog.models import Post

author_group, created = Group.objects.get_or_create(name="Author")
editor_group, created = Group.objects.get_or_create(name="Editor")
publisher_group, created = Group.objects.get_or_create(name="Publisher")

content_type = ContentType.objects.get_for_model(Post)
post_permission = Permission.objects.filter(content_type=content_type)
print([perm.codename for perm in post_permission])
# => ['add_post', 'change_post', 'delete_post', 'view_post']

for perm in post_permission:
    if perm.codename == "delete_post":
        publisher_group.permissions.add(perm)

    elif perm.codename == "change_post":
        editor_group.permissions.add(perm)
        publisher_group.permissions.add(perm)
    else:
        author_group.permissions.add(perm)
        editor_group.permissions.add(perm)
        publisher_group.permissions.add(perm)

user = User.objects.get(username="test")
user.groups.add(author_group)  # Add the user to the Author group

user = get_object_or_404(User, pk=user.id)

print(user.has_perm("blog.delete_post")) # => False
print(user.has_perm("blog.change_post")) # => False
print(user.has_perm("blog.view_post")) # => True
print(user.has_perm("blog.add_post")) # => True

Принудительное выполнение разрешений

Помимо администратора Django, разрешения обычно применяются на уровне представления, поскольку пользователь получает их из объекта запроса.

Для принудительного применения разрешений в представлениях на основе классов вы можете использовать PermissionRequiredMixin из django.contrib.auth.mixins следующим образом:

from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import ListView

from blog.models import Post

class PostListView(PermissionRequiredMixin, ListView):
    permission_required = "blog.view_post"
    template_name = "post.html"
    model = Post

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

from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import ListView

from blog.models import Post

class PostListView(PermissionRequiredMixin, ListView):
    permission_required = ("blog.view_post", "blog.add_post")
    template_name = "post.html"
    model = Post

Для представлений на основе функций используйте permission_required декоратор:

from django.contrib.auth.decorators import permission_required

@permission_required("blog.view_post")
def post_list_view(request):
    return HttpResponse()

Вы также можете проверить наличие разрешений в ваших шаблонах Django. С помощью контекстных процессоров аутентификации в Django переменная perms доступна по умолчанию при визуализации вашего шаблона. Переменная perms фактически содержит все разрешения в вашем приложении Django.

Например:

{% if perms.blog.view_post %}
  {# Your content here #}
{% endif %}

Разрешения на уровне модели

Вы также можете добавить пользовательские разрешения к модели Django с помощью параметров model Meta.

Давайте добавим флаг is_published к модели Post:

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=400)
    body = models.TextField()
    is_published = models.Boolean(default=False)

Далее мы установим пользовательское разрешение с именем set_published_status:

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=400)
    body = models.TextField()
    is_published = models.Boolean(default=False)

    class Meta:
        permissions = [
            (
                "set_published_status",
                "Can set the status of the post to either publish or not"
            )
        ]

Чтобы обеспечить соблюдение этого разрешения, мы можем использовать UserPassesTestMixin, на наш взгляд, предоставленный Django mixin, что дает нам возможность явно проверять, есть ли у пользователя требуемое разрешение или нет.

Вот как может выглядеть представление на основе классов, которое проверяет, есть ли у пользователя разрешение на установку статуса публикации для записи:

from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import render
from django.views.generic import View

from blog.models import Post

class PostListView(UserPassesTestMixin, View):
    template_name = "post_details.html"

    def test_func(self):
        return self.request.user.has_perm("blog.set_published_status")

    def post(self, request, *args, **kwargs):
        post_id = request.POST.get('post_id')
        published_status = request.POST.get('published_status')

        if post_id:
            post = Post.objects.get(pk=post_id)
            post.is_published = bool(published_status)
            post.save()

        return render(request, self.template_name)

Итак, с помощью UserPassesTestMixin вам нужно переопределить метод test_func класса и добавить свой собственный тест. Обратите внимание, что возвращаемое значение этого метода всегда должно быть логическим.

Разрешения на уровне объекта

Если вы используете фреймворк Django REST, у него уже есть разрешения объектного уровня, встроенные в базовый класс разрешений. BasePermission имеет has_permission, который в основном предназначен для просмотра списков и has_object_permission, который проверяет, есть ли у пользователя разрешение на доступ к одному экземпляру модели.

Подробнее о разрешениях в Django REST Framework читайте в разделе Разрешения в Django REST Framework.

Если вы не используете Django REST Framework, то для реализации разрешений на уровне объекта вы можете использовать стороннюю платформу, например:

Дополнительные пакеты, связанные с правами доступа, смотрите в разделе Пакеты Django.

Заключение

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

Вернуться на верх