Je suis un débutant dans le développement avec Django + Django Rest-framework et je travaille sur un projet qui fournit REST un accès Api. Je me demandais quelle était la meilleure pratique pour attribuer une autorisation différente à chaque action d'un ApiView ou d'un Viewset donné.
Supposons que j'ai défini certaines classes d'autorisations telles que 'IsAdmin', 'IsRole1', 'IsRole2', ..., et que je souhaite accorder des autorisations différentes aux actions uniques (par exemple, un utilisateur avec Role1 peut créer ou récupérer, un utilisateur avec Le rôle 2 peut être mis à jour et seul un administrateur peut le supprimer).
Comment puis-je structurer une vue basée sur une classe afin d'attribuer une classe d'autorisation aux actions "créer", "liste", "récupérer", "mettre à jour", "supprimer"? J'essaie de le faire pour avoir une classe qui peut être réutilisée pour différentes tables qui ont le même modèle d'autorisation.
Peut-être que je me noie dans un pouce d'eau, merci pour vos réponses.
Vous pouvez créer une classe d'autorisation personnalisée étendant BasePermission
de DRF.
Vous implémentez has_permission
où vous avez accès aux objets request
et view
. Tu peux vérifier request.user
pour le rôle approprié et renvoyez True
/False
selon le cas.
Jetez un oeil à la classe IsAuthenticatedOrReadOnly fournie (et à d'autres) pour un bon exemple de la facilité.
J'espère que ça aide.
Dans la documentation DRF,
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.
Supposons l'autorisation suivante sur l'objet user
permissons.py
from rest_framework import permissions
class UserPermission(permissions.BasePermission):
def has_permission(self, request, view):
if view.action == 'list':
return request.user.is_authenticated() and request.user.is_admin
Elif view.action == 'create':
return True
Elif view.action in ['retrieve', 'update', 'partial_update', 'destroy']:
return True
else:
return False
def has_object_permission(self, request, view, obj):
# Deny actions on objects if the user is not authenticated
if not request.user.is_authenticated():
return False
if view.action == 'retrieve':
return obj == request.user or request.user.is_admin
Elif view.action in ['update', 'partial_update']:
return obj == request.user or request.user.is_admin
Elif view.action == 'destroy':
return request.user.is_admin
else:
return False
views.py
from .models import User
from .permissions import UserPermission
from .serializers import UserSerializer
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (UserPermission,)
MODIFIER
Pour Django 2.0 remplacez is_authenticated()
par is_authenticated
. La méthode a été transformée en attribut.
Personnellement, je déteste ce type d'autorisations personnalisées frankenmonster, à mon avis, ce n'est pas très idiomatique en ce qui concerne Django framework; J'ai donc trouvé la solution suivante - elle est très similaire à la façon dont @list_route
Et @detail_route
Les décorateurs fonctionnent. Nous comptons sur le fait que les méthodes/fonctions sont des objets de première classe
Tout d'abord, je crée un tel décorateur:
decorators.py
def route_action_arguments(**kwargs):
"""
Add arguments to the action method
"""
def decorator(func):
func.route_action_kwargs = kwargs
return func
return decorator
Comme vous pouvez le voir, il ajoute un dictionnaire à la fonction qu'il décore avec des paramètres passés sous forme de liste d'arguments
Maintenant, j'ai créé un tel mixin: mixins.py
class RouteActionArgumentsMixin (object):
"""
Use action specific parameters to
provide:
- serializer
- permissions
"""
def _get_kwargs(self):
action = getattr(self, 'action')
if not action:
raise AttributeError
print('getting route kwargs for action:' + action)
action_method = getattr(self, action)
kwargs = getattr(action_method, 'route_action_kwargs')
print(dir(kwargs))
return kwargs
def get_serializer_class(self):
try:
kwargs = self._get_kwargs()
return kwargs['serializer']
except (KeyError, AttributeError):
return super(RouteActionArgumentsMixin, self).get_serializer_class()
def get_permissions(self):
try:
kwargs = self._get_kwargs()
return kwargs['permission_classes']
except (KeyError, AttributeError):
return super(RouteActionArgumentsMixin, self).get_permissions()
Mixin fait deux choses; lorsque get_permissions
est appelé, il vérifie quelle "action" est exécutée et recherche la collection permission_classes du route_action_kwargs
associé au viewset.action_method.route_action_kwargs
lorsque get_serializer_class
est appelé, il fait de même et sélectionne le serializer
dans route_action_kwargs
Maintenant, la façon dont nous pouvons l'utiliser:
@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
"""
User and profile managment viewset
"""
queryset = User.objects.all()
serializer_class = UserSerializer
@list_route(methods=['post'])
@route_action_arguments(permission_classes=(AllowAny,), serializer=LoginSerializer)
def login(self, request):
serializer = self.get_serializer_class()(data=request.data)
Pour les routs personnalisés que nous définissons explicitement, nous pouvons simplement définir le @route_action_arguments
Explicitement sur la méthode.
En termes de vues et de méthodes génériques, nous pouvons toujours les ajouter en utilisant le @method_decorator
@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
Django a une classe de persmissions appelée DjangoObjectPermissions qui utilise Django Guardian comme backend d'authentification.
Lorsque vous avez Django gardien actif dans vos paramètres, vous ajoutez simplement permission_classes = [DjandoObjectPermissions]
à votre vue et il effectue automatiquement l'authentification des autorisations, vous pouvez donc "CRUD" en fonction de l'autorisation définie pour un Django.contrib.auth
groupe ou utilisateur.
Voir un Gist avec un exemple.
Vous pouvez définir Django Guardian comme support d'authentification http://Django-guardian.readthedocs.org/en/latest/installation.html
Les vues basées sur les classes de RestFramework ont des méthodes pour chaque verbe HTTP (par exemple: HTTP GET => view.get () etc.). Il vous suffit d'utiliser les autorisations, les utilisateurs, les groupes et les décorateurs de Django.contrib.auth comme indiqué.