diff --git a/hc/front/tests/test_add_pushbover.py b/hc/front/tests/test_add_pushover.py similarity index 55% rename from hc/front/tests/test_add_pushbover.py rename to hc/front/tests/test_add_pushover.py index fc5b16f1..6f6d6be7 100644 --- a/hc/front/tests/test_add_pushbover.py +++ b/hc/front/tests/test_add_pushover.py @@ -3,52 +3,68 @@ from hc.api.models import Channel from hc.test import BaseTestCase -@override_settings(PUSHOVER_API_TOKEN="token", PUSHOVER_SUBSCRIPTION_URL="url") +@override_settings(PUSHOVER_API_TOKEN="token", PUSHOVER_SUBSCRIPTION_URL="http://example.org") class AddPushoverTestCase(BaseTestCase): - def test_instructions_work(self): + @override_settings(PUSHOVER_API_TOKEN=None) + def test_it_requires_api_token(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get("/integrations/add_pushover/") + self.assertEqual(r.status_code, 404) + + def test_instructions_work_without_login(self): + r = self.client.get("/integrations/add_pushover/") + self.assertContains(r, "Setup Guide") + + def test_it_shows_form(self): self.client.login(username="alice@example.org", password="password") r = self.client.get("/integrations/add_pushover/") self.assertContains(r, "Subscribe with Pushover") + def test_post_redirects(self): + self.client.login(username="alice@example.org", password="password") + payload = {"po_priority": 2} + r = self.client.post("/integrations/add_pushover/", form=payload) + self.assertEqual(r.status_code, 302) + + def test_post_requires_authenticated_user(self): + payload = {"po_priority": 2} + r = self.client.post("/integrations/add_pushover/", form=payload) + self.assertEqual(r.status_code, 200) + self.assertContains(r, "Setup Guide") + def test_it_adds_channel(self): self.client.login(username="alice@example.org", password="password") session = self.client.session - session["po_nonce"] = "n" + session["pushover"] = "foo" session.save() - params = "pushover_user_key=a&nonce=n&prio=0" + params = "pushover_user_key=a&state=foo&prio=0" r = self.client.get("/integrations/add_pushover/?%s" % params) - assert r.status_code == 302 + self.assertEqual(r.status_code, 302) channels = list(Channel.objects.all()) assert len(channels) == 1 assert channels[0].value == "a|0" - @override_settings(PUSHOVER_API_TOKEN=None) - def test_it_requires_api_token(self): - self.client.login(username="alice@example.org", password="password") - r = self.client.get("/integrations/add_pushover/") - self.assertEqual(r.status_code, 404) - def test_it_validates_priority(self): self.client.login(username="alice@example.org", password="password") session = self.client.session - session["po_nonce"] = "n" + session["pushover"] = "foo" session.save() - params = "pushover_user_key=a&nonce=n&prio=abc" + params = "pushover_user_key=a&state=foo&prio=abc" r = self.client.get("/integrations/add_pushover/?%s" % params) - assert r.status_code == 400 + self.assertEqual(r.status_code, 400) - def test_it_validates_nonce(self): + def test_it_validates_state(self): self.client.login(username="alice@example.org", password="password") session = self.client.session - session["po_nonce"] = "n" + session["pushover"] = "foo" session.save() - params = "pushover_user_key=a&nonce=INVALID&prio=0" + params = "pushover_user_key=a&state=INVALID&prio=0" r = self.client.get("/integrations/add_pushover/?%s" % params) - assert r.status_code == 403 + self.assertEqual(r.status_code, 400) diff --git a/hc/front/views.py b/hc/front/views.py index 18b6b86e..71a43b60 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -738,19 +738,21 @@ def add_discord(request): return render(request, "integrations/add_discord.html", ctx) -@login_required def add_pushover(request): if settings.PUSHOVER_API_TOKEN is None or settings.PUSHOVER_SUBSCRIPTION_URL is None: raise Http404("pushover integration is not available") + if not request.user.is_authenticated: + ctx = {"page": "channels"} + return render(request, "integrations/add_pushover.html", ctx) + if request.method == "POST": # Initiate the subscription - nonce = get_random_string() - request.session["po_nonce"] = nonce + state = _prepare_state(request, "pushover") failure_url = settings.SITE_ROOT + reverse("hc-channels") success_url = settings.SITE_ROOT + reverse("hc-add-pushover") + "?" + urlencode({ - "nonce": nonce, + "state": state, "prio": request.POST.get("po_priority", "0"), }) subscription_url = settings.PUSHOVER_SUBSCRIPTION_URL + "?" + urlencode({ @@ -762,34 +764,28 @@ def add_pushover(request): # Handle successful subscriptions if "pushover_user_key" in request.GET: - if "nonce" not in request.GET or "prio" not in request.GET: + key = _get_validated_code(request, "pushover", "pushover_user_key") + if key is None: return HttpResponseBadRequest() - # Validate nonce - if request.GET["nonce"] != request.session.get("po_nonce"): - return HttpResponseForbidden() - # Validate priority - if request.GET["prio"] not in ("-2", "-1", "0", "1", "2"): + prio = request.GET.get("prio") + if prio not in ("-2", "-1", "0", "1", "2"): return HttpResponseBadRequest() - # All looks well-- - del request.session["po_nonce"] - if request.GET.get("pushover_unsubscribed") == "1": # Unsubscription: delete all Pushover channels for this user Channel.objects.filter(user=request.user, kind="po").delete() return redirect("hc-channels") - else: - # Subscription - user_key = request.GET["pushover_user_key"] - priority = int(request.GET["prio"]) - channel = Channel(user=request.team.user, kind="po") - channel.value = "%s|%d" % (user_key, priority) - channel.save() - channel.assign_all_checks() - return redirect("hc-channels") + # Subscription + channel = Channel(user=request.team.user, kind="po") + channel.value = "%s|%s" % (key, prio) + channel.save() + channel.assign_all_checks() + + messages.success(request, "The Pushover integration has been added!") + return redirect("hc-channels") # Show Integration Settings form ctx = { diff --git a/static/img/integrations/setup_pushover_1.png b/static/img/integrations/setup_pushover_1.png new file mode 100644 index 00000000..0daaf912 Binary files /dev/null and b/static/img/integrations/setup_pushover_1.png differ diff --git a/static/img/integrations/setup_pushover_2.png b/static/img/integrations/setup_pushover_2.png new file mode 100644 index 00000000..df9b10a1 Binary files /dev/null and b/static/img/integrations/setup_pushover_2.png differ diff --git a/templates/integrations/add_pushover.html b/templates/integrations/add_pushover.html index 1a6b3a3e..754530cc 100644 --- a/templates/integrations/add_pushover.html +++ b/templates/integrations/add_pushover.html @@ -7,13 +7,99 @@ {% block content %}
+ Pushover delivers + real-time notifications on your Android, iPhone, iPad, and Desktop, + Android Wear and Apple Watch. You can set up {% site_name %} to + receive Pushover notifications in a few simple steps. +
+ {% else %} ++ {% site_name %} is a free and + open source + service for monitoring your cron jobs, background processes and + scheduled tasks. Before adding Pushover integration, please log into + {% site_name %}:
-Pushover is a service to receive - instant push notifications on your phone or tablet from a variety of - sources. If you bought the app on your mobile device, you can integrate it - with your {% site_name %} account in a few simple steps.
++ After logging in, go to "Integrations → Add Pushover". + Pushover supports different notification priorities from + silent to "Emergency". Select your preferred priority + and click "Subscribe with Pushover". +
++ You will be redirected to + pushover.net and + asked to confirm a subscription. Review the subscription + details and click "Subscribe Me". +
++ That is all! You will now be redirected back to + "Integrations" page on {% site_name %} and see + the new integration! +
+After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %} clicking on "Add to Slack", you should @@ -77,7 +77,7 @@
You should now be on a page that says "{% site_name %} would like access to TEAM NAME". Select the channel you want to @@ -94,7 +94,7 @@
That is all! You will now be redirected back to "Integrations" page on {% site_name %} and see