From 8799b4aa2f983a4a6ad331a6ade144f4377293d8 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Wed, 12 Nov 2025 11:54:55 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F(backend)=20role=20in=20as?= =?UTF-8?q?k=5Ffor=5Faccess=20must=20be=20lower=20than=20user=20role?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We check that the role set in a ask_for_access is not higher than the user's role accepting the request. We prevent case where ad min will grant a user owner in order to take control of the document. Only owner can accept an owner role. --- src/backend/core/api/viewsets.py | 14 +++++- .../test_api_documents_ask_for_access.py | 47 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 84402cea..a9f99d04 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -2162,7 +2162,19 @@ class DocumentAskForAccessViewSet( serializer = serializers.RoleSerializer(data=request.data) serializer.is_valid(raise_exception=True) - document_ask_for_access.accept(role=serializer.validated_data.get("role")) + document = self.get_document_or_404() + user_role = document.get_role(request.user) + target_role = serializer.validated_data.get("role") + + if models.RoleChoices.get_priority(user_role) < models.RoleChoices.get_priority( + target_role + ): + return drf.response.Response( + {"detail": "You cannot accept a role higher than your own."}, + status=drf.status.HTTP_400_BAD_REQUEST, + ) + + document_ask_for_access.accept(role=target_role) return drf.response.Response(status=drf.status.HTTP_204_NO_CONTENT) diff --git a/src/backend/core/tests/documents/test_api_documents_ask_for_access.py b/src/backend/core/tests/documents/test_api_documents_ask_for_access.py index 4e09dc5d..321315e8 100644 --- a/src/backend/core/tests/documents/test_api_documents_ask_for_access.py +++ b/src/backend/core/tests/documents/test_api_documents_ask_for_access.py @@ -749,6 +749,53 @@ def test_api_documents_ask_for_access_accept_authenticated_owner_or_admin_update assert document_access.role == RoleChoices.ADMIN +def test_api_documents_ask_for_access_accept_admin_cannot_accept_owner_role(): + """ + Admin users should not be able to accept document ask for access with the owner role. + """ + user = UserFactory() + document = DocumentFactory(users=[(user, RoleChoices.ADMIN)]) + document_ask_for_access = DocumentAskForAccessFactory( + document=document, role=RoleChoices.READER + ) + + client = APIClient() + client.force_login(user) + + response = client.post( + f"/api/v1.0/documents/{document.id}/ask-for-access/{document_ask_for_access.id}/accept/", + data={"role": RoleChoices.OWNER}, + ) + assert response.status_code == 400 + assert response.json() == { + "detail": "You cannot accept a role higher than your own." + } + + +def test_api_documents_ask_for_access_accept_owner_can_accept_owner_role(): + """ + Owner users should be able to accept document ask for access with the owner role. + """ + user = UserFactory() + document = DocumentFactory(users=[(user, RoleChoices.OWNER)]) + document_ask_for_access = DocumentAskForAccessFactory( + document=document, role=RoleChoices.READER + ) + + client = APIClient() + client.force_login(user) + + response = client.post( + f"/api/v1.0/documents/{document.id}/ask-for-access/{document_ask_for_access.id}/accept/", + data={"role": RoleChoices.OWNER}, + ) + assert response.status_code == 204 + + assert not DocumentAskForAccess.objects.filter( + id=document_ask_for_access.id + ).exists() + + @pytest.mark.parametrize("role", [RoleChoices.OWNER, RoleChoices.ADMIN]) def test_api_documents_ask_for_access_accept_authenticated_non_root_document(role): """