diff --git a/templates/zerver/emails/notify_new_login.source.html b/templates/zerver/emails/notify_new_login.source.html index bfcc469ff3..99d8aba4df 100644 --- a/templates/zerver/emails/notify_new_login.source.html +++ b/templates/zerver/emails/notify_new_login.source.html @@ -23,3 +23,7 @@ Server: {{ realm_uri }} Account: {{ user_email }} Time: {{ login_time }}

If you recognize this login activity, you can archive this notice.

Thanks,
Zulip Account Security

{% endblock %} + +{% block manage_preferences %} +

Manage email preferences | Unsubscribe from login notifications

+{% endblock %} diff --git a/zerver/signals.py b/zerver/signals.py index d78d948e96..8a2b9a52cb 100644 --- a/zerver/signals.py +++ b/zerver/signals.py @@ -10,6 +10,7 @@ from django.utils.timezone import \ from django.utils.timezone import now as timezone_now from django.utils.translation import ugettext_lazy as _ +from confirmation.models import one_click_unsubscribe_link from zerver.lib.queue import queue_json_publish from zerver.lib.send_email import FromAddress from zerver.models import UserProfile @@ -91,6 +92,7 @@ def email_on_new_login(sender: Any, user: UserProfile, request: Any, **kwargs: A context['device_ip'] = request.META.get('REMOTE_ADDR') or _("Unknown IP address") context['device_os'] = get_device_os(user_agent) context['device_browser'] = get_device_browser(user_agent) + context['unsubscribe_link'] = one_click_unsubscribe_link(user, 'login') email_dict = { 'template_prefix': 'zerver/emails/notify_new_login', diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py index 3790e18797..63edd8bae2 100644 --- a/zerver/tests/test_signup.py +++ b/zerver/tests/test_signup.py @@ -1568,6 +1568,24 @@ class EmailUnsubscribeTests(ZulipTestCase): self.assertFalse(user_profile.enable_digest_emails) self.assertEqual(0, ScheduledEmail.objects.filter(user=user_profile).count()) + def test_login_unsubscribe(self) -> None: + """ + We provide one-click unsubscribe links in login + e-mails that you can click even when logged out to update your + email notification settings. + """ + user_profile = self.example_user('hamlet') + user_profile.enable_login_emails = True + user_profile.save() + + unsubscribe_link = one_click_unsubscribe_link(user_profile, "login") + result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path) + + self.assertEqual(result.status_code, 200) + + user_profile.refresh_from_db() + self.assertFalse(user_profile.enable_login_emails) + class RealmCreationTest(ZulipTestCase): @override_settings(OPEN_REALM_CREATION=True) def check_able_to_create_realm(self, email: str) -> None: diff --git a/zerver/views/unsubscribe.py b/zerver/views/unsubscribe.py index 1ca5d63243..9a894e93d9 100644 --- a/zerver/views/unsubscribe.py +++ b/zerver/views/unsubscribe.py @@ -34,6 +34,9 @@ def do_welcome_unsubscribe(user_profile: UserProfile) -> None: def do_digest_unsubscribe(user_profile: UserProfile) -> None: do_change_notification_settings(user_profile, 'enable_digest_emails', False) +def do_login_unsubscribe(user_profile: UserProfile) -> None: + do_change_notification_settings(user_profile, 'enable_login_emails', False) + # The keys are part of the URL for the unsubscribe link and must be valid # without encoding. # The values are a tuple of (display name, unsubscribe function), where the @@ -41,7 +44,8 @@ def do_digest_unsubscribe(user_profile: UserProfile) -> None: email_unsubscribers = { "missed_messages": ("missed messages", do_missedmessage_unsubscribe), "welcome": ("welcome", do_welcome_unsubscribe), - "digest": ("digest", do_digest_unsubscribe) + "digest": ("digest", do_digest_unsubscribe), + "login": ("login", do_login_unsubscribe) } # Login NOT required. These are for one-click unsubscribes.