(back) accept for a owner the request to access a document

Add the action accepting a request to access a document. It is possible
to override the role from the request and also update an existing
DocumentAccess
This commit is contained in:
Manuel Raynaud
2025-06-18 15:50:12 +02:00
parent c2e46fa9e2
commit d33286019c
4 changed files with 212 additions and 2 deletions

View File

@@ -665,10 +665,22 @@ class InvitationSerializer(serializers.ModelSerializer):
return role
class RoleSerializer(serializers.Serializer):
"""Serializer validating role choices."""
role = serializers.ChoiceField(
choices=models.RoleChoices.choices, required=False, allow_null=True
)
class DocumentAskForAccessCreateSerializer(serializers.Serializer):
"""Serializer for creating a document ask for access."""
role = serializers.ChoiceField(choices=models.RoleChoices.choices, required=False, default=models.RoleChoices.READER)
role = serializers.ChoiceField(
choices=models.RoleChoices.choices,
required=False,
default=models.RoleChoices.READER,
)
class DocumentAskForAccessSerializer(serializers.ModelSerializer):
@@ -695,7 +707,8 @@ class DocumentAskForAccessSerializer(serializers.ModelSerializer):
if request:
return invitation.get_abilities(request.user)
return {}
class VersionFilterSerializer(serializers.Serializer):
"""Validate version filters applied to the list endpoint."""

View File

@@ -1837,6 +1837,17 @@ class DocumentAskForAccessViewSet(
return drf.response.Response(status=drf.status.HTTP_201_CREATED)
@drf.decorators.action(detail=True, methods=["post"])
def accept(self, request, *args, **kwargs):
"""Accept a document ask for access resource."""
document_ask_for_access = self.get_object()
serializer = serializers.RoleSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
document_ask_for_access.accept(role=serializer.validated_data.get("role"))
return drf.response.Response(status=drf.status.HTTP_204_NO_CONTENT)
class ConfigView(drf.views.APIView):
"""API ViewSet for sharing some public settings."""

View File

@@ -1205,8 +1205,22 @@ class DocumentAskForAccess(BaseModel):
"update": is_admin_or_owner,
"partial_update": is_admin_or_owner,
"retrieve": is_admin_or_owner,
"accept": is_admin_or_owner,
}
def accept(self, role=None):
"""Accept a document ask for access resource."""
if role is None:
role = self.role
DocumentAccess.objects.update_or_create(
document=self.document,
user=self.user,
defaults={"role": role},
create_defaults={"role": role},
)
self.delete()
class Template(BaseModel):
"""HTML and CSS code used for formatting the print around the MarkDown body."""

View File

@@ -190,6 +190,7 @@ def test_api_documents_ask_for_access_list_authenticated_own_request():
"+00:00", "Z"
),
"abilities": {
"accept": False,
"destroy": False,
"update": False,
"partial_update": False,
@@ -277,6 +278,7 @@ def test_api_documents_ask_for_access_list_owner_or_admin(role):
"+00:00", "Z"
),
"abilities": {
"accept": True,
"destroy": True,
"update": True,
"partial_update": True,
@@ -365,6 +367,7 @@ def test_api_documents_ask_for_access_retrieve_owner_or_admin(role):
"+00:00", "Z"
),
"abilities": {
"accept": True,
"destroy": True,
"update": True,
"partial_update": True,
@@ -443,3 +446,172 @@ def test_api_documents_ask_for_access_delete_owner_or_admin(role):
assert not DocumentAskForAccess.objects.filter(
id=document_ask_for_access.id
).exists()
## Accept
def test_api_documents_ask_for_access_accept_anonymous():
"""Anonymous users should not be able to accept document ask for access."""
document = DocumentFactory()
document_ask_for_access = DocumentAskForAccessFactory(
document=document, role=RoleChoices.READER
)
client = APIClient()
response = client.post(
f"/api/v1.0/documents/{document.id}/ask-for-access/{document_ask_for_access.id}/accept/"
)
assert response.status_code == 401
def test_api_documents_ask_for_access_accept_authenticated():
"""Authenticated users should not be able to accept document ask for access."""
document = DocumentFactory()
document_ask_for_access = DocumentAskForAccessFactory(
document=document, role=RoleChoices.READER
)
client = APIClient()
client.force_login(UserFactory())
response = client.post(
f"/api/v1.0/documents/{document.id}/ask-for-access/{document_ask_for_access.id}/accept/"
)
assert response.status_code == 404
@pytest.mark.parametrize("role", [RoleChoices.READER, RoleChoices.EDITOR])
def test_api_documents_ask_for_access_accept_authenticated_non_owner_or_admin(role):
"""Non owner or admin users should not be able to accept document ask for access."""
user = UserFactory()
document = DocumentFactory(users=[(user, role)])
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/"
)
assert response.status_code == 404
@pytest.mark.parametrize("role", [RoleChoices.OWNER, RoleChoices.ADMIN])
def test_api_documents_ask_for_access_accept_owner_or_admin(role):
"""Owner or admin users should be able to accept document ask for access."""
user = UserFactory()
document = DocumentFactory(users=[(user, role)])
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/"
)
assert response.status_code == 204
assert not DocumentAskForAccess.objects.filter(
id=document_ask_for_access.id
).exists()
assert DocumentAccess.objects.filter(
document=document, user=document_ask_for_access.user, role=RoleChoices.READER
).exists()
@pytest.mark.parametrize("role", [RoleChoices.OWNER, RoleChoices.ADMIN])
def test_api_documents_ask_for_access_accept_authenticated_specific_role(role):
"""
Owner or admin users should be able to accept document ask for access with a specific role.
"""
user = UserFactory()
document = DocumentFactory(users=[(user, role)])
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.EDITOR},
)
assert response.status_code == 204
assert not DocumentAskForAccess.objects.filter(
id=document_ask_for_access.id
).exists()
assert DocumentAccess.objects.filter(
document=document, user=document_ask_for_access.user, role=RoleChoices.EDITOR
).exists()
@pytest.mark.parametrize("role", [RoleChoices.OWNER, RoleChoices.ADMIN])
def test_api_documents_ask_for_access_accept_authenticated_owner_or_admin_update_access(
role,
):
"""
Owner or admin users should be able to accept document ask for access and update the access.
"""
user = UserFactory()
document = DocumentFactory(users=[(user, role)])
document_access = UserDocumentAccessFactory(
document=document, role=RoleChoices.READER
)
document_ask_for_access = DocumentAskForAccessFactory(
document=document, user=document_access.user, role=RoleChoices.EDITOR
)
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.EDITOR},
)
assert response.status_code == 204
assert not DocumentAskForAccess.objects.filter(
id=document_ask_for_access.id
).exists()
document_access.refresh_from_db()
assert document_access.role == RoleChoices.EDITOR
@pytest.mark.parametrize("role", [RoleChoices.OWNER, RoleChoices.ADMIN])
# pylint: disable=line-too-long
def test_api_documents_ask_for_access_accept_authenticated_owner_or_admin_update_access_with_specific_role(
role,
):
"""
Owner or admin users should be able to accept document ask for access and update the access
with a specific role.
"""
user = UserFactory()
document = DocumentFactory(users=[(user, role)])
document_access = UserDocumentAccessFactory(
document=document, role=RoleChoices.READER
)
document_ask_for_access = DocumentAskForAccessFactory(
document=document, user=document_access.user, role=RoleChoices.EDITOR
)
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.ADMIN},
)
assert response.status_code == 204
assert not DocumentAskForAccess.objects.filter(
id=document_ask_for_access.id
).exists()
document_access.refresh_from_db()
assert document_access.role == RoleChoices.ADMIN