kopia lustrzana https://github.com/wagtail/wagtail
Create new permission policy CollectionMangementPermissionPolicy
Also updated tests, docs, and made a migration to move from model-level permissions to new granular collection managementpull/7579/head
rodzic
a84c04af19
commit
c8d2562002
|
@ -43,7 +43,7 @@ The permission rules for images and documents work on a similar basis to pages.
|
|||
|
||||
Access to specific sets of images and documents can be controlled by setting up *collections*. By default all images and documents belong to the 'root' collection, but users with appropriate permissions can create new collections the Settings -> Collections area of the admin interface. Permissions set on 'root' apply to all collections, so a user with 'edit' permission for images in the root collection can edit all images; permissions set on other collections only apply to that collection and any of its sub-collections.
|
||||
|
||||
The 'choose' permission for images and documents determines which collections are visible within the chooser interface used to select images and document links for insertion into pages (and other models, such as snippets). Typically, all users are granted choose permission for all collections, allowing them to use any uploaded image or document on pages they create, but this permission can be limited to allow creating collections that are only available for use by specific groups.
|
||||
The 'choose' permission for images and documents determines which collections are visible within the chooser interface used to select images and document links for insertion into pages (and other models, such as snippets). Typically, all users are granted choose permission for all collections, allowing them to use any uploaded image or document on pages they create, but this permission can be limited to allow creating collections that are only visible to specific groups.
|
||||
|
||||
.. _collection_management_permissions:
|
||||
|
||||
|
@ -54,7 +54,10 @@ Permission for managing collections themselves can be attached at any point in t
|
|||
|
||||
* **Add** - grants the ability to create new collections underneath this collection.
|
||||
* **Edit** - grants the ability to edit the name of the collection, change its location in the collection tree, and to change the privacy settings for documents within this collection.
|
||||
* **Delete** - grants the ability to delete collections that were added below this collection. *Note:* a collection must be empty before it can be deleted.
|
||||
* **Delete** - grants the ability to delete collections that were added below this collection. *Note:* a collection must have no subcollections under it and the collection itself must be empty before it can be deleted.
|
||||
|
||||
.. Note::
|
||||
Users are not allowed to move or delete the collection that is used to assign them permission to manage collections.
|
||||
|
||||
Displaying custom permissions in the admin
|
||||
------------------------------------------
|
||||
|
|
|
@ -11,7 +11,9 @@ from wagtail.tests.utils import WagtailTestUtils
|
|||
|
||||
class CollectionInstanceTestUtils:
|
||||
def setUp(self):
|
||||
"""Common setup for testing collection views with per-instance permissions"""
|
||||
"""
|
||||
Common setup for testing collection views with per-instance permissions
|
||||
"""
|
||||
collection_content_type = ContentType.objects.get_for_model(Collection)
|
||||
self.add_permission = Permission.objects.get(
|
||||
content_type=collection_content_type, codename='add_collection'
|
||||
|
@ -28,6 +30,7 @@ class CollectionInstanceTestUtils:
|
|||
self.finance_collection = self.root_collection.add_child(name="Finance")
|
||||
self.marketing_collection = self.root_collection.add_child(name="Marketing")
|
||||
self.marketing_sub_collection = self.marketing_collection.add_child(name="Digital Marketing")
|
||||
self.marketing_sub_collection_2 = self.marketing_collection.add_child(name="Direct Mail Marketing")
|
||||
|
||||
self.marketing_group = Group.objects.create(name="Marketing Group")
|
||||
self.marketing_group.permissions.add(admin_permission)
|
||||
|
@ -106,7 +109,7 @@ class TestCollectionsIndexView(CollectionInstanceTestUtils, TestCase, WagtailTes
|
|||
self.assertEqual(response.context['message'], 'Sorry, you do not have permission to access this area.')
|
||||
|
||||
def test_marketing_user_with_change_permission(self):
|
||||
# Grant the marketing group permission to manage their collection
|
||||
# Grant the marketing group permission to make changes to their collections
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
|
@ -117,12 +120,12 @@ class TestCollectionsIndexView(CollectionInstanceTestUtils, TestCase, WagtailTes
|
|||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
[collection.name for collection in response.context['object_list']],
|
||||
['Marketing', 'Digital Marketing'])
|
||||
['Marketing', 'Digital Marketing', 'Direct Mail Marketing'])
|
||||
self.assertNotContains(response, "Finance")
|
||||
self.assertNotContains(response, "Add a collection")
|
||||
|
||||
def test_marketing_user_with_add_permission(self):
|
||||
# Grant the marketing group permission to manage their collection
|
||||
# Grant the marketing group permission to add to their collections
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
|
@ -133,10 +136,26 @@ class TestCollectionsIndexView(CollectionInstanceTestUtils, TestCase, WagtailTes
|
|||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
[collection.name for collection in response.context['object_list']],
|
||||
['Marketing', 'Digital Marketing'])
|
||||
['Marketing', 'Digital Marketing', 'Direct Mail Marketing'])
|
||||
self.assertNotContains(response, "Finance")
|
||||
self.assertContains(response, "Add a collection")
|
||||
|
||||
def test_marketing_user_with_delete_permission(self):
|
||||
# Grant the marketing group permission to add to their collections
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.delete_permission
|
||||
)
|
||||
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
[collection.name for collection in response.context['object_list']],
|
||||
['Marketing', 'Digital Marketing', 'Direct Mail Marketing'])
|
||||
self.assertNotContains(response, "Finance")
|
||||
self.assertNotContains(response, "Add a collection")
|
||||
|
||||
|
||||
class TestAddCollectionAsSuperuser(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
|
@ -299,21 +318,17 @@ class TestEditCollectionAsSuperuser(TestCase, WagtailTestUtils):
|
|||
class TestEditCollection(CollectionInstanceTestUtils, TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Grant the marketing group permission to manage their collection
|
||||
self.users_add_permission = GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.add_permission
|
||||
)
|
||||
# Grant the marketing group permission to edit their collection
|
||||
self.users_change_permission = GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.change_permission
|
||||
)
|
||||
self.users_delete_permission = GroupCollectionPermission.objects.create(
|
||||
# Grant the marketing group permission to add collections under this collection
|
||||
self.users_add_permission = GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.delete_permission
|
||||
permission=self.add_permission
|
||||
)
|
||||
self.login(self.marketing_user, password='password')
|
||||
|
||||
|
@ -329,20 +344,31 @@ class TestEditCollection(CollectionInstanceTestUtils, TestCase, WagtailTestUtils
|
|||
post_data
|
||||
)
|
||||
|
||||
def test_marketing_user_no_permissions(self):
|
||||
def test_marketing_user_no_change_permission(self):
|
||||
self.users_change_permission.delete()
|
||||
response = self.get(collection_id=self.marketing_collection.id)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(response.context['message'], 'Sorry, you do not have permission to access this area.')
|
||||
|
||||
def test_marketing_user_with_change_permission(self):
|
||||
def test_marketing_user_can_move_collection(self):
|
||||
# Retrieve edit form and check fields
|
||||
response = self.get(collection_id=self.marketing_sub_collection.id)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
form_fields = response.context['form'].fields
|
||||
# Can edit the name and move to new parent
|
||||
self.assertEqual(type(form_fields['name'].widget).__name__, 'TextInput')
|
||||
self.assertEqual(type(form_fields['parent'].widget).__name__, 'SelectWithDisabledOptions')
|
||||
self.assertContains(response, "Delete collection")
|
||||
# Now move the collection and check it did get moved and renamed
|
||||
self.post(self.marketing_sub_collection.pk, {'name': "New Collection Name", 'parent': self.marketing_sub_collection_2.pk})
|
||||
self.assertEqual(Collection.objects.get(pk=self.marketing_sub_collection.pk).name, "New Collection Name")
|
||||
self.assertEqual(Collection.objects.get(pk=self.marketing_sub_collection.pk).get_parent(), self.marketing_sub_collection_2)
|
||||
|
||||
def test_marketing_user_cannot_move_collection_if_no_add_permission(self):
|
||||
self.users_add_permission.delete()
|
||||
response = self.get(collection_id=self.marketing_sub_collection.id)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
form_fields = response.context['form'].fields
|
||||
self.assertEqual(type(form_fields['name'].widget).__name__, 'TextInput')
|
||||
self.assertEqual(type(form_fields['parent'].widget).__name__, 'HiddenInput')
|
||||
|
||||
def test_marketing_user_cannot_move_collection_permissions_are_assigned_to(self):
|
||||
response = self.get(collection_id=self.marketing_collection.id)
|
||||
|
@ -354,6 +380,19 @@ class TestEditCollection(CollectionInstanceTestUtils, TestCase, WagtailTestUtils
|
|||
self.assertEqual(type(form_fields['parent'].widget).__name__, 'HiddenInput')
|
||||
self.assertNotContains(response, "Delete collection")
|
||||
|
||||
def test_page_shows_delete_link_only_if_delete_permitted(self):
|
||||
# Retrieve edit form and check fields
|
||||
response = self.get(collection_id=self.marketing_sub_collection.id)
|
||||
self.assertNotContains(response, "Delete collection")
|
||||
# Add delete permission to parent collection an try again
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.delete_permission
|
||||
)
|
||||
response = self.get(collection_id=self.marketing_sub_collection.id)
|
||||
self.assertContains(response, "Delete collection")
|
||||
|
||||
|
||||
class TestDeleteCollectionAsSuperuser(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
|
@ -436,17 +475,7 @@ class TestDeleteCollectionAsSuperuser(TestCase, WagtailTestUtils):
|
|||
class TestDeleteCollection(CollectionInstanceTestUtils, TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Grant the marketing group permission to manage their collection
|
||||
self.users_add_permission = GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.add_permission
|
||||
)
|
||||
self.users_change_permission = GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
permission=self.change_permission
|
||||
)
|
||||
# Grant the marketing group permission to delete
|
||||
self.users_delete_permission = GroupCollectionPermission.objects.create(
|
||||
group=self.marketing_group,
|
||||
collection=self.marketing_collection,
|
||||
|
@ -472,9 +501,25 @@ class TestDeleteCollection(CollectionInstanceTestUtils, TestCase, WagtailTestUti
|
|||
self.assertTemplateUsed(response, 'wagtailadmin/generic/confirm_delete.html')
|
||||
|
||||
def test_cannot_delete_someone_elses_collection(self):
|
||||
response = self.get(collection_id=self.finance_collection.id)
|
||||
response = self.get(self.finance_collection.id)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_cannot_delete_their_own_root_collection(self):
|
||||
response = self.get(collection_id=self.marketing_collection.id)
|
||||
response = self.get(self.marketing_collection.id)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_cannot_delete_collection_with_descendants_get(self):
|
||||
self.marketing_sub_collection.add_child(instance=Collection(name='Another collection'))
|
||||
|
||||
response = self.get(self.marketing_sub_collection.id)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'wagtailadmin/collections/delete_not_empty.html')
|
||||
|
||||
def test_cannot_delete_collection_with_descendants_post(self):
|
||||
self.marketing_sub_collection.add_child(instance=Collection(name='Another collection'))
|
||||
|
||||
response = self.post(self.marketing_sub_collection.id)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
# Check that the collection was not deleted
|
||||
self.assertTrue(Collection.objects.get(id=self.marketing_sub_collection.id))
|
||||
|
|
|
@ -76,17 +76,21 @@ class Edit(EditView):
|
|||
# Only return descendants of the root node, so that the root is not editable
|
||||
return Collection.get_first_root_node().get_descendants()
|
||||
else:
|
||||
return self.permission_policy.collections_user_has_any_permission_for(
|
||||
self.request.user, ['change']
|
||||
return self.permission_policy.collections_user_has_permission_for(
|
||||
self.request.user, 'change'
|
||||
)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
user = self.request.user
|
||||
current_parent = form.instance.get_parent()
|
||||
# If this is currently at the top of the user's collection hierarchy,
|
||||
# If this instance is a collection used to assign permissions for this user,
|
||||
# do not let the user move this collection.
|
||||
if not self.permission_policy.user_has_permission_for_instance(user, 'add', current_parent):
|
||||
if self.permission_policy.user_has_any_permission_directly_on_instance(
|
||||
user, ['add', 'change', 'delete'], form.instance
|
||||
):
|
||||
form.fields['parent'].widget = HiddenInput()
|
||||
# or if user does not have add permission anywhere, then can't move collection
|
||||
elif not self.permission_policy.user_has_permission(user, 'add'):
|
||||
form.fields['parent'].widget = HiddenInput()
|
||||
else:
|
||||
# Filter collections offered in parent field by current user's add permissions
|
||||
|
@ -95,7 +99,7 @@ class Edit(EditView):
|
|||
form.fields['parent'].disabled_queryset = form.instance.get_descendants(inclusive=True)
|
||||
form.fields['parent'].empty_label = None
|
||||
|
||||
form.initial['parent'] = current_parent.pk
|
||||
form.initial['parent'] = form.instance.get_parent().pk
|
||||
return form
|
||||
|
||||
def save_instance(self):
|
||||
|
@ -117,10 +121,10 @@ class Edit(EditView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
# Can not delete the collection where the delete permission is assigned
|
||||
if self.request.user.is_superuser:
|
||||
can_delete = True
|
||||
else:
|
||||
# Can not delete the collection where the delete permission is assigned
|
||||
can_delete = self.permission_policy.descendants_of_collections_with_user_perm(
|
||||
self.request.user, ['delete']
|
||||
).filter(pk=self.object.pk).first()
|
||||
|
|
|
@ -12,7 +12,6 @@ from wagtail import __version__
|
|||
from wagtail.admin.admin_url_finder import ModelAdminURLFinder, register_admin_url_finder
|
||||
from wagtail.admin.auth import user_has_any_page_permission
|
||||
from wagtail.admin.forms.collections import GroupCollectionManagementPermissionFormSet
|
||||
from wagtail.admin.localization import get_available_admin_languages, get_available_admin_time_zones
|
||||
from wagtail.admin.menu import MenuItem, SubmenuMenuItem, reports_menu, settings_menu
|
||||
from wagtail.admin.navigation import get_explorable_root_page
|
||||
from wagtail.admin.rich_text import (
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def grant_instance_level_collection_management_permissions(apps, schema_editor):
|
||||
Collection = apps.get_model('wagtailcore.Collection')
|
||||
GroupCollectionPermission = apps.get_model('wagtailcore.GroupCollectionPermission')
|
||||
Permission = apps.get_model('auth.Permission')
|
||||
|
||||
# Give all groups who currently manage collections permission to manage root collection
|
||||
root_collection = Collection.objects.filter(depth=1).order_by('path').first()
|
||||
stock_permissions = Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
content_type__model='collection',
|
||||
codename__in=['add_collection', 'change_collection', 'delete_collection']
|
||||
).prefetch_related('group_set').all()
|
||||
|
||||
for perm in stock_permissions:
|
||||
for group in perm.group_set.all():
|
||||
gcp = GroupCollectionPermission(
|
||||
group_id=group.id,
|
||||
collection_id=root_collection.id,
|
||||
permission_id=perm.id
|
||||
)
|
||||
gcp.full_clean()
|
||||
gcp.save()
|
||||
# Now remove the model-level permissions from the group
|
||||
group.permissions.remove(perm)
|
||||
|
||||
|
||||
def revert_to_model_level_collection_management_permissions(apps, schema_editor):
|
||||
Collection = apps.get_model('wagtailcore.Collection')
|
||||
ContentType = apps.get_model('contenttypes.ContentType')
|
||||
GroupCollectionPermission = apps.get_model('wagtailcore.GroupCollectionPermission')
|
||||
Permission = apps.get_model('auth.Permission')
|
||||
|
||||
collection_content_type = ContentType.objects.get(
|
||||
model='collection',
|
||||
app_label='wagtailcore'
|
||||
)
|
||||
root_collection = Collection.objects.filter(depth=1).order_by('path').first()
|
||||
stock_permissions = Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
content_type__model='collection',
|
||||
codename__in=['add_collection', 'change_collection', 'delete_collection']
|
||||
).all()
|
||||
|
||||
# Give model-level permissions to all groups who have permission on the root collection
|
||||
group_collection_permissions = GroupCollectionPermission.objects.filter(
|
||||
collection=root_collection,
|
||||
permission__in=stock_permissions
|
||||
).prefetch_related('group')
|
||||
|
||||
for row in group_collection_permissions.all():
|
||||
perm = Permission.objects.get(
|
||||
content_type=collection_content_type,
|
||||
codename=row.permission.codename
|
||||
)
|
||||
row.group.permissions.add(perm)
|
||||
|
||||
# Now delete the instance-level collection management permissions
|
||||
group_collection_permissions.all().delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '0065_log_entry_uuid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
grant_instance_level_collection_management_permissions,
|
||||
revert_to_model_level_collection_management_permissions
|
||||
)
|
||||
]
|
|
@ -107,6 +107,22 @@ class CollectionPermissionLookupMixin:
|
|||
self._users_with_perm_filter(actions, collection=collection)
|
||||
).distinct()
|
||||
|
||||
def collections_user_has_any_permission_for(self, user, actions):
|
||||
"""
|
||||
Return a queryset of all collections in which the given user has
|
||||
permission to perform any of the given actions
|
||||
"""
|
||||
if user.is_active and user.is_superuser:
|
||||
# active superusers can perform any action (including unrecognised ones)
|
||||
# in any collection
|
||||
return Collection.objects.all()
|
||||
|
||||
elif not user.is_authenticated:
|
||||
return Collection.objects.none()
|
||||
|
||||
else:
|
||||
return self._collections_with_perm(user, actions)
|
||||
|
||||
def collections_user_has_permission_for(self, user, action):
|
||||
"""
|
||||
Return a queryset of all collections in which the given user has
|
||||
|
@ -179,22 +195,6 @@ class CollectionPermissionPolicy(CollectionPermissionLookupMixin, BaseDjangoAuth
|
|||
"""
|
||||
return self._users_with_perm(actions, collection=instance.collection)
|
||||
|
||||
def collections_user_has_any_permission_for(self, user, actions):
|
||||
"""
|
||||
Return a queryset of all collections in which the given user has
|
||||
permission to perform any of the given actions
|
||||
"""
|
||||
if user.is_active and user.is_superuser:
|
||||
# active superusers can perform any action (including unrecognised ones)
|
||||
# in any collection
|
||||
return Collection.objects.all()
|
||||
|
||||
elif not user.is_authenticated:
|
||||
return Collection.objects.none()
|
||||
|
||||
else:
|
||||
return self._collections_with_perm(user, actions)
|
||||
|
||||
|
||||
class CollectionOwnershipPermissionPolicy(
|
||||
CollectionPermissionLookupMixin, BaseDjangoAuthPermissionPolicy
|
||||
|
@ -370,7 +370,39 @@ class CollectionOwnershipPermissionPolicy(
|
|||
return Collection.objects.none()
|
||||
|
||||
|
||||
class CollectionMangementPermissionPolicy(CollectionPermissionPolicy):
|
||||
class CollectionMangementPermissionPolicy(
|
||||
CollectionPermissionLookupMixin, BaseDjangoAuthPermissionPolicy
|
||||
):
|
||||
def user_has_permission(self, user, action):
|
||||
"""
|
||||
Return whether the given user has permission to perform the given action
|
||||
on some or all instances of this model
|
||||
"""
|
||||
return self.user_has_any_permission(user, [action])
|
||||
|
||||
def user_has_any_permission(self, user, actions):
|
||||
"""
|
||||
Return whether the given user has permission to perform any of the given actions
|
||||
on some or all instances of this model.
|
||||
"""
|
||||
if not (user.is_active and user.is_authenticated):
|
||||
return False
|
||||
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
return GroupCollectionPermission.objects.filter(
|
||||
group__user=user,
|
||||
permission__in=self._get_permission_objects_for_actions(actions),
|
||||
).exists()
|
||||
|
||||
def users_with_any_permission(self, actions):
|
||||
"""
|
||||
Return a queryset of users who have permission to perform any of the given actions
|
||||
on some or all instances of this model
|
||||
"""
|
||||
return self._users_with_perm(actions)
|
||||
|
||||
def user_has_permission_for_instance(self, user, action, instance):
|
||||
"""
|
||||
Return whether the given user has permission to perform the given action on the
|
||||
|
@ -385,6 +417,19 @@ class CollectionMangementPermissionPolicy(CollectionPermissionPolicy):
|
|||
"""
|
||||
return self._check_perm(user, actions, collection=instance)
|
||||
|
||||
def user_has_any_permission_directly_on_instance(self, user, actions, instance):
|
||||
"""
|
||||
Return whether the given user has any permission assigned directly to the instance.
|
||||
|
||||
When editing collections, we do not allow users to move nodes used to assign their
|
||||
permissions as that might have unexpected effects on the permission cascade.
|
||||
"""
|
||||
return GroupCollectionPermission.objects.filter(
|
||||
group__user=user,
|
||||
permission__in=self._get_permission_objects_for_actions(actions),
|
||||
collection=instance,
|
||||
).exists()
|
||||
|
||||
def users_with_any_permission_for_instance(self, actions, instance):
|
||||
"""
|
||||
Return a queryset of all users who have permission to perform any of the given
|
||||
|
@ -392,6 +437,12 @@ class CollectionMangementPermissionPolicy(CollectionPermissionPolicy):
|
|||
"""
|
||||
return self._users_with_perm(actions, collection=instance)
|
||||
|
||||
def instances_user_has_permission_for(self, user, action):
|
||||
return self.collections_user_has_permission_for(user, action)
|
||||
|
||||
def instances_user_has_any_permission_for(self, user, actions):
|
||||
return self.collections_user_has_any_permission_for(user, actions)
|
||||
|
||||
def descendants_of_collections_with_user_perm(self, user, actions):
|
||||
"""
|
||||
Return a queryset of collections on which this user has a GroupCollectionPermission
|
||||
|
|
|
@ -4,7 +4,8 @@ from django.test import TestCase
|
|||
|
||||
from wagtail.core.models import Collection, GroupCollectionPermission
|
||||
from wagtail.core.permission_policies.collections import (
|
||||
CollectionOwnershipPermissionPolicy, CollectionPermissionPolicy)
|
||||
CollectionMangementPermissionPolicy, CollectionOwnershipPermissionPolicy,
|
||||
CollectionPermissionPolicy)
|
||||
from wagtail.core.tests.test_permission_policies import PermissionPolicyTestUtils
|
||||
from wagtail.documents.models import Document
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
|
@ -948,3 +949,292 @@ class TestCollectionOwnershipPermissionPolicy(PermissionPolicyTestCase):
|
|||
),
|
||||
[]
|
||||
)
|
||||
|
||||
|
||||
class TestCollectionManagementPermission(PermissionPolicyTestUtils, TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
self.policy = CollectionMangementPermissionPolicy(Collection)
|
||||
|
||||
# Permissions
|
||||
collection_content_type = ContentType.objects.get_for_model(Collection)
|
||||
add_collection_permission = Permission.objects.get(
|
||||
content_type=collection_content_type, codename='add_collection'
|
||||
)
|
||||
change_collection_permission = Permission.objects.get(
|
||||
content_type=collection_content_type, codename='change_collection'
|
||||
)
|
||||
delete_collection_permission = Permission.objects.get(
|
||||
content_type=collection_content_type, codename='delete_collection'
|
||||
)
|
||||
|
||||
# Collections
|
||||
self.root_collection = Collection.get_first_root_node()
|
||||
self.reports_collection = self.root_collection.add_child(name="Reports")
|
||||
self.reports_2020_collection = self.reports_collection.add_child(name="Reports 2020")
|
||||
|
||||
# Users with their groups/permissions
|
||||
self.superuser = self.create_superuser(
|
||||
'superuser', 'superuser@example.com', 'password'
|
||||
)
|
||||
self.inactive_superuser = self.create_superuser(
|
||||
'inactivesuperuser', 'inactivesuperuser@example.com', 'password'
|
||||
)
|
||||
self.inactive_superuser.is_active = False
|
||||
self.inactive_superuser.save()
|
||||
|
||||
# a user with change collection permission on reports via the report_changers group
|
||||
report_changers_group = Group.objects.create(name="Report changers")
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=report_changers_group,
|
||||
collection=self.reports_collection,
|
||||
permission=change_collection_permission
|
||||
)
|
||||
|
||||
self.report_changer = self.create_user(
|
||||
'reportchanger', 'reportchanger@example.com', 'password'
|
||||
)
|
||||
self.report_changer.groups.add(report_changers_group)
|
||||
|
||||
# a user with add collection permission on reports via the report_adders group
|
||||
report_adders_group = Group.objects.create(name="Report adders")
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=report_adders_group,
|
||||
collection=self.reports_collection,
|
||||
permission=add_collection_permission
|
||||
)
|
||||
self.report_adder = self.create_user(
|
||||
'reportadder', 'reportadder@example.com', 'password'
|
||||
)
|
||||
self.report_adder.groups.add(report_adders_group)
|
||||
|
||||
# a user with delete collection permission on reports via the report_deleters group
|
||||
report_deleters_group = Group.objects.create(name="Report deleters")
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=report_deleters_group,
|
||||
collection=self.reports_collection,
|
||||
permission=delete_collection_permission
|
||||
)
|
||||
self.report_deleter = self.create_user(
|
||||
'reportdeleter', 'reportdeleter@example.com', 'password'
|
||||
)
|
||||
self.report_deleter.groups.add(report_deleters_group)
|
||||
|
||||
# a user with no permissions
|
||||
self.useless_user = self.create_user(
|
||||
'uselessuser', 'uselessuser@example.com', 'password'
|
||||
)
|
||||
|
||||
self.anonymous_user = AnonymousUser()
|
||||
|
||||
def test_user_has_permission(self):
|
||||
self.assertUserPermissionMatrix([
|
||||
(self.superuser, True, True, True, True),
|
||||
(self.inactive_superuser, False, False, False, False),
|
||||
(self.report_changer, False, True, False, False),
|
||||
(self.report_adder, True, False, False, False),
|
||||
(self.report_deleter, False, False, True, False),
|
||||
(self.useless_user, False, False, False, False),
|
||||
(self.anonymous_user, False, False, False, False),
|
||||
])
|
||||
|
||||
def test_user_has_any_permission(self):
|
||||
users_with_permissions = [self.superuser, self.report_changer, self.report_adder, self.report_deleter]
|
||||
users_without_permissions = [self.inactive_superuser, self.useless_user, self.anonymous_user]
|
||||
|
||||
for user in users_with_permissions:
|
||||
self.assertTrue(
|
||||
self.policy.user_has_any_permission(user, ['add', 'change', 'delete'])
|
||||
)
|
||||
for user in users_without_permissions:
|
||||
self.assertFalse(
|
||||
self.policy.user_has_any_permission(user, ['add', 'change', 'delete'])
|
||||
)
|
||||
|
||||
def test_users_with_any_permission(self):
|
||||
users_with_add_or_change_or_delete_permission = self.policy.users_with_any_permission(
|
||||
['add', 'change', 'delete']
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(users_with_add_or_change_or_delete_permission, [
|
||||
self.superuser,
|
||||
self.report_changer,
|
||||
self.report_adder,
|
||||
self.report_deleter,
|
||||
])
|
||||
|
||||
def test_users_with_permission(self):
|
||||
users_with_change_permission = self.policy.users_with_permission('change')
|
||||
|
||||
self.assertResultSetEqual(users_with_change_permission, [
|
||||
self.superuser,
|
||||
self.report_changer,
|
||||
])
|
||||
|
||||
users_with_custom_permission = self.policy.users_with_permission('frobnicate')
|
||||
|
||||
self.assertResultSetEqual(users_with_custom_permission, [
|
||||
self.superuser,
|
||||
])
|
||||
|
||||
def test_only_superuser_has_permission_for_root_collection(self):
|
||||
self.assertUserInstancePermissionMatrix(self.root_collection, [
|
||||
(self.superuser, True, True, True),
|
||||
(self.inactive_superuser, False, False, False),
|
||||
(self.report_changer, False, False, False),
|
||||
(self.report_adder, False, False, False),
|
||||
(self.report_deleter, False, False, False),
|
||||
(self.useless_user, False, False, False),
|
||||
(self.anonymous_user, False, False, False),
|
||||
])
|
||||
|
||||
def test_user_has_permission_for_instance(self):
|
||||
# Reports collection is editable - as are its children
|
||||
self.assertUserInstancePermissionMatrix(self.reports_collection, [
|
||||
(self.superuser, True, True, True),
|
||||
(self.inactive_superuser, False, False, False),
|
||||
(self.report_changer, True, False, False),
|
||||
(self.report_deleter, False, True, False),
|
||||
(self.useless_user, False, False, False),
|
||||
(self.anonymous_user, False, False, False),
|
||||
])
|
||||
|
||||
self.assertUserInstancePermissionMatrix(self.reports_2020_collection, [
|
||||
(self.superuser, True, True, True),
|
||||
(self.inactive_superuser, False, False, False),
|
||||
(self.report_changer, True, False, False),
|
||||
(self.report_deleter, False, True, False),
|
||||
(self.useless_user, False, False, False),
|
||||
(self.anonymous_user, False, False, False),
|
||||
])
|
||||
|
||||
def test_user_has_any_permission_for_instance(self):
|
||||
users_with_permissions = [self.superuser, self.report_changer, self.report_adder, self.report_deleter]
|
||||
|
||||
for user in users_with_permissions:
|
||||
self.assertTrue(
|
||||
self.policy.user_has_any_permission_for_instance(
|
||||
user, ['add', 'change', 'delete'], self.reports_collection
|
||||
)
|
||||
)
|
||||
|
||||
self.assertFalse(
|
||||
self.policy.user_has_any_permission_for_instance(
|
||||
self.report_adder, ['add', 'change', 'delete'], self.root_collection
|
||||
)
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
self.policy.user_has_any_permission_for_instance(
|
||||
self.superuser, ['add', 'change', 'delete'], self.root_collection
|
||||
)
|
||||
)
|
||||
|
||||
def test_instances_user_has_permission_for(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_permission_for(self.superuser, 'change'),
|
||||
[self.root_collection, self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_permission_for(self.report_adder, 'add'),
|
||||
[self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_permission_for(self.report_adder, 'change'),
|
||||
[]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_permission_for(self.inactive_superuser, 'change'),
|
||||
[]
|
||||
)
|
||||
|
||||
def test_instances_user_has_any_permission_for(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_any_permission_for(self.superuser, ['add', 'change']),
|
||||
[self.root_collection, self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_any_permission_for(self.report_adder, ['add', 'change']),
|
||||
[self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.instances_user_has_any_permission_for(self.inactive_superuser, ['add', 'change']),
|
||||
[]
|
||||
)
|
||||
|
||||
def test_users_with_permission_for_instance(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.users_with_permission_for_instance('change', self.root_collection),
|
||||
[
|
||||
self.superuser
|
||||
]
|
||||
)
|
||||
self.assertResultSetEqual(
|
||||
self.policy.users_with_permission_for_instance('change', self.reports_collection),
|
||||
[
|
||||
self.superuser, self.report_changer
|
||||
]
|
||||
)
|
||||
self.assertResultSetEqual(
|
||||
self.policy.users_with_permission_for_instance('add', self.reports_collection),
|
||||
[
|
||||
self.superuser, self.report_adder
|
||||
]
|
||||
)
|
||||
|
||||
def test_users_with_any_permission_for_instance(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.users_with_any_permission_for_instance(
|
||||
['add', 'change', 'delete'], self.reports_2020_collection
|
||||
),
|
||||
[
|
||||
self.superuser, self.report_adder, self.report_changer, self.report_deleter
|
||||
]
|
||||
)
|
||||
|
||||
def test_collections_user_has_permission_for(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_permission_for(self.superuser, 'change'),
|
||||
[self.root_collection, self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_permission_for(self.report_adder, 'add'),
|
||||
[self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_permission_for(self.report_adder, 'change'),
|
||||
[]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_permission_for(self.inactive_superuser, 'change'),
|
||||
[]
|
||||
)
|
||||
|
||||
def test_collections_user_has_any_permission_for(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_any_permission_for(self.superuser, ['add', 'change']),
|
||||
[self.root_collection, self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_any_permission_for(self.report_adder, ['add', 'change']),
|
||||
[self.reports_collection, self.reports_2020_collection]
|
||||
)
|
||||
|
||||
self.assertResultSetEqual(
|
||||
self.policy.collections_user_has_any_permission_for(self.inactive_superuser, ['add', 'change']),
|
||||
[]
|
||||
)
|
||||
|
||||
def test_descendants_of_collections_with_user_perm(self):
|
||||
self.assertResultSetEqual(
|
||||
self.policy.descendants_of_collections_with_user_perm(self.report_adder, ['add', 'change']),
|
||||
[self.reports_2020_collection]
|
||||
)
|
||||
|
|
|
@ -59,14 +59,6 @@ def register_core_features(features):
|
|||
features.default_features.append('ul')
|
||||
|
||||
|
||||
@hooks.register('register_permissions')
|
||||
def register_collection_permissions():
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label='wagtailcore',
|
||||
codename__in=['add_collection', 'change_collection', 'delete_collection']
|
||||
)
|
||||
|
||||
|
||||
if getattr(settings, 'WAGTAIL_WORKFLOW_ENABLED', True):
|
||||
@hooks.register('register_permissions')
|
||||
def register_workflow_permissions():
|
||||
|
|
Ładowanie…
Reference in New Issue