From e148c237f1513ae11057e2794083b8645504d2e6 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Wed, 2 Jul 2025 14:13:15 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=82(back)=20restrict=20duplicate=20wit?= =?UTF-8?q?h=20accesses=20to=20admin=20or=20owner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only admin or owner should be able to duplicate a document with existing accesses. --- src/backend/core/api/viewsets.py | 4 +- .../documents/test_api_documents_duplicate.py | 50 +++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 78fc0242..61d3f20f 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -954,6 +954,8 @@ class DocumentViewSet( ) serializer.is_valid(raise_exception=True) with_accesses = serializer.validated_data.get("with_accesses", False) + roles = set(document.get_roles(request.user)) + is_owner_or_admin = bool(roles.intersection(set(models.PRIVILEGED_ROLES))) base64_yjs_content = document.content @@ -985,7 +987,7 @@ class DocumentViewSet( ] # If accesses should be duplicated, add other users' accesses as per original document - if with_accesses: + if with_accesses and is_owner_or_admin: original_accesses = models.DocumentAccess.objects.filter( document=document ).exclude(user=request.user) diff --git a/src/backend/core/tests/documents/test_api_documents_duplicate.py b/src/backend/core/tests/documents/test_api_documents_duplicate.py index 734c9a25..802a8a49 100644 --- a/src/backend/core/tests/documents/test_api_documents_duplicate.py +++ b/src/backend/core/tests/documents/test_api_documents_duplicate.py @@ -168,14 +168,17 @@ def test_api_documents_duplicate_success(index): assert response.status_code == 403 -def test_api_documents_duplicate_with_accesses(): - """Accesses should be duplicated if the user requests it specifically.""" +@pytest.mark.parametrize("role", ["owner", "administrator"]) +def test_api_documents_duplicate_with_accesses_admin(role): + """ + Accesses should be duplicated if the user requests it specifically and is owner or admin. + """ user = factories.UserFactory() client = APIClient() client.force_login(user) document = factories.DocumentFactory( - users=[user], + users=[(user, role)], title="document with accesses", ) user_access = factories.UserDocumentAccessFactory(document=document) @@ -205,3 +208,44 @@ def test_api_documents_duplicate_with_accesses(): assert duplicated_accesses.get(user=user).role == "owner" assert duplicated_accesses.get(user=user_access.user).role == user_access.role assert duplicated_accesses.get(team=team_access.team).role == team_access.role + + +@pytest.mark.parametrize("role", ["editor", "reader"]) +def test_api_documents_duplicate_with_accesses_non_admin(role): + """ + Accesses should not be duplicated if the user requests it specifically and is not owner + or admin. + """ + user = factories.UserFactory() + client = APIClient() + client.force_login(user) + + document = factories.DocumentFactory( + users=[(user, role)], + title="document with accesses", + ) + factories.UserDocumentAccessFactory(document=document) + factories.TeamDocumentAccessFactory(document=document) + + # Duplicate the document via the API endpoint requesting to duplicate accesses + response = client.post( + f"/api/v1.0/documents/{document.id!s}/duplicate/", + {"with_accesses": True}, + format="json", + ) + + assert response.status_code == 201 + + duplicated_document = models.Document.objects.get(id=response.json()["id"]) + assert duplicated_document.title == "Copy of document with accesses" + assert duplicated_document.content == document.content + assert duplicated_document.link_reach == document.link_reach + assert duplicated_document.link_role == document.link_role + assert duplicated_document.creator == user + assert duplicated_document.duplicated_from == document + assert duplicated_document.attachments == [] + + # Check that accesses were duplicated and the user who did the duplicate is forced as owner + duplicated_accesses = duplicated_document.accesses + assert duplicated_accesses.count() == 1 + assert duplicated_accesses.get(user=user).role == "owner"