From 0ebacd6929d7bf56dd4180451f566254b2347e25 Mon Sep 17 00:00:00 2001 From: Nicholas Staples Date: Tue, 5 Jan 2016 17:08:50 +0000 Subject: [PATCH] Refactor for code_not_received, sign_in, two_factor and verify. --- app/main/forms.py | 9 +- app/main/views/code_not_received.py | 41 ++-- app/main/views/sign_in.py | 34 +-- app/main/views/two_factor.py | 18 +- app/main/views/verify.py | 34 +-- app/templates/views/email-not-received.html | 8 +- app/templates/views/register.html | 2 +- app/templates/views/signin.html | 2 +- app/templates/views/text-not-received.html | 7 +- app/templates/views/two-factor.html | 9 +- app/templates/views/verify-mobile.html | 2 +- app/templates/views/verify.html | 17 +- .../app/main/views/test_code_not_received.py | 202 ++++++++++-------- tests/app/main/views/test_sign_in.py | 68 +++--- tests/app/main/views/test_two_factor.py | 78 +++---- tests/app/main/views/test_verify.py | 126 ++++++----- 16 files changed, 336 insertions(+), 321 deletions(-) diff --git a/app/main/forms.py b/app/main/forms.py index 1aceb8465..b1c4158ad 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -92,9 +92,9 @@ class EmailNotReceivedForm(Form): class TextNotReceivedForm(Form): - mobile_number = StringField('Mobile phone number', - validators=[DataRequired(message='Please enter your mobile number'), - Regexp(regex=mobile_number, message='Please enter a +44 mobile number')]) + mobile_number = StringField('Mobile phone number', validators=[ + DataRequired(message='Please enter your mobile number'), + Regexp(regex=mobile_number, message='Please enter a +44 mobile number')]) class AddServiceForm(Form): @@ -102,7 +102,8 @@ class AddServiceForm(Form): self.service_names = service_names super(AddServiceForm, self).__init__(*args, **kwargs) - service_name = StringField(validators=[DataRequired(message='Please enter your service name')]) + service_name = StringField(validators=[ + DataRequired(message='Please enter your service name')]) def validate_service_name(self, a): if self.service_name.data in self.service_names: diff --git a/app/main/views/code_not_received.py b/app/main/views/code_not_received.py index 5fce56c3b..da12c2ad0 100644 --- a/app/main/views/code_not_received.py +++ b/app/main/views/code_not_received.py @@ -1,4 +1,5 @@ -from flask import render_template, redirect, jsonify, session +from flask import ( + render_template, redirect, jsonify, session, url_for) from app.main import main from app.main.dao import users_dao @@ -6,39 +7,28 @@ from app.main.forms import EmailNotReceivedForm, TextNotReceivedForm from app.main.views import send_sms_code, send_email_code -@main.route("/email-not-received", methods=['GET']) -def email_not_received(): - user = users_dao.get_user_by_id(session['user_id']) - return render_template('views/email-not-received.html', - form=EmailNotReceivedForm(email_address=user.email_address)) - - -@main.route('/email-not-received', methods=['POST']) +@main.route('/email-not-received', methods=['GET', 'POST']) def check_and_resend_email_code(): - form = EmailNotReceivedForm() + # TODO there needs to be a way to regenerate a session id + user = users_dao.get_user_by_id(session['user_id']) + form = EmailNotReceivedForm(email_address=user.email_address) if form.validate_on_submit(): - user = users_dao.get_user_by_id(session['user_id']) users_dao.update_email_address(id=user.id, email_address=form.email_address.data) send_email_code(user_id=user.id, email=user.email_address) - return redirect('/verify') - return jsonify(form.errors), 400 + return redirect(url_for('.verify')) + return render_template('views/email-not-received.html', form=form) -@main.route("/text-not-received", methods=['GET']) -def text_not_received(): - user = users_dao.get_user_by_id(session['user_id']) - return render_template('views/text-not-received.html', form=TextNotReceivedForm(mobile_number=user.mobile_number)) - - -@main.route('/text-not-received', methods=['POST']) +@main.route('/text-not-received', methods=['GET', 'POST']) def check_and_resend_text_code(): - form = TextNotReceivedForm() + # TODO there needs to be a way to regenerate a session id + user = users_dao.get_user_by_id(session['user_id']) + form = TextNotReceivedForm(mobile_number=user.mobile_number) if form.validate_on_submit(): - user = users_dao.get_user_by_id(session['user_id']) users_dao.update_mobile_number(id=user.id, mobile_number=form.mobile_number.data) send_sms_code(user_id=user.id, mobile_number=user.mobile_number) - return redirect('/verify') - return jsonify(form.errors), 400 + return redirect(url_for('.verify')) + return render_template('views/text-not-received.html', form=form) @main.route('/verification-not-received', methods=['GET']) @@ -48,6 +38,7 @@ def verification_code_not_received(): @main.route('/send-new-code', methods=['GET']) def check_and_resend_verification_code(): + # TODO there needs to be a way to generate a new session id user = users_dao.get_user_by_id(session['user_id']) send_sms_code(user.id, user.mobile_number) - return redirect('/two-factor') + return redirect(url_for('main.two_factor')) diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index 3e29ab000..7ebf66343 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -1,4 +1,5 @@ -from flask import render_template, redirect, jsonify +from flask import ( + render_template, redirect, jsonify, url_for) from flask import session from app.main import main @@ -10,18 +11,21 @@ from app.main.views import send_sms_code @main.route('/sign-in', methods=(['GET', 'POST'])) def sign_in(): - form = LoginForm() - if form.validate_on_submit(): - user = users_dao.get_user_by_email(form.email_address.data) + try: + form = LoginForm() + if form.validate_on_submit(): + user = users_dao.get_user_by_email(form.email_address.data) + if user: + if not user.is_locked() and user.is_active() and check_hash(form.password.data, user.password): + send_sms_code(user.id, user.mobile_number) + session['user_id'] = user.id + return redirect(url_for('.two_factor')) + else: + users_dao.increment_failed_login_count(user.id) + # Vague error message for login + form.password.errors.append('Username or password is incorrect') - if user: - if not user.is_locked() and user.is_active() and check_hash(form.password.data, user.password): - send_sms_code(user.id, user.mobile_number) - session['user_id'] = user.id - return redirect('/two-factor') - else: - users_dao.increment_failed_login_count(user.id) - # Vague error message for login - form.password.errors.append('Username or password is incorrect') - - return render_template('views/signin.html', form=form) + return render_template('views/signin.html', form=form) + except: + import traceback + traceback.print_exc() diff --git a/app/main/views/two_factor.py b/app/main/views/two_factor.py index b06bb2029..3d7f9a480 100644 --- a/app/main/views/two_factor.py +++ b/app/main/views/two_factor.py @@ -1,4 +1,5 @@ -from flask import render_template, redirect, jsonify, session +from flask import ( + render_template, redirect, jsonify, session, url_for) from flask_login import login_user from app.main import main @@ -6,19 +7,14 @@ from app.main.dao import users_dao, verify_codes_dao from app.main.forms import TwoFactorForm -@main.route("/two-factor", methods=['GET']) -def render_two_factor(): - return render_template('views/two-factor.html', form=TwoFactorForm()) - - -@main.route('/two-factor', methods=['POST']) -def process_two_factor(): +@main.route('/two-factor', methods=['GET', 'POST']) +def two_factor(): form = TwoFactorForm() if form.validate_on_submit(): user = users_dao.get_user_by_id(session['user_id']) verify_codes_dao.use_code_for_user_and_type(user_id=user.id, code_type='sms') login_user(user) - return redirect('/dashboard') - else: - return jsonify(form.errors), 400 + return redirect(url_for('.dashboard')) + + return render_template('views/two-factor.html', form=form) diff --git a/app/main/views/verify.py b/app/main/views/verify.py index 96ae0cb76..f738d5b0a 100644 --- a/app/main/views/verify.py +++ b/app/main/views/verify.py @@ -1,4 +1,5 @@ -from flask import render_template, redirect, jsonify, session +from flask import ( + render_template, redirect, jsonify, session, url_for) from flask_login import login_user from app.main import main @@ -6,20 +7,19 @@ from app.main.dao import users_dao, verify_codes_dao from app.main.forms import VerifyForm -@main.route('/verify', methods=['GET']) -def render_verify(): - return render_template('views/verify.html', form=VerifyForm()) - - -@main.route('/verify', methods=['POST']) -def process_verify(): - form = VerifyForm() - if form.validate_on_submit(): +@main.route('/verify', methods=['GET', 'POST']) +def verify(): + # TODO there needs to be a way to regenerate a session id + try: user = users_dao.get_user_by_id(session['user_id']) - verify_codes_dao.use_code_for_user_and_type(user_id=user.id, code_type='email') - verify_codes_dao.use_code_for_user_and_type(user_id=user.id, code_type='sms') - users_dao.activate_user(user.id) - login_user(user) - return redirect('/add-service') - else: - return jsonify(form.errors), 400 + form = VerifyForm() + if form.validate_on_submit(): + verify_codes_dao.use_code_for_user_and_type(user_id=user.id, code_type='email') + verify_codes_dao.use_code_for_user_and_type(user_id=user.id, code_type='sms') + users_dao.activate_user(user.id) + login_user(user) + return redirect(url_for('.add_service')) + return render_template('views/verify.html', form=form) + except: + import traceback + traceback.print_exc() diff --git a/app/templates/views/email-not-received.html b/app/templates/views/email-not-received.html index 3b970bbf4..a379548dd 100644 --- a/app/templates/views/email-not-received.html +++ b/app/templates/views/email-not-received.html @@ -11,17 +11,13 @@ GOV.UK Notify

Check your email address

Check your email address is correct and then resend the confirmation code.

-

{{ form.hidden_tag() }} - - - {{ form.email_address(class="form-control-2-3", autocomplete="off") }}
- Your email address must end in .gov.uk + {{ render_field(form.email_address, class='form-control-2-3') }}

- +

diff --git a/app/templates/views/register.html b/app/templates/views/register.html index 610516f3b..b4f1c5ea6 100644 --- a/app/templates/views/register.html +++ b/app/templates/views/register.html @@ -10,7 +10,7 @@ GOV.UK Notify | Create an account

Create an account

-

If you've used GOV.UK Notify before, sign in to your account.

+

If you've used GOV.UK Notify before, sign in to your account.

{{ form.hidden_tag() }} diff --git a/app/templates/views/signin.html b/app/templates/views/signin.html index 859d843d8..036a34476 100644 --- a/app/templates/views/signin.html +++ b/app/templates/views/signin.html @@ -20,7 +20,7 @@ Sign in Forgotten password?

- +

diff --git a/app/templates/views/text-not-received.html b/app/templates/views/text-not-received.html index e63b7cb5c..e3562e907 100644 --- a/app/templates/views/text-not-received.html +++ b/app/templates/views/text-not-received.html @@ -14,12 +14,9 @@ GOV.UK Notify
{{ form.hidden_tag() }} + {{ render_field(form.mobile_number, class='form-control-2-3') }}

- - {{ form.mobile_number(class="form-control-1-4", autocomplete="off") }}
-

-

- +

diff --git a/app/templates/views/two-factor.html b/app/templates/views/two-factor.html index 8e3274706..091e4be6a 100644 --- a/app/templates/views/two-factor.html +++ b/app/templates/views/two-factor.html @@ -15,13 +15,10 @@ GOV.UK Notify | Text verification
{{ form.hidden_tag() }} + {{ render_field(form.sms_code, class='form-control-1-4') }} + I haven't received a text

-
- {{ form.sms_code(class="form-control-1-4", autocomplete="off") }}
- I haven't received a text -

-

- +

diff --git a/app/templates/views/verify-mobile.html b/app/templates/views/verify-mobile.html index ae66bbecf..e26f83592 100644 --- a/app/templates/views/verify-mobile.html +++ b/app/templates/views/verify-mobile.html @@ -15,7 +15,7 @@ GOV.UK Notify | Confirm mobile number

diff --git a/app/templates/views/verify.html b/app/templates/views/verify.html index 44ef74737..e12db20ac 100644 --- a/app/templates/views/verify.html +++ b/app/templates/views/verify.html @@ -14,19 +14,12 @@ GOV.UK Notify | Confirm email address and mobile number

{{ form.hidden_tag() }} + {{ render_field(form.email_code, class='form-control-1-4') }} + I haven't received an email + {{ render_field(form.sms_code, class='form-control-1-4') }} + I haven't received a text

- - {{ form.email_code(class="form-control-1-4", autocomplete="off") }}
- I haven't received an email -

-

- - {{ form.sms_code(class="form-control-1-4", autocomplete="off") }}
- I haven't received a text -

- -

- +

diff --git a/tests/app/main/views/test_code_not_received.py b/tests/app/main/views/test_code_not_received.py index c9b7ad2c9..d334c018f 100644 --- a/tests/app/main/views/test_code_not_received.py +++ b/tests/app/main/views/test_code_not_received.py @@ -1,151 +1,163 @@ from app.main.dao import verify_codes_dao, users_dao from tests.app.main import create_test_user +from flask import url_for def test_should_render_email_code_not_received_template_and_populate_email_address(notifications_admin, notifications_admin_db, - notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('pending') - session['user_id'] = user.id - response = client.get('/email-not-received') - assert response.status_code == 200 - assert 'Check your email address is correct and then resend the confirmation code' \ - in response.get_data(as_text=True) - assert 'value="test@user.gov.uk"' in response.get_data(as_text=True) + notify_db_session, + mocker): + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + _set_up_mocker(mocker) + user = create_test_user('pending') + session['user_id'] = user.id + response = client.get(url_for('main.check_and_resend_email_code')) + assert response.status_code == 200 + assert 'Check your email address is correct and then resend the confirmation code' \ + in response.get_data(as_text=True) + assert 'value="test@user.gov.uk"' in response.get_data(as_text=True) def test_should_check_and_resend_email_code_redirect_to_verify(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - _set_up_mocker(mocker) - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user.id, code='12345', code_type='email') - response = client.post('/email-not-received', - data={'email_address': 'test@user.gov.uk'}) - assert response.status_code == 302 - assert response.location == 'http://localhost/verify' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + _set_up_mocker(mocker) + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user.id, code='12345', code_type='email') + response = client.post(url_for('main.check_and_resend_email_code'), + data={'email_address': 'test@user.gov.uk'}) + assert response.status_code == 302 + assert response.location == url_for('main.verify', _external=True) def test_should_render_text_code_not_received_template(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - _set_up_mocker(mocker) - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user.id, code='12345', code_type='sms') - response = client.get('/text-not-received') - assert response.status_code == 200 - assert 'Check your mobile phone number is correct and then resend the confirmation code.' \ - in response.get_data(as_text=True) - assert 'value="+441234123412"' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + _set_up_mocker(mocker) + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user.id, code='12345', code_type='sms') + response = client.get(url_for('main.check_and_resend_text_code')) + assert response.status_code == 200 + assert 'Check your mobile phone number is correct and then resend the confirmation code.' \ + in response.get_data(as_text=True) + assert 'value="+441234123412"' def test_should_check_and_redirect_to_verify(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - _set_up_mocker(mocker) - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user.id, code='12345', code_type='sms') - response = client.post('/text-not-received', - data={'mobile_number': '+441234123412'}) - assert response.status_code == 302 - assert response.location == 'http://localhost/verify' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + _set_up_mocker(mocker) + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user.id, code='12345', code_type='sms') + response = client.post(url_for('main.check_and_resend_text_code'), + data={'mobile_number': '+441234123412'}) + assert response.status_code == 302 + assert response.location == url_for('main.verify', _external=True) def test_should_update_email_address_resend_code(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - _set_up_mocker(mocker) - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='email') - response = client.post('/email-not-received', - data={'email_address': 'new@address.gov.uk'}) - assert response.status_code == 302 - assert response.location == 'http://localhost/verify' - updated_user = users_dao.get_user_by_id(user.id) - assert updated_user.email_address == 'new@address.gov.uk' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + _set_up_mocker(mocker) + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='email') + response = client.post(url_for('main.check_and_resend_email_code'), + data={'email_address': 'new@address.gov.uk'}) + assert response.status_code == 302 + assert response.location == url_for('main.verify', _external=True) + updated_user = users_dao.get_user_by_id(user.id) + assert updated_user.email_address == 'new@address.gov.uk' def test_should_update_mobile_number_resend_code(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - _set_up_mocker(mocker) - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - response = client.post('/text-not-received', - data={'mobile_number': '+443456789012'}) - assert response.status_code == 302 - assert response.location == 'http://localhost/verify' - updated_user = users_dao.get_user_by_id(user.id) - assert updated_user.mobile_number == '+443456789012' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + _set_up_mocker(mocker) + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + response = client.post(url_for('main.check_and_resend_text_code'), + data={'mobile_number': '+443456789012'}) + assert response.status_code == 302 + assert response.location == url_for('main.verify', _external=True) + updated_user = users_dao.get_user_by_id(user.id) + assert updated_user.mobile_number == '+443456789012' def test_should_render_verification_code_not_received(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('active') - session['user_id'] = user.id - response = client.get('/verification-not-received') - assert response.status_code == 200 - assert 'Resend verification code' in response.get_data(as_text=True) - assert 'If you no longer have access to the phone with the number you registered for this service, ' \ - 'speak to your service manager to reset the number.' in response.get_data(as_text=True) + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('active') + session['user_id'] = user.id + response = client.get(url_for('main.verification_code_not_received')) + assert response.status_code == 200 + assert 'Resend verification code' in response.get_data(as_text=True) + assert 'If you no longer have access to the phone with the number you registered for this service, ' \ + 'speak to your service manager to reset the number.' in response.get_data(as_text=True) def test_check_and_redirect_to_two_factor(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('active') - session['user_id'] = user.id - _set_up_mocker(mocker) - response = client.get('/send-new-code') - assert response.status_code == 302 - assert response.location == 'http://localhost/two-factor' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('active') + session['user_id'] = user.id + _set_up_mocker(mocker) + response = client.get(url_for('main.check_and_resend_verification_code')) + assert response.status_code == 302 + assert response.location == url_for('main.two_factor', _external=True) def test_should_create_new_code_for_user(notifications_admin, notifications_admin_db, notify_db_session, mocker): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('active') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - _set_up_mocker(mocker) - response = client.get('/send-new-code') - assert response.status_code == 302 - assert response.location == 'http://localhost/two-factor' - codes = verify_codes_dao.get_codes(user_id=user.id, code_type='sms') - assert len(codes) == 2 - for x in ([used.code_used for used in codes]): - assert x is False + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('active') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + _set_up_mocker(mocker) + response = client.get(url_for('main.check_and_resend_verification_code')) + assert response.status_code == 302 + assert response.location == url_for('main.two_factor', _external=True) + codes = verify_codes_dao.get_codes(user_id=user.id, code_type='sms') + assert len(codes) == 2 + for x in ([used.code_used for used in codes]): + assert x is False def _set_up_mocker(mocker): diff --git a/tests/app/main/views/test_sign_in.py b/tests/app/main/views/test_sign_in.py index bedf6554c..116df29fa 100644 --- a/tests/app/main/views/test_sign_in.py +++ b/tests/app/main/views/test_sign_in.py @@ -2,10 +2,12 @@ from datetime import datetime from app.main.dao import users_dao from app.models import User +from flask import url_for def test_render_sign_in_returns_sign_in_template(notifications_admin): - response = notifications_admin.test_client().get('/sign-in') + with notifications_admin.test_request_context(): + response = notifications_admin.test_client().get(url_for('main.sign_in')) assert response.status_code == 200 assert 'Sign in' in response.get_data(as_text=True) assert 'Email address' in response.get_data(as_text=True) @@ -23,9 +25,11 @@ def test_process_sign_in_return_2fa_template(notifications_admin, notifications_ role_id=1, state='active') users_dao.insert_user(user) - response = notifications_admin.test_client().post('/sign-in', - data={'email_address': 'valid@example.gov.uk', - 'password': 'val1dPassw0rd!'}) + with notifications_admin.test_request_context(): + response = notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'valid@example.gov.uk', + 'password': 'val1dPassw0rd!'}) assert response.status_code == 302 assert response.location == 'http://localhost/two-factor' @@ -41,23 +45,27 @@ def test_should_return_locked_out_true_when_user_is_locked(notifications_admin, role_id=1, state='active') users_dao.insert_user(user) - for _ in range(10): - notifications_admin.test_client().post('/sign-in', - data={'email_address': 'valid@example.gov.uk', - 'password': 'whatIsMyPassword!'}) + with notifications_admin.test_request_context(): + for _ in range(10): + notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'valid@example.gov.uk', + 'password': 'whatIsMyPassword!'}) - response = notifications_admin.test_client().post('/sign-in', - data={'email_address': 'valid@example.gov.uk', - 'password': 'val1dPassw0rd!'}) + response = notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'valid@example.gov.uk', + 'password': 'val1dPassw0rd!'}) - assert response.status_code == 200 - assert 'Username or password is incorrect' in response.get_data(as_text=True) + assert response.status_code == 200 + assert 'Username or password is incorrect' in response.get_data(as_text=True) - another_bad_attempt = notifications_admin.test_client().post('/sign-in', - data={'email_address': 'valid@example.gov.uk', - 'password': 'whatIsMyPassword!'}) - assert another_bad_attempt.status_code == 200 - assert 'Username or password is incorrect' in response.get_data(as_text=True) + another_bad_attempt = notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'valid@example.gov.uk', + 'password': 'whatIsMyPassword!'}) + assert another_bad_attempt.status_code == 200 + assert 'Username or password is incorrect' in response.get_data(as_text=True) def test_should_return_active_user_is_false_if_user_is_inactive(notifications_admin, @@ -72,18 +80,22 @@ def test_should_return_active_user_is_false_if_user_is_inactive(notifications_ad state='inactive') users_dao.insert_user(user) - response = notifications_admin.test_client().post('/sign-in', - data={'email_address': 'inactive_user@example.gov.uk', - 'password': 'val1dPassw0rd!'}) + with notifications_admin.test_request_context(): + response = notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'inactive_user@example.gov.uk', + 'password': 'val1dPassw0rd!'}) assert response.status_code == 200 assert 'Username or password is incorrect' in response.get_data(as_text=True) def test_should_return_200_when_user_does_not_exist(notifications_admin, notifications_admin_db, notify_db_session): - response = notifications_admin.test_client().post('/sign-in', - data={'email_address': 'does_not_exist@gov.uk', - 'password': 'doesNotExist!'}) + with notifications_admin.test_request_context(): + response = notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'does_not_exist@gov.uk', + 'password': 'doesNotExist!'}) assert response.status_code == 200 assert 'Username or password is incorrect' in response.get_data(as_text=True) @@ -97,9 +109,11 @@ def test_should_return_200_when_user_is_not_active(notifications_admin, notifica role_id=1, state='pending') users_dao.insert_user(user) - response = notifications_admin.test_client().post('/sign-in', - data={'email_address': 'PendingUser@example.gov.uk', - 'password': 'val1dPassw0rd!'}) + with notifications_admin.test_request_context(): + response = notifications_admin.test_client().post( + url_for('main.sign_in'), data={ + 'email_address': 'PendingUser@example.gov.uk', + 'password': 'val1dPassw0rd!'}) assert response.status_code == 200 assert 'Username or password is incorrect' in response.get_data(as_text=True) diff --git a/tests/app/main/views/test_two_factor.py b/tests/app/main/views/test_two_factor.py index 6f0a4bfe3..edcc67a3e 100644 --- a/tests/app/main/views/test_two_factor.py +++ b/tests/app/main/views/test_two_factor.py @@ -1,56 +1,60 @@ -from flask import json +from flask import json, url_for from app.main.dao import verify_codes_dao from tests.app.main import create_test_user def test_should_render_two_factor_page(notifications_admin, notifications_admin_db, notify_db_session): - response = notifications_admin.test_client().get('/two-factor') - assert response.status_code == 200 - assert '''We've sent you a text message with a verification code.''' in response.get_data(as_text=True) + with notifications_admin.test_request_context(): + response = notifications_admin.test_client().get(url_for('main.two_factor')) + assert response.status_code == 200 + assert '''We've sent you a text message with a verification code.''' in response.get_data(as_text=True) def test_should_login_user_and_redirect_to_dashboard(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('active') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - response = client.post('/two-factor', - data={'sms_code': '12345'}) + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('active') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + response = client.post(url_for('main.two_factor'), + data={'sms_code': '12345'}) - assert response.status_code == 302 - assert response.location == 'http://localhost/dashboard' + assert response.status_code == 302 + assert response.location == url_for('main.dashboard', _external=True) -def test_should_return_400_with_sms_code_error_when_sms_code_is_wrong(notifications_admin, +def test_should_return_200_with_sms_code_error_when_sms_code_is_wrong(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('active') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - response = client.post('/two-factor', - data={'sms_code': '23456'}) - assert response.status_code == 400 - assert {'sms_code': ['Code does not match']} == json.loads(response.get_data(as_text=True)) + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('active') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + response = client.post(url_for('main.two_factor'), + data={'sms_code': '23456'}) + assert response.status_code == 200 + assert 'Code does not match' in response.get_data(as_text=True) def test_should_login_user_when_multiple_valid_codes_exist(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('active') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='sms') - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - verify_codes_dao.add_code(user_id=user.id, code='34567', code_type='sms') - assert len(verify_codes_dao.get_codes(user_id=user.id, code_type='sms')) == 3 - response = client.post('/two-factor', - data={'sms_code': '23456'}) - assert response.status_code == 302 - codes = verify_codes_dao.get_codes(user_id=user.id, code_type='sms') - # query will only return codes where code_used == False - assert len(codes) == 0 + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('active') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='sms') + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + verify_codes_dao.add_code(user_id=user.id, code='34567', code_type='sms') + assert len(verify_codes_dao.get_codes(user_id=user.id, code_type='sms')) == 3 + response = client.post(url_for('main.two_factor'), + data={'sms_code': '23456'}) + assert response.status_code == 302 + codes = verify_codes_dao.get_codes(user_id=user.id, code_type='sms') + # query will only return codes where code_used == False + assert len(codes) == 0 diff --git a/tests/app/main/views/test_verify.py b/tests/app/main/views/test_verify.py index 9398dff47..729bacfa8 100644 --- a/tests/app/main/views/test_verify.py +++ b/tests/app/main/views/test_verify.py @@ -1,79 +1,89 @@ -from flask import json +from flask import json, url_for from app.main.dao import users_dao, verify_codes_dao from tests.app.main import create_test_user def test_should_return_verify_template(notifications_admin, notifications_admin_db, notify_db_session): - response = notifications_admin.test_client().get('/verify') - assert response.status_code == 200 - assert 'Activate your account' in response.get_data(as_text=True) + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + # TODO this lives here until we work out how to + # reassign the session after it is lost mid register process + with client.session_transaction() as session: + user = create_test_user('pending') + session['user_id'] = user.id + response = client.get(url_for('main.verify')) + assert response.status_code == 200 + assert ( + "We've sent you confirmation codes by email and text message." + " You need to enter both codes here.") in response.get_data(as_text=True) def test_should_redirect_to_add_service_when_code_are_correct(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='email') - response = client.post('/verify', - data={'sms_code': '12345', - 'email_code': '23456'}) - assert response.status_code == 302 - assert response.location == 'http://localhost/add-service' + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='email') + response = client.post(url_for('main.verify'), + data={'sms_code': '12345', + 'email_code': '23456'}) + assert response.status_code == 302 + assert response.location == url_for('main.add_service', _external=True) def test_should_activate_user_after_verify(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='email') - client.post('/verify', - data={'sms_code': '12345', - 'email_code': '23456'}) + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='email') + client.post(url_for('main.verify'), + data={'sms_code': '12345', + 'email_code': '23456'}) - after_verify = users_dao.get_user_by_id(user.id) - assert after_verify.state == 'active' + after_verify = users_dao.get_user_by_id(user.id) + assert after_verify.state == 'active' -def test_should_return_400_when_codes_are_wrong(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('pending') - session['user_id'] = user.id - verify_codes_dao.add_code(user_id=user.id, code='23345', code_type='sms') - verify_codes_dao.add_code(user_id=user.id, code='98456', code_type='email') - response = client.post('/verify', - data={'sms_code': '12345', - 'email_code': '23456'}) - assert response.status_code == 400 - expected = {'sms_code': ['Code must be 5 digits', 'Code does not match'], - 'email_code': ['Code must be 5 digits', 'Code does not match']} - errors = json.loads(response.get_data(as_text=True)) - assert len(errors) == 2 - assert set(errors) == set(expected) +def test_should_return_200_when_codes_are_wrong(notifications_admin, notifications_admin_db, notify_db_session): + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('pending') + session['user_id'] = user.id + verify_codes_dao.add_code(user_id=user.id, code='23345', code_type='sms') + verify_codes_dao.add_code(user_id=user.id, code='98456', code_type='email') + response = client.post(url_for('main.verify'), + data={'sms_code': '12345', + 'email_code': '23456'}) + assert response.status_code == 200 + resp_data = response.get_data(as_text=True) + assert resp_data.count('Code does not match') == 2 def test_should_mark_all_codes_as_used_when_many_codes_exist(notifications_admin, notifications_admin_db, notify_db_session): - with notifications_admin.test_client() as client: - with client.session_transaction() as session: - user = create_test_user('pending') - session['user_id'] = user.id - code1 = verify_codes_dao.add_code(user_id=user.id, code='23345', code_type='sms') - code2 = verify_codes_dao.add_code(user_id=user.id, code='98456', code_type='email') - code3 = verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - code4 = verify_codes_dao.add_code(user_id=user.id, code='23412', code_type='email') - response = client.post('/verify', - data={'sms_code': '23345', - 'email_code': '23412'}) - assert response.status_code == 302 - assert verify_codes_dao.get_code_by_id(code1).code_used is True - assert verify_codes_dao.get_code_by_id(code2).code_used is True - assert verify_codes_dao.get_code_by_id(code3).code_used is True - assert verify_codes_dao.get_code_by_id(code4).code_used is True + with notifications_admin.test_request_context(): + with notifications_admin.test_client() as client: + with client.session_transaction() as session: + user = create_test_user('pending') + session['user_id'] = user.id + code1 = verify_codes_dao.add_code(user_id=user.id, code='23345', code_type='sms') + code2 = verify_codes_dao.add_code(user_id=user.id, code='98456', code_type='email') + code3 = verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + code4 = verify_codes_dao.add_code(user_id=user.id, code='23412', code_type='email') + response = client.post(url_for('main.verify'), + data={'sms_code': '23345', + 'email_code': '23412'}) + assert response.status_code == 302 + assert verify_codes_dao.get_code_by_id(code1).code_used is True + assert verify_codes_dao.get_code_by_id(code2).code_used is True + assert verify_codes_dao.get_code_by_id(code3).code_used is True + assert verify_codes_dao.get_code_by_id(code4).code_used is True