def add_slack_btn(request): code = request.GET.get("code", "") if len(code) < 8: return HttpResponseBadRequest() result = requests.post("https://slack.com/api/oauth.access", { "client_id": settings.SLACK_CLIENT_ID, "client_secret": settings.SLACK_CLIENT_SECRET, "code": code }) doc = result.json() if doc.get("ok"): channel = Channel() channel.user = request.team.user channel.kind = "slack" channel.value = result.text channel.save() channel.assign_all_checks() messages.success(request, "The Slack integration has been added!") else: s = doc.get("error") messages.warning(request, "Error message from slack: %s" % s) return redirect("hc-channels")
class UnsubscribeEmailTestCase(BaseTestCase): def setUp(self): super(UnsubscribeEmailTestCase, self).setUp() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_works(self): token = self.channel.make_token() url = "/integrations/%s/unsub/%s/" % (self.channel.code, token) r = self.client.get(url) self.assertContains(r, "has been unsubscribed", status_code=200) q = Channel.objects.filter(code=self.channel.code) self.assertEqual(q.count(), 0) def test_it_checks_token(self): url = "/integrations/%s/unsub/faketoken/" % self.channel.code r = self.client.get(url) self.assertContains(r, "link you just used is incorrect", status_code=200) def test_it_checks_channel_kind(self): self.channel.kind = "webhook" self.channel.save() token = self.channel.make_token() url = "/integrations/%s/unsub/%s/" % (self.channel.code, token) r = self.client.get(url) self.assertEqual(r.status_code, 400)
class ChannelChecksTestCase(BaseTestCase): def setUp(self): super(ChannelChecksTestCase, self).setUp() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_works(self): url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "*****@*****.**", status_code=200) def test_it_checks_owner(self): mallory = User(username="******", email="*****@*****.**") mallory.set_password("password") mallory.save() # channel does not belong to mallory so this should come back # with 403 Forbidden: url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) assert r.status_code == 403 def test_missing_channel(self): # Valid UUID but there is no channel for it: url = "/integrations/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/checks/" self.client.login(username="******", password="******") r = self.client.get(url) assert r.status_code == 404
class VerifyEmailTestCase(BaseTestCase): def setUp(self): super(VerifyEmailTestCase, self).setUp() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_works(self): token = self.channel.make_token() url = "/integrations/%s/verify/%s/" % (self.channel.code, token) r = self.client.post(url) assert r.status_code == 200, r.status_code channel = Channel.objects.get(code=self.channel.code) assert channel.email_verified def test_it_handles_bad_token(self): url = "/integrations/%s/verify/bad-token/" % self.channel.code r = self.client.post(url) assert r.status_code == 200, r.status_code channel = Channel.objects.get(code=self.channel.code) assert not channel.email_verified def test_missing_channel(self): # Valid UUID, and even valid token but there is no channel for it: code = "6837d6ec-fc08-4da5-a67f-08a9ed1ccf62" token = self.channel.make_token() url = "/integrations/%s/verify/%s/" % (code, token) r = self.client.post(url) assert r.status_code == 404
def add_telegram(request): chat_id, chat_type, chat_name = None, None, None qs = request.META["QUERY_STRING"] if qs: chat_id, chat_type, chat_name = signing.loads(qs, max_age=600) if request.method == "POST": channel = Channel(user=request.team.user, kind="telegram") channel.value = json.dumps({ "id": chat_id, "type": chat_type, "name": chat_name }) channel.save() channel.assign_all_checks() messages.success(request, "The Telegram integration has been added!") return redirect("hc-channels") ctx = { "chat_id": chat_id, "chat_type": chat_type, "chat_name": chat_name, "bot_name": settings.TELEGRAM_BOT_NAME } return render(request, "integrations/add_telegram.html", ctx)
def add_hipchat(request): if "installable_url" in request.GET: url = request.GET["installable_url"] assert url.startswith("https://api.hipchat.com") response = requests.get(url) if "oauthId" not in response.json(): messages.warning(request, "Something went wrong!") return redirect("hc-channels") channel = Channel(kind="hipchat") channel.user = request.team.user channel.value = response.text channel.save() channel.refresh_hipchat_access_token() channel.assign_all_checks() messages.success(request, "The HipChat integration has been added!") return redirect("hc-channels") install_url = "https://www.hipchat.com/addons/install?" + urlencode({ "url": settings.SITE_ROOT + reverse("hc-hipchat-capabilities") }) ctx = { "page": "channels", "install_url": install_url } return render(request, "integrations/add_hipchat.html", ctx)
def add_slack(request): if not settings.SLACK_CLIENT_ID and not request.user.is_authenticated: return redirect("hc-login") if request.method == "POST": form = AddUrlForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="slack") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddUrlForm() ctx = { "page": "channels", "form": form, "slack_client_id": settings.SLACK_CLIENT_ID } if settings.SLACK_CLIENT_ID: ctx["state"] = _prepare_state(request, "slack") return render(request, "integrations/add_slack.html", ctx)
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 request.method == "POST": # Initiate the subscription nonce = get_random_string() request.session["po_nonce"] = nonce failure_url = settings.SITE_ROOT + reverse("hc-channels") success_url = settings.SITE_ROOT + reverse("hc-add-pushover") + "?" + urlencode({ "nonce": nonce, "prio": request.POST.get("po_priority", "0"), }) subscription_url = settings.PUSHOVER_SUBSCRIPTION_URL + "?" + urlencode({ "success": success_url, "failure": failure_url, }) return redirect(subscription_url) # Handle successful subscriptions if "pushover_user_key" in request.GET: if "nonce" not in request.GET or "prio" not in request.GET: 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"): 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") # Show Integration Settings form ctx = { "page": "channels", "po_retry_delay": td(seconds=settings.PUSHOVER_EMERGENCY_RETRY_DELAY), "po_expiration": td(seconds=settings.PUSHOVER_EMERGENCY_EXPIRATION), } return render(request, "integrations/add_pushover.html", ctx)
def test_it_shows_channel_list_with_pushbullet(self): self.client.login(username="******", password="******") ch = Channel(user=self.alice, kind="pushbullet", value="test-token") ch.save() r = self.client.get("/admin/api/channel/") self.assertContains(r, "Pushbullet")
def test_it_assigns_channels(self): channel = Channel(user=self.alice) channel.save() r = self.post({"api_key": "abc", "channels": "*"}) self.assertEqual(r.status_code, 201) check = Check.objects.get() self.assertEqual(check.channel_set.get(), channel)
class NotifyTestCase(TestCase): def _setup_data(self, channel_kind, channel_value, email_verified=True): self.alice = User(username="******") self.alice.save() self.check = Check() self.check.status = "down" self.check.save() self.channel = Channel(user=self.alice) self.channel.kind = channel_kind self.channel.value = channel_value self.channel.email_verified = email_verified self.channel.save() self.channel.checks.add(self.check) @patch("hc.api.models.requests.get") def test_webhook(self, mock_get): self._setup_data("webhook", "http://example") mock_get.return_value.status_code = 200 self.channel.notify(self.check) mock_get.assert_called_with(u"http://example", timeout=5) @patch("hc.api.models.requests.get", side_effect=ReadTimeout) def test_webhooks_handle_timeouts(self, mock_get): self._setup_data("webhook", "http://example") self.channel.notify(self.check) assert Notification.objects.count() == 1 def test_email(self): self._setup_data("email", "*****@*****.**") self.channel.notify(self.check) assert Notification.objects.count() == 1 # And email should have been sent self.assertEqual(len(mail.outbox), 1) def test_it_skips_unverified_email(self): self._setup_data("email", "*****@*****.**", email_verified=False) self.channel.notify(self.check) assert Notification.objects.count() == 0 self.assertEqual(len(mail.outbox), 0) @patch("hc.api.models.requests.post") def test_pd(self, mock_post): self._setup_data("pd", "123") mock_post.return_value.status_code = 200 self.channel.notify(self.check) assert Notification.objects.count() == 1 args, kwargs = mock_post.call_args assert "trigger" in kwargs["data"]
def add_zendesk(request): if settings.ZENDESK_CLIENT_ID is None: raise Http404("zendesk integration is not available") if request.method == "POST": domain = request.POST.get("subdomain") request.session["subdomain"] = domain redirect_uri = settings.SITE_ROOT + reverse("hc-add-zendesk") auth_url = "https://%s.zendesk.com/oauth/authorizations/new?" % domain auth_url += urlencode({ "client_id": settings.ZENDESK_CLIENT_ID, "redirect_uri": redirect_uri, "response_type": "code", "scope": "requests:read requests:write", "state": _prepare_state(request, "zendesk") }) return redirect(auth_url) if "code" in request.GET: code = _get_validated_code(request, "zendesk") if code is None: return HttpResponseBadRequest() domain = request.session.pop("subdomain") url = "https://%s.zendesk.com/oauth/tokens" % domain redirect_uri = settings.SITE_ROOT + reverse("hc-add-zendesk") result = requests.post(url, { "client_id": settings.ZENDESK_CLIENT_ID, "client_secret": settings.ZENDESK_CLIENT_SECRET, "code": code, "grant_type": "authorization_code", "redirect_uri": redirect_uri, "scope": "read" }) doc = result.json() if "access_token" in doc: doc["subdomain"] = domain channel = Channel(kind="zendesk") channel.user = request.team.user channel.value = json.dumps(doc) channel.save() channel.assign_all_checks() messages.success(request, "The Zendesk integration has been added!") else: messages.warning(request, "Something went wrong") return redirect("hc-channels") ctx = {"page": "channels"} return render(request, "integrations/add_zendesk.html", ctx)
def test_it_shows_pushover_notifications(self): ch = Channel(kind="po", user=self.alice) ch.save() Notification(owner=self.check, channel=ch, check_status="down").save() url = "/checks/%s/log/" % self.check.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "Sent a Pushover notification", status_code=200)
def test_it_shows_webhook_notifications(self): ch = Channel(kind="webhook", user=self.alice, value="foo/$NAME") ch.save() Notification(owner=self.check, channel=ch, check_status="down").save() url = "/checks/%s/log/" % self.check.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "Called webhook foo/$NAME", status_code=200)
def _make_user(email): username = str(uuid.uuid4())[:30] user = User(username=username, email=email) user.save() channel = Channel() channel.user = user channel.kind = "email" channel.value = email channel.email_verified = True channel.save() return user
class RemoveChannelTestCase(BaseTestCase): def setUp(self): super(RemoveChannelTestCase, self).setUp() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_works(self): url = "/integrations/%s/remove/" % self.channel.code self.client.login(username="******", password="******") r = self.client.post(url) self.assertRedirects(r, "/integrations/") assert Channel.objects.count() == 0 def test_team_access_works(self): url = "/integrations/%s/remove/" % self.channel.code self.client.login(username="******", password="******") self.client.post(url) assert Channel.objects.count() == 0 def test_it_handles_bad_uuid(self): url = "/integrations/not-uuid/remove/" self.client.login(username="******", password="******") r = self.client.post(url) assert r.status_code == 400 def test_it_checks_owner(self): url = "/integrations/%s/remove/" % self.channel.code self.client.login(username="******", password="******") r = self.client.post(url) assert r.status_code == 403 def test_it_handles_missing_uuid(self): # Valid UUID but there is no channel for it: url = "/integrations/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/remove/" self.client.login(username="******", password="******") r = self.client.post(url) assert r.status_code == 302 def test_it_rejects_get(self): url = "/integrations/%s/remove/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertEqual(r.status_code, 405)
def test_it_unassigns_channels(self): channel = Channel(user=self.alice) channel.save() self.check.assign_all_channels() r = self.post(self.check.code, { "api_key": "abc", "channels": "" }) self.assertEqual(r.status_code, 200) check = Check.objects.get() self.assertEqual(check.channel_set.count(), 0)
def test_it_checks_check_user(self): charlies_channel = Channel(user=self.charlie, kind="email") charlies_channel.email = "*****@*****.**" charlies_channel.save() payload = { "channel": charlies_channel.code, "check-%s" % self.check.code: True } self.client.login(username="******", password="******") r = self.client.post("/integrations/", data=payload) # mc belongs to charlie but self.check does not-- assert r.status_code == 403
def add_webhook(request): if request.method == "POST": form = AddWebhookForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="webhook") channel.value = form.get_value() channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddWebhookForm() ctx = {"page": "channels", "form": form} return render(request, "integrations/add_webhook.html", ctx)
def add_victorops(request): if request.method == "POST": form = AddUrlForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="victorops") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddUrlForm() ctx = {"page": "channels", "form": form} return render(request, "integrations/add_victorops.html", ctx)
def handle(self, *args, **options): for user in User.objects.all(): q = Channel.objects.filter(user=user) q = q.filter(kind="email", email_verified=True, value=user.email) if q.count() > 0: continue print("Creating default channel for %s" % user.email) channel = Channel(user=user) channel.kind = "email" channel.value = user.email channel.email_verified = True channel.save() channel.checks.add(*Check.objects.filter(user=user))
def test_it_formats_complex_slack_value(self): ch = Channel(kind="slack", project=self.project) ch.value = json.dumps({ "ok": True, "team_name": "foo-team", "incoming_webhook": { "url": "http://example.org", "channel": "#bar" }, }) ch.save() self.client.login(username="******", password="******") r = self.client.get(self.channels_url) self.assertContains(r, "foo-team", status_code=200) self.assertContains(r, "#bar")
def add_email(request): if request.method == "POST": form = AddEmailForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="email") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() channel.send_verify_link() return redirect("hc-channels") else: form = AddEmailForm() ctx = {"page": "channels", "form": form} return render(request, "integrations/add_email.html", ctx)
def add_mattermost(request): if request.method == "POST": form = AddUrlForm(request.POST) if form.is_valid(): channel = Channel(project=request.project, kind="mattermost") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddUrlForm() ctx = {"page": "channels", "form": form, "project": request.project} return render(request, "integrations/add_mattermost.html", ctx)
def test_it_formats_complex_slack_value(self): ch = Channel(kind="slack", user=self.alice) ch.value = json.dumps({ "ok": True, "team_name": "foo-team", "incoming_webhook": { "url": "http://example.org", "channel": "#bar" } }) ch.save() self.client.login(username="******", password="******") r = self.client.get("/integrations/") self.assertContains(r, "foo-team", status_code=200) self.assertContains(r, "#bar")
def test_it_shows_email_notification(self): ch = Channel(kind="email", project=self.project) ch.value = json.dumps({ "value": "*****@*****.**", "up": True, "down": True }) ch.save() Notification(owner=self.check, channel=ch, check_status="down").save() self.client.login(username="******", password="******") r = self.client.get(self.url) self.assertContains(r, "Sent email to [email protected]", status_code=200)
class NotifyPdTestCase(BaseTestCase): def _setup_data(self, value, status="down", email_verified=True): self.check = Check(project=self.project) self.check.status = status self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(project=self.project) self.channel.kind = "pd" self.channel.value = value self.channel.email_verified = email_verified self.channel.save() self.channel.checks.add(self.check) @patch("hc.api.transports.requests.request") def test_pd(self, mock_post): self._setup_data("123") mock_post.return_value.status_code = 200 self.channel.notify(self.check) assert Notification.objects.count() == 1 args, kwargs = mock_post.call_args payload = kwargs["json"] self.assertEqual(payload["event_type"], "trigger") self.assertEqual(payload["service_key"], "123") @patch("hc.api.transports.requests.request") def test_pd_complex(self, mock_post): self._setup_data(json.dumps({"service_key": "456"})) mock_post.return_value.status_code = 200 self.channel.notify(self.check) assert Notification.objects.count() == 1 args, kwargs = mock_post.call_args payload = kwargs["json"] self.assertEqual(payload["event_type"], "trigger") self.assertEqual(payload["service_key"], "456") @override_settings(PD_ENABLED=False) def test_it_requires_pd_enabled(self): self._setup_data("123") self.channel.notify(self.check) n = Notification.objects.get() self.assertEqual(n.error, "PagerDuty notifications are not enabled.")
def add_discord(request): if settings.DISCORD_CLIENT_ID is None: raise Http404("discord integration is not available") redirect_uri = settings.SITE_ROOT + reverse("hc-add-discord") if "code" in request.GET: code = _get_validated_code(request, "discord") if code is None: return HttpResponseBadRequest() result = requests.post( "https://discordapp.com/api/oauth2/token", { "client_id": settings.DISCORD_CLIENT_ID, "client_secret": settings.DISCORD_CLIENT_SECRET, "code": code, "grant_type": "authorization_code", "redirect_uri": redirect_uri, }, ) doc = result.json() if "access_token" in doc: channel = Channel(kind="discord", project=request.project) channel.user = request.project.owner channel.value = result.text channel.save() channel.assign_all_checks() messages.success(request, "The Discord integration has been added!") else: messages.warning(request, "Something went wrong") return redirect("hc-channels") auth_url = "https://discordapp.com/api/oauth2/authorize?" + urlencode( { "client_id": settings.DISCORD_CLIENT_ID, "scope": "webhook.incoming", "redirect_uri": redirect_uri, "response_type": "code", "state": _prepare_state(request, "discord"), } ) ctx = {"page": "channels", "project": request.project, "authorize_url": auth_url} return render(request, "integrations/add_discord.html", ctx)
def add_discord(request): if settings.DISCORD_CLIENT_ID is None: raise Http404("discord integration is not available") redirect_uri = settings.SITE_ROOT + reverse("hc-add-discord") if "code" in request.GET: code = _get_validated_code(request, "discord") if code is None: return HttpResponseBadRequest() result = requests.post("https://discordapp.com/api/oauth2/token", { "client_id": settings.DISCORD_CLIENT_ID, "client_secret": settings.DISCORD_CLIENT_SECRET, "code": code, "grant_type": "authorization_code", "redirect_uri": redirect_uri }) doc = result.json() if "access_token" in doc: channel = Channel(kind="discord") channel.user = request.team.user channel.value = result.text channel.save() channel.assign_all_checks() messages.success(request, "The Discord integration has been added!") else: messages.warning(request, "Something went wrong") return redirect("hc-channels") auth_url = "https://discordapp.com/api/oauth2/authorize?" + urlencode({ "client_id": settings.DISCORD_CLIENT_ID, "scope": "webhook.incoming", "redirect_uri": redirect_uri, "response_type": "code", "state": _prepare_state(request, "discord") }) ctx = { "page": "channels", "authorize_url": auth_url } return render(request, "integrations/add_discord.html", ctx)
class BounceTestCase(BaseTestCase): def setUp(self): super(BounceTestCase, self).setUp() self.check = Check(project=self.project, status="up") self.check.save() self.channel = Channel(project=self.project, kind="email") self.channel.value = "*****@*****.**" self.channel.email_verified = True self.channel.save() self.n = Notification(owner=self.check, channel=self.channel) self.n.save() def test_it_works(self): url = "/api/v1/notifications/%s/bounce" % self.n.code r = self.client.post(url, "foo", content_type="text/plain") self.assertEqual(r.status_code, 200) self.n.refresh_from_db() self.assertEqual(self.n.error, "foo") self.channel.refresh_from_db() self.assertFalse(self.channel.email_verified) def test_it_checks_ttl(self): self.n.created = self.n.created - timedelta(minutes=60) self.n.save() url = "/api/v1/notifications/%s/bounce" % self.n.code r = self.client.post(url, "foo", content_type="text/plain") self.assertEqual(r.status_code, 403) def test_it_handles_long_payload(self): url = "/api/v1/notifications/%s/bounce" % self.n.code payload = "A" * 500 r = self.client.post(url, payload, content_type="text/plain") self.assertEqual(r.status_code, 200) def test_it_handles_missing_notification(self): fake_code = "07c2f548-9850-4b27-af5d-6c9dc157ec02" url = "/api/v1/notifications/%s/bounce" % fake_code r = self.client.post(url, "", content_type="text/plain") self.assertEqual(r.status_code, 404)
def test_channel_assignment(self): check = Check() check.status = "up" check.user = self.alice check.save() channel = Channel(user=self.alice) channel.save() channel.checks.add(check) count_before = channel.checks.count() resp = self.post({ "api_key": "abc", "channels": "*" }) count_after = channel.checks.count() self.assertEqual((count_after - count_before), 1)
def _make_user(email): username = str(uuid.uuid4())[:30] user = User(username=username, email=email) user.set_unusable_password() user.save() profile = Profile(user=user) profile.save() channel = Channel() channel.user = user channel.kind = "email" channel.value = email channel.email_verified = True channel.save() return user
def test_it_shows_webhook_notification(self): ch = Channel(kind="webhook", project=self.project) ch.value = json.dumps( { "method_down": "GET", "url_down": "foo/$NAME", "body_down": "", "headers_down": {}, } ) ch.save() Notification(owner=self.check, channel=ch, check_status="down").save() self.client.login(username="******", password="******") r = self.client.get(self.url) self.assertContains(r, "Called webhook foo/$NAME", status_code=200)
class RemoveChannelTestCase(TestCase): def setUp(self): super(RemoveChannelTestCase, self).setUp() self.alice = User(username="******", email="*****@*****.**") self.alice.set_password("password") self.alice.save() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_works(self): url = "/integrations/%s/remove/" % self.channel.code self.client.login(username="******", password="******") r = self.client.post(url) self.assertRedirects(r, "/integrations/") assert Channel.objects.count() == 0 def test_it_handles_bad_uuid(self): url = "/integrations/not-uuid/remove/" self.client.login(username="******", password="******") r = self.client.post(url) assert r.status_code == 400 def test_it_checks_owner(self): url = "/integrations/%s/remove/" % self.channel.code mallory = User(username="******", email="*****@*****.**") mallory.set_password("password") mallory.save() self.client.login(username="******", password="******") r = self.client.post(url) assert r.status_code == 403 def test_it_handles_missing_uuid(self): # Valid UUID but there is no channel for it: url = "/integrations/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/remove/" self.client.login(username="******", password="******") r = self.client.post(url) assert r.status_code == 404
def _make_user(email): username = str(uuid.uuid4())[:30] user = User(username=username, email=email) user.set_unusable_password() user.save() # Ensure a profile gets created Profile.objects.for_user(user) channel = Channel() channel.user = user channel.kind = "email" channel.value = email channel.email_verified = True channel.save() return user
def add_pushbullet(request): if settings.PUSHBULLET_CLIENT_ID is None: raise Http404("pushbullet integration is not available") if "code" in request.GET: code = _get_validated_code(request, "pushbullet") if code is None: return HttpResponseBadRequest() result = requests.post( "https://api.pushbullet.com/oauth2/token", { "client_id": settings.PUSHBULLET_CLIENT_ID, "client_secret": settings.PUSHBULLET_CLIENT_SECRET, "code": code, "grant_type": "authorization_code" }) doc = result.json() if "access_token" in doc: channel = Channel(kind="pushbullet", project=request.project) channel.user = request.project.owner channel.value = doc["access_token"] channel.save() channel.assign_all_checks() messages.success(request, "The Pushbullet integration has been added!") else: messages.warning(request, "Something went wrong") return redirect("hc-channels") redirect_uri = settings.SITE_ROOT + reverse("hc-add-pushbullet") authorize_url = "https://www.pushbullet.com/authorize?" + urlencode( { "client_id": settings.PUSHBULLET_CLIENT_ID, "redirect_uri": redirect_uri, "response_type": "code", "state": _prepare_state(request, "pushbullet") }) ctx = { "page": "channels", "project": request.project, "authorize_url": authorize_url } return render(request, "integrations/add_pushbullet.html", ctx)
def test_it_checks_check_owner(self): charlies_project = Project.objects.create(owner=self.charlie) url = f"/projects/{charlies_project.code}/integrations/" charlies_channel = Channel(project=charlies_project, kind="email") charlies_channel.email = "*****@*****.**" charlies_channel.save() payload = { "channel": charlies_channel.code, "check-%s" % self.check.code: True } self.client.login(username="******", password="******") r = self.client.post(url, data=payload) # charlies_channel belongs to charlie but self.check does not-- self.assertEqual(r.status_code, 403)
def add_pagertree(request, code): project = _get_project_for_user(request, code) if request.method == "POST": form = AddUrlForm(request.POST) if form.is_valid(): channel = Channel(project=project, kind="pagertree") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() return redirect("hc-p-channels", project.code) else: form = AddUrlForm() ctx = {"page": "channels", "project": project, "form": form} return render(request, "integrations/add_pagertree.html", ctx)
class NotifyPagertreeTestCase(BaseTestCase): def setUp(self): super().setUp() self.check = Check(project=self.project) self.check.status = "down" self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(project=self.project) self.channel.kind = "pagertree" self.channel.value = "123" self.channel.save() self.channel.checks.add(self.check) @patch("hc.api.transports.requests.request") def test_it_works(self, mock_post): mock_post.return_value.status_code = 200 self.channel.notify(self.check) assert Notification.objects.count() == 1 args, kwargs = mock_post.call_args payload = kwargs["json"] self.assertEqual(payload["event_type"], "trigger") @override_settings(PAGERTREE_ENABLED=False) def test_it_requires_pagertree_enabled(self): self.channel.notify(self.check) n = Notification.objects.get() self.assertEqual(n.error, "PagerTree notifications are not enabled.") @patch("hc.api.transports.requests.request") def test_it_does_not_escape_title(self, mock_post): mock_post.return_value.status_code = 200 self.check.name = "Foo & Bar" self.check.save() self.channel.notify(self.check) args, kwargs = mock_post.call_args payload = kwargs["json"] self.assertEqual(payload["title"], "Foo & Bar is DOWN")
def add_sms(request): if settings.TWILIO_AUTH is None: raise Http404("sms integration is not available") if request.method == "POST": form = AddSmsForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="sms") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddSmsForm() ctx = {"page": "channels", "form": form, "profile": request.team} return render(request, "integrations/add_sms.html", ctx)
def add_opsgenie(request, code): project = _get_project_for_user(request, code) if request.method == "POST": form = AddOpsGenieForm(request.POST) if form.is_valid(): channel = Channel(project=project, kind="opsgenie") v = {"region": form.cleaned_data["region"], "key": form.cleaned_data["key"]} channel.value = json.dumps(v) channel.save() channel.assign_all_checks() return redirect("hc-p-channels", project.code) else: form = AddOpsGenieForm() ctx = {"page": "channels", "project": project, "form": form} return render(request, "integrations/add_opsgenie.html", ctx)
def test_it_checks_check_user(self): mallory = User(username="******") mallory.set_password("password") mallory.save() mc = Channel(user=mallory, kind="email") mc.email = "*****@*****.**" mc.save() payload = { "channel": mc.code, "check-%s" % self.check.code: True } self.client.login(username="******", password="******") r = self.client.post("/integrations/", data=payload) # mc belongs to mallorym but self.check does not-- assert r.status_code == 403
def test_it_checks_check_user(self): mallory = User(username="******", email="*****@*****.**") mallory.set_password("password") mallory.save() mc = Channel(user=mallory, kind="email") mc.email = "*****@*****.**" mc.save() payload = { "channel": mc.code, "check-%s" % self.check.code: True } self.client.login(username="******", password="******") r = self.client.post("/integrations/", data=payload) # mc belongs to mallorym but self.check does not-- assert r.status_code == 403
def add_pdc(request, state=None): if settings.PD_VENDOR_KEY is None: raise Http404("pagerduty integration is not available") if state and request.user.is_authenticated: if "pd" not in request.session: return HttpResponseBadRequest() session_state = request.session.pop("pd") if session_state != state: return HttpResponseBadRequest() if request.GET.get("error") == "cancelled": messages.warning(request, "PagerDuty setup was cancelled") return redirect("hc-channels") channel = Channel(kind="pd", project=request.project) channel.user = request.project.owner channel.value = json.dumps({ "service_key": request.GET.get("service_key"), "account": request.GET.get("account"), }) channel.save() channel.assign_all_checks() messages.success(request, "The PagerDuty integration has been added!") return redirect("hc-channels") state = _prepare_state(request, "pd") callback = settings.SITE_ROOT + reverse("hc-add-pdc-state", args=[state]) connect_url = "https://connect.pagerduty.com/connect?" + urlencode( { "vendor": settings.PD_VENDOR_KEY, "callback": callback }) ctx = { "page": "channels", "project": request.project, "connect_url": connect_url } return render(request, "integrations/add_pdc.html", ctx)
class SwitchChannelTestCase(BaseTestCase): def setUp(self): super(SwitchChannelTestCase, self).setUp() self.check = Check(user=self.alice) self.check.save() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() self.url = "/checks/%s/channels/%s/enabled" % (self.check.code, self.channel.code) def test_it_enables(self): self.client.login(username="******", password="******") self.client.post(self.url, {"state": "on"}) self.assertTrue(self.channel in self.check.channel_set.all()) def test_it_disables(self): self.check.channel_set.add(self.channel) self.client.login(username="******", password="******") self.client.post(self.url, {"state": "off"}) self.assertFalse(self.channel in self.check.channel_set.all()) def test_it_checks_ownership(self): self.client.login(username="******", password="******") r = self.client.post(self.url, {"state": "on"}) self.assertEqual(r.status_code, 403) def test_it_checks_channels_ownership(self): cc = Check(user=self.charlie) cc.save() # Charlie will try to assign Alice's channel to his check: self.url = "/checks/%s/channels/%s/enabled" % (cc.code, self.channel.code) self.client.login(username="******", password="******") r = self.client.post(self.url, {"state": "on"}) self.assertEqual(r.status_code, 403)
def add_apprise(request): if not settings.APPRISE_ENABLED: raise Http404("apprise integration is not available") if request.method == "POST": form = AddAppriseForm(request.POST) if form.is_valid(): channel = Channel(project=request.project, kind="apprise") channel.value = form.cleaned_data["url"] channel.save() channel.assign_all_checks() messages.success(request, "The Apprise integration has been added!") return redirect("hc-channels") else: form = AddAppriseForm() ctx = {"page": "channels", "project": request.project, "form": form} return render(request, "integrations/add_apprise.html", ctx)
class NotifyPushbulletTestCase(BaseTestCase): def setUp(self): super().setUp() self.check = Check(project=self.project) self.check.name = "Foo" self.check.status = "up" self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(project=self.project) self.channel.kind = "pushbullet" self.channel.value = "fake-token" self.channel.save() self.channel.checks.add(self.check) @patch("hc.api.transports.requests.request") def test_it_works(self, mock_post): mock_post.return_value.status_code = 200 self.channel.notify(self.check) assert Notification.objects.count() == 1 _, kwargs = mock_post.call_args self.assertEqual(kwargs["json"]["type"], "note") self.assertEqual( kwargs["json"]["body"], 'The check "Foo" received a ping and is now UP.' ) self.assertEqual(kwargs["headers"]["Access-Token"], "fake-token") @patch("hc.api.transports.requests.request") def test_it_escapes_body(self, mock_post): mock_post.return_value.status_code = 200 self.check.name = "Foo & Bar" self.check.save() self.channel.notify(self.check) _, kwargs = mock_post.call_args self.assertEqual( kwargs["json"]["body"], 'The check "Foo & Bar" received a ping and is now UP.', )
def add_webhook(request): if request.method == "POST": form = AddWebhookForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="webhook") channel.value = form.get_value() channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddWebhookForm() ctx = { "page": "channels", "form": form, "now": timezone.now().replace(microsecond=0).isoformat() } return render(request, "integrations/add_webhook.html", ctx)
def add_pushbullet(request): if settings.PUSHBULLET_CLIENT_ID is None: raise Http404("pushbullet integration is not available") if "code" in request.GET: code = request.GET.get("code", "") if len(code) < 8: return HttpResponseBadRequest() result = requests.post("https://api.pushbullet.com/oauth2/token", { "client_id": settings.PUSHBULLET_CLIENT_ID, "client_secret": settings.PUSHBULLET_CLIENT_SECRET, "code": code, "grant_type": "authorization_code" }) doc = result.json() if "access_token" in doc: channel = Channel(kind="pushbullet") channel.user = request.team.user channel.value = doc["access_token"] channel.save() channel.assign_all_checks() messages.success(request, "The Pushbullet integration has been added!") else: messages.warning(request, "Something went wrong") return redirect("hc-channels") redirect_uri = settings.SITE_ROOT + reverse("hc-add-pushbullet") authorize_url = "https://www.pushbullet.com/authorize?" + urlencode({ "client_id": settings.PUSHBULLET_CLIENT_ID, "redirect_uri": redirect_uri, "response_type": "code" }) ctx = { "page": "channels", "authorize_url": authorize_url } return render(request, "integrations/add_pushbullet.html", ctx)
def add_email(request, code): project = _get_project_for_user(request, code) if request.method == "POST": form = AddEmailForm(request.POST) if form.is_valid(): channel = Channel(project=project, kind="email") channel.value = json.dumps( { "value": form.cleaned_data["value"], "up": form.cleaned_data["up"], "down": form.cleaned_data["down"], } ) channel.save() channel.assign_all_checks() is_own_email = form.cleaned_data["value"] == request.user.email if is_own_email or not settings.EMAIL_USE_VERIFICATION: # If user is subscribing *their own* address # we can skip the verification step. # Additionally, in self-hosted setting, administator has the # option to disable the email verification step altogether. channel.email_verified = True channel.save() else: channel.send_verify_link() return redirect("hc-p-channels", project.code) else: form = AddEmailForm() ctx = { "page": "channels", "project": project, "use_verification": settings.EMAIL_USE_VERIFICATION, "form": form, } return render(request, "integrations/add_email.html", ctx)
class UnsubscribeEmailTestCase(BaseTestCase): def setUp(self): super(UnsubscribeEmailTestCase, self).setUp() self.channel = Channel(project=self.project, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_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) q = Channel.objects.filter(code=self.channel.code) self.assertEqual(q.count(), 0) def test_it_checks_token(self): url = "/integrations/%s/unsub/faketoken/" % self.channel.code r = self.client.get(url) self.assertContains(r, "link you just used is incorrect", status_code=200) def test_it_checks_channel_kind(self): self.channel.kind = "webhook" self.channel.save() token = self.channel.make_token() url = "/integrations/%s/unsub/%s/" % (self.channel.code, token) r = self.client.get(url) self.assertEqual(r.status_code, 400) def test_it_serves_confirmation_form(self): token = self.channel.make_token() url = "/integrations/%s/unsub/%s/" % (self.channel.code, token) r = self.client.get(url) self.assertContains(r, "Please press the button below")
class NotifyLineTestCase(BaseTestCase): def setUp(self): super().setUp() self.check = Check(project=self.project) self.check.name = "Foo" self.check.status = "down" self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(project=self.project) self.channel.kind = "linenotify" self.channel.value = "fake-token" self.channel.save() self.channel.checks.add(self.check) @patch("hc.api.transports.requests.request") def test_it_works(self, mock_post): mock_post.return_value.status_code = 200 self.channel.notify(self.check) assert Notification.objects.count() == 1 args, kwargs = mock_post.call_args headers = kwargs["headers"] params = kwargs["params"] self.assertEqual(headers["Authorization"], "Bearer fake-token") self.assertIn("""The check "Foo" is DOWN""", params["message"]) @patch("hc.api.transports.requests.request") def test_it_does_not_escape_message(self, mock_post): mock_post.return_value.status_code = 200 self.check.name = "Foo & Bar" self.check.status = "up" self.check.save() self.channel.notify(self.check) args, kwargs = mock_post.call_args params = kwargs["params"] self.assertEqual(params["message"], 'The check "Foo & Bar" is now UP.')
class ChannelChecksTestCase(BaseTestCase): def setUp(self): super().setUp() self.channel = Channel(project=self.project, kind="email") self.channel.value = "*****@*****.**" self.channel.save() Check.objects.create(project=self.project, name="Database Backups") def test_it_works(self): url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "Database Backups") self.assertContains(r, "Assign Checks to Integration", status_code=200) def test_team_access_works(self): url = "/integrations/%s/checks/" % self.channel.code # Logging in as bob, not alice. Bob has team access so this # should work. self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "Assign Checks to Integration", status_code=200) def test_it_checks_owner(self): # channel does not belong to mallory so this should come back # with 403 Forbidden: url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertEqual(r.status_code, 404) def test_missing_channel(self): # Valid UUID but there is no channel for it: url = "/integrations/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/checks/" self.client.login(username="******", password="******") r = self.client.get(url) self.assertEqual(r.status_code, 404)
class NotifyMattermostTestCase(BaseTestCase): def setUp(self): super().setUp() self.check = Check(project=self.project) self.check.status = "down" self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(project=self.project) self.channel.kind = "mattermost" self.channel.value = "123" self.channel.save() self.channel.checks.add(self.check) @override_settings(MATTERMOST_ENABLED=False) def test_it_requires_mattermost_enabled(self): self.channel.notify(self.check) n = Notification.objects.get() self.assertEqual(n.error, "Mattermost notifications are not enabled.")
class NotifyTestCase(BaseTestCase): def _setup_data(self, value, status="down", email_verified=True): self.check = Check(project=self.project) self.check.status = status self.check.last_ping = now() - td(minutes=61) self.check.save() self.channel = Channel(project=self.project) self.channel.kind = "apprise" self.channel.value = value self.channel.email_verified = email_verified self.channel.save() self.channel.checks.add(self.check) @patch("apprise.Apprise") @override_settings(APPRISE_ENABLED=True) def test_apprise_enabled(self, mock_apprise): self._setup_data("123") mock_aobj = Mock() mock_aobj.add.return_value = True mock_aobj.notify.return_value = True mock_apprise.return_value = mock_aobj self.channel.notify(self.check) self.assertEqual(Notification.objects.count(), 1) self.check.status = "up" self.assertEqual(Notification.objects.count(), 1) @patch("apprise.Apprise") @override_settings(APPRISE_ENABLED=False) def test_apprise_disabled(self, mock_apprise): self._setup_data("123") mock_aobj = Mock() mock_aobj.add.return_value = True mock_aobj.notify.return_value = True mock_apprise.return_value = mock_aobj self.channel.notify(self.check) self.assertEqual(Notification.objects.count(), 1)
def add_sms(request): if settings.TWILIO_AUTH is None: raise Http404("sms integration is not available") if request.method == "POST": form = AddSmsForm(request.POST) if form.is_valid(): channel = Channel(user=request.team.user, kind="sms") channel.value = form.cleaned_data["value"] channel.save() channel.assign_all_checks() return redirect("hc-channels") else: form = AddSmsForm() ctx = { "page": "channels", "form": form, "profile": request.team } return render(request, "integrations/add_sms.html", ctx)
class ChannelChecksTestCase(BaseTestCase): def setUp(self): super(ChannelChecksTestCase, self).setUp() self.channel = Channel(user=self.alice, kind="email") self.channel.value = "*****@*****.**" self.channel.save() def test_it_works(self): url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "Assign Checks to Channel", status_code=200) def test_team_access_works(self): url = "/integrations/%s/checks/" % self.channel.code # Logging in as bob, not alice. Bob has team access so this # should work. self.client.login(username="******", password="******") r = self.client.get(url) self.assertContains(r, "Assign Checks to Channel", status_code=200) def test_it_checks_owner(self): # channel does not belong to mallory so this should come back # with 403 Forbidden: url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="******", password="******") r = self.client.get(url) assert r.status_code == 403 def test_missing_channel(self): # Valid UUID but there is no channel for it: url = "/integrations/6837d6ec-fc08-4da5-a67f-08a9ed1ccf62/checks/" self.client.login(username="******", password="******") r = self.client.get(url) assert r.status_code == 404