diff --git a/hc/front/tests/test_copy.py b/hc/front/tests/test_copy.py index 7d27e8ca..602dc45d 100644 --- a/hc/front/tests/test_copy.py +++ b/hc/front/tests/test_copy.py @@ -33,3 +33,11 @@ class CopyCheckTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") r = self.client.post(self.copy_url) self.assertEqual(r.status_code, 400) + + def test_it_requires_rw_access(self): + self.bobs_membership.rw = False + self.bobs_membership.save() + + self.client.login(username="bob@example.org", password="password") + r = self.client.post(self.copy_url) + self.assertEqual(r.status_code, 403) diff --git a/hc/front/tests/test_details.py b/hc/front/tests/test_details.py index ef5592bf..938f555a 100644 --- a/hc/front/tests/test_details.py +++ b/hc/front/tests/test_details.py @@ -58,3 +58,6 @@ class DetailsTestCase(BaseTestCase): self.assertNotContains(r, "Filtering Rules") self.assertNotContains(r, "pause-btn") self.assertNotContains(r, "Change Schedule") + self.assertNotContains(r, "Create a Copy…") + self.assertNotContains(r, "transfer-btn") + self.assertNotContains(r, "details-remove-check") diff --git a/hc/front/tests/test_remove_check.py b/hc/front/tests/test_remove_check.py index 3fd113c0..6e2b8ace 100644 --- a/hc/front/tests/test_remove_check.py +++ b/hc/front/tests/test_remove_check.py @@ -51,3 +51,11 @@ class RemoveCheckTestCase(BaseTestCase): self.client.login(username="bob@example.org", password="password") r = self.client.post(self.remove_url) self.assertRedirects(r, self.redirect_url) + + def test_it_requires_rw_access(self): + self.bobs_membership.rw = False + self.bobs_membership.save() + + self.client.login(username="bob@example.org", password="password") + r = self.client.post(self.remove_url) + self.assertEqual(r.status_code, 403) diff --git a/hc/front/tests/test_transfer.py b/hc/front/tests/test_transfer.py index f874f49e..5935816e 100644 --- a/hc/front/tests/test_transfer.py +++ b/hc/front/tests/test_transfer.py @@ -63,3 +63,13 @@ class TransferTestCase(BaseTestCase): payload = {"project": self.charlies_project.code} r = self.client.post(self.url, payload) self.assertEqual(r.status_code, 404) + + def test_it_requires_rw_access(self): + self.bobs_membership.rw = False + self.bobs_membership.save() + + payload = {"project": self.project.code} + + self.client.login(username="bob@example.org", password="password") + r = self.client.post(self.url, payload) + self.assertEqual(r.status_code, 403) diff --git a/hc/front/views.py b/hc/front/views.py index 130bdfe1..c0cdd14e 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -500,6 +500,9 @@ def resume(request, code): @login_required def remove_check(request, code): check, rw = _get_check_for_user(request, code) + if not rw: + return HttpResponseForbidden() + project = check.project check.delete() return redirect("hc-checks", project.code) @@ -579,6 +582,8 @@ def details(request, code): @login_required def transfer(request, code): check, rw = _get_check_for_user(request, code) + if not rw: + return HttpResponseForbidden() if request.method == "POST": target_project, rw = _get_project_for_user(request, request.POST["project"]) @@ -600,6 +605,8 @@ def transfer(request, code): @login_required def copy(request, code): check, rw = _get_check_for_user(request, code) + if not rw: + return HttpResponseForbidden() if check.project.num_checks_available() <= 0: return HttpResponseBadRequest() diff --git a/templates/front/details.html b/templates/front/details.html index 90afb480..b8e247d0 100644 --- a/templates/front/details.html +++ b/templates/front/details.html @@ -232,6 +232,7 @@ {% endif %} + {% if rw %}
Copy, Transfer, or permanently remove this check.
@@ -239,7 +240,6 @@