From 8ec42858f92bd84be1ca4278ce63c63b7f37f0aa Mon Sep 17 00:00:00 2001
From: Matt Westcott <matt@west.co.tt>
Date: Mon, 16 Oct 2023 19:59:43 +0100
Subject: [PATCH] Redirect away from user bulk actions when user has no
 permissions on users

---
 .../tests/test_bulk_actions/test_bulk_delete.py     | 13 +++++++++++++
 .../users/views/bulk_actions/user_bulk_action.py    | 10 ++++++++--
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/wagtail/users/tests/test_bulk_actions/test_bulk_delete.py b/wagtail/users/tests/test_bulk_actions/test_bulk_delete.py
index d508fd8af7..aa911dfbd6 100644
--- a/wagtail/users/tests/test_bulk_actions/test_bulk_delete.py
+++ b/wagtail/users/tests/test_bulk_actions/test_bulk_delete.py
@@ -1,4 +1,5 @@
 from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Permission
 from django.http import HttpRequest, HttpResponse
 from django.test import TestCase
 from django.urls import reverse
@@ -51,6 +52,18 @@ class TestUserDeleteView(WagtailTestUtils, TestCase):
             response, "wagtailusers/bulk_actions/confirm_bulk_delete.html"
         )
 
+    def test_user_permissions_required(self):
+        # Log in with a user that doesn't have permission to delete users
+        user = self.create_user(username="editor", password="password")
+        admin_permission = Permission.objects.get(
+            content_type__app_label="wagtailadmin", codename="access_admin"
+        )
+        user.user_permissions.add(admin_permission)
+        self.login(username="editor", password="password")
+
+        response = self.client.get(self.url)
+        self.assertRedirects(response, "/admin/")
+
     def test_bulk_delete(self):
         response = self.client.post(self.url)
 
diff --git a/wagtail/users/views/bulk_actions/user_bulk_action.py b/wagtail/users/views/bulk_actions/user_bulk_action.py
index 76d05db58d..1cc6aa4dc6 100644
--- a/wagtail/users/views/bulk_actions/user_bulk_action.py
+++ b/wagtail/users/views/bulk_actions/user_bulk_action.py
@@ -1,11 +1,17 @@
 from django.contrib.auth import get_user_model
 
 from wagtail.admin.views.bulk_action import BulkAction
+from wagtail.admin.views.generic.permissions import PermissionCheckedMixin
+from wagtail.permission_policies import ModelPermissionPolicy
 from wagtail.users.views.users import get_users_filter_query
 
+User = get_user_model()
 
-class UserBulkAction(BulkAction):
-    models = [get_user_model()]
+
+class UserBulkAction(PermissionCheckedMixin, BulkAction):
+    models = [User]
+    permission_policy = ModelPermissionPolicy(User)
+    any_permission_required = ["add", "change", "delete"]
 
     def get_all_objects_in_listing_query(self, parent_id):
         listing_objects = self.model.objects.all().values_list("pk", flat=True)