diff --git a/hc/front/decorators.py b/hc/front/decorators.py new file mode 100644 index 00000000..944a1f6b --- /dev/null +++ b/hc/front/decorators.py @@ -0,0 +1,18 @@ +from functools import wraps + +from django.conf import settings +from django.http import HttpResponse + + +def require_setting(key): + def decorator(f): + @wraps(f) + def wrapper(request, *args, **kwds): + if not getattr(settings, key): + return HttpResponse(status=404) + + return f(request, *args, **kwds) + + return wrapper + + return decorator diff --git a/hc/front/tests/test_add_discord_complete.py b/hc/front/tests/test_add_discord_complete.py index fd80987f..68f8c013 100644 --- a/hc/front/tests/test_add_discord_complete.py +++ b/hc/front/tests/test_add_discord_complete.py @@ -68,3 +68,9 @@ class AddDiscordCompleteTestCase(BaseTestCase): # Session should now be clean self.assertFalse("add_discord" in self.client.session) + + @override_settings(DISCORD_CLIENT_ID=None) + def test_it_requires_client_id(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url + "?code=12345678&state=bar") + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_add_pushbullet_complete.py b/hc/front/tests/test_add_pushbullet_complete.py index bdf682ae..8214bff9 100644 --- a/hc/front/tests/test_add_pushbullet_complete.py +++ b/hc/front/tests/test_add_pushbullet_complete.py @@ -61,3 +61,11 @@ class AddPushbulletTestCase(BaseTestCase): # Session should now be clean self.assertFalse("add_pushbullet" in self.client.session) + + @override_settings(PUSHBULLET_CLIENT_ID=None) + def test_it_requires_client_id(self): + url = self.url + "?code=12345678&state=bar&project=%s" % self.project.code + + self.client.login(username="alice@example.org", password="password") + r = self.client.get(url) + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_add_slack_btn.py b/hc/front/tests/test_add_slack_btn.py index b28a6c4d..360d6e22 100644 --- a/hc/front/tests/test_add_slack_btn.py +++ b/hc/front/tests/test_add_slack_btn.py @@ -20,3 +20,9 @@ class AddSlackBtnTestCase(BaseTestCase): # There should now be a key in session self.assertTrue("add_slack" in self.client.session) + + @override_settings(SLACK_CLIENT_ID=None) + def test_it_requires_client_id(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_add_slack_complete.py b/hc/front/tests/test_add_slack_complete.py index 6b124eae..4c805873 100644 --- a/hc/front/tests/test_add_slack_complete.py +++ b/hc/front/tests/test_add_slack_complete.py @@ -67,3 +67,9 @@ class AddSlackCompleteTestCase(BaseTestCase): r = self.client.get(url, follow=True) self.assertRedirects(r, self.channels_url) self.assertContains(r, "something went wrong") + + @override_settings(SLACK_CLIENT_ID=None) + def test_it_requires_client_id(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get("/integrations/add_slack_btn/?code=12345678&state=foo") + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_add_slack_help.py b/hc/front/tests/test_add_slack_help.py index f9b64188..3d75ea77 100644 --- a/hc/front/tests/test_add_slack_help.py +++ b/hc/front/tests/test_add_slack_help.py @@ -7,3 +7,8 @@ class AddSlackHelpTestCase(BaseTestCase): def test_instructions_work(self): r = self.client.get("/integrations/add_slack/") self.assertContains(r, "Setup Guide", status_code=200) + + @override_settings(SLACK_CLIENT_ID=None) + def test_it_requires_client_id(self): + r = self.client.get("/integrations/add_slack/") + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_add_trello.py b/hc/front/tests/test_add_trello.py index 0a4ec436..2b90cddd 100644 --- a/hc/front/tests/test_add_trello.py +++ b/hc/front/tests/test_add_trello.py @@ -5,18 +5,17 @@ from hc.api.models import Channel from hc.test import BaseTestCase +@override_settings(TRELLO_APP_KEY="foo") class AddTrelloTestCase(BaseTestCase): def setUp(self): super(AddTrelloTestCase, self).setUp() self.url = "/projects/%s/add_trello/" % self.project.code - @override_settings(TRELLO_APP_KEY="foo") def test_instructions_work(self): self.client.login(username="alice@example.org", password="password") r = self.client.get(self.url) self.assertContains(r, "Trello") - @override_settings(TRELLO_APP_KEY="foo") def test_it_works(self): form = { "settings": json.dumps( @@ -37,3 +36,9 @@ class AddTrelloTestCase(BaseTestCase): self.assertEqual(c.kind, "trello") self.assertEqual(c.trello_token, "fake-token") self.assertEqual(c.project, self.project) + + @override_settings(TRELLO_APP_KEY=None) + def test_it_requires_trello_app_key(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_trello_settings.py b/hc/front/tests/test_trello_settings.py new file mode 100644 index 00000000..cc7cd0ad --- /dev/null +++ b/hc/front/tests/test_trello_settings.py @@ -0,0 +1,26 @@ +from mock import patch + +from django.test.utils import override_settings +from hc.test import BaseTestCase + + +@override_settings(TRELLO_APP_KEY="foo") +class AddTrelloTestCase(BaseTestCase): + url = "/integrations/add_trello/settings/" + + @patch("hc.front.views.requests.get") + def test_it_works(self, mock_get): + mock_get.return_value.json.return_value = [ + {"name": "My Board", "lists": [{"name": "Alerts"}]} + ] + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url) + self.assertContains(r, "Please select the Trello list") + self.assertContains(r, "Alerts") + + @override_settings(TRELLO_APP_KEY=None) + def test_it_requires_trello_app_key(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 404) diff --git a/hc/front/views.py b/hc/front/views.py index 81a505bb..3bd045e3 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -34,6 +34,7 @@ from hc.api.models import ( Notification, ) from hc.api.transports import Telegram +from hc.front.decorators import require_setting from hc.front.forms import ( AddAppriseForm, AddEmailForm, @@ -878,11 +879,9 @@ def add_webhook(request, code): return render(request, "integrations/add_webhook.html", ctx) +@require_setting("SHELL_ENABLED") @login_required def add_shell(request, code): - if not settings.SHELL_ENABLED: - raise Http404("shell integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": form = AddShellForm(request.POST) @@ -925,19 +924,15 @@ def add_pd(request, code): return render(request, "integrations/add_pd.html", ctx) +@require_setting("PD_VENDOR_KEY") def add_pdc_help(request): - if settings.PD_VENDOR_KEY is None: - raise Http404("pagerduty integration is not available") - ctx = {"page": "channels"} return render(request, "integrations/add_pdc.html", ctx) +@require_setting("PD_VENDOR_KEY") @login_required def add_pdc(request, code): - if settings.PD_VENDOR_KEY is None: - raise Http404("pagerduty integration is not available") - project = _get_project_for_user(request, code) state = token_urlsafe() @@ -953,11 +948,9 @@ def add_pdc(request, code): return render(request, "integrations/add_pdc.html", ctx) +@require_setting("PD_VENDOR_KEY") @login_required def add_pdc_complete(request, code, state): - if settings.PD_VENDOR_KEY is None: - raise Http404("pagerduty integration is not available") - if "pd" not in request.session: return HttpResponseBadRequest() @@ -1048,19 +1041,15 @@ def add_slack(request, code): return render(request, "integrations/add_slack.html", ctx) +@require_setting("SLACK_CLIENT_ID") def add_slack_help(request): - if not settings.SLACK_CLIENT_ID: - raise Http404("slack integration is not available") - ctx = {"page": "channels"} return render(request, "integrations/add_slack_btn.html", ctx) +@require_setting("SLACK_CLIENT_ID") @login_required def add_slack_btn(request, code): - if not settings.SLACK_CLIENT_ID: - raise Http404("slack integration is not available") - project = _get_project_for_user(request, code) state = token_urlsafe() @@ -1082,11 +1071,9 @@ def add_slack_btn(request, code): return render(request, "integrations/add_slack_btn.html", ctx) +@require_setting("SLACK_CLIENT_ID") @login_required def add_slack_complete(request): - if not settings.SLACK_CLIENT_ID: - raise Http404("slack integration is not available") - if "add_slack" not in request.session: return HttpResponseForbidden() @@ -1142,11 +1129,9 @@ def add_mattermost(request, code): return render(request, "integrations/add_mattermost.html", ctx) +@require_setting("PUSHBULLET_CLIENT_ID") @login_required def add_pushbullet(request, code): - if settings.PUSHBULLET_CLIENT_ID is None: - raise Http404("pushbullet integration is not available") - project = _get_project_for_user(request, code) redirect_uri = settings.SITE_ROOT + reverse("hc-add-pushbullet-complete") @@ -1170,11 +1155,9 @@ def add_pushbullet(request, code): return render(request, "integrations/add_pushbullet.html", ctx) +@require_setting("PUSHBULLET_CLIENT_ID") @login_required def add_pushbullet_complete(request): - if settings.PUSHBULLET_CLIENT_ID is None: - raise Http404("pushbullet integration is not available") - if "add_pushbullet" not in request.session: return HttpResponseForbidden() @@ -1211,11 +1194,9 @@ def add_pushbullet_complete(request): return redirect("hc-p-channels", project.code) +@require_setting("DISCORD_CLIENT_ID") @login_required def add_discord(request, code): - if settings.DISCORD_CLIENT_ID is None: - raise Http404("discord integration is not available") - project = _get_project_for_user(request, code) redirect_uri = settings.SITE_ROOT + reverse("hc-add-discord-complete") state = token_urlsafe() @@ -1235,11 +1216,9 @@ def add_discord(request, code): return render(request, "integrations/add_discord.html", ctx) +@require_setting("DISCORD_CLIENT_ID") @login_required def add_discord_complete(request): - if settings.DISCORD_CLIENT_ID is None: - raise Http404("discord integration is not available") - if "add_discord" not in request.session: return HttpResponseForbidden() @@ -1283,11 +1262,9 @@ def add_pushover_help(request): return render(request, "integrations/add_pushover_help.html", ctx) +@require_setting("PUSHOVER_API_TOKEN") @login_required def add_pushover(request, code): - if settings.PUSHOVER_API_TOKEN is None: - raise Http404("pushover integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": @@ -1451,11 +1428,9 @@ def add_telegram(request): return render(request, "integrations/add_telegram.html", ctx) +@require_setting("TWILIO_AUTH") @login_required def add_sms(request, code): - if settings.TWILIO_AUTH is None: - raise Http404("sms integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": form = AddSmsForm(request.POST) @@ -1479,11 +1454,9 @@ def add_sms(request, code): return render(request, "integrations/add_sms.html", ctx) +@require_setting("TWILIO_USE_WHATSAPP") @login_required def add_whatsapp(request, code): - if not settings.TWILIO_USE_WHATSAPP: - raise Http404("whatsapp integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": form = AddSmsForm(request.POST) @@ -1513,11 +1486,9 @@ def add_whatsapp(request, code): return render(request, "integrations/add_whatsapp.html", ctx) +@require_setting("TRELLO_APP_KEY") @login_required def add_trello(request, code): - if settings.TRELLO_APP_KEY is None: - raise Http404("trello integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": channel = Channel(project=project, kind="trello") @@ -1548,11 +1519,9 @@ def add_trello(request, code): return render(request, "integrations/add_trello.html", ctx) +@require_setting("MATRIX_ACCESS_TOKEN") @login_required def add_matrix(request, code): - if settings.MATRIX_ACCESS_TOKEN is None: - raise Http404("matrix integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": form = AddMatrixForm(request.POST) @@ -1582,11 +1551,9 @@ def add_matrix(request, code): return render(request, "integrations/add_matrix.html", ctx) +@require_setting("APPRISE_ENABLED") @login_required def add_apprise(request, code): - if not settings.APPRISE_ENABLED: - raise Http404("apprise integration is not available") - project = _get_project_for_user(request, code) if request.method == "POST": form = AddAppriseForm(request.POST) @@ -1605,6 +1572,7 @@ def add_apprise(request, code): return render(request, "integrations/add_apprise.html", ctx) +@require_setting("TRELLO_APP_KEY") @login_required @require_POST def trello_settings(request):