♻️(backend) rely on set_role_to from DocumentAskForAccess abilities

Like in other abilities, we compute a set_role_to property on the
abilities. This set_role_to contains all the roles lower or equal than
the current user role. We rely on this propoerty to validate the accept
endpoint and it will be used by the front allpication to built the role
select list.
This commit is contained in:
Manuel Raynaud
2025-11-13 14:44:28 +01:00
parent bf68a5ae40
commit 1292c33a58
3 changed files with 32 additions and 22 deletions

View File

@@ -2162,13 +2162,12 @@ class DocumentAskForAccessViewSet(
serializer = serializers.RoleSerializer(data=request.data) serializer = serializers.RoleSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
document = self.get_document_or_404() target_role = serializer.validated_data.get(
user_role = document.get_role(request.user) "role", document_ask_for_access.role
target_role = serializer.validated_data.get("role") )
abilities = document_ask_for_access.get_abilities(request.user)
if models.RoleChoices.get_priority(user_role) < models.RoleChoices.get_priority( if target_role not in abilities["set_role_to"]:
target_role
):
return drf.response.Response( return drf.response.Response(
{"detail": "You cannot accept a role higher than your own."}, {"detail": "You cannot accept a role higher than your own."},
status=drf.status.HTTP_400_BAD_REQUEST, status=drf.status.HTTP_400_BAD_REQUEST,

View File

@@ -1205,23 +1205,14 @@ class DocumentAskForAccess(BaseModel):
def get_abilities(self, user): def get_abilities(self, user):
"""Compute and return abilities for a given user.""" """Compute and return abilities for a given user."""
roles = [] user_role = self.document.get_role(user)
is_admin_or_owner = user_role in PRIVILEGED_ROLES
if user.is_authenticated: set_role_to = [
teams = user.teams role
try: for role in RoleChoices.values
roles = self.user_roles or [] if RoleChoices.get_priority(role) <= RoleChoices.get_priority(user_role)
except AttributeError: ]
try:
roles = self.document.accesses.filter(
models.Q(user=user) | models.Q(team__in=teams),
).values_list("role", flat=True)
except (self._meta.model.DoesNotExist, IndexError):
roles = []
is_admin_or_owner = bool(
set(roles).intersection({RoleChoices.OWNER, RoleChoices.ADMIN})
)
return { return {
"destroy": is_admin_or_owner, "destroy": is_admin_or_owner,
@@ -1229,6 +1220,7 @@ class DocumentAskForAccess(BaseModel):
"partial_update": is_admin_or_owner, "partial_update": is_admin_or_owner,
"retrieve": is_admin_or_owner, "retrieve": is_admin_or_owner,
"accept": is_admin_or_owner, "accept": is_admin_or_owner,
"set_role_to": set_role_to,
} }
def accept(self, role=None): def accept(self, role=None):

View File

@@ -287,6 +287,7 @@ def test_api_documents_ask_for_access_list_authenticated_own_request():
"update": False, "update": False,
"partial_update": False, "partial_update": False,
"retrieve": False, "retrieve": False,
"set_role_to": [],
}, },
} }
], ],
@@ -356,6 +357,15 @@ def test_api_documents_ask_for_access_list_owner_or_admin(role):
response = client.get(f"/api/v1.0/documents/{document.id}/ask-for-access/") response = client.get(f"/api/v1.0/documents/{document.id}/ask-for-access/")
assert response.status_code == 200 assert response.status_code == 200
expected_set_role_to = [
RoleChoices.READER,
RoleChoices.EDITOR,
RoleChoices.ADMIN,
]
if role == RoleChoices.OWNER:
expected_set_role_to.append(RoleChoices.OWNER)
assert response.json() == { assert response.json() == {
"count": 3, "count": 3,
"next": None, "next": None,
@@ -375,6 +385,7 @@ def test_api_documents_ask_for_access_list_owner_or_admin(role):
"update": True, "update": True,
"partial_update": True, "partial_update": True,
"retrieve": True, "retrieve": True,
"set_role_to": expected_set_role_to,
}, },
} }
for document_ask_for_access in document_ask_for_accesses for document_ask_for_access in document_ask_for_accesses
@@ -467,6 +478,13 @@ def test_api_documents_ask_for_access_retrieve_owner_or_admin(role):
f"/api/v1.0/documents/{document.id}/ask-for-access/{document_ask_for_access.id}/" f"/api/v1.0/documents/{document.id}/ask-for-access/{document_ask_for_access.id}/"
) )
assert response.status_code == 200 assert response.status_code == 200
expected_set_role_to = [
RoleChoices.READER,
RoleChoices.EDITOR,
RoleChoices.ADMIN,
]
if role == RoleChoices.OWNER:
expected_set_role_to.append(RoleChoices.OWNER)
assert response.json() == { assert response.json() == {
"id": str(document_ask_for_access.id), "id": str(document_ask_for_access.id),
"document": str(document.id), "document": str(document.id),
@@ -481,6 +499,7 @@ def test_api_documents_ask_for_access_retrieve_owner_or_admin(role):
"update": True, "update": True,
"partial_update": True, "partial_update": True,
"retrieve": True, "retrieve": True,
"set_role_to": expected_set_role_to,
}, },
} }