فهرست منبع

fix(api): move and simplify AuthenticatedActionAuthenticator

Peter Thomassen 5 سال پیش
والد
کامیت
dd5a104eed
4فایلهای تغییر یافته به همراه28 افزوده شده و 35 حذف شده
  1. 22 0
      api/desecapi/authentication.py
  2. 2 2
      api/desecapi/serializers.py
  3. 2 2
      api/desecapi/tests/test_user_management.py
  4. 2 31
      api/desecapi/views.py

+ 22 - 0
api/desecapi/authentication.py

@@ -118,6 +118,28 @@ class EmailPasswordPayloadAuthentication(BaseAuthentication):
         return self.authenticate_credentials(serializer.data['email'], serializer.data['password'], request)
 
 
+class AuthenticatedActionAuthentication(BaseAuthentication):
+    """
+    Authenticates a request based on whether the serializer determines the validity of the given verification code
+    and additional data (using `serializer.is_valid()`). The serializer's input data will be determined by (a) the
+    view's 'code' kwarg and (b) the request payload for POST requests.
+
+    If the request is valid, the AuthenticatedAction instance will be attached to the request as `auth` attribute.
+    """
+    def authenticate(self, request):
+        view = request.parser_context['view']
+        data = {**request.data, 'code': view.kwargs['code']}  # order crucial to avoid override from payload!
+        serializer = view.serializer_class(data=data, context=view.get_serializer_context())
+        serializer.is_valid(raise_exception=True)
+        try:
+            action = serializer.Meta.model(**serializer.validated_data)
+        except ValueError:
+            exc = getattr(view, 'authentication_exception', exceptions.AuthenticationFailed)
+            raise exc('Invalid code.')
+
+        return action.user, action
+
+
 class TokenHasher(PBKDF2PasswordHasher):
     algorithm = 'pbkdf2_sha256_iter1'
     iterations = 1

+ 2 - 2
api/desecapi/serializers.py

@@ -623,9 +623,9 @@ class AuthenticatedActionSerializer(serializers.ModelSerializer):
             # decode from single string
             unpacked_data = self._unpack_code(data.pop('code'))
         except KeyError:
-            raise serializers.ValidationError({'code': ['No verification code.']})
+            raise serializers.ValidationError({'code': ['This field is required.']})
         except ValueError:
-            raise serializers.ValidationError({'code': ['Invalid verification code.']})
+            raise serializers.ValidationError({'code': ['Invalid code.']})
 
         # add extra fields added by the user
         unpacked_data.update(**data)

+ 2 - 2
api/desecapi/tests/test_user_management.py

@@ -352,14 +352,14 @@ class UserManagementTestCase(DesecTestCase, PublicSuffixMockMixin):
     def assertVerificationFailureInvalidCodeResponse(self, response):
         return self.assertContains(
             response=response,
-            text="Invalid input.",
+            text="Invalid code.",
             status_code=status.HTTP_400_BAD_REQUEST
         )
 
     def assertVerificationFailureExpiredCodeResponse(self, response):
         return self.assertContains(
             response=response,
-            text="Invalid verification code.",
+            text="Invalid code.",
             status_code=status.HTTP_400_BAD_REQUEST
         )
 

+ 2 - 31
api/desecapi/views.py

@@ -516,37 +516,8 @@ class AuthenticatedActionView(generics.GenericAPIView):
     Abstract class. Deserializes the given payload according the serializers specified by the view extending
     this class. If the `serializer.is_valid`, `act` is called on the action object.
     """
-
-    class AuthenticatedActionAuthenticator(BaseAuthentication):
-        """
-        Authenticates a request based on whether the serializer determines the validity of the given verification code
-        and additional data (using `serializer.is_valid()`). The serializer's input data will be determined by (a) the
-        view's 'code' kwarg and (b) the request payload for POST requests. Request methods other than GET and POST will
-        fail authentication regardless of other conditions.
-
-        If the request is valid, the AuthenticatedAction instance will be attached to the request as `auth` attribute.
-
-        Note that this class will raise ValidationError instead of AuthenticationFailed, usually resulting in status
-        400 instead of 403.
-        """
-
-        def __init__(self, view):
-            super().__init__()
-            self.view = view
-
-        def authenticate(self, request):
-            data = {**request.data, 'code': self.view.kwargs['code']}  # order crucial to avoid override from payload!
-            serializer = self.view.serializer_class(data=data, context=self.view.get_serializer_context())
-            serializer.is_valid(raise_exception=True)
-            try:
-                action = serializer.Meta.model(**serializer.validated_data)
-            except ValueError:
-                raise ValidationError()
-
-            return action.user, action
-
-    def get_authenticators(self):
-        return [self.AuthenticatedActionAuthenticator(self)]
+    authentication_classes = (auth.AuthenticatedActionAuthentication,)
+    authentication_exception = ValidationError
 
     def perform_authentication(self, request):
         # Delay authentication until request.auth or request.user is first accessed.