Browse Source

feat(api): passwd reset when acti. user without domain and passwd

Nils Wisiol 5 years ago
parent
commit
1e79142480
2 changed files with 25 additions and 9 deletions
  1. 7 3
      api/desecapi/tests/test_user_management.py
  2. 18 6
      api/desecapi/views.py

+ 7 - 3
api/desecapi/tests/test_user_management.py

@@ -260,7 +260,7 @@ class UserManagementTestCase(DesecTestCase, PublicSuffixMockMixin):
     def assertRegistrationVerificationSuccessResponse(self, response):
         return self.assertContains(
             response=response,
-            text="Success! Please log in at",
+            text="Success!",
             status_code=status.HTTP_200_OK
         )
 
@@ -463,7 +463,7 @@ class UserManagementTestCase(DesecTestCase, PublicSuffixMockMixin):
 class UserLifeCycleTestCase(UserManagementTestCase):
 
     def test_life_cycle(self):
-        self.email, self.password = self._test_registration()
+        self.email, self.password = self._test_registration(self.random_username(), self.random_password())
         self.password = self._test_reset_password(self.email)
         mail.outbox = []
         self.token = self._test_login()
@@ -498,13 +498,17 @@ class NoUserAccountTestCase(UserLifeCycleTestCase):
         with self.get_psl_context_manager(local_public_suffix):
             self._test_registration_with_domain(domain=self.random_domain_name(suffix=local_public_suffix))
 
+    def test_registration_without_domain_and_password(self):
+        email, password = self._test_registration(self.random_username(), None)
+        self.assertResetPasswordEmail(email)
+
     def test_registration_with_tampered_domain(self):
         PublicSuffixMockMixin.setUpMockPatch(self)
         with self.get_psl_context_manager('.'):
             self._test_registration_with_domain(tampered_domain='evil.com')
 
     def test_registration_known_account(self):
-        email, _ = self._test_registration()
+        email, _ = self._test_registration(self.random_username(), self.random_password())
         self.assertRegistrationSuccessResponse(self.register_user(email, self.random_password())[2])
         self.assertNoEmailSent()
 

+ 18 - 6
api/desecapi/views.py

@@ -4,6 +4,7 @@ import binascii
 import django.core.exceptions
 from django.conf import settings
 from django.contrib.auth import user_logged_in
+from django.contrib.auth.hashers import is_password_usable
 from django.core.mail import EmailMessage
 from django.http import Http404
 from django.shortcuts import redirect
@@ -499,17 +500,21 @@ class AccountResetPasswordView(generics.GenericAPIView):
         except models.User.DoesNotExist:
             pass
         else:
-            action = models.AuthenticatedResetPasswordUserAction(user=user)
-            verification_code = serializers.AuthenticatedResetPasswordUserActionSerializer(action).data['code']
-            user.send_email('reset-password', context={
-                'confirmation_link': reverse('confirm-reset-password', request=request, args=[verification_code])
-            })
+            self.send_reset_token(user, request)
 
         # This request is unauthenticated, so don't expose whether we did anything.
         return Response(data={'detail': 'Please check your mailbox for further password reset instructions. '
                                         'If you did not receive an email, please contact support.'},
                         status=status.HTTP_202_ACCEPTED)
 
+    @staticmethod
+    def send_reset_token(user, request):
+        action = models.AuthenticatedResetPasswordUserAction(user=user)
+        verification_code = serializers.AuthenticatedResetPasswordUserActionSerializer(action).data['code']
+        user.send_email('reset-password', context={
+            'confirmation_link': reverse('confirm-reset-password', request=request, args=[verification_code])
+        })
+
 
 class AuthenticatedActionView(generics.GenericAPIView):
     """
@@ -585,8 +590,15 @@ class AuthenticatedActivateUserActionView(AuthenticatedActionView):
         return PDNSChangeTracker.track(lambda: serializer.save(owner=action.user))
 
     def _finalize_without_domain(self):
+        login_url = self.request.build_absolute_uri(reverse('v1:login'))
+        if not is_password_usable(self.request.auth.user.password):
+            AccountResetPasswordView.send_reset_token(self.request.auth.user, self.request)
+            return Response({
+                'detail': f'Success! We sent you instructions on how to reset your password. Afterwards, Please log in '
+                          f'at {login_url}.'
+            })
         return Response({
-                'detail': 'Success! Please log in at {}.'.format(self.request.build_absolute_uri(reverse('v1:login')))
+                'detail': f'Success! Please log in at {login_url}.'
             })
 
     def _finalize_with_domain(self, domain):