Fix 'any' methods on collection permission policies when only 'choose' action is specified

When the 'choose' permission was introduced in
d18ffb0233, all the logic is put in an
`elif` block. It means that this is only ever checked if the
`foo_any_permission_bar` permission policy methods are called without
'change' nor 'delete' in the actions list.

If the user only has a 'choose' permission, and the method is used
with a list of actions that contains both 'change'/'delete' and
'choose', e.g. `instances_user_has_any_permission_for(user, {'change',
'delete', 'choose'}`, it won't return any results.

This is inconsistent with how the logic for these 'any' methods
should work.

Instead of `elif`, the original implementation should add 'choose' to
the list checked in the previous `if`.

This commit does it slightly differently by enforcing for the known
actions in the beginning of the methods, but it still yields the same
results.
pull/10477/head
Sage Abdullah 2023-06-27 16:52:54 +01:00 zatwierdzone przez Matt Westcott
rodzic 1d3569130d
commit 8b43b232be
2 zmienionych plików z 328 dodań i 135 usunięć

Wyświetl plik

@ -247,72 +247,82 @@ class CollectionOwnershipPermissionPolicy(
return user.is_active and user.is_superuser
def users_with_any_permission(self, actions):
if "change" in actions or "delete" in actions:
# either 'add' or 'change' permission means that there are *potentially*
# some instances they can edit
real_actions = ["add", "change"]
elif "add" in actions:
real_actions = ["add"]
elif "choose" in actions:
real_actions = ["choose"]
else:
known_actions = set(actions) & {"add", "choose", "change"}
# "delete" is considered equivalent to "change"
if "delete" in actions:
known_actions.add("change")
# users with only "add" permission can still change instances they own
if "change" in known_actions:
known_actions.add("add")
if not known_actions:
# none of the actions passed in here are ones that we recognise, so only
# allow them for active superusers
return get_user_model().objects.filter(is_active=True, is_superuser=True)
return self._users_with_perm(real_actions)
return self._users_with_perm(known_actions)
def user_has_permission_for_instance(self, user, action, instance):
return self.user_has_any_permission_for_instance(user, [action], instance)
def user_has_any_permission_for_instance(self, user, actions, instance):
if "change" in actions or "delete" in actions:
if self._check_perm(user, ["change"], collection=instance.collection):
return True
elif (
self._check_perm(user, ["add"], collection=instance.collection)
and getattr(instance, self.owner_field_name) == user
):
return True
else:
return False
elif "choose" in actions:
return self._check_perm(user, ["choose"], collection=instance.collection)
known_actions = set(actions) & {"add", "choose", "change"}
# "delete" is considered equivalent to "change"
if "delete" in actions:
known_actions.add("change")
# users with only "add" permission can still change instances they own
if (
"change" in known_actions
and getattr(instance, self.owner_field_name) == user
):
known_actions.add("add")
if known_actions:
return self._check_perm(user, known_actions, collection=instance.collection)
else:
# 'change' and 'delete' are the only actions that are well-defined
# 'change', 'delete', and 'choose' are the only actions that are well-defined
# for specific instances. Other actions are only available to
# active superusers.
return user.is_active and user.is_superuser
def instances_user_has_any_permission_for(self, user, actions):
known_actions = set(actions) & {"change", "choose"}
# "delete" is considered equivalent to "change"
if "delete" in actions:
known_actions.add("change")
if user.is_active and user.is_superuser:
# active superusers can perform any action (including unrecognised ones)
# on any instance
return self.model.objects.all()
elif not user.is_authenticated:
return self.model.objects.none()
elif "change" in actions or "delete" in actions:
# return instances which are:
# - in (a descendant of) a collection for which they have 'change' permission
# - OR in (a descendant of) a collection for which they have 'add' permission,
# and are owned by them
elif known_actions:
# if "change" or "delete" in actions, return instances which are:
# - in (a descendant of) a collection for which they have "change" permission
# - OR in (a descendant of) a collection for which they have "add" permission,
# and are owned by them
# if "choose" in actions, return instances which are:
# - in (a descendant of) a collection for which they have "choose" permission.
# Note that if the actions contain both cases, the results will be combined
# because the user has "any" of the permissions in the actions.
change_perm_filter = Q(
collection__in=list(self._collections_with_perm(user, ["change"]))
)
collections = self._collections_with_perm(user, known_actions)
perm_filter = Q(collection__in=collections)
add_perm_filter = Q(
collection__in=list(self._collections_with_perm(user, ["add"]))
) & Q(**{self.owner_field_name: user})
# "add" permission implies "change" permission,
# but only if the instance is owned by the user
if "change" in known_actions:
perm_filter |= Q(
collection__in=self._collections_with_perm(user, ["add"])
) & Q(**{self.owner_field_name: user})
return self.model.objects.filter(change_perm_filter | add_perm_filter)
elif "choose" in actions:
# Return instances which are in (a descendant of) a collection for which they
# have 'choose' permission.
choose_perm_filter = Q(
collection__in=list(self._collections_with_perm(user, ["choose"]))
)
return self.model.objects.filter(choose_perm_filter)
return self.model.objects.filter(perm_filter)
else:
# action is either not recognised, or is the 'add' action which is
# not meaningful for existing instances. As such, non-superusers
@ -320,29 +330,25 @@ class CollectionOwnershipPermissionPolicy(
return self.model.objects.none()
def users_with_any_permission_for_instance(self, actions, instance):
if "change" in actions or "delete" in actions:
# form a filter expression of the users with 'change' permission
# over this instance's collection
filter_expr = self._users_with_perm_filter(
["change"], collection=instance.collection
)
known_actions = set(actions) & {"choose", "change"}
# add on the item's owner, if they still have 'add' permission
# (and the owner field isn't blank)
# "delete" is considered equivalent to "change"
if "delete" in actions:
known_actions.add("change")
filter_expr = self._users_with_perm_filter(
known_actions, collection=instance.collection
)
# users with only "add" permission can still change instances they own
if "change" in known_actions:
owner = getattr(instance, self.owner_field_name)
if owner is not None and self._check_perm(
owner, ["add"], collection=instance.collection
owner, {"add"}, collection=instance.collection
):
filter_expr = filter_expr | Q(pk=owner.pk)
filter_expr |= Q(pk=owner.pk)
# return the filtered queryset
return get_user_model().objects.filter(filter_expr).distinct()
elif "choose" in actions:
# Form a filter expression of the users with 'choose' permission
# over this instance's collection.
filter_expr = self._users_with_perm_filter(
["choose"], collection=instance.collection
)
if known_actions:
return get_user_model().objects.filter(filter_expr).distinct()
else:
# action is either not recognised, or is the 'add' action which is
@ -355,6 +361,16 @@ class CollectionOwnershipPermissionPolicy(
Return a queryset of all collections in which the given user has
permission to perform any of the given actions
"""
known_actions = set(actions) & {"add", "choose", "change"}
# "delete" is considered equivalent to "change"
if "delete" in actions:
known_actions.add("change")
# users with only "add" permission can still change instances they own
if "change" in known_actions:
known_actions.add("add")
if user.is_active and user.is_superuser:
# active superusers can perform any action (including unrecognised ones)
# in any collection
@ -363,17 +379,8 @@ class CollectionOwnershipPermissionPolicy(
elif not user.is_authenticated:
return Collection.objects.none()
elif "change" in actions or "delete" in actions:
# return collections which are covered by either 'add' or 'change' permissions
# (since collections with 'add' permissions can *potentially* contain instances
# they own and can therefore edit)
return self._collections_with_perm(user, ["add", "change"])
elif "add" in actions:
return self._collections_with_perm(user, ["add"])
elif "choose" in actions:
return self._collections_with_perm(user, ["choose"])
elif known_actions:
return self._collections_with_perm(user, known_actions)
else:
# action is not recognised, and so non-superusers

Wyświetl plik

@ -23,6 +23,9 @@ class PermissionPolicyTestCase(PermissionPolicyTestUtils, WagtailTestUtils, Test
change_doc_permission = Permission.objects.get(
content_type=document_content_type, codename="change_document"
)
choose_doc_permission = Permission.objects.get(
content_type=document_content_type, codename="choose_document"
)
# Collections
self.root_collection = Collection.get_first_root_node()
@ -50,6 +53,13 @@ class PermissionPolicyTestCase(PermissionPolicyTestUtils, WagtailTestUtils, Test
permission=add_doc_permission,
)
report_choosers_group = Group.objects.create(name="Report choosers")
GroupCollectionPermission.objects.create(
group=report_choosers_group,
collection=self.reports_collection,
permission=choose_doc_permission,
)
# Users
self.superuser = self.create_superuser(
"superuser", "superuser@example.com", "password"
@ -86,6 +96,12 @@ class PermissionPolicyTestCase(PermissionPolicyTestUtils, WagtailTestUtils, Test
)
self.report_adder.groups.add(report_adders_group)
# a user with choose_document permission on reports via the report_choosers group
self.report_chooser = self.create_user(
"reportchooser", "reportchooser@example.com", "password"
)
self.report_chooser.groups.add(report_choosers_group)
# a user with no permissions
self.useless_user = self.create_user(
"uselessuser", "uselessuser@example.com", "password"
@ -137,15 +153,17 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
def test_user_has_permission(self):
self.assertUserPermissionMatrix(
[
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, False, True, False, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, False, True, False, False),
(self.report_adder, True, False, False, False),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
]
(self.superuser, True, True, True, True, True),
(self.inactive_superuser, False, False, False, False, False),
(self.doc_changer, False, True, False, False, False),
(self.inactive_doc_changer, False, False, False, False, False),
(self.report_changer, False, True, False, False, False),
(self.report_adder, True, False, False, False, False),
(self.report_chooser, False, False, False, False, True),
(self.useless_user, False, False, False, False, False),
(self.anonymous_user, False, False, False, False, False),
],
actions=("add", "change", "delete", "frobnicate", "choose"),
)
def test_user_has_any_permission(self):
@ -163,22 +181,28 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
self.assertTrue(
self.policy.user_has_any_permission(self.report_adder, ["add", "change"])
)
self.assertTrue(
self.policy.user_has_any_permission(
self.report_chooser, ["change", "choose"]
)
)
self.assertFalse(
self.policy.user_has_any_permission(self.anonymous_user, ["add", "change"])
)
def test_users_with_any_permission(self):
users_with_add_or_change_permission = self.policy.users_with_any_permission(
["add", "change"]
users_with_add_change_or_choose_permission = (
self.policy.users_with_any_permission(["add", "change", "choose"])
)
self.assertResultSetEqual(
users_with_add_or_change_permission,
users_with_add_change_or_choose_permission,
[
self.superuser,
self.doc_changer,
self.report_changer,
self.report_adder,
self.report_chooser,
],
)
@ -194,6 +218,15 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
],
)
users_with_choose_permission = self.policy.users_with_permission("choose")
self.assertResultSetEqual(
users_with_choose_permission,
[
self.superuser,
self.report_chooser,
],
)
users_with_custom_permission = self.policy.users_with_permission("frobnicate")
self.assertResultSetEqual(
@ -209,15 +242,17 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
self.assertUserInstancePermissionMatrix(
self.changer_doc,
[
(self.superuser, True, True, True),
(self.inactive_superuser, False, False, False),
(self.doc_changer, True, False, False),
(self.inactive_doc_changer, False, False, False),
(self.report_changer, False, False, False),
(self.report_adder, False, False, False),
(self.useless_user, False, False, False),
(self.anonymous_user, False, False, False),
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, True, False, False, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, False, False, False, False),
(self.report_adder, False, False, False, False),
(self.report_chooser, False, False, False, False),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
],
actions=("change", "delete", "frobnicate", "choose"),
)
# document in 'reports' is editable by users with permissions
@ -225,15 +260,17 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
self.assertUserInstancePermissionMatrix(
self.useless_report,
[
(self.superuser, True, True, True),
(self.inactive_superuser, False, False, False),
(self.doc_changer, True, False, False),
(self.inactive_doc_changer, False, False, False),
(self.report_changer, True, False, False),
(self.report_adder, False, False, False),
(self.useless_user, False, False, False),
(self.anonymous_user, False, False, False),
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, True, False, False, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, True, False, False, False),
(self.report_adder, False, False, False, False),
(self.report_chooser, False, False, False, True),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
],
actions=("change", "delete", "frobnicate", "choose"),
)
def test_user_has_any_permission_for_instance(self):
@ -249,6 +286,12 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
)
)
self.assertTrue(
self.policy.user_has_any_permission_for_instance(
self.report_chooser, ["change", "delete", "choose"], self.useless_report
)
)
self.assertFalse(
self.policy.user_has_any_permission_for_instance(
self.anonymous_user, ["change", "delete"], self.changer_doc
@ -305,6 +348,19 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_permission_for(
self.report_chooser,
"choose",
),
[
self.changer_report,
self.useless_report,
self.adder_report,
self.anonymous_report,
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_permission_for(
self.useless_user,
@ -367,6 +423,18 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_any_permission_for(
self.report_chooser, ["change", "delete", "choose"]
),
[
self.changer_report,
self.adder_report,
self.useless_report,
self.anonymous_report,
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_any_permission_for(
self.useless_user, ["change", "delete"]
@ -396,6 +464,12 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
),
[self.superuser, self.doc_changer, self.report_changer],
)
self.assertResultSetEqual(
self.policy.users_with_permission_for_instance(
"choose", self.useless_report
),
[self.superuser, self.report_chooser],
)
self.assertResultSetEqual(
self.policy.users_with_permission_for_instance(
"change", self.useless_report
@ -422,6 +496,17 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
),
[self.superuser, self.doc_changer, self.report_changer],
)
self.assertResultSetEqual(
self.policy.users_with_any_permission_for_instance(
["change", "delete", "choose"], self.useless_report
),
[
self.superuser,
self.doc_changer,
self.report_changer,
self.report_chooser,
],
)
self.assertResultSetEqual(
self.policy.users_with_any_permission_for_instance(
["change", "delete"], self.useless_report
@ -484,6 +569,14 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_permission_for(
self.report_chooser,
"choose",
),
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_permission_for(
self.useless_user,
@ -543,6 +636,13 @@ class TestCollectionPermissionPolicy(PermissionPolicyTestCase):
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_any_permission_for(
self.report_chooser, ["change", "delete", "choose"]
),
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_any_permission_for(
self.useless_user, ["change", "delete"]
@ -569,15 +669,17 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
def test_user_has_permission(self):
self.assertUserPermissionMatrix(
[
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, False, True, True, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, False, True, True, False),
(self.report_adder, True, True, True, False),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
]
(self.superuser, True, True, True, True, True),
(self.inactive_superuser, False, False, False, False, False),
(self.doc_changer, False, True, True, False, False),
(self.inactive_doc_changer, False, False, False, False, False),
(self.report_changer, False, True, True, False, False),
(self.report_adder, True, True, True, False, False),
(self.report_chooser, False, False, False, False, True),
(self.useless_user, False, False, False, False, False),
(self.anonymous_user, False, False, False, False, False),
],
actions=("add", "change", "delete", "frobnicate", "choose"),
)
def test_user_has_any_permission(self):
@ -598,22 +700,28 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
self.assertTrue(
self.policy.user_has_any_permission(self.report_adder, ["change", "delete"])
)
self.assertTrue(
self.policy.user_has_any_permission(
self.report_chooser, ["change", "choose"]
)
)
self.assertFalse(
self.policy.user_has_any_permission(self.anonymous_user, ["add", "change"])
)
def test_users_with_any_permission(self):
users_with_add_or_change_permission = self.policy.users_with_any_permission(
["add", "change"]
users_with_add_change_or_choose_permission = (
self.policy.users_with_any_permission(["add", "change", "choose"])
)
self.assertResultSetEqual(
users_with_add_or_change_permission,
users_with_add_change_or_choose_permission,
[
self.superuser,
self.doc_changer,
self.report_changer,
self.report_adder,
self.report_chooser,
],
)
@ -630,6 +738,15 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
],
)
users_with_choose_permission = self.policy.users_with_permission("choose")
self.assertResultSetEqual(
users_with_choose_permission,
[
self.superuser,
self.report_chooser,
],
)
users_with_custom_permission = self.policy.users_with_permission("frobnicate")
self.assertResultSetEqual(
@ -645,15 +762,17 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
self.assertUserInstancePermissionMatrix(
self.changer_doc,
[
(self.superuser, True, True, True),
(self.inactive_superuser, False, False, False),
(self.doc_changer, True, True, False),
(self.inactive_doc_changer, False, False, False),
(self.report_changer, False, False, False),
(self.report_adder, False, False, False),
(self.useless_user, False, False, False),
(self.anonymous_user, False, False, False),
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, True, True, False, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, False, False, False, False),
(self.report_adder, False, False, False, False),
(self.report_chooser, False, False, False, False),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
],
actions=("change", "delete", "frobnicate", "choose"),
)
# document in 'reports' is editable by users with permissions
@ -661,30 +780,34 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
self.assertUserInstancePermissionMatrix(
self.useless_report,
[
(self.superuser, True, True, True),
(self.inactive_superuser, False, False, False),
(self.doc_changer, True, True, False),
(self.inactive_doc_changer, False, False, False),
(self.report_changer, True, True, False),
(self.report_adder, False, False, False),
(self.useless_user, False, False, False),
(self.anonymous_user, False, False, False),
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, True, True, False, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, True, True, False, False),
(self.report_adder, False, False, False, False),
(self.report_chooser, False, False, False, True),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
],
actions=("change", "delete", "frobnicate", "choose"),
)
# adder_report is editable by its owner (who only has 'add' permission)
self.assertUserInstancePermissionMatrix(
self.adder_report,
[
(self.superuser, True, True, True),
(self.inactive_superuser, False, False, False),
(self.doc_changer, True, True, False),
(self.inactive_doc_changer, False, False, False),
(self.report_changer, True, True, False),
(self.report_adder, True, True, False),
(self.useless_user, False, False, False),
(self.anonymous_user, False, False, False),
(self.superuser, True, True, True, True),
(self.inactive_superuser, False, False, False, False),
(self.doc_changer, True, True, False, False),
(self.inactive_doc_changer, False, False, False, False),
(self.report_changer, True, True, False, False),
(self.report_adder, True, True, False, False),
(self.report_chooser, False, False, False, True),
(self.useless_user, False, False, False, False),
(self.anonymous_user, False, False, False, False),
],
actions=("change", "delete", "frobnicate", "choose"),
)
def test_user_has_any_permission_for_instance(self):
@ -712,6 +835,12 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
)
)
self.assertTrue(
self.policy.user_has_any_permission_for_instance(
self.report_chooser, ["change", "delete", "choose"], self.useless_report
)
)
self.assertFalse(
self.policy.user_has_any_permission_for_instance(
self.anonymous_user, ["change", "delete"], self.changer_doc
@ -768,6 +897,19 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_permission_for(
self.report_chooser,
"choose",
),
[
self.changer_report,
self.useless_report,
self.adder_report,
self.anonymous_report,
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_permission_for(
self.useless_user,
@ -840,6 +982,18 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_any_permission_for(
self.report_chooser, ["change", "delete", "choose"]
),
[
self.changer_report,
self.adder_report,
self.useless_report,
self.anonymous_report,
],
)
self.assertResultSetEqual(
self.policy.instances_user_has_any_permission_for(
self.useless_user, ["change", "delete"]
@ -879,6 +1033,12 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
self.policy.users_with_permission_for_instance("change", self.adder_report),
[self.superuser, self.doc_changer, self.report_changer, self.report_adder],
)
self.assertResultSetEqual(
self.policy.users_with_permission_for_instance(
"choose", self.useless_report
),
[self.superuser, self.report_chooser],
)
self.assertResultSetEqual(
self.policy.users_with_permission_for_instance(
"change", self.useless_report
@ -905,6 +1065,17 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
),
[self.superuser, self.doc_changer, self.report_changer, self.report_adder],
)
self.assertResultSetEqual(
self.policy.users_with_any_permission_for_instance(
["change", "delete", "choose"], self.useless_report
),
[
self.superuser,
self.doc_changer,
self.report_changer,
self.report_chooser,
],
)
self.assertResultSetEqual(
self.policy.users_with_any_permission_for_instance(
["change", "delete"], self.useless_report
@ -967,6 +1138,14 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_permission_for(
self.report_chooser,
"choose",
),
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_permission_for(
self.useless_user,
@ -1026,6 +1205,13 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_any_permission_for(
self.report_chooser, ["change", "delete", "choose"]
),
[self.reports_collection],
)
self.assertResultSetEqual(
self.policy.collections_user_has_any_permission_for(
self.useless_user, ["change", "delete"]