def test_token_is_created(self): """We test whether we are able to create a token""" payload = { 'email': '*****@*****.**', 'callback_url': 'http://www.example.com' } token = TokenHandler().create_verification_token(payload) # a valid token should be decoded self.assertTrue(TokenHandler().validate_token(token))
def test_not_alphanumeric_password(self): """"Tests for a request with a password that is not alphanumeric.""" signup_data = { "user": { "username": "******", "email": "*****@*****.**", "password": "******", "callback_url": "https://medium.com" } } payload = { "email": "*****@*****.**", "callback_url": "https://www.youtube.com/" } token = TokenHandler().create_verification_token(payload) not_alphanumeric_password_data = { "user_password": { "password": "******", "confirm_password": "******", "token": token } } not_alphanumeric_password_data_response = { "errors": { "password": ["Password should be alphanumeric"] } } self.client.post(self.register_url, signup_data, format='json') response = self.client.put(self.url, not_alphanumeric_password_data, format='json') self.assertEqual(response.data, not_alphanumeric_password_data_response) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_an_invalid_token_cannot_be_decoded(self): """If a user passes an invalid jwt token, it should not be decoded""" token = '' res = TokenHandler().validate_token(token) self.assertEqual(res, 'Error. Could not decode token!')
def test_long_password(self): """"Tests for a request with a password longer than 128 characters.""" signup_data = { "user": { "username": "******", "email": "*****@*****.**", "password": "******", "callback_url": "https://medium.com" } } payload = { "email": "*****@*****.**", "callback_url": "https://www.youtube.com/" } token = TokenHandler().create_verification_token(payload) long_password_data = { "user_password": { "password": "******" * 50, "confirm_password": "******" * 50, "token": token } } long_password_data_response = { "errors": { "password": ["Password should not be longer than 128 characters"] } } self.client.post(self.register_url, signup_data, format='json') response = self.client.put(self.url, long_password_data, format='json') self.assertEqual(response.data, long_password_data_response) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_expired_token_results_in_an_error(self): """If a token expires, users should not be verified by providing it""" token_expiry = date.today() - timedelta(1) payload = { 'email': '*****@*****.**', 'callback_url': 'http://www.example.com' } token = jwt.encode( { 'email': payload['email'], 'callback_url': payload['callback_url'], 'exp': int(token_expiry.strftime('%s')) }, settings.SECRET_KEY, algorithm='HS256') with self.assertRaises(exceptions.AuthenticationFailed) as e: TokenHandler().validate_token(token) self.assertEqual( str(e.exception), 'Your token has expired. Make a new token and try again')
def test_not_matching_passwords(self): """"Tests for a request with passwords that do not match.""" signup_data = { "user": { "username": "******", "email": "*****@*****.**", "password": "******", "callback_url": "https://medium.com" } } payload = { "email": "*****@*****.**", "callback_url": "https://www.youtube.com/" } token = TokenHandler().create_verification_token(payload) password_data = { "user_password": { "password": "******", "confirm_password": "******", "token": token } } self.client.post(self.register_url, signup_data, format='json') user = get_object_or_404(User, email="*****@*****.**") user_id = user.id token_data = {"user": user_id, "token": token} serializer = PasswordResetTokenSerializer(data=token_data) serializer.is_valid(raise_exception=True) serializer.save() password_data_response = {"message": "Passwords do not Match"} response = self.client.put(self.url, password_data, format='json') self.assertEqual(response.data, password_data_response)
def test_no_password_field(self): """Tests for a request with no password field.""" signup_data = { "user": { "username": "******", "email": "*****@*****.**", "password": "******", "callback_url": "https://medium.com" } } payload = { "email": "*****@*****.**", "callback_url": "https://www.youtube.com/" } token = TokenHandler().create_verification_token(payload) blank_password_data = { "user_password": { "password": "", "confirm_password": "", "token": token } } blank_password_data_response = { "errors": { "password": ["Password field cannot be blank"] } } self.client.post(self.register_url, signup_data, format='json') response = self.client.put(self.url, blank_password_data, format='json') self.assertEqual(response.data, blank_password_data_response) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_decoding_failure_because_payload_is_not_dictionary(self): """The payload to be encoded should be a dictionary""" payload = ['just a list'] with self.assertRaises(TypeError) as e: TokenHandler().create_verification_token(payload) self.assertEqual(str(e.exception), 'Payload must be a dictionary!')
def post(self, request): data = request.data.get('payload') serializer = PasswordResetSerializer(data=data) serializer.is_valid(raise_exception=True) user_email = data['email'] callback_url = data['callback_url'] message = "A password reset link has been sent to your email." try: user = User.objects.get(email=user_email) user_id = user.id payload = {"email": user_email, "callback_url": callback_url} token = TokenHandler().create_verification_token(payload) token_data = {"user": user_id, "token": token} serializer = PasswordResetTokenSerializer(data=token_data) serializer.is_valid(raise_exception=True) serializer.save() TokenHandler().send_password_reset_link(user_email, token, callback_url) return Response({"message": message}, status=status.HTTP_200_OK) except User.DoesNotExist: return Response({"message": message}, status=status.HTTP_200_OK)
def test_users_can_get_verified(self): """Unverified users should be able to verify their accounts""" user = User.objects.create_user(username='******', email='*****@*****.**', password='******') user.save() data = {'email': '*****@*****.**', 'username': '******', 'callback_url': 'http://www.youtube.com'} token = TokenHandler().create_verification_token(data) client = APIClient() res = client.get(reverse('authentication:verify email', args=(token,))) self.assertEqual(res.status_code, status.HTTP_302_FOUND)
def put(self, request): """ put: Update a user's password with a new password. """ try: data = request.data.get('user_password') serializer = PasswordChangeSerializer(data=data) serializer.is_valid(raise_exception=True) token = data['token'] user = PasswordResetToken.objects.get(token=token) is_valid = user.is_valid if is_valid: credentials = TokenHandler().validate_token(token) password = data['password'] confirm_password = data['confirm_password'] if password != confirm_password: return Response({"message": "Passwords do not Match"}) serializer = PasswordChangeSerializer(data=data) serializer.is_valid(raise_exception=True) serializer = PasswordChangeSerializer(instance=User, data=data, partial=True) serializer = User.objects.get(email=credentials['email']) serializer.set_password(password) serializer.save() user.is_valid = False user.save() return Response({'message': 'Your password has been changed.'}, status=status.HTTP_202_ACCEPTED) else: return Response( { 'message': 'Sorry, we couldn\'t find that password reset' ' key in our database. Please send another request.' }, status=status.HTTP_404_NOT_FOUND) except PasswordResetToken.DoesNotExist: return Response( {'message': 'A user with the given token does not exist.'}, status=status.HTTP_404_NOT_FOUND)
def test_using_same_link_twice(self): """Tests for a request with an already used token.""" signup_data = { "user": { "username": "******", "email": "*****@*****.**", "password": "******", "callback_url": "https://medium.com" } } payload = { "email": "*****@*****.**", "callback_url": "https://medium.com" } token = TokenHandler().create_verification_token(payload) data = { "user_password": { "password": "******", "confirm_password": "******", "token": token } } data_response = { "message": "Sorry, we couldn't find that password reset key in our database." " Please send another request." } self.client.post(self.register_url, signup_data, format='json') user = get_object_or_404(User, email="*****@*****.**") user_id = user.id token_data = {"user": user_id, "token": token} serializer = PasswordResetTokenSerializer(data=token_data) serializer.is_valid(raise_exception=True) serializer.save() self.client.post(self.register_url, signup_data, format='json') self.client.put(self.url, data, format='json') response = self.client.put(self.url, data, format='json') self.assertEqual(response.data, data_response) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def get(self, request, token): decoded_token = TokenHandler().validate_token(token) if 'email' not in decoded_token: return Response({'error': 'invalid token'}, status=status.HTTP_400_BAD_REQUEST) # we check if the user exists and whether they are verified. # if we don't find a user we raise an error # if we find a registered user, we raise an error try: user = User.objects.get(email=decoded_token['email']) except User.DoesNotExist: return Response( {'email': 'No user with this email has been registered'}, status=status.HTTP_404_NOT_FOUND) if user.is_verified is True: return Response({'email': 'This email has already been verified'}, status=status.HTTP_400_BAD_REQUEST) user.is_verified = True user.save() return HttpResponseRedirect(decoded_token['callback_url'])
def post(self, request): """This is the method that will be called when users want a new verification token.""" data = request.data serializer = self.serializer_class(data=data) serializer.is_valid(raise_exception=True) payload = serializer.create_payload(data) token = TokenHandler().create_verification_token(payload) user_email = payload['email'] domain = settings.DOMAIN template_name = 'email_verification.html' context = { 'username': payload['username'], 'token': token, 'domain': domain } html_message = render_to_string(template_name, context) text_message = strip_tags(html_message) thread = Thread(target=mail.send_mail, args=[ 'Please verify your email', text_message, settings.FROM_EMAIL, [ user_email, ], html_message ]) thread.setDaemon(True) thread.start() message = { 'message': 'New verification token created. Please proceed to your email ' + # noqa user_email + ' to verify your account.' } return Response(message, status=status.HTTP_201_CREATED)
def post(self, request): user = request.data.get('user', {}) # The create serializer, validate serializer, save serializer pattern # below is common and you will see it a lot throughout this course and # your own work later on. Get familiar with it. serializer = self.serializer_class(data=user) serializer.is_valid(raise_exception=True) user_email = serializer.validated_data['email'] username = serializer.validated_data['username'] callback = {'url': serializer.validated_data['callback_url']} token_payload = {'email': user_email, 'callback_url': callback['url']} domain = settings.DOMAIN token = TokenHandler().create_verification_token(token_payload) template_name = 'email_verification.html' context = {'username': username, 'token': token, 'domain': domain} # https://stackoverflow.com/questions/3005080/how-to-send-html-email-with-django-with-dynamic-content-in-it html_message = render_to_string(template_name, context) text_message = strip_tags(html_message) thread = Thread(target=mail.send_mail, args=[ 'Please verify your email', text_message, settings.FROM_EMAIL, [ user_email, ], html_message ]) thread.setDaemon(True) thread.start() message = { 'message': 'Successfully created your account. Please proceed to your email ' + # noqa user_email + ' to verify your account.' } serializer.save() return Response(message, status=status.HTTP_201_CREATED)
def test_valid_new_password(self): """Tests for a request with a valid new password.""" signup_data = { "user": { "username": "******", "email": "*****@*****.**", "password": "******", "callback_url": "https://medium.com" } } payload = { "email": "*****@*****.**", "callback_url": "https://medium.com" } token = TokenHandler().create_verification_token(payload) data = { "user_password": { "password": "******", "confirm_password": "******", "token": token } } self.client.post(self.register_url, signup_data, format='json') user = get_object_or_404(User, email="*****@*****.**") user_id = user.id token_data = {"user": user_id, "token": token} serializer = PasswordResetTokenSerializer(data=token_data) serializer.is_valid(raise_exception=True) serializer.save() data_response = {"message": "Your password has been changed."} response = self.client.put(self.url, data, format='json') self.assertEqual(response.data, data_response) self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
def test_token_can_only_be_encoded_if_neccessary_keys_are_passed(self): """For a token to be created, we need the `email` and `callback_url` to be provided""" error = TokenHandler().create_verification_token({}) self.assertEqual(error, 'Please provide email and callback_url')