From a7d243b9b613eee102588bf1a9093746fad80af4 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Fri, 16 Aug 2024 13:21:37 +0200 Subject: [PATCH] Ensure that moderators without explicit edit permission on snippets are granted access to the ping endpoint Fixes #12209 --- wagtail/admin/tests/test_editing_sessions.py | 65 +++++++++++++++++++- wagtail/admin/views/editing_sessions.py | 18 ++++-- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/wagtail/admin/tests/test_editing_sessions.py b/wagtail/admin/tests/test_editing_sessions.py index 5a817c1c25..0463e8f227 100644 --- a/wagtail/admin/tests/test_editing_sessions.py +++ b/wagtail/admin/tests/test_editing_sessions.py @@ -10,7 +10,7 @@ from django.utils import timezone from freezegun import freeze_time from wagtail.admin.models import EditingSession -from wagtail.models import GroupPagePermission, Page +from wagtail.models import GroupPagePermission, Page, Workflow, WorkflowContentType from wagtail.test.testapp.models import ( Advert, AdvertWithCustomPrimaryKey, @@ -1100,6 +1100,69 @@ class TestPingView(WagtailTestUtils, TestCase): ) self.assertEqual(response.status_code, 404) + def test_moderator_without_explicit_edit_permission_on_snippet(self): + snippet = FullFeaturedSnippet.objects.create(text="Test snippet") + snippet.save_revision(user=self.other_user) + + # Assign default workflow to the snippet model + snippet_content_type = ContentType.objects.get_for_model(FullFeaturedSnippet) + workflow = Workflow.objects.get() + WorkflowContentType.objects.create( + content_type=snippet_content_type, + workflow=workflow, + ) + + # submit snippet for moderation + workflow = snippet.get_workflow() + workflow.start(snippet, self.other_user) + + # make user a moderator. The Moderators group has no explicit permission over the + # FullFeaturedSnippet model, and is only granted access to it via the workflow + moderators = Group.objects.get(name="Moderators") + self.user.is_superuser = False + self.user.save() + self.user.groups.add(moderators) + + session = EditingSession.objects.create( + user=self.user, + content_type=ContentType.objects.get_for_model(FullFeaturedSnippet), + object_id=snippet.pk, + last_seen_at=TIMESTAMP_1, + ) + + # access to the ping endpoint should be granted + response = self.client.post( + reverse( + "wagtailadmin_editing_sessions:ping", + args=("tests", "fullfeaturedsnippet", snippet.id, session.id), + ) + ) + self.assertEqual(response.status_code, 200) + + def test_locked_snippet(self): + snippet = FullFeaturedSnippet.objects.create(text="Test snippet") + + snippet.locked = True + snippet.locked_by = self.other_user + snippet.locked_at = TIMESTAMP_PAST + snippet.save() + + session = EditingSession.objects.create( + user=self.user, + content_type=ContentType.objects.get_for_model(FullFeaturedSnippet), + object_id=snippet.pk, + last_seen_at=TIMESTAMP_1, + ) + + # access to the ping endpoint should be granted + response = self.client.post( + reverse( + "wagtailadmin_editing_sessions:ping", + args=("tests", "fullfeaturedsnippet", snippet.id, session.id), + ) + ) + self.assertEqual(response.status_code, 200) + def test_must_post(self): response = self.client.get( reverse( diff --git a/wagtail/admin/views/editing_sessions.py b/wagtail/admin/views/editing_sessions.py index c6a1aadcfb..f2ed2f9c88 100644 --- a/wagtail/admin/views/editing_sessions.py +++ b/wagtail/admin/views/editing_sessions.py @@ -11,7 +11,7 @@ from django.views.decorators.http import require_POST from wagtail.admin.models import EditingSession from wagtail.admin.ui.editing_sessions import EditingSessionsList from wagtail.admin.utils import get_user_display_name -from wagtail.models import Page, Revision, RevisionMixin +from wagtail.models import Page, Revision, RevisionMixin, WorkflowMixin @require_POST @@ -34,10 +34,18 @@ def ping(request, app_label, model_name, object_id, session_id): except AttributeError: # model is neither a Page nor a snippet raise Http404 - else: - can_edit = permission_policy.user_has_permission_for_instance( - request.user, "change", obj - ) + + can_edit = permission_policy.user_has_permission_for_instance( + request.user, "change", obj + ) + if not can_edit and isinstance(obj, WorkflowMixin): + workflow = obj.get_workflow() + if workflow is not None: + current_workflow_task = obj.current_workflow_task + can_edit = ( + current_workflow_task + and current_workflow_task.user_can_access_editor(obj, request.user) + ) if not can_edit: raise Http404