web-dev-qa-db-fra.com

Django REST Framework - Autorisations séparées par méthodes

J'écris une API en utilisant Django REST Framework et je me demande si peut spécifier des autorisations par méthode lors de l'utilisation des vues basées sur les classes).

Lire la documentation Je vois que c'est assez facile à faire si vous écrivez des vues basées sur des fonctions, en utilisant simplement le @permission_classes décorateur sur la fonction des vues que vous souhaitez protéger avec des autorisations. Cependant, je ne vois pas de moyen de faire la même chose lors de l'utilisation de CBV avec la classe APIView, car alors je spécifie les autorisations pour la classe complète avec le permission_classes, mais qui sera ensuite appliqué à toutes les méthodes de classe (get, post, put...).

Ainsi, est-il possible d'avoir les vues API écrites avec des CBV et de spécifier également des autorisations différentes pour chaque méthode d'une classe de vue?

43
José L. Patiño

Les autorisations sont appliquées à l'ensemble de la classe View, mais vous pouvez prendre en compte des aspects de la demande (comme la méthode telle que GET ou POST) dans votre décision d'autorisation.

Voyez le IsAuthenticatedOrReadOnly intégré comme exemple:

SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']

class IsAuthenticatedOrReadOnly(BasePermission):
    """
    The request is authenticated as a user, or is a read-only request.
    """

    def has_permission(self, request, view):
        if (request.method in SAFE_METHODS or
            request.user and
            request.user.is_authenticated()):
            return True
        return False
43
Kevin Stone

J'ai rencontré le même problème lors de l'utilisation de CBV, car j'ai une logique d'autorisations assez complexe en fonction de la méthode de demande.

La solution que j'ai trouvée a été d'utiliser l'application tierce 'rest_condition' listée au bas de cette page

http://www.Django-rest-framework.org/api-guide/permissions

https://github.com/caxap/rest_condition

Je viens de diviser la logique du flux d'autorisations afin que chaque branche s'exécute, selon la méthode de demande.

from rest_condition import And, Or, Not

class MyClassBasedView(APIView):

    permission_classes = [Or(And(IsReadOnlyRequest, IsAllowedRetrieveThis, IsAllowedRetrieveThat),
                             And(IsPostRequest, IsAllowedToCreateThis, ...),
                             And(IsPutPatchRequest, ...),
                             And(IsDeleteRequest, ...)]

Ainsi, le "Ou" détermine quelle branche des autorisations doit s'exécuter en fonction de la méthode de demande et le "Et" encapsule les autorisations relatives à la méthode de demande acceptée, donc tous doivent passer pour que l'autorisation soit accordée. Vous pouvez également mélanger "Ou", "Et" et "Non" dans chaque flux pour créer des autorisations encore plus complexes.

Les classes d'autorisation pour exécuter chaque branche ressemblent simplement à ceci,

class IsReadyOnlyRequest(permissions.BasePermission):

    def has_permission(self, request, view):
        return request.method in permissions.SAFE_METHODS


class IsPostRequest(permissions.BasePermission):

    def has_permission(self, request, view):
        return request.method == "POST"


... #You get the idea
47
james

J'ai rencontré ce problème et je voulais vraiment utiliser le @permission_classes décorateur pour marquer certaines méthodes d'affichage personnalisées avec des autorisations spécifiques. J'ai fini par trouver un mixin:

class PermissionsPerMethodMixin(object):
    def get_permissions(self):
        """
        Allows overriding default permissions with @permission_classes
        """
        view = getattr(self, self.action)
        if hasattr(view, 'permission_classes'):
            return [permission_class() for permission_class in view.permission_classes]
        return super().get_permissions()

Un exemple d'utilisation:

from rest_framework.decorators import action, permission_classes  # other imports elided

class MyViewset(PermissionsPerMethodMixin, viewsets.ModelViewSet):
    permission_classes = (IsAuthenticatedOrReadOnly,)  # used for default ViewSet endpoints
    queryset = MyModel.objects.all()
    serializer_class = MySerializer

    @action(detail=False, methods=['get'])
    @permission_classes((IsAuthenticated,))  # overrides IsAuthenticatedOrReadOnly
    def search(self, request):
        return do_search(request)  # ...
1
GDorn