From c0e2a478f6ceee53f55c0f150de36523d738678c Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Fri, 9 Feb 2018 14:16:10 +0000 Subject: [PATCH] Allow admin to specify domain for email auth links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to https://github.com/alphagov/notifications-api/pull/1515 This lets the admin app pass in a domain to use for email auth links, so that when it’s running on a different URL users who try to sign in will get an email auth link for the domain they sign in on, not the default admin domain for the environment in which the API is running. --- app/user/rest.py | 6 +++--- app/user/users_schema.py | 1 + app/utils.py | 4 ++-- tests/app/user/test_rest_verify.py | 27 +++++++++++++++++++++++---- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/user/rest.py b/app/user/rest.py index 87e9cf305..b4011ebe7 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -196,7 +196,7 @@ def send_user_email_code(user_to_send_to, data): secret_code = str(uuid.uuid4()) personalisation = { 'name': user_to_send_to.name, - 'url': _create_2fa_url(user_to_send_to, secret_code, data.get('next')) + 'url': _create_2fa_url(user_to_send_to, secret_code, data.get('next'), data.get('email_auth_link_host')) } create_2fa_code( @@ -413,10 +413,10 @@ def _create_confirmation_url(user, email_address): return url_with_token(data, url, current_app.config) -def _create_2fa_url(user, secret_code, next_redir): +def _create_2fa_url(user, secret_code, next_redir, email_auth_link_host): data = json.dumps({'user_id': str(user.id), 'secret_code': secret_code}) url = '/email-auth/' - ret = url_with_token(data, url, current_app.config) + ret = url_with_token(data, url, current_app.config, base_url=email_auth_link_host) if next_redir: ret += '?{}'.format(urlencode({'next': next_redir})) return ret diff --git a/app/user/users_schema.py b/app/user/users_schema.py index 4f4b27f8c..cc8bb1040 100644 --- a/app/user/users_schema.py +++ b/app/user/users_schema.py @@ -22,6 +22,7 @@ post_send_user_email_code_schema = { # doesn't need 'to' as we'll just grab user.email_address. but lets keep it # as allowed to keep admin code cleaner, but only as null to prevent confusion 'to': {'type': 'null'}, + 'email_auth_link_host': {'type': ['string', 'null']}, 'next': {'type': ['string', 'null']}, }, 'required': [], diff --git a/app/utils.py b/app/utils.py index 49f6d61dc..b1367cc7d 100644 --- a/app/utils.py +++ b/app/utils.py @@ -20,10 +20,10 @@ def pagination_links(pagination, endpoint, **kwargs): return links -def url_with_token(data, url, config): +def url_with_token(data, url, config, base_url=None): from notifications_utils.url_safe_token import generate_token token = generate_token(data, config['SECRET_KEY'], config['DANGEROUS_SALT']) - base_url = config['ADMIN_BASE_URL'] + url + base_url = (base_url or config['ADMIN_BASE_URL']) + url return base_url + token diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index c43507784..b97f4ef81 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -343,12 +343,30 @@ def test_reset_failed_login_count_returns_404_when_user_does_not_exist(client): assert resp.status_code == 404 -def test_send_user_email_code(admin_request, mocker, sample_user, email_2fa_code_template): +@pytest.mark.parametrize('data, expected_auth_url', ( + ( + {}, + 'http://localhost:6012/email-auth/.', + ), + ( + {'to': None}, + 'http://localhost:6012/email-auth/.', + ), + ( + {'to': None, 'email_auth_link_host': 'https://example.com'}, + 'https://example.com/email-auth/.', + ), +)) +def test_send_user_email_code( + admin_request, + mocker, + sample_user, + email_2fa_code_template, + data, + expected_auth_url, +): deliver_email = mocker.patch('app.celery.provider_tasks.deliver_email.apply_async') - data = { - 'to': None - } admin_request.post( 'user.send_user_2fa_code', code_type='email', @@ -361,6 +379,7 @@ def test_send_user_email_code(admin_request, mocker, sample_user, email_2fa_code assert noti.to == sample_user.email_address assert str(noti.template_id) == current_app.config['EMAIL_2FA_TEMPLATE_ID'] assert noti.personalisation['name'] == 'Test User' + assert noti.personalisation['url'].startswith(expected_auth_url) deliver_email.assert_called_once_with( [str(noti.id)], queue='notify-internal-tasks'