diff --git a/hc/front/tests/test_channel_checks.py b/hc/front/tests/test_channel_checks.py index 84698ede..534a0f3b 100644 --- a/hc/front/tests/test_channel_checks.py +++ b/hc/front/tests/test_channel_checks.py @@ -31,7 +31,7 @@ class ChannelChecksTestCase(BaseTestCase): url = "/integrations/%s/checks/" % self.channel.code self.client.login(username="charlie@example.org", password="password") r = self.client.get(url) - assert r.status_code == 403 + self.assertEqual(r.status_code, 404) def test_missing_channel(self): # Valid UUID but there is no channel for it: @@ -39,4 +39,4 @@ class ChannelChecksTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.get(url) - assert r.status_code == 404 + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_remove_channel.py b/hc/front/tests/test_remove_channel.py index 50ca2063..1f02b7ab 100644 --- a/hc/front/tests/test_remove_channel.py +++ b/hc/front/tests/test_remove_channel.py @@ -14,7 +14,7 @@ class RemoveChannelTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(url) - self.assertRedirects(r, "/integrations/") + self.assertRedirects(r, self.channels_url) assert Channel.objects.count() == 0 @@ -37,7 +37,7 @@ class RemoveChannelTestCase(BaseTestCase): self.client.login(username="charlie@example.org", password="password") r = self.client.post(url) - assert r.status_code == 403 + self.assertEqual(r.status_code, 404) def test_it_handles_missing_uuid(self): # Valid UUID but there is no channel for it: @@ -45,7 +45,7 @@ class RemoveChannelTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(url) - assert r.status_code == 302 + self.assertEqual(r.status_code, 404) def test_it_rejects_get(self): url = "/integrations/%s/remove/" % self.channel.code diff --git a/hc/front/tests/test_send_test_notification.py b/hc/front/tests/test_send_test_notification.py index 0b95aca1..3732dd4b 100644 --- a/hc/front/tests/test_send_test_notification.py +++ b/hc/front/tests/test_send_test_notification.py @@ -20,7 +20,7 @@ class SendTestNotificationTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, {}, follow=True) - self.assertRedirects(r, "/integrations/") + self.assertRedirects(r, self.channels_url) self.assertContains(r, "Test notification sent!") # And email should have been sent @@ -52,7 +52,7 @@ class SendTestNotificationTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, {}, follow=True) - self.assertRedirects(r, "/integrations/") + self.assertRedirects(r, self.channels_url) self.assertContains(r, "Test notification sent!") def test_it_handles_webhooks_with_no_urls(self): @@ -73,5 +73,10 @@ class SendTestNotificationTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, {}, follow=True) - self.assertRedirects(r, "/integrations/") + self.assertRedirects(r, self.channels_url) self.assertContains(r, "Could not send a test notification") + + def test_it_checks_channel_ownership(self): + self.client.login(username="charlie@example.org", password="password") + r = self.client.post(self.url, {}, follow=True) + self.assertEqual(r.status_code, 404) diff --git a/hc/front/tests/test_update_channel.py b/hc/front/tests/test_update_channel.py index 5cc20798..168019c7 100644 --- a/hc/front/tests/test_update_channel.py +++ b/hc/front/tests/test_update_channel.py @@ -16,8 +16,8 @@ class UpdateChannelTestCase(BaseTestCase): payload = {"channel": self.channel.code, "check-%s" % self.check.code: True} self.client.login(username="alice@example.org", password="password") - r = self.client.post("/integrations/", data=payload) - self.assertRedirects(r, "/integrations/") + r = self.client.post(self.channels_url, data=payload) + self.assertRedirects(r, self.channels_url) channel = Channel.objects.get(code=self.channel.code) checks = channel.checks.all() @@ -30,19 +30,19 @@ class UpdateChannelTestCase(BaseTestCase): # Logging in as bob, not alice. Bob has team access so this # should work. self.client.login(username="bob@example.org", password="password") - r = self.client.post("/integrations/", data=payload, follow=True) + r = self.client.post(self.channels_url, data=payload, follow=True) self.assertEqual(r.status_code, 200) def test_it_checks_channel_user(self): payload = {"channel": self.channel.code} self.client.login(username="charlie@example.org", password="password") - r = self.client.post("/integrations/", data=payload) + r = self.client.post(self.channels_url, data=payload) # self.channel does not belong to charlie, this should fail-- - assert r.status_code == 403 + self.assertEqual(r.status_code, 404) - def test_it_checks_check_user(self): + def test_it_checks_check_owner(self): charlies_project = Project.objects.create(owner=self.charlie) charlies_channel = Channel(project=charlies_project, kind="email") charlies_channel.email = "charlie@example.org" @@ -50,18 +50,18 @@ class UpdateChannelTestCase(BaseTestCase): payload = {"channel": charlies_channel.code, "check-%s" % self.check.code: True} self.client.login(username="charlie@example.org", password="password") - r = self.client.post("/integrations/", data=payload) + r = self.client.post(self.channels_url, data=payload) # mc belongs to charlie but self.check does not-- - assert r.status_code == 403 + self.assertEqual(r.status_code, 404) def test_it_handles_missing_channel(self): # Correct UUID but there is no channel for it: payload = {"channel": "6837d6ec-fc08-4da5-a67f-08a9ed1ccf62"} self.client.login(username="alice@example.org", password="password") - r = self.client.post("/integrations/", data=payload) - assert r.status_code == 400 + r = self.client.post(self.channels_url, data=payload) + self.assertEqual(r.status_code, 400) def test_it_handles_missing_check(self): # check- key has a correct UUID but there's no check object for it @@ -71,5 +71,5 @@ class UpdateChannelTestCase(BaseTestCase): } self.client.login(username="alice@example.org", password="password") - r = self.client.post("/integrations/", data=payload) - assert r.status_code == 400 + r = self.client.post(self.channels_url, data=payload) + self.assertEqual(r.status_code, 400) diff --git a/hc/front/tests/test_update_channel_name.py b/hc/front/tests/test_update_channel_name.py index 7d7bc2f3..4d6c92ff 100644 --- a/hc/front/tests/test_update_channel_name.py +++ b/hc/front/tests/test_update_channel_name.py @@ -15,7 +15,7 @@ class UpdateChannelNameTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.url, data=payload) - self.assertRedirects(r, "/integrations/") + self.assertRedirects(r, self.channels_url) self.channel.refresh_from_db() self.assertEqual(self.channel.name, "My work email") @@ -36,7 +36,7 @@ class UpdateChannelNameTestCase(BaseTestCase): self.client.login(username="charlie@example.org", password="password") r = self.client.post(self.url, data=payload) - self.assertEqual(r.status_code, 403) + self.assertEqual(r.status_code, 404) def test_it_handles_missing_uuid(self): # Valid UUID but there is no check for it: diff --git a/hc/front/views.py b/hc/front/views.py index e161c71a..404a4618 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -95,10 +95,10 @@ def _get_check_for_user(request, code): if not request.user.is_authenticated: raise Http404("not found") - if request.user.is_superuser: - q = Check.objects - else: - q = request.profile.checks_from_all_projects() + q = Check.objects + if not request.user.is_superuser: + project_ids = request.profile.projects().values("id") + q = q.filter(project_id__in=project_ids) try: return q.get(code=code) @@ -106,6 +106,23 @@ def _get_check_for_user(request, code): raise Http404("not found") +def _get_channel_for_user(request, code): + """ Return specified channel if current user has access to it. """ + + if not request.user.is_authenticated: + raise Http404("not found") + + q = Channel.objects + if not request.user.is_superuser: + project_ids = request.profile.projects().values("id") + q = q.filter(project_id__in=project_ids) + + try: + return q.get(code=code) + except Channel.DoesNotExist: + raise Http404("not found") + + def _get_project_for_user(request, project_code): """ Return true if current user has access to the specified account. """ @@ -662,7 +679,7 @@ def channels(request, code=None): new_checks.append(check) channel.checks.set(new_checks) - return redirect("hc-channels") + return redirect("hc-p-channels", project.code) channels = Channel.objects.filter(project=project) channels = channels.order_by("created") @@ -693,9 +710,7 @@ def channels(request, code=None): @login_required def channel_checks(request, code): - channel = get_object_or_404(Channel, code=code) - if channel.project_id != request.project.id: - return HttpResponseForbidden() + channel = _get_channel_for_user(request, code) assigned = set(channel.checks.values_list("code", flat=True).distinct()) checks = Check.objects.filter(project=request.project).order_by("created") @@ -708,16 +723,14 @@ def channel_checks(request, code): @require_POST @login_required def update_channel_name(request, code): - channel = get_object_or_404(Channel, code=code) - if channel.project_id != request.project.id: - return HttpResponseForbidden() + channel = _get_channel_for_user(request, code) form = ChannelNameForm(request.POST) if form.is_valid(): channel.name = form.cleaned_data["name"] channel.save() - return redirect("hc-channels") + return redirect("hc-p-channels", channel.project.code) def verify_email(request, code, token): @@ -768,9 +781,7 @@ def unsubscribe_email(request, code, signed_token): @require_POST @login_required def send_test_notification(request, code): - channel = get_object_or_404(Channel, code=code) - if channel.project_id != request.project.id: - return HttpResponseForbidden() + channel = _get_channel_for_user(request, code) dummy = Check(name="TEST", status="down") dummy.last_ping = timezone.now() - td(days=1) @@ -792,20 +803,17 @@ def send_test_notification(request, code): else: messages.success(request, "Test notification sent!") - return redirect("hc-channels") + return redirect("hc-p-channels", channel.project.code) @require_POST @login_required def remove_channel(request, code): - # user may refresh the page during POST and cause two deletion attempts - channel = Channel.objects.filter(code=code).first() - if channel: - if channel.project_id != request.project.id: - return HttpResponseForbidden() - channel.delete() + channel = _get_channel_for_user(request, code) + project = channel.project + channel.delete() - return redirect("hc-channels") + return redirect("hc-p-channels", project.code) @login_required