# -*- coding: utf-8 -*- """ test_recoverable ~~~~~~~~~~~~~~~~ Recoverable functionality tests """ import time import pytest from flask import Flask from flask_security.core import UserMixin from flask_security.signals import reset_password_instructions_sent, password_reset from flask_security.utils import capture_reset_password_requests, string_types from utils import authenticate, logout pytestmark = pytest.mark.recoverable() def test_recoverable_flag(app, client, get_message): recorded_resets = [] recorded_instructions_sent = [] @password_reset.connect_via(app) def on_password_reset(app, user): recorded_resets.append(user) @reset_password_instructions_sent.connect_via(app) def on_instructions_sent(app, user, token): assert isinstance(app, Flask) assert isinstance(user, UserMixin) assert isinstance(token, string_types) recorded_instructions_sent.append(user) # Test the reset view response = client.get('/reset') assert b'

Send password reset instructions

' in response.data # Test submitting email to reset password creates a token and sends email with capture_reset_password_requests() as requests: with app.mail.record_messages() as outbox: response = client.post('/reset', data=dict(email='joe@lp.com'), follow_redirects=True) assert len(recorded_instructions_sent) == 1 assert len(outbox) == 1 assert response.status_code == 200 assert get_message('PASSWORD_RESET_REQUEST', email='joe@lp.com') in response.data token = requests[0]['token'] # Test view for reset token response = client.get('/reset/' + token) assert b'

Reset password

' in response.data # Test submitting a new password response = client.post('/reset/' + token, data={ 'password': 'newpassword', 'password_confirm': 'newpassword' }, follow_redirects=True) assert get_message('PASSWORD_RESET') in response.data assert len(recorded_resets) == 1 logout(client) # Test logging in with the new password response = authenticate(client, 'joe@lp.com', 'newpassword', follow_redirects=True) assert b'Hello joe@lp.com' in response.data logout(client) # Test submitting JSON response = client.post('/reset', data='{"email": "joe@lp.com"}', headers={ 'Content-Type': 'application/json' }) assert response.headers['Content-Type'] == 'application/json' assert 'user' not in response.jdata['response'] logout(client) # Test invalid email response = client.post('/reset', data=dict(email='bogus@lp.com'), follow_redirects=True) assert get_message('USER_DOES_NOT_EXIST') in response.data logout(client) # Test invalid token response = client.post('/reset/bogus', data={ 'password': 'newpassword', 'password_confirm': 'newpassword' }, follow_redirects=True) assert get_message('INVALID_RESET_PASSWORD_TOKEN') in response.data # Test mangled token token = ("WyIxNjQ2MzYiLCIxMzQ1YzBlZmVhM2VhZjYwODgwMDhhZGU2YzU0MzZjMiJd.BZEw_Q.lQyo3npdPZtcJ" "_sNHVHP103syjM&url_id=fbb89a8328e58c181ea7d064c2987874bc54a23d") response = client.post('/reset/' + token, data={ 'password': 'newpassword', 'password_confirm': 'newpassword' }, follow_redirects=True) assert get_message('INVALID_RESET_PASSWORD_TOKEN') in response.data @pytest.mark.settings(reset_password_within='1 milliseconds') def test_expired_reset_token(client, get_message): with capture_reset_password_requests() as requests: client.post('/reset', data=dict(email='joe@lp.com'), follow_redirects=True) user = requests[0]['user'] token = requests[0]['token'] time.sleep(1) response = client.post('/reset/' + token, data={ 'password': 'newpassword', 'password_confirm': 'newpassword' }, follow_redirects=True) msg = get_message('PASSWORD_RESET_EXPIRED', within='1 milliseconds', email=user.email) assert msg in response.data def test_used_reset_token(client, get_message): with capture_reset_password_requests() as requests: client.post('/reset', data=dict(email='joe@lp.com'), follow_redirects=True) token = requests[0]['token'] # use the token response = client.post('/reset/' + token, data={ 'password': 'newpassword', 'password_confirm': 'newpassword' }, follow_redirects=True) assert get_message('PASSWORD_RESET') in response.data logout(client) # attempt to use it a second time response2 = client.post('/reset/' + token, data={ 'password': 'otherpassword', 'password_confirm': 'otherpassword' }, follow_redirects=True) msg = get_message('INVALID_RESET_PASSWORD_TOKEN') assert msg in response2.data def test_reset_passwordless_user(client, get_message): with capture_reset_password_requests() as requests: client.post('/reset', data=dict(email='jess@lp.com'), follow_redirects=True) token = requests[0]['token'] # use the token response = client.post('/reset/' + token, data={ 'password': 'newpassword', 'password_confirm': 'newpassword' }, follow_redirects=True) assert get_message('PASSWORD_RESET') in response.data @pytest.mark.settings(reset_url='/custom_reset') def test_custom_reset_url(client): response = client.get('/custom_reset') assert response.status_code == 200 @pytest.mark.settings(reset_password_template='custom_security/reset_password.html', forgot_password_template='custom_security/forgot_password.html') def test_custom_reset_templates(client): response = client.get('/reset') assert b'CUSTOM FORGOT PASSWORD' in response.data with capture_reset_password_requests() as requests: client.post('/reset', data=dict(email='joe@lp.com'), follow_redirects=True) token = requests[0]['token'] response = client.get('/reset/' + token) assert b'CUSTOM RESET PASSWORD' in response.data