def test_too_many_reset_attempts(self, mock_request): with app.app_context(): token = verification.generate_email_token("test.com") mock_request.get(url_banner_api, status_code=404) mock_request.post(url_reset_password_request, status_code=200) mock_request.get( url_get_respondent_by_email, status_code=200, json={"firstName": "Bob", "id": "123456", "password_verification_token": token}, ) mock_request.get(url_password_reset_counter, status_code=200, json={"counter": 5}) mock_request.delete(url_password_reset_counter, status_code=200, json={}) mock_request.post( f"{TestingConfig.PARTY_URL}/party-api/v1/respondents/123456/password-verification-token", status_code=200, json={"message": "Successfully added token"}, ) response = self.app.post("passwords/forgot-password", data=self.email_form, follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertIn( "You've tried to reset your password too many times".encode(), response.data, )
def test_reset_password_get_success(self, mock_object): mock_object.get(url_verify_token, status_code=200) with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.get(f"passwords/reset-password/{token}", follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue('Reset your password'.encode() in response.data)
def test_reset_password_no_password(self, mock_object): mock_object.get(url_verify_token, status_code=200) password_form = {} with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue("Password is required".encode() in response.data)
def test_reset_password_post_different_passwords(self, mock_object): mock_object.get(url_verify_token, status_code=200) password_form = {"password": "******", "password_confirm": "Gizmo007!"} with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue('Your passwords do not match'.encode() in response.data)
def test_reset_password_post_token_invalid(self, mock_object): mock_object.put(url_password_change, status_code=404) password_form = {"password": "******", "password_confirm": "Gizmo007!"} with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 404) self.assertTrue('Page not found'.encode() in response.data)
def test_resend_verification_email_using_expired_token(self, mock_object, mock_notify): mock_object.get('http://*****:*****@test.com") response = self.app.get(f'passwords/resend-password-email-expired-token/{token}', follow_redirects=True) self.assertEqual(response.status_code, 200) mock_notify.assert_called_once() self.assertTrue('Check your email'.encode() in response.data)
def test_reset_password_put_party_service_fail(self, mock_object): mock_object.put(url_password_change, status_code=500) password_form = {"password": "******", "password_confirm": "Gizmo007!"} with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 500) self.assertTrue("An error has occurred".encode() in response.data)
def test_reset_password_post_requirements_fail(self, mock_object): mock_object.get(url_verify_token, status_code=200) password_form = {"password": "******", "password_confirm": "Gizmo007a"} with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 200) print(response.data) self.assertTrue("Your password doesn't meet the requirements".encode() in response.data)
def test_reset_password_post_token_expired(self, mock_request): mock_request.get(url_banner_api, status_code=404) mock_request.put(url_password_change, status_code=409) password_form = {"password": "******", "password_confirm": "Gizmo007!Gizmo"} with app.app_context(): token = verification.generate_email_token("test.com") response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue("Your link has expired".encode() in response.data)
def test_reset_password_get_token_not_found(self, mock_request): mock_request.get(url_banner_api, status_code=404) mock_request.get(url_verify_token, status_code=200) with app.app_context(): token = verification.generate_email_token("failing_email_token.com") mock_request.get( url_get_respondent_by_email, status_code=200, json={"firstName": "Bob", "id": "123456", "password_verification_token": []}, ) response = self.app.get(f"passwords/reset-password/{token}", follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue("Your link is invalid or has already been used".encode() in response.data)
def test_reset_password_get_success(self, mock_request): mock_request.get(url_banner_api, status_code=404) mock_request.get(url_verify_token, status_code=200) with app.app_context(): token = verification.generate_email_token("test.com") mock_request.get( url_get_respondent_by_email, status_code=200, json={"firstName": "Bob", "id": "123456", "password_verification_token": token}, ) response = self.app.get(f"passwords/reset-password/{token}", follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue("Reset your password".encode() in response.data)
def test_reset_password_post_requirements_fail(self, mock_request): mock_request.get(url_banner_api, status_code=404) mock_request.get(url_verify_token, status_code=200) password_form = {"password": "******", "password_confirm": "Gizmo007a"} with app.app_context(): token = verification.generate_email_token("test.com") mock_request.get( url_get_respondent_by_email, status_code=200, json={"firstName": "Bob", "id": "123456", "password_verification_token": token}, ) response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue("Your password doesn't meet the requirements".encode() in response.data)
def test_resend_verification_email_using_expired_token(self, mock_request, mock_notify): mock_request.get(url_banner_api, status_code=404) mock_request.get( "http://*****:*****@test.com") mock_request.get(url_password_reset_counter, status_code=200, json={"counter": 0}) mock_request.delete(url_password_reset_counter, status_code=200, json={}) mock_request.post( f"{TestingConfig.PARTY_URL}/party-api/v1/respondents/123456/password-verification-token", status_code=200, json={"message": "Successfully added token"}, ) response = self.app.get(f"passwords/resend-password-email-expired-token/{token}", follow_redirects=True) self.assertEqual(response.status_code, 200) mock_notify.assert_called_once() self.assertTrue("Check your email".encode() in response.data)
def test_reset_password_post_success(self, mock_request): mock_request.get(url_banner_api, status_code=404) mock_request.put(url_password_change, status_code=200) password_form = {"password": "******", "password_confirm": "Gizmo007!Gizmo"} with app.app_context(): token = verification.generate_email_token("test.com") mock_request.get( url_get_respondent_by_email, status_code=200, json={"firstName": "Bob", "id": "123456", "password_verification_token": token}, ) mock_request.delete( f"{TestingConfig.PARTY_URL}/party-api/v1/respondents/123456/password-verification-token/{token}", status_code=200, json={"message": "Successfully removed token"}, ) mock_request.delete(url_password_reset_counter, status_code=200) response = self.app.post(f"passwords/reset-password/{token}", data=password_form, follow_redirects=True) self.assertEqual(response.status_code, 200) self.assertTrue("Your password has been changed".encode() in response.data)
def request_password_change(email): respondent = party_controller.get_respondent_by_email(email) if not respondent: logger.info("Respondent does not exist") return redirect(url_for('passwords_bp.reset_password_check_email')) party_id = str(respondent['id']) logger.info("Requesting password change", party_id=party_id) token = verification.generate_email_token(email) url_root = request.url_root # url_for comes with a leading slash, so strip off the trailing slash in url_root if there is one if url_root.endswith('/'): url_root = url_root[:-1] verification_url = url_root + url_for('passwords_bp.post_reset_password', token=token) personalisation = { 'RESET_PASSWORD_URL': verification_url, 'FIRST_NAME': respondent['firstName'] } logger.info('Reset password url', url=verification_url, party_id=party_id) try: NotifyGateway(app.config).request_to_notify(email=email, personalisation=personalisation, reference=party_id) logger.info('Password reset email successfully sent', party_id=party_id) except RasNotifyError: # Note: intentionally suppresses exception logger.error('Error sending request to Notify Gateway', respondent_id=party_id, exc_info=True) return redirect(url_for('passwords_bp.reset_password_check_email'))
def request_password_change(email): respondent = party_controller.get_respondent_by_email(email) if not respondent: logger.info("Respondent does not exist") return redirect(url_for("passwords_bp.reset_password_trouble")) party_id = str(respondent["id"]) password_reset_counter = party_controller.get_password_reset_counter( party_id)["counter"] if password_reset_counter != 0: try: email = verification.decode_email_token( respondent["password_verification_token"], app.config["PASSWORD_RESET_ATTEMPTS_TIMEOUT"]) except SignatureExpired: try: party_controller.reset_password_reset_counter(party_id) password_reset_counter = 0 except ApiError: logger.error("Error resetting password reset counter") return redirect(url_for("passwords_bp.reset_password_trouble")) if password_reset_counter >= 5: logger.error("Password reset attempts exceeded") return redirect( url_for("passwords_bp.exceeded_number_of_reset_attempts")) logger.info("Requesting password change", party_id=party_id) token = verification.generate_email_token(email) url_root = request.url_root # url_for comes with a leading slash, so strip off the trailing slash in url_root if there is one if url_root.endswith("/"): url_root = url_root[:-1] verification_url = url_root + url_for("passwords_bp.post_reset_password", token=token) personalisation = { "RESET_PASSWORD_URL": verification_url, "FIRST_NAME": respondent["firstName"] } logger.info("Reset password url", url=verification_url, party_id=party_id) party_controller.post_verification_token(email, token) try: NotifyGateway(app.config).request_to_notify( email=email, personalisation=personalisation, reference=party_id) logger.info("Password reset email successfully sent", party_id=party_id) except RasNotifyError: # Note: intentionally suppresses exception logger.error("Error sending request to Notify Gateway", respondent_id=party_id, exc_info=True) # Get real time counter to check how many attempts are left password_reset_counter = party_controller.get_password_reset_counter( party_id)["counter"] if password_reset_counter == 4: flash(message="You have 1 try left to reset your password", category="warn") return redirect( url_for("passwords_bp.reset_password_check_email", token=token))