mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-18 13:39:41 -04:00
Merge pull request #3670 from alphagov/show-broadcast-tour-when-password-reset
Take user to page they are meant to visit in various sign-in flow scenarios
This commit is contained in:
@@ -2,6 +2,8 @@ import pytest
|
||||
from bs4 import BeautifulSoup
|
||||
from flask import url_for
|
||||
|
||||
from tests.conftest import SERVICE_ONE_ID
|
||||
|
||||
|
||||
def test_should_render_email_verification_resend_show_email_address_and_resend_verify_email(
|
||||
client,
|
||||
@@ -27,23 +29,32 @@ def test_should_render_email_verification_resend_show_email_address_and_resend_v
|
||||
mock_send_verify_email.assert_called_with(api_user_active['id'], api_user_active['email_address'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_should_render_correct_resend_template_for_active_user(
|
||||
client,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_send_verify_code,
|
||||
redirect_url
|
||||
):
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {
|
||||
'id': api_user_active['id'],
|
||||
'email': api_user_active['email_address']}
|
||||
response = client.get(url_for('main.check_and_resend_text_code'))
|
||||
response = client.get(url_for('main.check_and_resend_text_code', next=redirect_url))
|
||||
assert response.status_code == 200
|
||||
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
assert page.h1.string == 'Resend security code'
|
||||
# there shouldn't be a form for updating mobile number
|
||||
assert page.find('form') is None
|
||||
assert page.find('a', class_="govuk-button")['href'] == url_for(
|
||||
'main.check_and_resend_verification_code',
|
||||
next=redirect_url
|
||||
)
|
||||
|
||||
|
||||
def test_should_render_correct_resend_template_for_pending_user(
|
||||
@@ -71,6 +82,10 @@ def test_should_render_correct_resend_template_for_pending_user(
|
||||
assert page.find('form').input['value'] == api_user_pending['mobile_number']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
@pytest.mark.parametrize('phone_number_to_register_with', [
|
||||
'+447700900460',
|
||||
'+1800-555-555',
|
||||
@@ -82,6 +97,7 @@ def test_should_resend_verify_code_and_update_mobile_for_pending_user(
|
||||
mock_update_user_attribute,
|
||||
mock_send_verify_code,
|
||||
phone_number_to_register_with,
|
||||
redirect_url
|
||||
):
|
||||
mocker.patch('app.user_api_client.get_user_by_email', return_value=api_user_pending)
|
||||
|
||||
@@ -89,10 +105,10 @@ def test_should_resend_verify_code_and_update_mobile_for_pending_user(
|
||||
session['user_details'] = {
|
||||
'id': api_user_pending['id'],
|
||||
'email': api_user_pending['email_address']}
|
||||
response = client.post(url_for('main.check_and_resend_text_code'),
|
||||
response = client.post(url_for('main.check_and_resend_text_code', next=redirect_url),
|
||||
data={'mobile_number': phone_number_to_register_with})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.verify', _external=True)
|
||||
assert response.location == url_for('main.verify', _external=True, next=redirect_url)
|
||||
|
||||
mock_update_user_attribute.assert_called_once_with(
|
||||
api_user_pending['id'],
|
||||
@@ -105,27 +121,37 @@ def test_should_resend_verify_code_and_update_mobile_for_pending_user(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_check_and_redirect_to_two_factor_if_user_active(
|
||||
client,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_send_verify_code,
|
||||
redirect_url
|
||||
):
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {
|
||||
'id': api_user_active['id'],
|
||||
'email': api_user_active['email_address']}
|
||||
response = client.get(url_for('main.check_and_resend_verification_code'))
|
||||
response = client.get(url_for('main.check_and_resend_verification_code', next=redirect_url))
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.two_factor', _external=True)
|
||||
assert response.location == url_for('main.two_factor', _external=True, next=redirect_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_check_and_redirect_to_verify_if_user_pending(
|
||||
client,
|
||||
mocker,
|
||||
api_user_pending,
|
||||
mock_get_user_pending,
|
||||
mock_send_verify_code,
|
||||
redirect_url
|
||||
):
|
||||
|
||||
mocker.patch('app.user_api_client.get_user_by_email', return_value=api_user_pending)
|
||||
@@ -134,9 +160,9 @@ def test_check_and_redirect_to_verify_if_user_pending(
|
||||
session['user_details'] = {
|
||||
'id': api_user_pending['id'],
|
||||
'email': api_user_pending['email_address']}
|
||||
response = client.get(url_for('main.check_and_resend_verification_code'))
|
||||
response = client.get(url_for('main.check_and_resend_verification_code', next=redirect_url))
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.verify', _external=True)
|
||||
assert response.location == url_for('main.verify', _external=True, next=redirect_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('endpoint', [
|
||||
@@ -152,3 +178,28 @@ def test_redirect_to_sign_in_if_not_logged_in(
|
||||
|
||||
assert response.location == url_for('main.sign_in', _external=True)
|
||||
assert response.status_code == 302
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_should_render_correct_email_not_received_template_for_active_user(
|
||||
client,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_send_verify_code,
|
||||
redirect_url
|
||||
):
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {
|
||||
'id': api_user_active['id'],
|
||||
'email': api_user_active['email_address']}
|
||||
response = client.get(url_for('main.email_not_received', next=redirect_url))
|
||||
assert response.status_code == 200
|
||||
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
assert page.h1.string == 'Resend email link'
|
||||
# there shouldn't be a form for updating mobile number
|
||||
assert page.find('form') is None
|
||||
assert page.find('a', class_="govuk-button")['href'] == url_for('main.resend_email_link', next=redirect_url)
|
||||
|
||||
@@ -4,6 +4,7 @@ from notifications_python_client.errors import HTTPError
|
||||
|
||||
import app
|
||||
from tests import user_json
|
||||
from tests.conftest import SERVICE_ONE_ID
|
||||
|
||||
|
||||
def test_should_render_forgot_password(client):
|
||||
@@ -31,7 +32,23 @@ def test_should_redirect_to_password_reset_sent_for_valid_email(
|
||||
assert response.status_code == 200
|
||||
assert 'Click the link in the email to reset your password.' \
|
||||
in response.get_data(as_text=True)
|
||||
app.user_api_client.send_reset_password_url.assert_called_once_with(sample_user['email_address'])
|
||||
app.user_api_client.send_reset_password_url.assert_called_once_with(sample_user['email_address'], next_string=None)
|
||||
|
||||
|
||||
def test_forgot_password_sends_next_link_with_reset_password_email_request(
|
||||
client,
|
||||
fake_uuid,
|
||||
mocker,
|
||||
):
|
||||
sample_user = user_json(email_address='test@user.gov.uk')
|
||||
mocker.patch('app.user_api_client.send_reset_password_url', return_value=None)
|
||||
response = client.post(
|
||||
url_for('.forgot_password') + f"?next=/services/{SERVICE_ONE_ID}/templates",
|
||||
data={'email_address': sample_user['email_address']})
|
||||
assert response.status_code == 200
|
||||
app.user_api_client.send_reset_password_url.assert_called_once_with(
|
||||
sample_user['email_address'], next_string=f'/services/{SERVICE_ONE_ID}/templates'
|
||||
)
|
||||
|
||||
|
||||
def test_should_redirect_to_password_reset_sent_for_missing_email(
|
||||
@@ -48,4 +65,6 @@ def test_should_redirect_to_password_reset_sent_for_missing_email(
|
||||
assert response.status_code == 200
|
||||
assert 'Click the link in the email to reset your password.' \
|
||||
in response.get_data(as_text=True)
|
||||
app.user_api_client.send_reset_password_url.assert_called_once_with(api_user_active['email_address'])
|
||||
app.user_api_client.send_reset_password_url.assert_called_once_with(
|
||||
api_user_active['email_address'], next_string=None
|
||||
)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from flask import url_for
|
||||
from itsdangerous import SignatureExpired
|
||||
from notifications_utils.url_safe_token import generate_token
|
||||
|
||||
from tests.conftest import url_for_endpoint_with_token
|
||||
from tests.conftest import SERVICE_ONE_ID, url_for_endpoint_with_token
|
||||
|
||||
|
||||
def test_should_render_new_password_template(
|
||||
@@ -36,21 +37,26 @@ def test_should_return_404_when_email_address_does_not_exist(
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_should_redirect_to_two_factor_when_password_reset_is_successful(
|
||||
app_,
|
||||
client,
|
||||
mock_get_user_by_email_request_password_reset,
|
||||
mock_login,
|
||||
mock_send_verify_code,
|
||||
mock_reset_failed_login_count
|
||||
mock_reset_failed_login_count,
|
||||
redirect_url
|
||||
):
|
||||
user = mock_get_user_by_email_request_password_reset.return_value
|
||||
data = json.dumps({'email': user['email_address'], 'created_at': str(datetime.utcnow())})
|
||||
token = generate_token(data, app_.config['SECRET_KEY'], app_.config['DANGEROUS_SALT'])
|
||||
response = client.post(url_for_endpoint_with_token('.new_password', token=token),
|
||||
response = client.post(url_for_endpoint_with_token('.new_password', token=token, next=redirect_url),
|
||||
data={'new_password': 'a-new_password'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('.two_factor', _external=True)
|
||||
assert response.location == url_for('.two_factor', _external=True, next=redirect_url)
|
||||
mock_get_user_by_email_request_password_reset.assert_called_once_with(user['email_address'])
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from bs4 import BeautifulSoup
|
||||
from flask import url_for
|
||||
|
||||
from app.models.user import User
|
||||
from tests.conftest import normalize_spaces
|
||||
from tests.conftest import SERVICE_ONE_ID, normalize_spaces
|
||||
|
||||
|
||||
def test_render_sign_in_template_for_new_user(
|
||||
@@ -27,6 +27,20 @@ def test_render_sign_in_template_for_new_user(
|
||||
assert 'Sign in again' not in normalize_spaces(page.text)
|
||||
|
||||
|
||||
def test_render_sign_in_template_with_next_link_for_password_reset(
|
||||
client_request
|
||||
):
|
||||
client_request.logout()
|
||||
page = client_request.get(
|
||||
'main.sign_in',
|
||||
_optional_args=f"?next=/services/{SERVICE_ONE_ID}/templates",
|
||||
_test_page_title=False
|
||||
)
|
||||
forgot_password_link = page.find('a', class_="govuk-link govuk-link--no-visited-state page-footer-secondary-link")
|
||||
assert forgot_password_link.text == 'Forgotten your password?'
|
||||
assert forgot_password_link['href'] == url_for('main.forgot_password', next=f'/services/{SERVICE_ONE_ID}/templates')
|
||||
|
||||
|
||||
def test_sign_in_explains_session_timeout(client):
|
||||
response = client.get(url_for('main.sign_in', next='/foo'))
|
||||
assert response.status_code == 200
|
||||
@@ -92,6 +106,10 @@ def test_logged_in_user_redirects_to_account(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
@pytest.mark.parametrize('email_address, password', [
|
||||
('valid@example.gov.uk', 'val1dPassw0rd!'),
|
||||
(' valid@example.gov.uk ', ' val1dPassw0rd! '),
|
||||
@@ -105,34 +123,40 @@ def test_process_sms_auth_sign_in_return_2fa_template(
|
||||
mock_verify_password,
|
||||
email_address,
|
||||
password,
|
||||
redirect_url
|
||||
):
|
||||
response = client.post(
|
||||
url_for('main.sign_in'), data={
|
||||
url_for('main.sign_in', next=redirect_url), data={
|
||||
'email_address': email_address,
|
||||
'password': password})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('.two_factor', _external=True)
|
||||
assert response.location == url_for('.two_factor', next=redirect_url, _external=True)
|
||||
mock_verify_password.assert_called_with(api_user_active['id'], password)
|
||||
mock_get_user_by_email.assert_called_with('valid@example.gov.uk')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_process_email_auth_sign_in_return_2fa_template(
|
||||
client,
|
||||
api_user_active_email_auth,
|
||||
mock_send_verify_code,
|
||||
mock_verify_password,
|
||||
mocker
|
||||
mocker,
|
||||
redirect_url
|
||||
):
|
||||
mocker.patch('app.user_api_client.get_user', return_value=api_user_active_email_auth)
|
||||
mocker.patch('app.user_api_client.get_user_by_email', return_value=api_user_active_email_auth)
|
||||
|
||||
response = client.post(
|
||||
url_for('main.sign_in'), data={
|
||||
url_for('main.sign_in', next=redirect_url), data={
|
||||
'email_address': 'valid@example.gov.uk',
|
||||
'password': 'val1dPassw0rd!'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('.two_factor_email_sent', _external=True)
|
||||
mock_send_verify_code.assert_called_with(api_user_active_email_auth['id'], 'email', None, None)
|
||||
assert response.location == url_for('.two_factor_email_sent', _external=True, next=redirect_url)
|
||||
mock_send_verify_code.assert_called_with(api_user_active_email_auth['id'], 'email', None, redirect_url)
|
||||
mock_verify_password.assert_called_with(api_user_active_email_auth['id'], 'val1dPassw0rd!')
|
||||
|
||||
|
||||
@@ -175,16 +199,21 @@ def test_should_return_redirect_when_user_is_pending(
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_should_attempt_redirect_when_user_is_pending(
|
||||
client,
|
||||
mock_get_user_by_email_pending,
|
||||
mock_verify_password,
|
||||
redirect_url
|
||||
):
|
||||
response = client.post(
|
||||
url_for('main.sign_in'), data={
|
||||
url_for('main.sign_in', next=redirect_url), data={
|
||||
'email_address': 'pending_user@example.gov.uk',
|
||||
'password': 'val1dPassw0rd!'})
|
||||
assert response.location == url_for('main.resend_email_verification', _external=True)
|
||||
assert response.location == url_for('main.resend_email_verification', _external=True, next=redirect_url)
|
||||
assert response.status_code == 302
|
||||
|
||||
|
||||
|
||||
@@ -11,11 +11,41 @@ from tests.conftest import (
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('request_url', ['two_factor_email_sent', 'revalidate_email_sent'])
|
||||
@pytest.mark.parametrize('redirect_url', [None, f'/services/{SERVICE_ONE_ID}/templates'])
|
||||
@pytest.mark.parametrize('email_resent, page_title', [
|
||||
(None, 'Check your email'),
|
||||
(True, 'Email resent')
|
||||
])
|
||||
def test_two_factor_email_sent_page(
|
||||
client,
|
||||
email_resent,
|
||||
page_title,
|
||||
redirect_url,
|
||||
request_url
|
||||
):
|
||||
response = client.get(url_for(f'main.{request_url}', next=redirect_url, email_resent=email_resent))
|
||||
assert response.status_code == 200
|
||||
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
assert page.h1.string == page_title
|
||||
# there shouldn't be a form for updating mobile number
|
||||
assert page.find('form') is None
|
||||
resend_email_link = page.find('a', class_="govuk-link govuk-link--no-visited-state page-footer-secondary-link")
|
||||
assert resend_email_link.text == 'Not received an email?'
|
||||
assert resend_email_link['href'] == url_for('main.email_not_received', next=redirect_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_should_render_two_factor_page(
|
||||
client,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mocker
|
||||
mocker,
|
||||
redirect_url
|
||||
):
|
||||
# TODO this lives here until we work out how to
|
||||
# reassign the session after it is lost mid register process
|
||||
@@ -24,7 +54,7 @@ def test_should_render_two_factor_page(
|
||||
'id': api_user_active['id'],
|
||||
'email': api_user_active['email_address']}
|
||||
mocker.patch('app.user_api_client.get_user', return_value=api_user_active)
|
||||
response = client.get(url_for('main.two_factor'))
|
||||
response = client.get(url_for('main.two_factor', next=redirect_url))
|
||||
assert response.status_code == 200
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
assert page.select_one('main p').text.strip() == (
|
||||
@@ -36,6 +66,10 @@ def test_should_render_two_factor_page(
|
||||
assert page.select_one('input')['type'] == 'tel'
|
||||
assert page.select_one('input')['pattern'] == '[0-9]*'
|
||||
|
||||
assert page.select_one(
|
||||
'a:contains("Not received a text message?")'
|
||||
)['href'] == url_for('main.check_and_resend_text_code', next=redirect_url)
|
||||
|
||||
|
||||
@freeze_time('2020-01-27T12:00:00')
|
||||
def test_should_login_user_and_should_redirect_to_next_url(
|
||||
@@ -78,11 +112,15 @@ def test_should_send_email_and_redirect_to_info_page_if_user_needs_to_revalidate
|
||||
session['user_details'] = {
|
||||
'id': api_user_active['id'],
|
||||
'email': api_user_active['email_address']}
|
||||
response = client.post(url_for('main.two_factor', next='/services/{}'.format(SERVICE_ONE_ID)),
|
||||
response = client.post(url_for('main.two_factor', next=f'/services/{SERVICE_ONE_ID}'),
|
||||
data={'sms_code': '12345'})
|
||||
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.revalidate_email_sent', _external=True)
|
||||
assert response.location == url_for(
|
||||
'main.revalidate_email_sent',
|
||||
_external=True,
|
||||
next=f'/services/{SERVICE_ONE_ID}'
|
||||
)
|
||||
mock_send_verify_code.assert_called_with(api_user_active['id'], 'email', None, mocker.ANY)
|
||||
|
||||
|
||||
@@ -313,17 +351,22 @@ def test_valid_two_factor_email_link_logs_in_user(
|
||||
assert response.location == url_for('main.show_accounts_or_dashboard', _external=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_two_factor_email_link_has_expired(
|
||||
app_,
|
||||
valid_token,
|
||||
client,
|
||||
mock_send_verify_code,
|
||||
fake_uuid
|
||||
fake_uuid,
|
||||
redirect_url
|
||||
):
|
||||
|
||||
with set_config(app_, 'EMAIL_2FA_EXPIRY_SECONDS', -1):
|
||||
response = client.post(
|
||||
url_for_endpoint_with_token('main.two_factor_email', token=valid_token),
|
||||
url_for_endpoint_with_token('main.two_factor_email', token=valid_token, next=redirect_url),
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
@@ -331,6 +374,8 @@ def test_two_factor_email_link_has_expired(
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
|
||||
assert page.h1.text.strip() == 'The link has expired'
|
||||
assert page.select_one('a:contains("Sign in again")')['href'] == url_for('main.sign_in', next=redirect_url)
|
||||
|
||||
assert mock_send_verify_code.called is False
|
||||
|
||||
|
||||
@@ -346,20 +391,26 @@ def test_two_factor_email_link_is_invalid(
|
||||
assert normalize_spaces(
|
||||
page.select_one('.banner-dangerous').text
|
||||
) == "There’s something wrong with the link you’ve used."
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.parametrize('redirect_url', [
|
||||
None,
|
||||
f'/services/{SERVICE_ONE_ID}/templates',
|
||||
])
|
||||
def test_two_factor_email_link_is_already_used(
|
||||
client,
|
||||
valid_token,
|
||||
mocker,
|
||||
mock_send_verify_code
|
||||
mock_send_verify_code,
|
||||
redirect_url
|
||||
|
||||
):
|
||||
mocker.patch('app.user_api_client.check_verify_code', return_value=(False, 'Code has expired'))
|
||||
|
||||
response = client.post(
|
||||
url_for_endpoint_with_token('main.two_factor_email', token=valid_token),
|
||||
url_for_endpoint_with_token('main.two_factor_email', token=valid_token, next=redirect_url),
|
||||
follow_redirects=True
|
||||
)
|
||||
|
||||
@@ -367,6 +418,8 @@ def test_two_factor_email_link_is_already_used(
|
||||
assert response.status_code == 200
|
||||
|
||||
assert page.h1.text.strip() == 'The link has expired'
|
||||
assert page.select_one('a:contains("Sign in again")')['href'] == url_for('main.sign_in', next=redirect_url)
|
||||
|
||||
assert mock_send_verify_code.called is False
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user