From c3b2b0b0f1296822b7060beb8599ccfb7fb84292 Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Thu, 13 Jul 2023 10:55:04 +0300 Subject: [PATCH] fix 'User already exists' when updating an accepted invitation ensure only pending invitations can be updated --- .../tests/viewsets/test_project_viewset.py | 22 +++++++++++++++++++ .../project_invitation_serializer.py | 17 ++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/onadata/apps/api/tests/viewsets/test_project_viewset.py b/onadata/apps/api/tests/viewsets/test_project_viewset.py index d783de2a2a..0e6c413a38 100644 --- a/onadata/apps/api/tests/viewsets/test_project_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_project_viewset.py @@ -3152,6 +3152,28 @@ def test_update_email_only(self): }, ) + def test_only_pending_allowed(self): + """Only pending invitation can be updated""" + for value, _ in ProjectInvitation.Status.choices: + invitation = self.project.invitations.create( + email=f"jandoe-{value}@example.com", + role="editor", + status=value, + ) + payload = { + "email": "rihanna@example.com", + "role": "readonly", + "invitation_id": invitation.id, + } + request = self.factory.put("/", data=payload, **self.extra) + response = self.view(request, pk=self.project.pk) + + if value == ProjectInvitation.Status.PENDING: + self.assertEqual(response.status_code, 200) + + else: + self.assertEqual(response.status_code, 400) + class RevokeInvitationTestCase(TestAbstractViewSet): """Tests for revoke invitation""" diff --git a/onadata/libs/serializers/project_invitation_serializer.py b/onadata/libs/serializers/project_invitation_serializer.py index 06180bac2a..084acd4450 100644 --- a/onadata/libs/serializers/project_invitation_serializer.py +++ b/onadata/libs/serializers/project_invitation_serializer.py @@ -52,10 +52,6 @@ def validate_email(self, email): ]: raise serializers.ValidationError(_(err_msg)) - # email should not be of an existing user - if User.objects.filter(email=email).exists(): - raise serializers.ValidationError(_("User already exists")) - return email def validate_role(self, role): @@ -73,6 +69,10 @@ def create(self, validated_data): ).exists(): raise serializers.ValidationError(_("Invitation already exists.")) + # email should not be of an existing user + if User.objects.filter(email=validated_data["email"]).exists(): + raise serializers.ValidationError(_("User already exists")) + instance = super().create(validated_data) instance.invited_by = self.context["request"].user instance.save() @@ -81,6 +81,15 @@ def create(self, validated_data): return instance + def update(self, instance, validated_data): + # only a pending invitation can be updated + if instance.status != ProjectInvitation.Status.PENDING: + raise serializers.ValidationError( + _("Only pending invitations can be updated") + ) + + return super().update(instance, validated_data) + # pylint: disable=abstract-method class ProjectInvitationUpdateBaseSerializer(serializers.Serializer):