Django REST framework permissions au niveau de l'objet

J'utilise Django REST Framework pour accéder à une ressource 'user'.

comme les informations utilisateur sont personnelles, Je ne veux pas d'une requête GET pour lister chaque utilisateur sur le système, sauf s'il s'agit d'un administrateur.

si l'utilisateur spécifie son id, et qu'ils sont connectés, je voudrais qu'ils puissent voir leurs détails et les modifier (mettre post supprimer) si nécessaire.

donc en résumé, dis-allow méthode GET pour quiconque n'est pas un admin et allow GET POST DELETE PUT on utilisateurs connectés lors de la consultation de leurs informations.

alors j'ai créé la classe de permission personnalisée:

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

Cela n'a pas fonctionné. Après quelques déboguages j'ai trouvé qu'il vérifie d'abord has_permission, puis qu'il vérifie has_object_permission. Donc si nous ne franchissons pas ce premier obstacle GET /user/, alors il ne considérera même pas le prochain GET /user/id.

<!-Donc, en résumé, quelqu'un sait comment je ferais pour que ça marche?

Merci :)

EDIT:

J'utilisais des ModelViewSets, qui ont ce problème comme je l'ai décrit.

mais si vous divisez la fonctionnalité List avec le détail, alors vous pouvez leur donner des classes de permission séparées:

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
21
demandé sur user1830568 2013-09-06 00:18:48

4 réponses

je l'ai fait dans le passé en utilisant une autorisation personnalisée et remplacée has_object_permission comme suit:

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

vous pouvez faire des choses plus détaillées telles que nier des types de requêtes spécifiques (par exemple, pour autoriser une requête GET pour tous les utilisateurs):

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

puis dans votre vue vous lui dites d'utiliser la classe de permissions:

from my_custom_permissions import MyUserPermissions

class UserView(generics.ListCreateAPIView):
    ...
    permission_classes = (MyUserPermissions, )
    ...
18
répondu will-hart 2017-08-24 07:19:02

j'ai un besoin similaire. Appelons mon application!--5-->. Voici ce que j'ai trouvé.

tout d'abord, mettez ceci dans 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

Puis 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)

Puis 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,)

En passant, notez que vous pouvez utiliser un différents sérialiseur pour ces deux viewsets, ce qui signifie que vous pouvez afficher des attributs différents dans le list vue que dans le retrieve voir! Pour exemple:

# 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',)

Puis 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)

...

j'ai été légèrement surpris que router accepté le même modèle à deux reprises, mais il ne semble fonctionner.

Caveat lector: j'ai confirmé que tout fonctionne via le navigateur API, mais je n'ai pas essayé mise à jour via l'API encore.

10
répondu Tim Ruddick 2014-05-02 21:54:41

Pour les faux upons, le documentation sous les limites de la permission au niveau de l'objet dit:

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.

ainsi, la vue des détails fonctionnera mais pour la liste, vous aurez besoin de filtre contre l'utilisateur courant.

7
répondu keni 2015-07-09 18:39:15

encore une chose à la réponse de @will-hart.

Dans DRF3 documentation,

Note: la méthode has_object_permission au niveau de l'instance ne sera appelée que si les vérifications has_permission au niveau de la vue ont déjà passé

par conséquent,has_permission devrait être spécifié utiliser 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

cependant, le code ci-dessus donnera la permission à n'importe qui quand l'utilisateur essaye d'obtenir la liste de l'utilisateur. Dans ce cas, il serait préférable d' donner la permission selon action, pas 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
6
répondu Chemical Programmer 2016-06-06 05:49:30