Skip to content

Commit 4aba797

Browse files
committed
🐛(backend) allow editor to delete subpages
An editor is allowed to create a subpages but not to delete them. We change the abilities to be coherent between the creation and the deletion. Fixes #1193
1 parent 0892c05 commit 4aba797

File tree

4 files changed

+59
-4
lines changed

4 files changed

+59
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ and this project adheres to
2424

2525
- 🐛(makefile) Windows compatibility fix for Docker volume mounting #1264
2626
- 🐛(minio) fix user permission error with Minio and Windows #1264
27+
- 🐛(backend) allow editor to delete subpages #1296
2728

2829
## [3.5.0] - 2025-07-31
2930

src/backend/core/models.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,8 @@ def get_abilities(self, user):
762762
can_update = (
763763
is_owner_or_admin or role == RoleChoices.EDITOR
764764
) and not is_deleted
765+
can_create_children = can_update and user.is_authenticated
766+
can_destroy = is_owner if self.is_root() else can_create_children
765767

766768
ai_allow_reach_from = settings.AI_ALLOW_REACH_FROM
767769
ai_access = any(
@@ -784,11 +786,11 @@ def get_abilities(self, user):
784786
"media_check": can_get,
785787
"can_edit": can_update,
786788
"children_list": can_get,
787-
"children_create": can_update and user.is_authenticated,
789+
"children_create": can_create_children,
788790
"collaboration_auth": can_get,
789791
"cors_proxy": can_get,
790792
"descendants": can_get,
791-
"destroy": is_owner,
793+
"destroy": can_destroy,
792794
"duplicate": can_get and user.is_authenticated,
793795
"favorite": can_get and user.is_authenticated,
794796
"link_configuration": is_owner_or_admin,

src/backend/core/tests/documents/test_api_documents_retrieve.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def test_api_documents_retrieve_authenticated_public_or_authenticated_parent(rea
300300
"collaboration_auth": True,
301301
"descendants": True,
302302
"cors_proxy": True,
303-
"destroy": False,
303+
"destroy": grand_parent.link_role == "editor",
304304
"duplicate": True,
305305
"favorite": True,
306306
"invite_owner": False,
@@ -494,7 +494,7 @@ def test_api_documents_retrieve_authenticated_related_parent():
494494
"collaboration_auth": True,
495495
"descendants": True,
496496
"cors_proxy": True,
497-
"destroy": access.role == "owner",
497+
"destroy": access.role != "reader",
498498
"duplicate": True,
499499
"favorite": True,
500500
"invite_owner": access.role == "owner",

src/backend/core/tests/test_models_documents.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,58 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
593593
}
594594

595595

596+
@pytest.mark.parametrize(
597+
"is_authenticated,role,link_reach,link_role,can_destroy",
598+
[
599+
(True, "owner", "restricted", "editor", True),
600+
(True, "owner", "restricted", "reader", True),
601+
(True, "owner", "authenticated", "editor", True),
602+
(True, "owner", "authenticated", "reader", True),
603+
(True, "owner", "public", "editor", True),
604+
(True, "owner", "public", "reader", True),
605+
(True, "administrator", "restricted", "editor", True),
606+
(True, "administrator", "restricted", "reader", True),
607+
(True, "administrator", "authenticated", "editor", True),
608+
(True, "administrator", "authenticated", "reader", True),
609+
(True, "administrator", "public", "editor", True),
610+
(True, "administrator", "public", "reader", True),
611+
(True, "editor", "restricted", "editor", True),
612+
(True, "editor", "restricted", "reader", True),
613+
(True, "editor", "authenticated", "editor", True),
614+
(True, "editor", "authenticated", "reader", True),
615+
(True, "editor", "public", "editor", True),
616+
(True, "editor", "public", "reader", True),
617+
(True, "reader", "restricted", "editor", False),
618+
(True, "reader", "restricted", "reader", False),
619+
(True, "reader", "authenticated", "editor", True),
620+
(True, "reader", "authenticated", "reader", False),
621+
(True, "reader", "public", "editor", True),
622+
(True, "reader", "public", "reader", False),
623+
(False, None, "restricted", "editor", False),
624+
(False, None, "restricted", "reader", False),
625+
(False, None, "authenticated", "editor", False),
626+
(False, None, "authenticated", "reader", False),
627+
(False, None, "public", "editor", False),
628+
(False, None, "public", "reader", False),
629+
],
630+
)
631+
def test_models_documents_get_abilities_children_destroy(
632+
is_authenticated, role, link_reach, link_role, can_destroy
633+
):
634+
"""For a sub document, if a user can create children, he can destroy it."""
635+
user = factories.UserFactory() if is_authenticated else AnonymousUser()
636+
parent = factories.DocumentFactory(link_reach=link_reach, link_role=link_role)
637+
document = factories.DocumentFactory(
638+
link_reach=link_reach, link_role=link_role, parent=parent
639+
)
640+
if is_authenticated:
641+
factories.UserDocumentAccessFactory(document=parent, user=user, role=role)
642+
643+
abilities = document.get_abilities(user)
644+
assert abilities["children_create"] is can_destroy
645+
assert abilities["destroy"] is can_destroy
646+
647+
596648
@override_settings(AI_ALLOW_REACH_FROM="public")
597649
@pytest.mark.parametrize(
598650
"is_authenticated,reach",

0 commit comments

Comments
 (0)