2017-10-06 5 views
2

Mon code est d'avoir deux mixins, BasicAuthMixin et JWTAuthMixin comme mentionné ci-dessous. Il suffit de supposer que méthode self.authenticate retourne vrai et ne soulève aucune exception:Django appel mixin par classes d'un autre à base de classe mixin

from django.http import JsonResponse 
from django.utils.decorators import method_decorator 
from django.views.decorators.csrf import csrf_exempt 
from django.views.generic import View 

class BasicAuthMixin(View): 

    """ 
    Add this mixin to the views where Basic Auth is required. 
    """ 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     try: 
      self.authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Forbidden'}, status=403, content_type='application/json') 
     return super(BasicAuthMixin, self).dispatch(request, *args, **kwargs) 

class JWTAuthMixin(View): 
    """ 
    Add this mixin to the views where JWT based authentication is required. 
    """ 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     try: 
      self.authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Forbidden'}, status=403, content_type='application/json') 
     return super(JWTAuthMixin, self).dispatch(request, *args, **kwargs) 

Ces mixins sont utilisés dans les vues basées sur l'authentification nécessaire.

Le problème réel commence à partir d'ici: Je suis en train de créer un autre mixin AllAuthMixin qui, lorsqu'il est inclus dans une vue détermine automatiquement les mixins doivent être appelés en fonction de la tête d'authentification fourni:

class AllAuthMixin(View): 

    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     if auth.startswith('Bearer'): 
      return JWTAuthMixin.as_view()(request, *args, **kwargs) 
     elif auth.startswith('Basic'): 
      return BasicAuthMixin.as_view()(request, *args, **kwargs) 
     raise Exception('Unauthorized Access to Saurav APIs', 403) 

une fois que je inclus AllAuthMixin dans l'un des vue disent /test appelle effectivement les Mixins appropriés, mais retourne Méthode non autorisée (GET):/test

Je débogués et trouvé que Méthode non autorisée message d'erreur vient d'en bas ligne si j'utilise auth de base:

return super(BasicAuthMixin, self).dispatch(request, *args, **kwargs) 

suivant illustre un exemple très simple d'appeler mon point de vue avec auth de base:

>>> import requests 
>>> requests.get('http://127.0.0.1:8000/test', auth=('UserName', 'Password')) 
<Response [405]> 

Je ne suis pas sûr de ce que je fais mal ici. Quelqu'un peut-il m'aider s'il vous plaît à comprendre le problème ou tout autre moyen d'y parvenir. Ce que je veux, c'est réutiliser les mixins déjà déclarés: BasicAuthMixn et JWTAuthMixin.

Répondre

2

Il y a un problème de conception ici, les deux mixins sont implémentés en intercepter la méthode dispatch et en appelant super. La façon dont vous implémentez AllAuthMixin en appelant également dispatch signifie que vous devez les avoir à la fois dans son MRO et "trick" super dans le choix approprié, ce qui n'est pas une bonne idée.

Une autre façon de mettre en œuvre AllAuthMixin est de ne pas appeler dispatch mais instancier et appeler authenticate sur eux:

class AllAuthMixin(View): 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     try: 
      if auth.startswith('Bearer'): 
       JWTAuthMixin().authenticate(request) # raises on failed auth 
      elif auth.startswith('Basic'): 
       BasicAuthMixin().authenticate(request) 
     except: 
      raise Exception('Unauthorized Access to Saurav APIs', 403) 

     return super(AllAuthMixin, self).dispatch(request, *args, **kwargs) 

Une agréable façon de réutiliser le code serait de séparer l'authentification dans sa propre classe et faire mixins individuels qui en font usage. De cette façon, vous aurez une meilleure séparation des préoccupations.

Quelque chose comme:

class BasicAuth(object): 
    def authenticate(self, request): 
     # raise if not authed 
     print("Basic auth") 

class JWTAuth(object): 
    def authenticate(self, request): 
     # raise if not authed 
     print("JWT auth") 

class AuthMixin(View): 
    def authenticate(self, request): 
     raise NotImplementedError('Implement in subclass') 

    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     try: 
      self.authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Forbidden'}, status=403) 

     return super(AuthMixin, self).dispatch(request, *args, **kwargs) 

class BasicAuthMixin(BasicAuth, AuthMixin): 
    pass 

class JWTAuthMixin(JWTAuth, AuthMixin): 
    pass 

class AllAuthMixin(AuthMixin): 
    def authenticate(self, request): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     try: 
      if auth.startswith('Bearer'): 
       return JWTAuth().authenticate(request) 
      elif auth.startswith('Basic'): 
       return BasicAuth().authenticate(request) 
     except: 
      return JsonResponse({'status': 403, 'message': 'Other'}, status=403) 

class SomeView(AllAuthMixin, View): 
    def get(self, request): 
     return JsonResponse({'status': 200, 'message': 'OK'}) 

- réponse originale -

Vous appelez as_view pour chaque mixin dans AllAuthMixin, en appelant as_view()(request, *args, *kwargs) vous forcer mixin à répondre à la demande mais comme elle n'a pas de méthode get, elle renvoie 405 La méthode n'est pas autorisée comme décrit in the docs.

Vous devriez appellerez dispatch et aussi faire AllAuthMixin Hériter des deux mixins enfant de passer correctement self à dispatch. Comme si:

class AllAuthMixin(JWTAuthMixin, BasicAuthMixin): 
    @method_decorator(csrf_exempt) 
    def dispatch(self, request, *args, **kwargs): 
     auth = request.META.get('HTTP_AUTHORIZATION') or '' 
     if auth.startswith('Bearer'): 
      return JWTAuthMixin.dispatch(self, request, *args, **kwargs) 
     elif auth.startswith('Basic'): 
      return BasicAuthMixin.dispatch(self, request, *args, **kwargs) 
     raise Exception('Unauthorized Access to Saurav APIs', 403) 

class SomeView(AllAuthMixin, View): 
    def get(self, request): 
     return JsonResponse({'status': 200, 'message': 'OK'}) 
+0

Laissez-nous [continuer cette discussion dans le chat] (http://chat.stackoverflow.com/rooms/156110/discussion-between-saurav-kumar-and-yeray-diaz-diaz). –

+0

** La méthode de dispatch ** de chaque mixins fait beaucoup de choses que je n'ai pas mentionnées ici; comme la validation, en soulevant des exceptions, en renvoyant la réponse json appropriée avec le code de réponse approprié. N'appelant pas la méthode d'envoi m'amenant à écrire des codes en double dans ** AllAuthMixin **. Mais c'est bien! ** Votre suggestion récente a fonctionné ** et au moins je ne perturbe pas le code existant. Merci beaucoup pour votre temps précieux. Très apprécié :) –