DRY-rest-permissions 以某种方式不检查我的对象权限,除了全局权限

DRY-rest-permissions somehow does not check my object permissions except global permissions

我最近开始实施 dry-rest-permissions,但我似乎无法检查 has_object_permissions,似乎只有全局权限对我有用。

我对实现权限相当陌生,这是我第一次实现 DRY-rest-permissions 并且最近才开始在 django rest 框架中编码,因此对于缺乏知识提前表示歉意。

目前我正在尝试通过简单地让用户调用 URL 来删除公司对象,然后 URL 获取当前用户的 active_company 然后仅将其删除如果当前 useractive_companycompany_owner

但我发现,我无法has_object_permissions在任何地方工作?

我注意到,如果我删除 has_write_permission(request),然后点击 company_delete URL,则会出现以下错误:

'<class 'company.models.Company'>' does not have 'has_write_permission' or 'has_company_delete_permission' defined.

这意味着它甚至不查找 has_object_company_delete_permission。这意味着它只检查全局权限而不是任何对象权限,我在这里可能做错了什么?

我的模型:

class Company(models.Model):
    company_name = models.CharField(max_length=100)
    company_orders = models.IntegerField(blank=True, null=True)
    company_icon = models.ImageField(
        upload_to='media/company_icon', blank=True)
    company_owner = models.ForeignKey(
        User, on_delete=models.SET_NULL, blank=True, null=True)
    company_employees = models.ManyToManyField(
        User, blank=True, null=True, related_name="company_employees")

    def __str__(self):
        return self.company_name

    @staticmethod
    def has_write_permission(request):
        return False

    def has_object_company_delete_permission(self, request):
        return self.company_owner == request.user

我的观点

class CompanyView(viewsets.ModelViewSet):  # made for viewing details
    permission_classes = (DRYPermissions, )
    queryset = Company.objects.all()
    serializer_class = CompanySerializer

    def create(self, request):
        try:

            company_name = request.data['company_name']
            company_orders = request.data['company_orders']
            company_owner = request.data['company_owner']
            company_owner_obj = User.objects.get(id=company_owner)
            company = Company(company_name=company_name,
                              company_orders=company_orders, company_owner=company_owner_obj)
            company.save()

        except Exception as error:
            response = {
                'error': str(error)
            }
            return Response(response, status=status.HTTP_400_BAD_REQUEST)

        response = {
            'message': 'Company created'
        }

        return Response(response, status=status.HTTP_201_CREATED)

    def company_details(self, request):
        try:
            company_id = request.user.active_company.id

            company = Company.objects.get(id=company_id)

            serialized_data = CompanySerializer(company)

        except Exception as error:
            response = {
                'error': str(error)
            }
            return Response(response)

        return Response(serialized_data.data)

    def company_edit(self, request, **kwargs):
        try:
            company_id = request.user.active_company.id
            company = Company.objects.get(id=company_id)
            serializer = CompanySerializer(
                company, data=request.data, partial=True)
            if serializer.is_valid():
                serializer.save()
        except Exception as error:
            response = {
                'message': str(error)
            }
            return Response(response)
        response = {
            'message': 'Edited Successfully'
        }
        return Response(response)

    def company_delete(self, request):
        try:
            company_id = request.user.active_company.id
            company = Company.objects.filter(id=company_id)
            company.delete()
        except Exception as error:
            response = {
                'message': str(error)
            }
            return Response(response)
        response = {
            'message': 'Deleted Successfully'
        }
        return Response(response)

我的网址

urlpatterns = [
    #    Company URLs
    path('company_create/',
         CompanyView.as_view({'post': 'create'}), name='company_create'),  # Create company
    path('company_edit/',
         CompanyView.as_view(), name='company_edit'),  # Edit company details
    path('company_delete/',
         CompanyView.as_view({'delete': 'company_delete'}), name='company_delete'),  # Delete company
    path('company_details/',
         CompanyView.as_view({'get': 'company_details'}), name='company_details'),  # get company details (owner, employees etc)
]

我的序列化器

class CompanySerializer(serializers.ModelSerializer):
    company_owner = LimitedUserSerializer(read_only=True)

    class Meta:
        model = Company
        fields = ['id', 'company_name', 'company_orders',
                  'company_icon', 'company_owner']

this part of the documentation 中所述,始终首先检查全局权限,只有在通过全局权限时才检查对象权限。

文档来源:

DRY Rest Permissions allows you to define both global and object level permissions.

Global permissions are always checked first and define the ability of a user to take an action on an entire model. For example you can define whether a user has the ability to update any projects from the database.

Object permissions are checked if global permissions pass and define whether a user has the ability to perform a specific action on a single object. These are also known as row level permissions. Note: list and create actions are the only standard actions that are only global. There is no such object level permission call because they are whole table actions.

在这种情况下,您实际上有多个问题需要更正:

  • 确保所有拥有活跃公司的用户 has_write_permission return True
  • 确保重命名 has_object_company_delete_permission 因为我们不需要函数名称中的模型名称

示例:

   @staticmethod
   def has_write_permission(request):
       # Everybody can create/update/delete if no specific rule says otherwise
       return True

   def has_object_delete_permission(self, request):
       # Only owner can delete
       return self.company_owner == request.user

   def has_object_update_permission(self, request):
       # Only owner can update
       return self.company_owner == request.user

输出:

  • 每个人都可以创造
  • 只有所有者可以更新
  • 只有所有者可以删除

我知道仅仅删除一个对象似乎有点矫枉过正,但根据一些经验,它允许您清楚地定义 ans setup 权限,还可以通过使用 [=15= 轻松地与前端共享通用规则] 和 DRYGlobalPermissionsField

PS:这个答案来自 my origin answer on Github 让人们可以轻松地从 Whosebug

找到解决方案

它似乎以某种方式不检查我的任何自定义操作中的对象权限,因此在 get_object() 中添加以下检查或自定义操作本身修复了此问题。

self.check_object_permissions(self.request, obtainedObject)

我的代码中的示例:

@action(detail=True, methods=['patch'], pk=None)
def company_edit(self, request, **kwargs):
    try:
        company_id = request.user.active_company.id
        company = Company.objects.get(id=company_id)
        serializer = CompanySerializer(
            company, data=request.data, partial=True)
        self.check_object_permissions(self.request, company)
        if serializer.is_valid():
            serializer.save()
    except Exception as error:
        response = {
            'message': str(error)
        }
        return Response(response)
    response = {
        'message': 'Edited Successfully'
    }
    return Response(response)