mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-07 11:28:25 -04:00
Stop automatically resending email verification links
This commit stops a new email verification link from being sent to a user if they click on an email link which has expired or which has already been used. Instead, they will be see an error message with a link to the sign in page. This stops the situation where someone could log in indefinitely (without the needing to enter their password) by trying to use a used / expired email verification link and receiving a valid link automatically.
This commit is contained in:
@@ -2,7 +2,6 @@ import json
|
|||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
current_app,
|
current_app,
|
||||||
flash,
|
|
||||||
redirect,
|
redirect,
|
||||||
render_template,
|
render_template,
|
||||||
request,
|
request,
|
||||||
@@ -42,25 +41,14 @@ def two_factor_email(token):
|
|||||||
current_app.config['EMAIL_2FA_EXPIRY_SECONDS']
|
current_app.config['EMAIL_2FA_EXPIRY_SECONDS']
|
||||||
))
|
))
|
||||||
except SignatureExpired:
|
except SignatureExpired:
|
||||||
# lets decode again, without the expiry, to get the user id out
|
return render_template('views/email-link-invalid.html')
|
||||||
orig_data = json.loads(check_token(
|
|
||||||
token,
|
|
||||||
current_app.config['SECRET_KEY'],
|
|
||||||
current_app.config['DANGEROUS_SALT'],
|
|
||||||
None
|
|
||||||
))
|
|
||||||
session['user_details'] = {'id': orig_data['user_id']}
|
|
||||||
flash("The link in the email we sent you has expired. We’ve sent you a new one.")
|
|
||||||
return redirect(url_for('.resend_email_link'))
|
|
||||||
|
|
||||||
user_id = token_data['user_id']
|
user_id = token_data['user_id']
|
||||||
# checks if code was already used
|
# checks if code was already used
|
||||||
logged_in, msg = user_api_client.check_verify_code(user_id, token_data['secret_code'], "email")
|
logged_in, msg = user_api_client.check_verify_code(user_id, token_data['secret_code'], "email")
|
||||||
|
|
||||||
if not logged_in:
|
if not logged_in:
|
||||||
flash("This link has already been used")
|
return render_template('views/email-link-invalid.html')
|
||||||
session['user_details'] = {'id': user_id}
|
|
||||||
return redirect(url_for('.resend_email_link'))
|
|
||||||
return log_in_user(user_id)
|
return log_in_user(user_id)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -129,13 +129,7 @@ class UserApiClient(NotifyAdminAPIClient):
|
|||||||
return True, ''
|
return True, ''
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
if e.status_code == 400 or e.status_code == 404:
|
if e.status_code == 400 or e.status_code == 404:
|
||||||
if 'Code not found' in e.message:
|
return False, e.message
|
||||||
return False, 'Code not found'
|
|
||||||
elif 'Code has expired' in e.message:
|
|
||||||
return False, 'Code has expired'
|
|
||||||
else:
|
|
||||||
# TODO what is the default message?
|
|
||||||
return False, 'Code not found'
|
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def get_users_for_service(self, service_id):
|
def get_users_for_service(self, service_id):
|
||||||
|
|||||||
19
app/templates/views/email-link-invalid.html
Normal file
19
app/templates/views/email-link-invalid.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "withoutnav_template.html" %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block per_page_title %}
|
||||||
|
Invalid email link
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="column-two-thirds">
|
||||||
|
<h1 class="heading-large">The link has expired</h1>
|
||||||
|
|
||||||
|
<p><a href="{{ url_for('main.sign_in') }}">Sign in again</a> to get a new link.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -235,11 +235,8 @@ def test_two_factor_email_link_has_expired(
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
assert normalize_spaces(
|
assert page.h1.text.strip() == 'The link has expired'
|
||||||
page.select_one('.banner-dangerous').text
|
mock_send_verify_code.assert_not_called
|
||||||
) == "The link in the email we sent you has expired. We’ve sent you a new one."
|
|
||||||
assert page.h1.text.strip() == 'Email resent'
|
|
||||||
mock_send_verify_code.assert_called_once_with(fake_uuid, 'email', None)
|
|
||||||
|
|
||||||
|
|
||||||
def test_two_factor_email_link_is_invalid(
|
def test_two_factor_email_link_is_invalid(
|
||||||
@@ -272,11 +269,11 @@ def test_two_factor_email_link_is_already_used(
|
|||||||
)
|
)
|
||||||
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
assert normalize_spaces(
|
|
||||||
page.select_one('.banner-dangerous').text
|
|
||||||
) == "This link has already been used"
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
assert page.h1.text.strip() == 'The link has expired'
|
||||||
|
mock_send_verify_code.assert_not_called
|
||||||
|
|
||||||
|
|
||||||
def test_two_factor_email_link_when_user_is_locked_out(
|
def test_two_factor_email_link_when_user_is_locked_out(
|
||||||
client,
|
client,
|
||||||
@@ -292,11 +289,11 @@ def test_two_factor_email_link_when_user_is_locked_out(
|
|||||||
)
|
)
|
||||||
|
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
assert normalize_spaces(
|
|
||||||
page.select_one('.banner-dangerous').text
|
|
||||||
) == "This link has already been used"
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
assert page.h1.text.strip() == 'The link has expired'
|
||||||
|
mock_send_verify_code.assert_not_called
|
||||||
|
|
||||||
|
|
||||||
def test_two_factor_email_link_used_when_user_already_logged_in(
|
def test_two_factor_email_link_used_when_user_already_logged_in(
|
||||||
logged_in_client,
|
logged_in_client,
|
||||||
|
|||||||
Reference in New Issue
Block a user