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