J'utilise Django REST Framework pour accéder à une ressource 'utilisateur').
Étant donné que les informations utilisateur sont personnelles, je ne souhaite pas qu'une demande GET répertorie tous les utilisateurs du système, À MOINS QU'ils ne soient administrateurs.
Si l'utilisateur spécifie son identifiant et qu'il est connecté, j'aimerais qu'il puisse voir ses coordonnées et les modifier (PUT POST DELETE) si nécessaire.
Donc, en résumé, désactivez la méthode GET pour toute personne qui n'est pas administrateur et autorisez GET POST DELETE PUT sur les utilisateurs connectés lors de l'affichage de leurs informations.
j'ai donc créé la classe d'autorisation 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
Ça n'a pas marché. Après quelques débogages, j'ai trouvé qu'il vérifie d'abord has_permission, puis vérifie has_object_permission. Donc, si nous ne dépassons pas ce premier obstacle GET/user /, il ne considérera même pas le prochain GET/user/id.
donc, en résumé, est-ce que quelqu'un sait comment je procéderais pour que cela fonctionne?
Merci :)
MODIFIER:
J'utilisais ModelViewSets, qui ont ce problème comme je l'ai décrit.
Mais si vous divisez la fonctionnalité de liste avec le détail, vous pouvez leur donner des classes d'autorisation distinctes:
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
J'ai fait cela dans le passé en utilisant une autorisation personnalisée et j'ai remplacé 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 refuser des types de demande spécifiques (par exemple pour autoriser une demande 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
Ensuite, à votre avis, vous lui dites d'utiliser la classe d'autorisations:
from my_custom_permissions import MyUserPermissions
class UserView(generics.ListCreateAPIView):
...
permission_classes = (MyUserPermissions, )
...
J'ai un besoin similaire. Permet d'appeler mon application x
. 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 dans 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 dans 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,)
Soit dit en passant, notez que vous pouvez utiliser un sérialiseur différent pour ces deux ensembles de vues, ce qui signifie que vous pouvez afficher différents attributs dans la vue list
que dans la vue retrieve
! Par 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 dans 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
ait accepté le même schéma deux fois, mais cela semble fonctionner.
Avertissement: j'ai confirmé que tout fonctionne via le navigateur API, mais je n'ai pas essayé mise à jour via l'API encore.
Pour les trébuchements, la documentation sous les limitations de l'autorisation au niveau 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 détaillée fonctionnera mais pour la liste, vous devrez filtrer par rapport à l'utilisateur actuel.
Encore une chose à la réponse de @ will-hart.
Dans la documentation DRF3,
Remarque: La méthode has_object_permission au niveau de l'instance ne sera appelée que si les contrôles has_permission au niveau de la vue sont déjà passés.
Donc, has_permission
doit être spécifié pour 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 lorsque l'utilisateur essaie d'obtenir la liste des utilisateurs. Dans ce cas, il serait préférable de donner la permission selon action
, pas le 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