diff --git a/runtests.py b/runtests.py index b8f81f20d5..4dd0476706 100755 --- a/runtests.py +++ b/runtests.py @@ -82,6 +82,9 @@ if not settings.configured: 'wagtail.wagtailredirects', 'wagtail.tests', ], + PASSWORD_HASHERS=( + 'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher + ), WAGTAILSEARCH_BACKENDS=WAGTAILSEARCH_BACKENDS, WAGTAIL_SITE_NAME='Test Site' ) diff --git a/wagtail/tests/fixtures/test.json b/wagtail/tests/fixtures/test.json index 0a468c92d9..99296f7b27 100644 --- a/wagtail/tests/fixtures/test.json +++ b/wagtail/tests/fixtures/test.json @@ -69,7 +69,8 @@ "content_type": ["tests", "eventpage"], "path": "0001000100010001", "url_path": "/home/events/christmas/", - "slug": "christmas" + "slug": "christmas", + "owner": 2 } }, { @@ -96,7 +97,8 @@ "content_type": ["tests", "eventpage"], "path": "0001000100010002", "url_path": "/home/events/tentative-unpublished-event/", - "slug": "tentative-unpublished-event" + "slug": "tentative-unpublished-event", + "owner": 2 } }, { @@ -111,6 +113,34 @@ } }, +{ + "pk": 6, + "model": "wagtailcore.page", + "fields": { + "title": "Someone Else's Event", + "numchild": 1, + "show_in_menus": true, + "live": false, + "depth": 4, + "content_type": ["tests", "eventpage"], + "path": "0001000100010003", + "url_path": "/home/events/someone-elses-event/", + "slug": "someone-elses-event", + "owner": 3 + } +}, +{ + "pk": 6, + "model": "tests.eventpage", + "fields": { + "date_from": "2015-07-04", + "audience": "private", + "location": "The moon", + "body": "

your name's not down, you're not coming in

", + "cost": "Free (but not for you)" + } +}, + { "pk": 1, "model": "wagtailcore.site", @@ -120,5 +150,140 @@ "port": 80, "is_default_site": true } +}, + +{ + "pk": 3, + "model": "auth.group", + "fields": { + "name": "Event editors", + "permissions": [ + ["access_admin", "wagtailadmin", "admin"], + ["add_image", "wagtailimages", "image"], + ["change_image", "wagtailimages", "image"], + ["delete_image", "wagtailimages", "image"] + ] + } +}, +{ + "pk": 4, + "model": "auth.group", + "fields": { + "name": "Event moderators", + "permissions": [ + ["access_admin", "wagtailadmin", "admin"], + ["add_image", "wagtailimages", "image"], + ["change_image", "wagtailimages", "image"], + ["delete_image", "wagtailimages", "image"] + ] + } +}, +{ + "pk": 1, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event editors"], + "page": 3, + "permission_type": "add" + } +}, +{ + "pk": 2, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event moderators"], + "page": 3, + "permission_type": "add" + } +}, +{ + "pk": 3, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event moderators"], + "page": 3, + "permission_type": "edit" + } +}, +{ + "pk": 4, + "model": "wagtailcore.grouppagepermission", + "fields": { + "group": ["Event moderators"], + "page": 3, + "permission_type": "publish" + } +}, + +{ + "pk": 1, + "model": "auth.user", + "fields": { + "username": "superuser", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": true, + "is_staff": true, + "groups": [ + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "superuser@example.com" + } +}, +{ + "pk": 2, + "model": "auth.user", + "fields": { + "username": "eventeditor", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "groups": [ + ["Event editors"] + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "eventeditor@example.com" + } +}, +{ + "pk": 3, + "model": "auth.user", + "fields": { + "username": "eventmoderator", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "groups": [ + ["Event moderators"] + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "eventmoderator@example.com" + } +}, +{ + "pk": 4, + "model": "auth.user", + "fields": { + "username": "inactiveuser", + "first_name": "", + "last_name": "", + "is_active": false, + "is_superuser": false, + "is_staff": false, + "groups": [ + ["Event moderators"] + ], + "user_permissions": [], + "password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22", + "email": "inactiveuser@example.com" + } } ] diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index df6caf888e..90e6998080 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -742,7 +742,7 @@ class PagePermissionTester(object): def can_move_to(self, destination): # reject the logically impossible cases first - if self.page == destination or destination.is_child_of(self.page): + if self.page == destination or destination.is_descendant_of(self.page): return False # and shortcut the trivial 'everything' / 'nothing' permissions diff --git a/wagtail/wagtailcore/tests.py b/wagtail/wagtailcore/tests.py index eb1b15a3dc..58ff51ada0 100644 --- a/wagtail/wagtailcore/tests.py +++ b/wagtail/wagtailcore/tests.py @@ -1,8 +1,9 @@ from django.test import TestCase, Client from django.http import HttpRequest, Http404 -from wagtail.wagtailcore.models import Page, Site +from django.contrib.auth.models import User +from wagtail.wagtailcore.models import Page, Site from wagtail.tests.models import EventPage @@ -134,3 +135,164 @@ class TestServeView(TestCase): c = Client() response = c.get('/christmas/', HTTP_HOST='localhost') self.assertEqual(response.status_code, 404) + + +class TestPagePermission(TestCase): + fixtures = ['test.json'] + + def test_nonpublisher_page_permissions(self): + event_editor = User.objects.get(username='eventeditor') + homepage = Page.objects.get(url_path='/home/') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + someone_elses_event_page = EventPage.objects.get(url_path='/home/events/someone-elses-event/') + + homepage_perms = homepage.permissions_for_user(event_editor) + christmas_page_perms = christmas_page.permissions_for_user(event_editor) + unpub_perms = unpublished_event_page.permissions_for_user(event_editor) + someone_elses_event_perms = someone_elses_event_page.permissions_for_user(event_editor) + + self.assertFalse(homepage_perms.can_add_subpage()) + self.assertTrue(christmas_page_perms.can_add_subpage()) + self.assertTrue(unpub_perms.can_add_subpage()) + self.assertTrue(someone_elses_event_perms.can_add_subpage()) + + self.assertFalse(homepage_perms.can_edit()) + self.assertTrue(christmas_page_perms.can_edit()) + self.assertTrue(unpub_perms.can_edit()) + self.assertFalse(someone_elses_event_perms.can_edit()) # basic 'add' permission doesn't allow editing pages owned by someone else + + self.assertFalse(homepage_perms.can_delete()) + self.assertFalse(christmas_page_perms.can_delete()) # cannot delete because it is published + self.assertTrue(unpub_perms.can_delete()) + self.assertFalse(someone_elses_event_perms.can_delete()) + + self.assertFalse(homepage_perms.can_publish()) + self.assertFalse(christmas_page_perms.can_publish()) + self.assertFalse(unpub_perms.can_publish()) + + self.assertFalse(homepage_perms.can_unpublish()) + self.assertFalse(christmas_page_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_unpublish()) + + self.assertFalse(homepage_perms.can_publish_subpage()) + self.assertFalse(christmas_page_perms.can_publish_subpage()) + self.assertFalse(unpub_perms.can_publish_subpage()) + + self.assertFalse(homepage_perms.can_reorder_children()) + self.assertFalse(christmas_page_perms.can_reorder_children()) + self.assertFalse(unpub_perms.can_reorder_children()) + + self.assertFalse(homepage_perms.can_move()) + self.assertFalse(christmas_page_perms.can_move()) # cannot move because this would involve unpublishing from its current location + self.assertTrue(unpub_perms.can_move()) + self.assertFalse(someone_elses_event_perms.can_move()) + + self.assertFalse(christmas_page_perms.can_move_to(unpublished_event_page)) # cannot move because this would involve unpublishing from its current location + self.assertTrue(unpub_perms.can_move_to(christmas_page)) + self.assertFalse(unpub_perms.can_move_to(homepage)) # no permission to create pages at destination + self.assertFalse(unpub_perms.can_move_to(unpublished_event_page)) # cannot make page a child of itself + + + def test_publisher_page_permissions(self): + event_moderator = User.objects.get(username='eventmoderator') + homepage = Page.objects.get(url_path='/home/') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + + homepage_perms = homepage.permissions_for_user(event_moderator) + christmas_page_perms = christmas_page.permissions_for_user(event_moderator) + unpub_perms = unpublished_event_page.permissions_for_user(event_moderator) + + self.assertFalse(homepage_perms.can_add_subpage()) + self.assertTrue(christmas_page_perms.can_add_subpage()) + self.assertTrue(unpub_perms.can_add_subpage()) + + self.assertFalse(homepage_perms.can_edit()) + self.assertTrue(christmas_page_perms.can_edit()) + self.assertTrue(unpub_perms.can_edit()) + + self.assertFalse(homepage_perms.can_delete()) + self.assertTrue(christmas_page_perms.can_delete()) # cannot delete because it is published + self.assertTrue(unpub_perms.can_delete()) + + self.assertFalse(homepage_perms.can_publish()) + self.assertTrue(christmas_page_perms.can_publish()) + self.assertTrue(unpub_perms.can_publish()) + + self.assertFalse(homepage_perms.can_unpublish()) + self.assertTrue(christmas_page_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_unpublish()) # cannot unpublish a page that isn't published + + self.assertFalse(homepage_perms.can_publish_subpage()) + self.assertTrue(christmas_page_perms.can_publish_subpage()) + self.assertTrue(unpub_perms.can_publish_subpage()) + + self.assertFalse(homepage_perms.can_reorder_children()) + self.assertTrue(christmas_page_perms.can_reorder_children()) + self.assertTrue(unpub_perms.can_reorder_children()) + + self.assertFalse(homepage_perms.can_move()) + self.assertTrue(christmas_page_perms.can_move()) + self.assertTrue(unpub_perms.can_move()) + + self.assertTrue(christmas_page_perms.can_move_to(unpublished_event_page)) + self.assertTrue(unpub_perms.can_move_to(christmas_page)) + self.assertFalse(unpub_perms.can_move_to(homepage)) # no permission to create pages at destination + self.assertFalse(unpub_perms.can_move_to(unpublished_event_page)) # cannot make page a child of itself + + def test_inactive_user_has_no_permissions(self): + user = User.objects.get(username='inactiveuser') + christmas_page = EventPage.objects.get(url_path='/home/events/christmas/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + + christmas_page_perms = christmas_page.permissions_for_user(user) + unpub_perms = unpublished_event_page.permissions_for_user(user) + + self.assertFalse(unpub_perms.can_add_subpage()) + self.assertFalse(unpub_perms.can_edit()) + self.assertFalse(unpub_perms.can_delete()) + self.assertFalse(unpub_perms.can_publish()) + self.assertFalse(christmas_page_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_publish_subpage()) + self.assertFalse(unpub_perms.can_reorder_children()) + self.assertFalse(unpub_perms.can_move()) + self.assertFalse(unpub_perms.can_move_to(christmas_page)) + + def test_superuser_has_full_permissions(self): + user = User.objects.get(username='superuser') + homepage = Page.objects.get(url_path='/home/') + root = Page.objects.get(url_path='/') + unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/') + + homepage_perms = homepage.permissions_for_user(user) + root_perms = root.permissions_for_user(user) + unpub_perms = unpublished_event_page.permissions_for_user(user) + + self.assertTrue(homepage_perms.can_add_subpage()) + self.assertTrue(root_perms.can_add_subpage()) + + self.assertTrue(homepage_perms.can_edit()) + self.assertFalse(root_perms.can_edit()) # root is not a real editable page, even to superusers + + self.assertTrue(homepage_perms.can_delete()) + self.assertFalse(root_perms.can_delete()) + + self.assertTrue(homepage_perms.can_publish()) + self.assertFalse(root_perms.can_publish()) + + self.assertTrue(homepage_perms.can_unpublish()) + self.assertFalse(root_perms.can_unpublish()) + self.assertFalse(unpub_perms.can_unpublish()) + + self.assertTrue(homepage_perms.can_publish_subpage()) + self.assertTrue(root_perms.can_publish_subpage()) + + self.assertTrue(homepage_perms.can_reorder_children()) + self.assertTrue(root_perms.can_reorder_children()) + + self.assertTrue(homepage_perms.can_move()) + self.assertFalse(root_perms.can_move()) + + self.assertTrue(homepage_perms.can_move_to(root)) + self.assertFalse(homepage_perms.can_move_to(unpublished_event_page))