diff --git a/app/main/forms.py b/app/main/forms.py index c543dc2a2..b2fc0573e 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -62,6 +62,7 @@ from app.models.roles_and_permissions import ( roles, ) from app.utils import merge_jsonlike +from app.utils.user import distinct_email_addresses def get_time_value_and_label(future_time): @@ -1060,6 +1061,10 @@ class InviteUserForm(BaseInviteUserForm, PermissionsForm): class BroadcastInviteUserForm(BaseInviteUserForm, BroadcastPermissionsForm): email_address = email_address(gov_user=True) + def validate_email_address(self, field): + if not distinct_email_addresses(field.data, self.invalid_email_address): + raise ValidationError("You cannot send an invitation to yourself") + class InviteOrgUserForm(StripWhitespaceForm): email_address = email_address(gov_user=False) diff --git a/app/utils/user.py b/app/utils/user.py index 577abb305..23a12ccb1 100644 --- a/app/utils/user.py +++ b/app/utils/user.py @@ -66,3 +66,14 @@ def _email_address_ends_with(email_address, known_domains): )) for known in known_domains ) + + +def normalise_email_address_aliases(email_address): + local_part, domain = email_address.split('@') + local_part = local_part.split('+')[0].replace('.', '') + + return f'{local_part}@{domain}'.lower() + + +def distinct_email_addresses(*args): + return len(args) == len(set(map(normalise_email_address_aliases, args))) diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py index 11a74420c..3d69afe6e 100644 --- a/tests/app/main/views/test_manage_users.py +++ b/tests/app/main/views/test_manage_users.py @@ -1443,6 +1443,70 @@ def test_user_cant_invite_themselves( assert not mock_create_invite.called +@pytest.mark.parametrize('email_address', ( + 'test@user.gov.uk', + 'TEST@user.gov.uk', + 'test@USER.gov.uk', + 'test+test@user.gov.uk', + 'te.st@user.gov.uk', + pytest.param('test2@user.gov.uk', marks=pytest.mark.xfail), + pytest.param('test@other.gov.uk', marks=pytest.mark.xfail), +)) +def test_broadcast_user_cant_invite_themselves_or_their_aliases( + client_request, + service_one, + mocker, + active_user_with_permissions, + mock_create_invite, + mock_get_template_folders, + email_address, +): + service_one['permissions'] += ['broadcast'] + page = client_request.post( + 'main.invite_user', + service_id=SERVICE_ONE_ID, + _data={ + 'email_address': email_address, + 'permissions_field': [] + }, + _expected_status=200, + ) + assert normalize_spaces(page.select_one('span.govuk-error-message').text) == ( + "Error: You cannot send an invitation to yourself" + ) + assert mock_create_invite.called is False + + +@pytest.mark.parametrize('extra_service_permissions', ( + pytest.param([], marks=pytest.mark.xfail), + ['broadcast'], +)) +def test_platform_admin_cant_invite_themselves_to_broadcast_services( + client_request, + service_one, + mocker, + platform_admin_user, + mock_create_invite, + mock_get_template_folders, + extra_service_permissions, +): + service_one['permissions'] += extra_service_permissions + client_request.login(platform_admin_user) + page = client_request.post( + 'main.invite_user', + service_id=SERVICE_ONE_ID, + _data={ + 'email_address': platform_admin_user['email_address'], + 'permissions_field': [] + }, + _expected_status=200, + ) + assert normalize_spaces(page.select_one('span.govuk-error-message').text) == ( + "Error: You cannot send an invitation to yourself" + ) + assert mock_create_invite.called is False + + def test_no_permission_manage_users_page( client_request, service_one,