浏览代码

Prevent shadowing of permissions with the same codename action (#11667)

This can cause issues where e.g. stale permissions become the ones used
to render the checkboxes after a RenameModel operation (changing the
Python class name, not just the verbose_name).

A similar issue can also be triggered with custom permissions that share
the same codename action (first part of the codename before _) as the
built-in permissions.
Sage Abdullah 1 年之前
父节点
当前提交
3b87644e07

+ 1 - 0
CHANGELOG.txt

@@ -10,6 +10,7 @@ Changelog
  * Support creating images in .ico format (Jake Howard)
  * Add the ability to disable the usage of a shared password for enhanced security for the private pages and collections (documents) feature (Salvo Polizzi, Jake Howard)
  * Add system checks to ensure that `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT`, `WAGTAIL_TIME_FORMAT` are correctly configured (Rohit Sharma, Coen van der Kamp)
+ * Allow custom permissions with the same prefix as built-in permissions (Sage Abdullah)
  * Fix: Fix typo in `__str__` for MySQL search index (Jake Howard)
  * Fix: Ensure that unit tests correctly check for migrations in all core Wagtail apps (Matt Westcott)
  * Fix: Correctly handle `date` objects on `human_readable_date` template tag (Jhonatan Lopes)

+ 1 - 0
docs/releases/6.1.md

@@ -19,6 +19,7 @@ depth: 1
  * Support creating images in .ico format (Jake Howard)
  * Add the ability to disable the usage of a shared password for enhanced security for the [private pages](private_pages) and [collections (documents)](private_collections) feature (Salvo Polizzi, Jake Howard)
  * Add system checks to ensure that `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT`, `WAGTAIL_TIME_FORMAT` are [correctly configured](wagtail_date_time_formats) (Rohit Sharma, Coen van der Kamp)
+ * Allow custom permissions with the same prefix as built-in permissions (Sage Abdullah)
 
 
 ### Bug fixes

+ 2 - 2
docs/topics/permissions.md

@@ -63,8 +63,8 @@ Users are not allowed to move or delete the collection that is used to assign th
 
 See Django's documentation on [custom permissions](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions) for details on how to set permissions up.
 
-```{note}
-Custom permissions starting with `add_`, `change_`, or `delete_` are not currently supported in Wagtail as these will conflict with standard model permissions.
+```{versionadded} 6.1
+The ability to have custom permissions with codenames starting with `add_`, `change_`, or `delete_` was added.
 ```
 
 ## Displaying custom permissions in the admin

+ 6 - 1
wagtail/users/templatetags/wagtailusers_tags.py

@@ -135,7 +135,12 @@ def format_permissions(permission_bound_field):
             # identify the main categories of permission, and assign to
             # the relevant dict key, else bung in the 'custom_perms' list
             permission_action = perm.codename.split("_")[0]
-            if permission_action in main_permission_names:
+            is_known = (
+                permission_action in main_permission_names
+                and perm.codename == f"{permission_action}_{perm.content_type.model}"
+            )
+
+            if is_known:
                 if permission_action in extra_perms_exist:
                     extra_perms_exist[permission_action] = True
                 content_perms_dict[permission_action] = {

+ 46 - 0
wagtail/users/tests/test_admin_views.py

@@ -1533,6 +1533,13 @@ class TestGroupCreateView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
             | Q(codename__startswith="publish")
         ).delete()
 
+        # A custom permission that happens to also start with "change"
+        Permission.objects.filter(
+            codename="change_text",
+            content_type__app_label="tests",
+            content_type__model="custompermissionmodel",
+        ).delete()
+
         response = self.get()
 
         self.assertInHTML("Custom permissions", response.content.decode(), count=0)
@@ -1591,6 +1598,45 @@ class TestGroupCreateView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
         self.assertNotContains(response, "Cause chaos for advanced permission model")
         self.assertNotContains(response, "Manage custom permission model")
 
+    def test_permission_with_same_action(self):
+        """
+        https://github.com/wagtail/wagtail/issues/11650
+        Ensure that permissions with the same action (part before the first _ in
+        the codename) are not hidden.
+        """
+        response = self.get()
+        soup = self.get_soup(response.content)
+        main_change_permission = Permission.objects.get(
+            codename="change_custompermissionmodel",
+            content_type__app_label="tests",
+            content_type__model="custompermissionmodel",
+        )
+        custom_change_permission = Permission.objects.get(
+            codename="change_text",
+            content_type__app_label="tests",
+            content_type__model="custompermissionmodel",
+        )
+
+        # Main change permission is in the dedicated column, so it's directly
+        # inside a <td>, not inside a <fieldset>"
+        self.assertIsNotNone(
+            soup.select_one(f'td > input[value="{main_change_permission.pk}"]')
+        )
+        self.assertIsNone(
+            soup.select_one(f'td > fieldset input[value="{main_change_permission.pk}"]')
+        )
+
+        # Custom "change_text" permission is in the custom permissions column,
+        # so it's inside a <fieldset> and not directly inside a <td>
+        self.assertIsNone(
+            soup.select_one(f'td > input[value="{custom_change_permission.pk}"]')
+        )
+        self.assertIsNotNone(
+            soup.select_one(
+                f'td > fieldset input[value="{custom_change_permission.pk}"]'
+            )
+        )
+
 
 class TestGroupEditView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
     def setUp(self):