Django REST框架对象级权限

时间:2013-09-05 20:18:49

标签: python django permissions django-rest-framework

我正在使用Django REST Framework来访问资源'user'。

由于用户信息是个人信息,我不希望GET请求列出系统中的每个用户,除非他们是管理员。

如果用户指定了他们的ID,并且他们已登录,我希望他们能够查看他们的详细信息并在需要时修改它们(PUT POST DELETE)。

总而言之,对于不是管理员的人,请禁用GET方法,并在查看其信息时允许登录用户使用GET POST DELETE PUT。

所以我创建了自定义权限类:

class UserPermissions(permissions.BasePermission):
    """
    Owners of the object or admins can do anything.
    Everyone else can do nothing.
"""

    def has_permission(self, request, view):
        # if admin: True otherwise False
    def has_object_permission(self, request, view, obj):
        # if request.user is the same user that is contained within the obj then allow

这不起作用。经过一些调试后,我发现它首先检查has_permission,然后检查has_object_permission。因此,如果我们没有超过第一个障碍GET / user /,那么它甚至不会考虑下一个GET / user / id。

总而言之,有没有人知道如何让它发挥作用?

谢谢:)

修改

我正在使用ModelViewSets,它确实存在我所描述的这个问题。

但是如果你将List功能与Detail分开,那么你可以给它们单独的权限类:

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes=(UserPermissionsAll,)

class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes=(UserPermissionsObj,)

class UserPermissionsAll(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""

    def has_permission(self, request, view):
        if request.user.is_staff:
            return True
        else:
            return False

class UserPermissionsObj(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True

        return obj == request.user

5 个答案:

答案 0 :(得分:20)

我过去使用自定义权限执行此操作,并覆盖has_object_permission,如下所示:

from rest_framework import permissions


class MyUserPermissions(permissions.BasePermission):
    """
    Handles permissions for users.  The basic rules are

     - owner may GET, PUT, POST, DELETE
     - nobody else can access
     """

    def has_object_permission(self, request, view, obj):

        # check if user is owner
        return request.user == obj

您可以执行一些更详细的操作,例如拒绝特定请求类型(例如,允许对所有用户进行GET请求):

class MyUserPermissions(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):

        # Allow get requests for all
        if request.method == 'GET':
            return True
        return request.user == obj

然后在您的视图中,您告诉它使用权限类:

from my_custom_permissions import MyUserPermissions

class UserView(generics.ListCreateAPIView):
    ...
    permission_classes = (MyUserPermissions, )
    ...

答案 1 :(得分:11)

我有类似的需求。让我们调用我的应用x。这就是我想出来的。

首先,将其放在x/viewsets.py

# viewsets.py
from rest_framework import mixins, viewsets

class DetailViewSet(
  mixins.CreateModelMixin,
  mixins.RetrieveModelMixin,
  mixins.UpdateModelMixin,
  mixins.DestroyModelMixin,
  viewsets.GenericViewSet):
    pass

class ReadOnlyDetailViewSet(
  mixins.RetrieveModelMixin,
  viewsets.GenericViewSet):
    pass

class ListViewSet(
  mixins.ListModelMixin,
  viewsets.GenericViewSet):
    pass

然后在x/permissions.py

# permissions.py
from rest_framework import permissions

class UserIsOwnerOrAdmin(permissions.BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated()

    def check_object_permission(self, user, obj):
        return (user and user.is_authenticated() and
          (user.is_staff or obj == user))

    def has_object_permission(self, request, view, obj):
        return self.check_object_permission(request.user, obj)

然后在x/views.py

# views.py
from x.viewsets import DetailViewSet, ListViewSet
from rest_framework import permissions

class UserDetailViewSet(DetailViewSet):
    queryset = User.objects.all()
    serializer_class = UserDetailSerializer
    permission_classes = (UserIsOwnerOrAdmin,)

class UserViewSet(ListViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes (permissions.IsAdminUser,)

顺便提一下,请注意您可以为这两个视图集使用 不同的序列化程序 ,这意味着您可以在list视图中显示不同的属性在retrieve视图中!例如:

# serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'url',)

class UserDetailSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'groups', 'profile', 'password',)
        write_only_fields = ('password',)

然后在x/urls.py

# urls.py
from x import views
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'users', views.UserDetailViewSet)

...

我有点惊讶router两次接受相同的模式,但它似乎确实有用。

警告lector:我已经通过API浏览器确认这一切都有效,但我还没有通过API尝试 更新

答案 2 :(得分:9)

对于绊脚石,对象级别权限限制下的documentation说:

For performance reasons the generic views will not automatically apply object level permissions to each instance in a queryset when returning a list of objects.

因此,详情视图可以使用,但对于列表,您需要filter针对当前用户。

答案 3 :(得分:7)

对于@ will-hart的答案还有一件事。

在DRF3文档中,

  

注意:只有在已经通过了视图级别的has_permission检查时才会调用实例级别的has_object_permission方法

因此,应指定 has_permission 以使用has_object_permission

from rest_framework import permissions

class MyUserPermissions(permissions.BasePermission):

    def has_permission(self, request, view):
        return True

    def has_object_permission(self, request, view, obj):
        return request.user == obj

但是,当用户尝试获取用户列表时,上述代码将授予任何人权限。在这种情况下,最好根据action提供权限,而不是HTTP method

from rest_framework import permissions

def has_permission(self, request, view):
    if request.user.is_superuser:
        return True
    elif view.action == 'retrieve':
        return True
    else:
        return False

def has_object_permission(self, request, view, obj):
    if request.user.is_superuser:
        return True
    elif view.action == 'retrieve':
        return obj == request.user or request.user.is_staff

答案 4 :(得分:1)

这是对重写has_object_permission()方法的澄清。使用复杂权限时,返回False将无法正常工作。 有关更多详细信息,请参阅此问题 https://github.com/encode/django-rest-framework/issues/7117