Раздельные разрешения Django для групп и компаний пользователей
У меня есть модель CustomUser
, каждый пользователь может быть связан с несколькими компаниями. Я инициировал свое приложение с 3 общими группами Django; viewer, editor и supervisor и каждый пользователь может быть членом только одной группы.
Разрешения
Я использовал некоторые глобальные разрешения Django, такие как разрешение {app_label}.add_{class_name}
с группой editor для экземпляра. Параллельно я использовал также django-guardian
разрешения на уровне объектов, чтобы глубже изучить разрешения моих объектов, поскольку они варьируются в зависимости от значений некоторых атрибутов объекта.
Итак, в конце концов, каждый пользователь (в зависимости от его группы) имеет некоторые глобальные разрешения и некоторые разрешения на уровне объекта. Пока что все работает нормально и без проблем.
Проблема
Как было сказано ранее, каждый пользователь связан с несколькими компаниями. Кроме того, каждый объект в моей базе данных связан с определенной компанией. Поэтому мне необходимо иметь более высокий уровень прав доступа, чтобы каждый пользователь мог иметь дело и взаимодействовать ТОЛЬКО с объектами, которые связаны с одной из его компаний, он/она - зритель, редактор или руководитель.
Предложенные решения
Вот решения, о которых я думал, но каждое из них имеет некоторые недостатки, и я не предпочитаю их на самом деле:
- Управлять этим также через
django-guardian
, предоставляя права пользователям групп, а не непосредственно группам, чтобы я мог контролировать предоставление прав только объектам компании. Но это решение имеет множество недостатков. Это тяжелая операция по предоставлению прав на объект для каждого пользователя, поскольку у меня много пользователей в каждой компании. Кроме того, если я изменил группу одного из пользователей или добавил нового пользователя, мне придется проделать дополнительную работу, чтобы добавить или изменить его/ее разрешения. .
- Обрабатывать это явно в каждом представлении в views.py, всегда получая компанию объекта в самом начале и проверяя, является ли она одной из компаний пользователя, затем явно возвращая 403 для экземпляра, если нет. Недостатками этого решения являются: кажется, что я делаю свой собственный слой разрешений вместо того, чтобы полагаться на один из двух имеющихся у меня слоев разрешений, также это требует дополнительной работы с каждым представлением в явном виде и может быть некоторой избыточностью. .
Мне больше нравится второе решение, чем первое, поскольку я могу реализовать единый слой 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.')