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
-
+ {% if can_add_customer_user %}
+
+ {% 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)