forked from ungleich-public/dynamicweb
558 lines
29 KiB
Python
558 lines
29 KiB
Python
from django.contrib.auth.models import AnonymousUser, Group
|
|
from django.contrib.sites.models import Site
|
|
from django.test import override_settings
|
|
|
|
from cms.api import add_plugin, create_page
|
|
from cms.cache.permissions import get_permission_cache, set_permission_cache
|
|
from cms.constants import PAGE_TYPES_ID
|
|
from cms.models import (
|
|
ACCESS_CHILDREN,
|
|
ACCESS_PAGE,
|
|
ACCESS_PAGE_AND_DESCENDANTS,
|
|
CMSPlugin,
|
|
Page,
|
|
PageContent,
|
|
PagePermission,
|
|
)
|
|
from cms.test_utils.testcases import CMSTestCase
|
|
from cms.utils import page_permissions
|
|
|
|
|
|
@override_settings(CMS_PERMISSION=True, CMS_PUBLIC_FOR="all")
|
|
class CopyPermissionsTests(CMSTestCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.admin = self.get_superuser()
|
|
self.actor = self.get_staff_user_with_no_permissions()
|
|
for codename in ("add_page", "change_page", "add_text", "change_text"):
|
|
self.add_permission(self.actor, codename)
|
|
self.target = create_page("target", "nav_playground.html", "en")
|
|
self.target_permission = self.add_page_permission(
|
|
self.actor, self.target, can_add=True, can_change=True, grant_on=ACCESS_PAGE
|
|
)
|
|
self.source = create_page("source", "nav_playground.html", "en")
|
|
self.secret = create_page("secret", "nav_playground.html", "en", parent=self.source)
|
|
self.add_page_permission(self.admin, self.secret, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.marker = "CONFIDENTIAL-COPY-REGRESSION"
|
|
add_plugin(self.secret.get_placeholders("en").get(slot="body"), "TextPlugin", "en", body=self.marker)
|
|
|
|
def copy(self, copy_permissions, source=None, position=0):
|
|
with self.login_user_context(self.actor):
|
|
response = self.client.post(
|
|
self.get_admin_url(Page, "copy_page", (source or self.source).pk),
|
|
{"position": position, "target": self.target.pk, "copy_permissions": copy_permissions},
|
|
)
|
|
# Permission checks after the POST should use a fresh request's user.
|
|
self.actor = type(self.actor).objects.get(pk=self.actor.pk)
|
|
return response
|
|
|
|
def assertCopyDenied(self, copy_permissions):
|
|
counts = (Page.objects.count(), CMSPlugin.objects.count(), PagePermission.objects.count())
|
|
response = self.copy(copy_permissions)
|
|
self.assertEqual(response.json()["status"], 403)
|
|
self.assertEqual(counts, (Page.objects.count(), CMSPlugin.objects.count(), PagePermission.objects.count()))
|
|
|
|
def test_unreadable_descendant_requires_copy_permissions(self):
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, self.source))
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, self.secret))
|
|
self.assertFalse(page_permissions.user_can_change_page(self.actor, self.secret))
|
|
self.assertEqual(self.client.get(self.secret.get_absolute_url()).status_code, 404)
|
|
self.assertCopyDenied("")
|
|
|
|
def test_unreadable_descendant_can_be_copied_with_permissions(self):
|
|
response = self.copy("on")
|
|
self.assertEqual(response.status_code, 200)
|
|
copied = Page.objects.get(pk=response.json()["id"]).get_child_pages().get()
|
|
self.assertTrue(copied.has_view_restrictions(copied.site))
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, copied))
|
|
self.assertEqual(self.client.get(copied.get_absolute_url()).status_code, 404)
|
|
with self.login_user_context(self.admin):
|
|
self.assertContains(self.client.get(copied.get_absolute_url()), self.marker)
|
|
|
|
def test_destination_change_grant_cannot_expose_unreadable_descendant(self):
|
|
self.target_permission.grant_on = ACCESS_PAGE_AND_DESCENDANTS
|
|
self.target_permission.save()
|
|
self.assertCopyDenied("on")
|
|
|
|
def test_destination_view_grant_cannot_expose_unreadable_descendant(self):
|
|
self.add_page_permission(self.actor, self.target, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
self.assertCopyDenied("on")
|
|
|
|
def test_destination_group_grant_cannot_expose_unreadable_descendant(self):
|
|
group = Group.objects.create(name="Destination viewers")
|
|
self.actor.groups.add(group)
|
|
self.add_page_permission(None, self.target, group=group, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
self.assertCopyDenied("on")
|
|
|
|
@override_settings(
|
|
CMS_LANGUAGES={
|
|
1: [{"code": "en", "name": "English"}],
|
|
2: [{"code": "en", "name": "English"}],
|
|
}
|
|
)
|
|
def test_destination_site_global_grant_cannot_expose_unreadable_descendant(self):
|
|
site = Site.objects.create(domain="destination.example", name="Destination")
|
|
self.target = create_page("other-site", "nav_playground.html", "en", site=site)
|
|
permission = self.add_global_permission(self.actor, can_add=True, can_change=True)
|
|
permission.sites.set([site])
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, self.secret))
|
|
with self.settings(SITE_ID=site.pk):
|
|
self.assertCopyDenied("on")
|
|
|
|
def test_unreadable_grandchild_requires_copy_permissions(self):
|
|
self.secret.pagepermission_set.all().delete()
|
|
grandchild = create_page("grandchild", "nav_playground.html", "en", parent=self.secret)
|
|
self.add_page_permission(self.admin, grandchild, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, self.secret))
|
|
self.assertCopyDenied("")
|
|
|
|
def test_destination_immediate_children_grant_does_not_expose_descendant(self):
|
|
self.add_page_permission(self.actor, self.target, can_view=True, grant_on=ACCESS_CHILDREN)
|
|
response = self.copy("on")
|
|
self.assertEqual(response.status_code, 200)
|
|
copied = Page.objects.get(pk=response.json()["id"]).get_child_pages().get()
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, copied))
|
|
|
|
def test_copy_permissions_preserves_root_restriction(self):
|
|
self.add_page_permission(self.actor, self.source, can_view=True, grant_on=ACCESS_PAGE)
|
|
response = self.copy("on")
|
|
self.assertEqual(response.status_code, 200)
|
|
copied = Page.objects.get(pk=response.json()["id"])
|
|
self.assertTrue(copied.pagepermission_set.filter(user=self.actor, can_view=True).exists())
|
|
self.assertEqual(self.client.get(copied.get_absolute_url()).status_code, 404)
|
|
|
|
def test_copy_permissions_invalidates_permission_caches(self):
|
|
self.add_page_permission(self.actor, self.source, can_change=True, grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_change_page(self.actor, self.source))
|
|
|
|
copied = self.source.copy(
|
|
self.source.site,
|
|
parent_page=self.target,
|
|
permissions=True,
|
|
user=self.actor,
|
|
)
|
|
|
|
self.assertTrue(page_permissions.user_can_change_page(self.actor, copied))
|
|
|
|
def test_copy_permissions_invalidates_another_users_cached_actions(self):
|
|
recipient = self._create_user("recipient", is_staff=True, is_superuser=False)
|
|
self.add_permission(recipient, "change_page")
|
|
self.add_page_permission(recipient, self.source, can_change=True, grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_change_page(recipient, self.source))
|
|
|
|
copied = self.source.copy(
|
|
self.source.site,
|
|
parent_page=self.target,
|
|
permissions=True,
|
|
user=self.actor,
|
|
)
|
|
|
|
self.assertTrue(page_permissions.user_can_change_page(recipient, copied))
|
|
|
|
def test_copy_permissions_invalidates_cache_again_on_commit(self):
|
|
self.add_page_permission(self.actor, self.source, can_change=True, grant_on=ACCESS_PAGE)
|
|
|
|
with self.captureOnCommitCallbacks(execute=True) as callbacks:
|
|
self.source.copy(
|
|
self.source.site,
|
|
parent_page=self.target,
|
|
permissions=True,
|
|
user=self.actor,
|
|
)
|
|
# Simulate a concurrent request caching pre-commit permission rows.
|
|
set_permission_cache(self.actor, self.source.site, "change_page", [])
|
|
self.assertEqual(get_permission_cache(self.actor, self.source.site, "change_page"), [])
|
|
|
|
self.assertEqual(len(callbacks), 1)
|
|
self.assertIsNone(get_permission_cache(self.actor, self.source.site, "change_page"))
|
|
|
|
def test_readable_descendants_can_be_copied_without_permissions(self):
|
|
self.add_page_permission(self.actor, self.secret, can_view=True, grant_on=ACCESS_PAGE)
|
|
response = self.copy("")
|
|
self.assertEqual(response.status_code, 200)
|
|
copied = Page.objects.get(pk=response.json()["id"]).get_child_pages().get()
|
|
self.assertFalse(copied.has_view_restrictions(copied.site))
|
|
self.assertContains(self.client.get(copied.get_absolute_url()), self.marker)
|
|
|
|
def test_inherited_restrictions_survive_outside_source_subtree(self):
|
|
ancestor = create_page("ancestor", "nav_playground.html", "en")
|
|
source = create_page("nested-source", "nav_playground.html", "en", parent=ancestor)
|
|
child = create_page("nested-child", "nav_playground.html", "en", parent=source)
|
|
self.add_page_permission(self.admin, ancestor, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
self.add_page_permission(self.actor, ancestor, can_view=True, grant_on=ACCESS_CHILDREN)
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, source))
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, child))
|
|
response = self.copy("on", source=source)
|
|
self.assertEqual(response.status_code, 200)
|
|
copied = Page.objects.get(pk=response.json()["id"])
|
|
copied_child = copied.get_child_pages().get()
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, copied))
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, copied_child))
|
|
for page in (copied, copied_child):
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), page))
|
|
self.assertTrue(PagePermission.objects.for_page(page).filter(user=self.admin, can_view=True).exists())
|
|
self.assertFalse(page.pagepermission_set.filter(can_change=True).exists())
|
|
added_child = create_page("later-child", "nav_playground.html", "en", parent=copied_child)
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), added_child))
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, added_child))
|
|
|
|
def test_unreadable_root_is_still_rejected(self):
|
|
response = self.copy("on", source=self.secret)
|
|
self.assertEqual(response.json()["status"], 403)
|
|
|
|
|
|
@override_settings(CMS_PERMISSION=True, CMS_PUBLIC_FOR="all")
|
|
class MovePermissionsTests(CMSTestCase):
|
|
"""A moved subtree leaves the ancestors that restricted it behind."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.admin = self.get_superuser()
|
|
self.actor = self.get_staff_user_with_no_permissions()
|
|
for codename in ("add_page", "change_page", "add_text", "change_text"):
|
|
self.add_permission(self.actor, codename)
|
|
self.target = create_page("target", "nav_playground.html", "en")
|
|
self.target_permission = self.add_page_permission(
|
|
self.actor, self.target, can_add=True, can_change=True, grant_on=ACCESS_PAGE
|
|
)
|
|
self.ancestor = create_page("ancestor", "nav_playground.html", "en")
|
|
self.add_page_permission(self.admin, self.ancestor, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
self.source = create_page("source", "nav_playground.html", "en", parent=self.ancestor)
|
|
self.secret = create_page("secret", "nav_playground.html", "en", parent=self.source)
|
|
self.marker = "CONFIDENTIAL-MOVE-REGRESSION"
|
|
add_plugin(self.secret.get_placeholders("en").get(slot="body"), "TextPlugin", "en", body=self.marker)
|
|
|
|
def move(self, page=None, target=None, position=0):
|
|
page = page or self.source
|
|
with self.login_user_context(self.actor):
|
|
response = self.client.post(
|
|
self.get_admin_url(Page, "move_page", page.pk),
|
|
{"id": page.pk, "position": position, "target": (target or self.target).pk},
|
|
)
|
|
# Permission checks after the POST should use a fresh request's user.
|
|
self.actor = type(self.actor).objects.get(pk=self.actor.pk)
|
|
return response
|
|
|
|
def grant_source(self, grant_on=ACCESS_PAGE_AND_DESCENDANTS):
|
|
return self.add_page_permission(
|
|
self.actor, self.source, can_change=True, can_move_page=True, grant_on=grant_on
|
|
)
|
|
|
|
def test_move_preserves_inherited_restriction(self):
|
|
"""The editor of a restricted branch cannot publish it by moving it."""
|
|
self.grant_source()
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, self.secret))
|
|
|
|
response = self.move()
|
|
self.assertEqual(response.json().get("status", 200), 200)
|
|
|
|
source = Page.objects.get(pk=self.source.pk)
|
|
secret = Page.objects.get(pk=self.secret.pk)
|
|
self.assertEqual(source.parent_id, self.target.pk)
|
|
for page in (source, secret):
|
|
self.assertTrue(page.has_view_restrictions(page.site))
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), page))
|
|
self.assertEqual(self.client.get(page.get_absolute_url()).status_code, 404)
|
|
with self.login_user_context(self.admin):
|
|
self.assertContains(self.client.get(secret.get_absolute_url()), self.marker)
|
|
|
|
def test_move_grants_no_editing_privileges(self):
|
|
"""Only ``can_view`` is materialized, never the ancestor's other flags."""
|
|
self.add_page_permission(self.admin, self.ancestor, can_change=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
self.grant_source()
|
|
self.move()
|
|
|
|
source = Page.objects.get(pk=self.source.pk)
|
|
self.assertTrue(source.pagepermission_set.filter(user=self.admin, can_view=True).exists())
|
|
# The actor's own grant stays; the ancestor's can_change is not imported.
|
|
self.assertFalse(source.pagepermission_set.filter(user=self.admin, can_change=True).exists())
|
|
|
|
def test_move_keeps_unreadable_descendant_hidden(self):
|
|
"""The mover may not read ``secret``; the move must not change that."""
|
|
self.grant_source(grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, self.source))
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, self.secret))
|
|
|
|
response = self.move()
|
|
self.assertEqual(response.json().get("status", 200), 200)
|
|
|
|
secret = Page.objects.get(pk=self.secret.pk)
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, secret))
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), secret))
|
|
self.assertEqual(self.client.get(secret.get_absolute_url()).status_code, 404)
|
|
|
|
def test_destination_change_grant_cannot_expose_unreadable_descendant(self):
|
|
"""A destination grant would widen access, so the move is refused."""
|
|
self.grant_source(grant_on=ACCESS_PAGE)
|
|
self.target_permission.grant_on = ACCESS_PAGE_AND_DESCENDANTS
|
|
self.target_permission.save()
|
|
|
|
response = self.move()
|
|
self.assertEqual(response.json()["status"], 403)
|
|
self.assertEqual(Page.objects.get(pk=self.source.pk).parent_id, self.ancestor.pk)
|
|
|
|
def test_destination_view_grant_cannot_expose_unreadable_descendant(self):
|
|
self.grant_source(grant_on=ACCESS_PAGE)
|
|
self.add_page_permission(self.actor, self.target, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
|
|
response = self.move()
|
|
self.assertEqual(response.json()["status"], 403)
|
|
self.assertEqual(Page.objects.get(pk=self.source.pk).parent_id, self.ancestor.pk)
|
|
|
|
def test_move_inside_restricted_branch_adds_no_permissions(self):
|
|
"""Reordering inside the branch keeps inheritance, so nothing is copied."""
|
|
self.grant_source()
|
|
self.add_page_permission(self.actor, self.ancestor, can_add=True, can_change=True,
|
|
grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
count = PagePermission.objects.count()
|
|
|
|
response = self.move(target=self.ancestor)
|
|
self.assertEqual(response.json().get("status", 200), 200)
|
|
|
|
source = Page.objects.get(pk=self.source.pk)
|
|
self.assertEqual(PagePermission.objects.count(), count)
|
|
self.assertTrue(source.has_view_restrictions(source.site))
|
|
|
|
def test_move_into_restricted_branch_adds_no_permissions(self):
|
|
"""An unrestricted page moved into a restricted branch inherits, no rows."""
|
|
public = create_page("public", "nav_playground.html", "en")
|
|
self.add_page_permission(self.actor, public, can_change=True, can_move_page=True, grant_on=ACCESS_PAGE)
|
|
self.add_page_permission(self.actor, self.ancestor, can_add=True, can_change=True,
|
|
grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
count = PagePermission.objects.count()
|
|
|
|
response = self.move(page=public, target=self.ancestor)
|
|
self.assertEqual(response.json().get("status", 200), 200)
|
|
|
|
public = Page.objects.get(pk=public.pk)
|
|
self.assertEqual(PagePermission.objects.count(), count)
|
|
self.assertTrue(public.has_view_restrictions(public.site))
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), public))
|
|
|
|
def test_move_of_unrestricted_page_adds_no_permissions(self):
|
|
public = create_page("public", "nav_playground.html", "en")
|
|
self.add_page_permission(self.actor, public, can_change=True, can_move_page=True, grant_on=ACCESS_PAGE)
|
|
count = PagePermission.objects.count()
|
|
|
|
response = self.move(page=public)
|
|
self.assertEqual(response.json().get("status", 200), 200)
|
|
|
|
public = Page.objects.get(pk=public.pk)
|
|
self.assertEqual(PagePermission.objects.count(), count)
|
|
self.assertFalse(public.has_view_restrictions(public.site))
|
|
self.assertEqual(self.client.get(public.get_absolute_url()).status_code, 200)
|
|
|
|
|
|
@override_settings(CMS_PERMISSION=True, CMS_PUBLIC_FOR="all")
|
|
class DuplicatePermissionsTests(CMSTestCase):
|
|
"""``Duplicate`` copies a single page; its restrictions come along."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.admin = self.get_superuser()
|
|
self.actor = self.get_staff_user_with_no_permissions()
|
|
for codename in ("add_page", "change_page", "add_text", "change_text"):
|
|
self.add_permission(self.actor, codename)
|
|
self.target = create_page("target", "nav_playground.html", "en")
|
|
self.add_page_permission(self.actor, self.target, can_add=True, can_change=True, grant_on=ACCESS_PAGE)
|
|
self.marker = "CONFIDENTIAL-DUPLICATE-REGRESSION"
|
|
|
|
def duplicate(self, source):
|
|
content = source.get_admin_content("en")
|
|
endpoint = self.get_admin_url(PageContent, "duplicate", content.pk)
|
|
with self.login_user_context(self.actor):
|
|
response = self.client.post(
|
|
f"{endpoint}?parent_page={self.target.pk}",
|
|
{
|
|
"title": "duplicate",
|
|
"slug": "duplicate",
|
|
"template": "nav_playground.html",
|
|
"language": "en",
|
|
"source": source.pk,
|
|
"parent_page": self.target.pk,
|
|
"_save": 1,
|
|
},
|
|
)
|
|
self.actor = type(self.actor).objects.get(pk=self.actor.pk)
|
|
return response, self.target.get_child_pages().first()
|
|
|
|
def test_duplicate_preserves_own_restriction(self):
|
|
source = create_page("source", "nav_playground.html", "en")
|
|
add_plugin(source.get_placeholders("en").get(slot="body"), "TextPlugin", "en", body=self.marker)
|
|
self.add_page_permission(self.admin, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.add_page_permission(self.actor, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, source))
|
|
|
|
response, copy = self.duplicate(source)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIsNotNone(copy)
|
|
self.assertTrue(copy.has_view_restrictions(copy.site))
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), copy))
|
|
self.assertEqual(self.client.get(copy.get_absolute_url()).status_code, 404)
|
|
self.assertFalse(copy.pagepermission_set.filter(can_change=True).exists())
|
|
with self.login_user_context(self.admin):
|
|
self.assertContains(self.client.get(copy.get_absolute_url()), self.marker)
|
|
|
|
def test_duplicate_preserves_inherited_restriction(self):
|
|
ancestor = create_page("ancestor", "nav_playground.html", "en")
|
|
self.add_page_permission(self.admin, ancestor, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
self.add_page_permission(self.actor, ancestor, can_view=True, grant_on=ACCESS_PAGE_AND_DESCENDANTS)
|
|
source = create_page("source", "nav_playground.html", "en", parent=ancestor)
|
|
add_plugin(source.get_placeholders("en").get(slot="body"), "TextPlugin", "en", body=self.marker)
|
|
|
|
response, copy = self.duplicate(source)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertTrue(copy.has_view_restrictions(copy.site))
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), copy))
|
|
self.assertEqual(self.client.get(copy.get_absolute_url()).status_code, 404)
|
|
# A page added below the copy stays protected, as it would below the source.
|
|
child = create_page("child", "nav_playground.html", "en", parent=copy)
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), child))
|
|
|
|
def test_duplicate_of_unrestricted_page_adds_no_permissions(self):
|
|
source = create_page("source", "nav_playground.html", "en")
|
|
add_plugin(source.get_placeholders("en").get(slot="body"), "TextPlugin", "en", body=self.marker)
|
|
count = PagePermission.objects.count()
|
|
|
|
response, copy = self.duplicate(source)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(PagePermission.objects.count(), count)
|
|
self.assertFalse(copy.has_view_restrictions(copy.site))
|
|
self.assertContains(self.client.get(copy.get_absolute_url()), self.marker)
|
|
|
|
|
|
@override_settings(CMS_PERMISSION=True, CMS_PUBLIC_FOR="all")
|
|
class PageTypeSourcePermissionsTests(CMSTestCase):
|
|
"""``Add page`` can copy a page type; its restrictions come along.
|
|
|
|
Page types can no longer be created through the UI, but rows migrated from
|
|
django CMS 3.x are still offered as ``source`` on the add form.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.admin = self.get_superuser()
|
|
self.actor = self.get_staff_user_with_no_permissions()
|
|
for codename in ("add_page", "change_page", "add_text", "change_text"):
|
|
self.add_permission(self.actor, codename)
|
|
self.target = create_page("target", "nav_playground.html", "en")
|
|
self.add_page_permission(self.actor, self.target, can_add=True, can_change=True, grant_on=ACCESS_PAGE)
|
|
self.marker = "CONFIDENTIAL-PAGE-TYPE-REGRESSION"
|
|
|
|
def create_page_type(self, title, site=None):
|
|
root = create_page("Page Types", "nav_playground.html", "en", site=site, reverse_id=PAGE_TYPES_ID)
|
|
page_type = create_page(title, "nav_playground.html", "en", site=site, parent=root)
|
|
Page.objects.filter(pk__in=(root.pk, page_type.pk)).update(is_page_type=True)
|
|
add_plugin(page_type.get_placeholders("en").get(slot="body"), "TextPlugin", "en", body=self.marker)
|
|
return Page.objects.get(pk=page_type.pk)
|
|
|
|
def add_from_source(self, source):
|
|
endpoint = self.get_admin_url(PageContent, "add")
|
|
with self.login_user_context(self.actor):
|
|
response = self.client.post(
|
|
f"{endpoint}?parent_page={self.target.pk}",
|
|
{
|
|
"title": "from-page-type",
|
|
"slug": "from-page-type",
|
|
"template": "nav_playground.html",
|
|
"language": "en",
|
|
"source": source.pk,
|
|
"parent_page": self.target.pk,
|
|
"_save": 1,
|
|
},
|
|
)
|
|
self.actor = type(self.actor).objects.get(pk=self.actor.pk)
|
|
return response, self.target.get_child_pages().first()
|
|
|
|
def assertAddDenied(self, source):
|
|
counts = (Page.objects.count(), CMSPlugin.objects.count())
|
|
response, copy = self.add_from_source(source)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIsNone(copy)
|
|
self.assertEqual(counts, (Page.objects.count(), CMSPlugin.objects.count()))
|
|
|
|
def test_rejects_page_type_user_cannot_view(self):
|
|
source = self.create_page_type("secret-type")
|
|
self.add_page_permission(self.admin, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.assertFalse(page_permissions.user_can_view_page(self.actor, source))
|
|
self.assertAddDenied(source)
|
|
|
|
@override_settings(
|
|
CMS_LANGUAGES={
|
|
1: [{"code": "en", "name": "English"}],
|
|
2: [{"code": "en", "name": "English"}],
|
|
}
|
|
)
|
|
def test_rejects_page_type_from_another_site(self):
|
|
site = Site.objects.create(domain="other.example", name="Other")
|
|
self.assertAddDenied(self.create_page_type("other-site-type", site=site))
|
|
|
|
def test_rejects_page_type_root(self):
|
|
source = self.create_page_type("visible-type")
|
|
self.assertAddDenied(source.parent)
|
|
|
|
def test_readable_page_type_can_be_used(self):
|
|
source = self.create_page_type("visible-type")
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, source))
|
|
|
|
response, copy = self.add_from_source(source)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIsNotNone(copy)
|
|
self.assertFalse(copy.is_page_type)
|
|
self.assertContains(self.client.get(copy.get_absolute_url()), self.marker)
|
|
|
|
def test_restricted_page_type_keeps_its_restriction(self):
|
|
source = self.create_page_type("restricted-type")
|
|
self.add_page_permission(self.admin, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.add_page_permission(self.actor, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, source))
|
|
|
|
response, copy = self.add_from_source(source)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertTrue(copy.has_view_restrictions(copy.site))
|
|
self.assertFalse(page_permissions.user_can_view_page(AnonymousUser(), copy))
|
|
self.assertEqual(self.client.get(copy.get_absolute_url()).status_code, 404)
|
|
|
|
def test_rejects_source_when_adding_translation(self):
|
|
source = self.create_page_type("restricted-type")
|
|
self.add_page_permission(self.admin, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
self.add_page_permission(self.actor, source, can_view=True, grant_on=ACCESS_PAGE)
|
|
destination = create_page("destination", "nav_playground.html", "de")
|
|
self.add_page_permission(self.actor, destination, can_change=True, grant_on=ACCESS_PAGE)
|
|
self.assertTrue(page_permissions.user_can_view_page(self.actor, source))
|
|
self.assertTrue(page_permissions.user_can_view_page(AnonymousUser(), destination))
|
|
|
|
endpoints = (
|
|
self.get_admin_url(PageContent, "add"),
|
|
self.get_admin_url(PageContent, "duplicate", source.get_content_obj("en").pk),
|
|
)
|
|
data = {
|
|
"cms_page": destination.pk,
|
|
"source": source.pk,
|
|
"language": "en",
|
|
"title": "translation",
|
|
"slug": "translation",
|
|
"_save": 1,
|
|
}
|
|
models = (Page, PageContent, CMSPlugin, PagePermission)
|
|
counts = tuple(model.objects.count() for model in models)
|
|
with self.login_user_context(self.actor):
|
|
for endpoint in endpoints:
|
|
with self.subTest(endpoint=endpoint):
|
|
response = self.client.post(
|
|
f"{endpoint}?cms_page={destination.pk}&language=en&parent_page={self.target.pk}",
|
|
data,
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn("source", response.context["adminform"].form.errors)
|
|
self.assertFalse(destination.pagecontent_set.filter(language="en").exists())
|
|
self.assertEqual(counts, tuple(model.objects.count() for model in models))
|
|
|
|
# Adding a translation without a source remains supported.
|
|
data.pop("source")
|
|
response = self.client.post(
|
|
f"{endpoints[0]}?cms_page={destination.pk}&language=en&parent_page={self.target.pk}",
|
|
data,
|
|
)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertNotContains(self.client.get(destination.get_absolute_url("en")), self.marker)
|