Как сделать пользовательскую проверку прав доступа для вложенного сериализатора с вложенными моделями, имеющими разные права доступа в django
Я создаю webapp, где пользователь может создать команду, добавить членов и их проекты. Все работает хорошо, но теперь наступает время разрешений. Одна модель будет Team, а другая Project. Сейчас я написал пользовательские разрешения для обеих моделей, расширяющие BasePermission.
Операция/разрешение будет :
User1 created a team
Team1, can add any members and add his projects (no permission to add others project)members of
Team1can add their own projects and edit (CRU) projects added by others. No permission for the members to delete Team1, only creator can delete the team.A project can only be edited by the members of the team to which it is added. Others cannot. Only creator of the project can delete it.
Разрешения:
from rest_framework import permissions
from .models import Team,Project
from rest_framework import serializers
class ProjectPermission(permissions.BasePermission):
message = "You do not have permission to perform this action with Project that doesn't belong to you or you are not a member of the team for this Project"
def has_object_permission(self, request,view, obj):
if not request.method in permissions.SAFE_METHODS:
if request.method != "DELETE":
if obj.team: #Team can be null when creating a project
return obj.created_by == request.user or request.user in obj.team.members.all()
return obj.created_by == request.user
return obj.created_by == request.user
return request.user.is_authenticated
def has_permission(self, request, view):
return request.user.is_authenticated
class TeamPermission(permissions.BasePermission):
message = "You do not have permission to perform this action with Team that is not created by you or you are not a member with full permission"
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return request.user.is_authenticated
else:
if request.method != "DELETE":
if obj.created_by == request.user or request.user in obj.members.all():
return True
return obj.created_by == request.user
def has_permission(self, request, view):
if not request.method in permissions.SAFE_METHODS:
if request.method =="DELETE":
return True
Projects = request.data.get('Projects')
if request.method =="PUT" or request.method == "PATCH":
if len(Projects)==0:return True # for removing a list projects an empty array is passed, in the serializer set function "remove set(current projects - empty list)" is used to identify the projects to remove
if Projects:
perm = []
for Project in Projects:
try:
#this is the issue, if there are 100 projects to be added, it does 100 queries to fetch the object
perm.append(ProjectPermission().has_object_permission(request, view,Project.objects.get(id=Project)))
except Exception as e:
raise serializers.ValidationError(e)
if False in perm:
raise serializers.ValidationError(
{"detail":"You do not have permission to perform this action with Project that doesn't belong to you or you are not a member of the team for this Project"})
return True
else:
return request.user.is_authenticated
Эта строка в конце вызывает 100 запросов к бд для 100 проектов. я могу сделать фильтр по списку проектов. но есть ли альтернативы?
perm.append(ProjectPermission().has_object_permission(request, view,Project.objects.get(id=Project))
Просмотров:
class ProjectView(viewsets.ModelViewSet):
'''
Returns a list of all the Projects. created by user and others
Supports CRUD
'''
queryset=Project.objects.select_related('team','created_by').all()
serializer_class=ProjectSerializer
permission_classes=[ProjectPermission]
class TeamView(viewsets.ModelViewSet):
"""
Returns a list of all the teams. created by user and others
Supports CRUD
"""
queryset=Team.objects.prefetch_related('members','projects').select_related('created_by').all()
serializer_class=TeamSerializer
permission_classes=[TeamPermission]
Есть ли эффективный способ добиться того, что мне нужно? Я читал о добавлении разрешения в моделях, но не представляю, как это можно сделать в данном случае. Любое предложение было бы большой помощью
Давайте разберемся, любой User может создать Team, поэтому существует одно разрешение IsAuthenticated
Любой Team участник может CRU проект, поэтому здесь есть три разрешения:
IsAuthenticated & ( IsTeamOwner | IsTeamMember)
для удаления проекта существует два разрешения (IsAuthenticated & IsProjectOwner) и так далее
Итак, разрешения могут быть примерно такими:
class IsTeamOwner(BasePermission):
message = "You do not have permission to perform this action"
def has_object_permission(self, request, view, obj):
return obj.created_by == request.user
class IsProjectOwner(BasePermission):
message = "You do not have permission to perform this action"
def has_object_permission(self, request, view, obj):
return obj.created_by == request.user
class IsTeamMember(BasePermission):
message = "You do not have permission to perform this action"
def has_object_permission(self, request, view, obj):
return request.user in obj.members.all()