Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to

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

## [3.5.0] - 2025-07-31

Expand Down
6 changes: 4 additions & 2 deletions src/backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ def get_abilities(self, user):
can_update = (
is_owner_or_admin or role == RoleChoices.EDITOR
) and not is_deleted
can_create_children = can_update and user.is_authenticated
can_destroy = is_owner if self.is_root() else can_create_children

ai_allow_reach_from = settings.AI_ALLOW_REACH_FROM
ai_access = any(
Expand All @@ -784,11 +786,11 @@ def get_abilities(self, user):
"media_check": can_get,
"can_edit": can_update,
"children_list": can_get,
"children_create": can_update and user.is_authenticated,
"children_create": can_create_children,
"collaboration_auth": can_get,
"cors_proxy": can_get,
"descendants": can_get,
"destroy": is_owner,
"destroy": can_destroy,
"duplicate": can_get and user.is_authenticated,
"favorite": can_get and user.is_authenticated,
"link_configuration": is_owner_or_admin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def test_api_documents_retrieve_authenticated_public_or_authenticated_parent(rea
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": False,
"destroy": grand_parent.link_role == "editor",
"duplicate": True,
"favorite": True,
"invite_owner": False,
Expand Down Expand Up @@ -494,7 +494,7 @@ def test_api_documents_retrieve_authenticated_related_parent():
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": access.role == "owner",
"destroy": access.role != "reader",
"duplicate": True,
"favorite": True,
"invite_owner": access.role == "owner",
Expand Down
52 changes: 52 additions & 0 deletions src/backend/core/tests/test_models_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,58 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
}


@pytest.mark.parametrize(
"is_authenticated,role,link_reach,link_role,can_destroy",
[
(True, "owner", "restricted", "editor", True),
(True, "owner", "restricted", "reader", True),
(True, "owner", "authenticated", "editor", True),
(True, "owner", "authenticated", "reader", True),
(True, "owner", "public", "editor", True),
(True, "owner", "public", "reader", True),
(True, "administrator", "restricted", "editor", True),
(True, "administrator", "restricted", "reader", True),
(True, "administrator", "authenticated", "editor", True),
(True, "administrator", "authenticated", "reader", True),
(True, "administrator", "public", "editor", True),
(True, "administrator", "public", "reader", True),
(True, "editor", "restricted", "editor", True),
(True, "editor", "restricted", "reader", True),
(True, "editor", "authenticated", "editor", True),
(True, "editor", "authenticated", "reader", True),
(True, "editor", "public", "editor", True),
(True, "editor", "public", "reader", True),
(True, "reader", "restricted", "editor", False),
(True, "reader", "restricted", "reader", False),
(True, "reader", "authenticated", "editor", True),
(True, "reader", "authenticated", "reader", False),
(True, "reader", "public", "editor", True),
(True, "reader", "public", "reader", False),
(False, None, "restricted", "editor", False),
(False, None, "restricted", "reader", False),
(False, None, "authenticated", "editor", False),
(False, None, "authenticated", "reader", False),
(False, None, "public", "editor", False),
(False, None, "public", "reader", False),
],
)
def test_models_documents_get_abilities_children_destroy(
is_authenticated, role, link_reach, link_role, can_destroy
):
"""For a sub document, if a user can create children, he can destroy it."""
user = factories.UserFactory() if is_authenticated else AnonymousUser()
parent = factories.DocumentFactory(link_reach=link_reach, link_role=link_role)
document = factories.DocumentFactory(
link_reach=link_reach, link_role=link_role, parent=parent
)
if is_authenticated:
factories.UserDocumentAccessFactory(document=parent, user=user, role=role)

abilities = document.get_abilities(user)
assert abilities["children_create"] is can_destroy
assert abilities["destroy"] is can_destroy


@override_settings(AI_ALLOW_REACH_FROM="public")
@pytest.mark.parametrize(
"is_authenticated,reach",
Expand Down
Loading