diff --git a/app/main/views/invites.py b/app/main/views/invites.py
index 43e994e06..2da96a154 100644
--- a/app/main/views/invites.py
+++ b/app/main/views/invites.py
@@ -38,7 +38,7 @@ def accept_invite(token):
invited_user = invite_api_client.check_token(token)
- if not current_user.is_anonymous and current_user.email_address != invited_user.email_address:
+ if not current_user.is_anonymous and current_user.email_address.lower() != invited_user.email_address.lower():
message = Markup("""
You’re signed in as {}.
This invite is for another email address.
diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py
index eb77ad7a7..20033afa0 100644
--- a/app/main/views/sign_in.py
+++ b/app/main/views/sign_in.py
@@ -40,7 +40,7 @@ def sign_in():
if user and session.get('invited_user'):
invited_user = session.get('invited_user')
- if user.email_address != invited_user['email_address']:
+ if user.email_address.lower() != invited_user['email_address'].lower():
flash("You can't accept an invite for another person.")
session.pop('invited_user', None)
abort(403)
diff --git a/app/notify_client/invite_api_client.py b/app/notify_client/invite_api_client.py
index 352aaef77..a3b2589b7 100644
--- a/app/notify_client/invite_api_client.py
+++ b/app/notify_client/invite_api_client.py
@@ -9,6 +9,7 @@ class InviteApiClient(NotifyAdminAPIClient):
def init_app(self, app):
self.base_url = app.config['API_HOST_NAME']
+ self.admin_url = app.config['ADMIN_BASE_URL']
self.service_id = app.config['ADMIN_CLIENT_USER_NAME']
self.api_key = app.config['ADMIN_CLIENT_SECRET']
@@ -18,7 +19,8 @@ class InviteApiClient(NotifyAdminAPIClient):
'email_address': email_address,
'from_user': invite_from_id,
'permissions': permissions,
- 'auth_type': auth_type
+ 'auth_type': auth_type,
+ 'invite_link_host': self.admin_url,
}
data = _attach_current_user(data)
resp = self.post(url='/service/{}/invite'.format(service_id), data=data)
diff --git a/app/templates/partials/jobs/notifications.html b/app/templates/partials/jobs/notifications.html
index 70a086715..5a9c3ffd7 100644
--- a/app/templates/partials/jobs/notifications.html
+++ b/app/templates/partials/jobs/notifications.html
@@ -31,7 +31,7 @@
{% elif notifications %}
- Download this report
+ Download this report
{{ time_left }}
diff --git a/app/templates/views/check/ok.html b/app/templates/views/check/ok.html
index 792f016d1..85f79b97b 100644
--- a/app/templates/views/check/ok.html
+++ b/app/templates/views/check/ok.html
@@ -39,7 +39,7 @@
{% if template.template_type != 'letter' or not request.args.from_test %}
{% else %}
- Download as a printable PDF
+ Download as a printable PDF
{% endif %}
diff --git a/app/templates/views/dashboard/_inbox_messages.html b/app/templates/views/dashboard/_inbox_messages.html
index b46b16a8c..bba1e741d 100644
--- a/app/templates/views/dashboard/_inbox_messages.html
+++ b/app/templates/views/dashboard/_inbox_messages.html
@@ -4,7 +4,7 @@
{% if messages %}
- Download these messages
+ Download these messages
{% endif %}
{% call(item, row_number) list_table(
diff --git a/app/templates/views/notifications/check.html b/app/templates/views/notifications/check.html
index 19c5d34e4..a58eee691 100644
--- a/app/templates/views/notifications/check.html
+++ b/app/templates/views/notifications/check.html
@@ -53,7 +53,7 @@
{% if template.template_type != 'letter' or not request.args.from_test %}
{% else %}
-
Download as a printable PDF
+
Download as a printable PDF
{% endif %}
{% endif %}
diff --git a/app/templates/views/notifications/notification.html b/app/templates/views/notifications/notification.html
index f0ef58dba..f50a3c634 100644
--- a/app/templates/views/notifications/notification.html
+++ b/app/templates/views/notifications/notification.html
@@ -35,7 +35,7 @@
Estimated delivery date: {{ estimated_letter_delivery_date|string|format_date_short }}
- Download as a PDF
+ Download as a PDF
{% endif %}
diff --git a/app/templates/views/send.html b/app/templates/views/send.html
index 0049292f6..0ab3345ca 100644
--- a/app/templates/views/send.html
+++ b/app/templates/views/send.html
@@ -40,7 +40,7 @@
{% endcall %}
- Download this example
+ Download this example
Your file will populate this template ({{ template.name }})
diff --git a/requirements.txt b/requirements.txt
index bbc4342a4..80dff8cdd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -20,4 +20,4 @@ notifications-python-client==4.7.1
awscli==1.14.16
awscli-cwlogs>=1.4,<1.5
-git+https://github.com/alphagov/notifications-utils.git@23.3.5#egg=notifications-utils==23.3.5
+git+https://github.com/alphagov/notifications-utils.git@23.4.0#egg=notifications-utils==23.4.0
diff --git a/tests/app/main/views/test_accept_invite.py b/tests/app/main/views/test_accept_invite.py
index ae94139c8..0ff67f30b 100644
--- a/tests/app/main/views/test_accept_invite.py
+++ b/tests/app/main/views/test_accept_invite.py
@@ -315,6 +315,29 @@ def test_signed_in_existing_user_cannot_use_anothers_invite(
assert mock_accept_invite.call_count == 0
+def test_accept_invite_does_not_treat_email_addresses_as_case_sensitive(
+ logged_in_client,
+ mocker,
+ api_user_active,
+ sample_invite,
+ service_one,
+ mock_accept_invite,
+ mock_get_user_by_email
+):
+ mocker.patch('app.main.views.invites.check_token')
+
+ # the email address of api_user_active is 'test@user.gov.uk'
+ sample_invite['email_address'] = 'TEST@user.gov.uk'
+ invite = InvitedUser(**sample_invite)
+ mocker.patch('app.invite_api_client.check_token', return_value=invite)
+ mocker.patch('app.user_api_client.get_users_for_service', return_value=[api_user_active])
+
+ response = logged_in_client.get(url_for('main.accept_invite', token='thisisnotarealtoken'))
+
+ assert response.status_code == 302
+ assert response.location == url_for('main.service_dashboard', service_id=service_one['id'], _external=True)
+
+
def test_new_invited_user_verifies_and_added_to_service(
client,
service_one,
diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py
index 43a9c784e..2b812ca3f 100644
--- a/tests/app/main/views/test_jobs.py
+++ b/tests/app/main/views/test_jobs.py
@@ -97,7 +97,7 @@ def test_should_show_page_for_one_job(
job_id=fake_uuid,
status=status_argument,
)
- csv_link = page.find('a', {'download': 'download'})
+ csv_link = page.select_one('a[download]')
assert csv_link['href'] == url_for(
'main.view_job_csv',
service_id=service_one['id'],
diff --git a/tests/app/main/views/test_sign_in.py b/tests/app/main/views/test_sign_in.py
index 6f44c8d4a..563d29751 100644
--- a/tests/app/main/views/test_sign_in.py
+++ b/tests/app/main/views/test_sign_in.py
@@ -170,3 +170,30 @@ def test_should_attempt_redirect_when_user_is_pending(
'password': 'val1dPassw0rd!'})
assert response.location == url_for('main.resend_email_verification', _external=True)
assert response.status_code == 302
+
+
+def test_email_address_is_treated_case_insensitively_when_signing_in_as_invited_user(
+ client,
+ mocker,
+ mock_verify_password,
+ api_user_active,
+ sample_invite,
+ mock_accept_invite,
+ mock_send_verify_code
+):
+ sample_invite['email_address'] = 'TEST@user.gov.uk'
+
+ mocker.patch('app.user_api_client.get_user_by_email_or_none', return_value=api_user_active)
+ mocker.patch('app.main.views.sign_in._get_and_verify_user', return_value=api_user_active)
+
+ with client.session_transaction() as session:
+ session['invited_user'] = sample_invite
+
+ response = client.post(
+ url_for('main.sign_in'), data={
+ 'email_address': 'test@user.gov.uk',
+ 'password': 'val1dPassw0rd!'})
+
+ assert mock_accept_invite.called
+ assert response.status_code == 302
+ assert mock_send_verify_code.called
diff --git a/tests/app/notify_client/test_invite_client.py b/tests/app/notify_client/test_invite_client.py
index 94e80863d..528c74a36 100644
--- a/tests/app/notify_client/test_invite_client.py
+++ b/tests/app/notify_client/test_invite_client.py
@@ -1,4 +1,40 @@
-from app.notify_client.invite_api_client import InviteApiClient
+from unittest.mock import ANY
+from app import invite_api_client
+
+
+def test_client_creates_invite(
+ app_,
+ mocker,
+ fake_uuid,
+ sample_invite,
+):
+
+ mocker.patch('app.notify_client.current_user')
+
+ mock_post = mocker.patch(
+ 'app.invite_api_client.post',
+ return_value={'data': dict.fromkeys({
+ 'id', 'service', 'from_user', 'email_address',
+ 'permissions', 'status', 'created_at', 'auth_type'
+ })}
+ )
+
+ invite_api_client.create_invite(
+ '12345', '67890', 'test@example.com', 'send_messages', 'sms_auth'
+ )
+
+ mock_post.assert_called_once_with(
+ url='/service/{}/invite'.format('67890'),
+ data={
+ 'auth_type': 'sms_auth',
+ 'email_address': 'test@example.com',
+ 'from_user': '12345',
+ 'service': '67890',
+ 'created_by': ANY,
+ 'permissions': 'send_messages',
+ 'invite_link_host': 'http://localhost:6012',
+ }
+ )
def test_client_returns_invite(mocker, sample_invite):
@@ -10,10 +46,9 @@ def test_client_returns_invite(mocker, sample_invite):
expected_url = '/service/{}/invite'.format(service_id)
- client = InviteApiClient()
mock_get = mocker.patch('app.notify_client.invite_api_client.InviteApiClient.get', return_value=expected_data)
- invites = client.get_invites_for_service(service_id)
+ invites = invite_api_client.get_invites_for_service(service_id)
mock_get.assert_called_once_with(expected_url)
assert len(invites) == 1