diff --git a/zerver/actions/user_settings.py b/zerver/actions/user_settings.py index 57ebbd37b8..b17efd148a 100644 --- a/zerver/actions/user_settings.py +++ b/zerver/actions/user_settings.py @@ -7,6 +7,7 @@ from django.db.models import F from django.utils.timezone import now as timezone_now from confirmation.models import Confirmation, create_confirmation_link +from confirmation.settings import STATUS_REVOKED from zerver.actions.presence import do_update_user_presence from zerver.lib.avatar import avatar_url from zerver.lib.cache import ( @@ -155,6 +156,11 @@ def do_start_email_change_process(user_profile: UserProfile, new_email: str) -> realm=user_profile.realm, ) + # Deactivate existing email change requests + EmailChangeStatus.objects.filter(realm=user_profile.realm, user_profile=user_profile).exclude( + id=obj.id, + ).update(status=STATUS_REVOKED) + activation_url = create_confirmation_link(obj, Confirmation.EMAIL_CHANGE) from zerver.context_processors import common_context diff --git a/zerver/tests/test_email_change.py b/zerver/tests/test_email_change.py index 9ad689fd6a..324429db18 100644 --- a/zerver/tests/test_email_change.py +++ b/zerver/tests/test_email_change.py @@ -131,6 +131,25 @@ class EmailChangeTestCase(ZulipTestCase): response = self.client_get(activation_url) self.assertEqual(response.status_code, 404) + def test_change_email_revokes(self) -> None: + user_profile = self.example_user("hamlet") + self.login_user(user_profile) + old_email = user_profile.delivery_email + + first_email = "hamlet-newer@zulip.com" + first_url = self.generate_email_change_link(first_email) + second_email = "hamlet-newest@zulip.com" + second_url = self.generate_email_change_link(second_email) + response = self.client_get(first_url) + self.assertEqual(response.status_code, 404) + user_profile.refresh_from_db() + self.assertEqual(user_profile.delivery_email, old_email) + + response = self.client_get(second_url) + self.assertEqual(response.status_code, 200) + user_profile.refresh_from_db() + self.assertEqual(user_profile.delivery_email, second_email) + def test_change_email_deactivated_user_realm(self) -> None: new_email = "hamlet-new@zulip.com" user_profile = self.example_user("hamlet") diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py index f7ee5f6310..6f87c134cd 100644 --- a/zerver/views/user_settings.py +++ b/zerver/views/user_settings.py @@ -80,6 +80,14 @@ def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpRes id=email_change_object.user_profile_id ) + if user_profile.delivery_email != old_email: + # This is not expected to be possible, since we deactivate + # any previous email changes when we create a new one, but + # double-check. + return render_confirmation_key_error( + request, ConfirmationKeyError(ConfirmationKeyError.EXPIRED) + ) # nocoverage + if user_profile.realm.deactivated: return redirect_to_deactivation_notice()