Раздельные разрешения Django для групп и компаний пользователей

У меня есть модель CustomUser, каждый пользователь может быть связан с несколькими компаниями. Я инициировал свое приложение с 3 общими группами Django; viewer, editor и supervisor и каждый пользователь может быть членом только одной группы.

Разрешения

Я использовал некоторые глобальные разрешения Django, такие как разрешение {app_label}.add_{class_name} с группой editor для экземпляра. Параллельно я использовал также django-guardian разрешения на уровне объектов, чтобы глубже изучить разрешения моих объектов, поскольку они варьируются в зависимости от значений некоторых атрибутов объекта.

Итак, в конце концов, каждый пользователь (в зависимости от его группы) имеет некоторые глобальные разрешения и некоторые разрешения на уровне объекта. Пока что все работает нормально и без проблем.

Проблема

Как было сказано ранее, каждый пользователь связан с несколькими компаниями. Кроме того, каждый объект в моей базе данных связан с определенной компанией. Поэтому мне необходимо иметь более высокий уровень прав доступа, чтобы каждый пользователь мог иметь дело и взаимодействовать ТОЛЬКО с объектами, которые связаны с одной из его компаний, он/она - зритель, редактор или руководитель.

Предложенные решения

Вот решения, о которых я думал, но каждое из них имеет некоторые недостатки, и я не предпочитаю их на самом деле:

  1. Управлять этим также через django-guardian, предоставляя права пользователям групп, а не непосредственно группам, чтобы я мог контролировать предоставление прав только объектам компании. Но это решение имеет множество недостатков. Это тяжелая операция по предоставлению прав на объект для каждого пользователя, поскольку у меня много пользователей в каждой компании. Кроме того, если я изменил группу одного из пользователей или добавил нового пользователя, мне придется проделать дополнительную работу, чтобы добавить или изменить его/ее разрешения.
  2. .
  3. Обрабатывать это явно в каждом представлении в views.py, всегда получая компанию объекта в самом начале и проверяя, является ли она одной из компаний пользователя, затем явно возвращая 403 для экземпляра, если нет. Недостатками этого решения являются: кажется, что я делаю свой собственный слой разрешений вместо того, чтобы полагаться на один из двух имеющихся у меня слоев разрешений, также это требует дополнительной работы с каждым представлением в явном виде и может быть некоторой избыточностью.
  4. .

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

Согласны ли вы с каким-либо из этих решений или у вас есть другое предложенное решение?

Я последовал другому решению и сделал это не через разрешения. Я создал общий QuerySet и добавил его со всеми моделями, которые связаны с компаниями:

class ByCompanyQuerySet(models.QuerySet):
    def all_by_companies(self, user):
        if user.is_superuser:
            return self.all()
        return self.filter(company__in=user.companies.all())


class FooModel(models.Model):
    foo = models.CharField(max_length=100)

    objects = ByCompanyQuerySet.as_manager()

Затем я использовал эту all_by_company во всех моих связанных представлениях на основе классов, а также создал общую функцию get_object_by_company_or_404, которая будет использоваться во всех представлениях деталей вместо get_object_or_404:

def get_object_by_company_or_404(my_model, user, *args, **kwargs):
    try:
        return my_model.objects.select_related('company').all_by_companies(user).get(*args, **kwargs)
    except my_model.DoesNotExist:
        raise Http404('This item does not exist.')
Вернуться на верх