diff --git a/weblate_web/crm/templates/payments/customer_detail.html b/weblate_web/crm/templates/payments/customer_detail.html index 86b25cdca..fefc27e33 100644 --- a/weblate_web/crm/templates/payments/customer_detail.html +++ b/weblate_web/crm/templates/payments/customer_detail.html @@ -52,12 +52,14 @@

Actions

-
-

{% translate "Add user to customer" %}

- {% csrf_token %} - {{ customer_user_form }} - -
+ {% if can_add_customer_user %} +
+

{% translate "Add user to customer" %}

+ {% csrf_token %} + {{ customer_user_form }} + +
+ {% endif %}

Services

{% include "weblate_web/service_list_content.html" with object_list=services %} diff --git a/weblate_web/crm/tests.py b/weblate_web/crm/tests.py index 5b375e5e9..a8ed14044 100644 --- a/weblate_web/crm/tests.py +++ b/weblate_web/crm/tests.py @@ -297,6 +297,50 @@ def test_customer_detail_renders_manual_note_form(self): self.assertContains(response, 'name="note"') self.assertContains(response, "Add note") + def test_customer_detail_hides_add_customer_user_for_readonly_staff(self): + customer = self.create_customer() + readonly_user = User.objects.create_user( + username="readonly", email="readonly@example.com", is_staff=True + ) + readonly_user.user_permissions.add( + Permission.objects.get( + codename="view_customer", + content_type__app_label="payments", + content_type__model="customer", + ) + ) + self.client.force_login(readonly_user) + + response = self.client.get(customer.get_absolute_url()) + + self.assertEqual(response.status_code, 200) + self.assertNotContains(response, 'name="add_customer_user"') + + def test_customer_detail_rejects_add_customer_user_for_readonly_staff(self): + customer = self.create_customer() + readonly_user = User.objects.create_user( + username="readonly", email="readonly@example.com", is_staff=True + ) + readonly_user.user_permissions.add( + Permission.objects.get( + codename="view_customer", + content_type__app_label="payments", + content_type__model="customer", + ) + ) + self.client.force_login(readonly_user) + + response = self.client.post( + customer.get_absolute_url(), + { + "add_customer_user": "1", + "email": "new@example.com", + "full_name": "New User", + }, + ) + + self.assertEqual(response.status_code, 403) + def test_customer_detail_adds_manual_note(self): customer = self.create_customer() note = ( diff --git a/weblate_web/crm/views.py b/weblate_web/crm/views.py index c699bec52..56cb01727 100644 --- a/weblate_web/crm/views.py +++ b/weblate_web/crm/views.py @@ -522,6 +522,7 @@ def get_queryset(self) -> CustomerQuerySet: class CustomerDetailView(CRMMixin, DetailView[Customer]): # type: ignore[misc] model = Customer permission = "payments.view_customer" + add_customer_user_permission = "payments.change_customer" title = "Customer detail" def get_context_data(self, **kwargs): @@ -545,10 +546,20 @@ def get_context_data(self, **kwargs): context["customer_user_form"] = CustomerUserForm( self.request.POST if add_customer_user else None ) + context["can_add_customer_user"] = self.request.user.has_perm( + self.add_customer_user_permission + ) context["services"] = self.object.service_set.customer_services() context["donations"] = self.object.service_set.donations() return context + @classmethod + def check_add_customer_user_permission( + cls, request: AuthenticatedHttpRequest + ) -> None: + if not request.user.has_perm(cls.add_customer_user_permission): + raise PermissionDenied + def get_title(self) -> str: return self.object.verbose_name @@ -642,6 +653,7 @@ def link_customer_hosted_user( return True def add_customer_user(self, request, customer: Customer, *args, **kwargs): + self.check_add_customer_user_permission(request) form = CustomerUserForm(request.POST) if not form.is_valid(): show_form_errors(request, form)