diff --git a/app/main/forms.py b/app/main/forms.py index af03280d1..d0685d7dd 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): @@ -1042,12 +1043,14 @@ class BroadcastPermissionsForm(BasePermissionsForm): class BaseInviteUserForm(): email_address = email_address(gov_user=False) - def __init__(self, invalid_email_address, *args, **kwargs): + def __init__(self, inviter_email_address, *args, **kwargs): super().__init__(*args, **kwargs) - self.invalid_email_address = invalid_email_address.lower() + self.inviter_email_address = inviter_email_address def validate_email_address(self, field): - if field.data.lower() == self.invalid_email_address and not current_user.platform_admin: + if current_user.platform_admin: + return + if field.data.lower() == self.inviter_email_address.lower(): raise ValidationError("You cannot send an invitation to yourself") @@ -1058,16 +1061,20 @@ 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.inviter_email_address): + raise ValidationError("You cannot send an invitation to yourself") + class InviteOrgUserForm(StripWhitespaceForm): email_address = email_address(gov_user=False) - def __init__(self, invalid_email_address, *args, **kwargs): + def __init__(self, inviter_email_address, *args, **kwargs): super(InviteOrgUserForm, self).__init__(*args, **kwargs) - self.invalid_email_address = invalid_email_address.lower() + self.inviter_email_address = inviter_email_address.lower() def validate_email_address(self, field): - if field.data.lower() == self.invalid_email_address and not current_user.platform_admin: + if field.data.lower() == self.inviter_email_address and not current_user.platform_admin: raise ValidationError("You cannot send an invitation to yourself") diff --git a/app/main/views/manage_users.py b/app/main/views/manage_users.py index cd0daed86..4333baf23 100644 --- a/app/main/views/manage_users.py +++ b/app/main/views/manage_users.py @@ -59,7 +59,7 @@ def invite_user(service_id, user_id=None): form_class = InviteUserForm form = form_class( - invalid_email_address=current_user.email_address, + inviter_email_address=current_user.email_address, all_template_folders=current_service.all_template_folders, folder_permissions=[f['id'] for f in current_service.all_template_folders] ) diff --git a/app/main/views/organisations.py b/app/main/views/organisations.py index 12405cbd8..867337266 100644 --- a/app/main/views/organisations.py +++ b/app/main/views/organisations.py @@ -184,7 +184,7 @@ def manage_org_users(org_id): @user_has_permissions() def invite_org_user(org_id): form = InviteOrgUserForm( - invalid_email_address=current_user.email_address + inviter_email_address=current_user.email_address ) if form.validate_on_submit(): email_address = form.email_address.data 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 8b20013bd..0cb4ec20d 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,