diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ff3d5c1..37e98488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. - Show a warning when running with DEBUG=True - Add "channels" attribute to the Check API resource - Can specify channel codes when updating a check via API +- Added a workaround for email agents automatically opening "Unsubscribe" links ### Bug Fixes - During DST transition, handle ambiguous dates as pre-transition diff --git a/hc/accounts/tests/test_unsubscribe_reports.py b/hc/accounts/tests/test_unsubscribe_reports.py index 8d4eddbb..37e982ba 100644 --- a/hc/accounts/tests/test_unsubscribe_reports.py +++ b/hc/accounts/tests/test_unsubscribe_reports.py @@ -17,7 +17,7 @@ class UnsubscribeReportsTestCase(BaseTestCase): url = "/accounts/unsubscribe_reports/%s/" % sig r = self.client.get(url) - self.assertContains(r, "You have been unsubscribed") + self.assertContains(r, "Unsubscribed") self.profile.refresh_from_db() self.assertFalse(self.profile.reports_allowed) @@ -30,3 +30,17 @@ class UnsubscribeReportsTestCase(BaseTestCase): url = "/accounts/unsubscribe_reports/invalid/" r = self.client.get(url) self.assertContains(r, "Incorrect Link") + + def test_post_works(self): + sig = signing.TimestampSigner(salt="reports").sign("alice") + url = "/accounts/unsubscribe_reports/%s/" % sig + + r = self.client.post(url) + self.assertContains(r, "Unsubscribed") + + def test_it_serves_confirmation_form(self): + sig = signing.TimestampSigner(salt="reports").sign("alice") + url = "/accounts/unsubscribe_reports/%s/?ask=1" % sig + + r = self.client.get(url) + self.assertContains(r, "Please press the button below") diff --git a/hc/accounts/views.py b/hc/accounts/views.py index c64373e0..cc87c8de 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -355,6 +355,11 @@ def unsubscribe_reports(request, username): except signing.BadSignature: return render(request, "bad_link.html") + # Some email servers open links in emails to check for malicious content. + # To work around this, we serve a form that auto-submits with JS. + if "ask" in request.GET and request.method != "POST": + return render(request, "accounts/unsubscribe_submit.html") + user = User.objects.get(username=username) profile = Profile.objects.for_user(user) profile.reports_allowed = False diff --git a/hc/front/tests/test_unsubscribe_email.py b/hc/front/tests/test_unsubscribe_email.py index a6c8fcab..ab2926ab 100644 --- a/hc/front/tests/test_unsubscribe_email.py +++ b/hc/front/tests/test_unsubscribe_email.py @@ -36,3 +36,17 @@ class UnsubscribeEmailTestCase(BaseTestCase): r = self.client.get(url) self.assertEqual(r.status_code, 400) + + def test_post_works(self): + token = self.channel.make_token() + url = "/integrations/%s/unsub/%s/" % (self.channel.code, token) + + r = self.client.post(url) + self.assertContains(r, "has been unsubscribed", status_code=200) + + def test_it_serves_confirmation_form(self): + token = self.channel.make_token() + url = "/integrations/%s/unsub/%s/?ask=1" % (self.channel.code, token) + + r = self.client.get(url) + self.assertContains(r, "Please press the button below") diff --git a/hc/front/views.py b/hc/front/views.py index fbd2af4d..b26a7acc 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -516,6 +516,11 @@ def unsubscribe_email(request, code, token): if channel.kind != "email": return HttpResponseBadRequest() + # Some email servers open links in emails to check for malicious content. + # To work around this, we serve a form that auto-submits with JS. + if "ask" in request.GET and request.method != "POST": + return render(request, "accounts/unsubscribe_submit.html") + channel.delete() return render(request, "front/unsubscribe_success.html") diff --git a/templates/accounts/unsubscribed.html b/templates/accounts/unsubscribed.html index 0f2e4fe0..68f8c745 100644 --- a/templates/accounts/unsubscribed.html +++ b/templates/accounts/unsubscribed.html @@ -1,12 +1,12 @@ -{% extends "base.html" %} +{% extends "base_bare.html" %} +{% load hc_extras %} -{% block content %} -
-
-
-

You have been unsubscribed

-
-
-
+{% block title %}Unsubscribed{% endblock %} +{% block content %} +

Unsubscribed

+

+ Your email address has been unsubscribed from + {% site_name %} reports. +

{% endblock %} diff --git a/templates/base_bare.html b/templates/base_bare.html index 35bf23f2..f24da3e4 100644 --- a/templates/base_bare.html +++ b/templates/base_bare.html @@ -2,23 +2,45 @@ - {% block title %}{% site_name %} - Monitor Cron Jobs. Get Notified When Your Cron Jobs Fail{% endblock %} + {% block title %}{% site_name %}{% endblock %} - - {% compress css %} - - + - - {% block containers %} -
- {% block content %}{% endblock %} -
- {% endblock %} + + {% block content %}{% endblock %} diff --git a/templates/emails/alert-body-html.html b/templates/emails/alert-body-html.html index 24e506ab..c9938870 100644 --- a/templates/emails/alert-body-html.html +++ b/templates/emails/alert-body-html.html @@ -22,7 +22,7 @@ The {% escaped_site_name %} Team {% block unsub %}
- + Unsubscribe {% endblock %} diff --git a/templates/emails/report-body-html.html b/templates/emails/report-body-html.html index 862fc420..4d1a7cca 100644 --- a/templates/emails/report-body-html.html +++ b/templates/emails/report-body-html.html @@ -41,7 +41,7 @@ The {% escaped_site_name %} Team {% block unsub %}
- + Unsubscribe {% endblock %} diff --git a/templates/front/unsubscribe_success.html b/templates/front/unsubscribe_success.html index 55188024..175dffb3 100644 --- a/templates/front/unsubscribe_success.html +++ b/templates/front/unsubscribe_success.html @@ -1,19 +1,12 @@ -{% extends "base.html" %} +{% extends "base_bare.html" %} {% load hc_extras %} -{% block content %} -
-
-
-

Unsubscribed

-
-

- Your email address has been unsubscribed from - {% site_name %} notifications. -

-
-
-
-
+{% block title %}Unsubscribed{% endblock %} +{% block content %} +

Unsubscribed

+

+ Your email address has been unsubscribed from + {% site_name %} notifications. +

{% endblock %}