diff --git a/src/backend/core/api/client/viewsets.py b/src/backend/core/api/client/viewsets.py index 600101f..a6a6f01 100644 --- a/src/backend/core/api/client/viewsets.py +++ b/src/backend/core/api/client/viewsets.py @@ -136,7 +136,7 @@ class ContactViewSet( ): """Contact ViewSet""" - permission_classes = [permissions.IsOwnedOrPublic] + permission_classes = [permissions.AccessPermission] queryset = models.Contact.objects.all() serializer_class = serializers.ContactSerializer throttle_classes = [BurstRateThrottle, SustainedRateThrottle] @@ -150,8 +150,10 @@ class ContactViewSet( # List only contacts that: queryset = queryset.filter( + # - is public (owner is None) + Q(owner__isnull=True) # - are owned by the user - Q(owner=user) + | Q(owner=user) # - are profile contacts for a user from the same organization | Q(user__organization_id=user.organization_id), # - are not overriden by another contact diff --git a/src/backend/core/models.py b/src/backend/core/models.py index a272ec5..730bf11 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -187,6 +187,27 @@ class Contact(BaseModel): error_message = f"Validation error in '{field_path:s}': {e.message}" raise exceptions.ValidationError({"data": [error_message]}) from e + def get_abilities(self, user): + """ + Compute and return abilities for a given user on the contact. + + Beware that the model allows owner to be None, we are still not + sure about the use case for this and the API does not allow this. + For now, we still consider here, a contact without owner is "public" + so we allow access to it. + """ + is_owner = user == self.owner + is_profile_member_or_same_organization = bool(self.user) and ( + self.user.organization_id == user.organization_id + ) + + return { + "get": is_owner or is_profile_member_or_same_organization or not self.owner, + "patch": is_owner, + "put": is_owner, + "delete": is_owner and not self.user, # Can't delete a profile contact + } + class ServiceProvider(BaseModel): """ diff --git a/src/backend/core/tests/contacts/test_core_api_contacts_delete.py b/src/backend/core/tests/contacts/test_core_api_contacts_delete.py index bcdaa01..e296069 100644 --- a/src/backend/core/tests/contacts/test_core_api_contacts_delete.py +++ b/src/backend/core/tests/contacts/test_core_api_contacts_delete.py @@ -86,10 +86,10 @@ def test_api_contacts_delete_authenticated_owner(): def test_api_contacts_delete_authenticated_profile(): """ - Authenticated users should be allowed to delete their profile contact. + Authenticated users should not be allowed to delete their profile contact. """ user = factories.UserFactory() - contact = factories.ContactFactory(owner=user, user=user) + contact = factories.ProfileContactFactory(user=user) client = APIClient() client.force_login(user) @@ -98,8 +98,8 @@ def test_api_contacts_delete_authenticated_profile(): f"/api/v1.0/contacts/{contact.id!s}/", ) - assert response.status_code == 204 - assert models.Contact.objects.exists() is False + assert response.status_code == 403 + assert models.Contact.objects.exists() is True def test_api_contacts_delete_authenticated_other():