web-dev-qa-db-fra.com

Django rest-framework par permission d'action

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.

34
lWhitmore

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.

17
Carlton Gibson

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

  • Liste: personnel uniquement
  • Créer: n'importe qui
  • Récupérer: soi-même ou son personnel
  • Mise à jour, mise à jour partielle: propre ou personnel
  • Détruire: personnel uniquement

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.

57

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):
6
Marcin Kudzia

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

4
user2205363

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é.

2