diff --git a/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py b/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py index 8ee77a00df..aaed53b8d5 100644 --- a/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_user_profile_viewset.py @@ -291,40 +291,6 @@ def test_profile_create(self, mock_send_verification_email): self.assertTrue(user.is_active) self.assertTrue(user.check_password(password), password) - @override_settings(CELERY_TASK_ALWAYS_EAGER=True) - @override_settings(ENABLE_EMAIL_VERIFICATION=True) - @patch( - ( - "onadata.libs.serializers.user_profile_serializer." - "send_verification_email.delay" - ) - ) - def test_accept_invitaton(self, mock_send_email): - """An invitation is accepted successfuly""" - self._project_create() - invitation = ProjectInvitation.objects.create( - email="janedoe@example.com", - project=self.project, - role="editor", - ) - # user registers using same email as invitation email - data = _profile_data() - del data["name"] - data["email"] = invitation.email - request = self.factory.post( - "/api/v1/profiles", - data=json.dumps(data), - content_type="application/json", - **self.extra, - ) - response = self.view(request) - self.assertEqual(response.status_code, 201) - user = User.objects.get(username="deno") - mock_send_email.assert_called_once() - invitation.refresh_from_db() - self.assertEqual(invitation.status, ProjectInvitation.Status.ACCEPTED) - self.assertTrue(EditorRole.user_has_role(user, self.project)) - def _create_user_using_profiles_endpoint(self, data): request = self.factory.post( "/api/v1/profiles", @@ -1488,3 +1454,107 @@ def test_account_activation_emails(self, mock_send_mail): ), ] ) + + @override_settings(CELERY_TASK_ALWAYS_EAGER=True) + @override_settings(ENABLE_EMAIL_VERIFICATION=True) + @patch( + ( + "onadata.libs.serializers.user_profile_serializer." + "send_verification_email.delay" + ) + ) + def test_accept_invitaton(self, mock_send_email): + """An invitation is accepted successfuly""" + self._project_create() + invitation = ProjectInvitation.objects.create( + email="janedoe@example.com", + project=self.project, + role="editor", + ) + # user registers using same email as invitation email + data = _profile_data() + del data["name"] + data["email"] = invitation.email + request = self.factory.post( + "/api/v1/profiles", + data=json.dumps(data), + content_type="application/json", + **self.extra, + ) + response = self.view(request) + self.assertEqual(response.status_code, 201) + user = User.objects.get(username="deno") + mock_send_email.assert_called_once() + invitation.refresh_from_db() + self.assertEqual(invitation.status, ProjectInvitation.Status.ACCEPTED) + self.assertTrue(EditorRole.user_has_role(user, self.project)) + + @override_settings(CELERY_TASK_ALWAYS_EAGER=True) + @override_settings(ENABLE_EMAIL_VERIFICATION=True) + @override_settings( + AUTH_PASSWORD_VALIDATORS=[ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", # noqa + }, + ] + ) + @patch( + ( + "onadata.libs.serializers.user_profile_serializer." + "send_verification_email.delay" + ) + ) + def test_password_optional(self, mock_send_verification_email): + """Field `password` is optional""" + # password not provided + data = _profile_data() + del data["name"] + del data["password"] + request = self.factory.post( + "/api/v1/profiles", + data=json.dumps(data), + content_type="application/json", + **self.extra, + ) + response = self.view(request) + self.assertEqual(response.status_code, 201) + profile = UserProfile.objects.get(user__username=data["username"]) + data["id"] = profile.user.pk + data["gravatar"] = profile.gravatar + data["url"] = "http://testserver/api/v1/profiles/deno" + data["user"] = "http://testserver/api/v1/users/deno" + data["metadata"] = {} + data["metadata"]["last_password_edit"] = profile.metadata["last_password_edit"] + data["joined_on"] = profile.user.date_joined + data["name"] = "%s %s" % ("Dennis", "erama") + self.assertEqual(response.data, data) + self.assertTrue(mock_send_verification_email.called) + user = User.objects.get(username="deno") + self.assertTrue(user.is_active) + # password blank + data = _profile_data() + user.delete() + del data["name"] + data["password"] = "" + request = self.factory.post( + "/api/v1/profiles", + data=json.dumps(data), + content_type="application/json", + **self.extra, + ) + response = self.view(request) + self.assertEqual(response.status_code, 201) + profile = UserProfile.objects.get(user__username=data["username"]) + data["id"] = profile.user.pk + data["gravatar"] = profile.gravatar + data["url"] = "http://testserver/api/v1/profiles/deno" + data["user"] = "http://testserver/api/v1/users/deno" + data["metadata"] = {} + data["metadata"]["last_password_edit"] = profile.metadata["last_password_edit"] + data["joined_on"] = profile.user.date_joined + data["name"] = "%s %s" % ("Dennis", "erama") + del data["password"] + self.assertEqual(response.data, data) + self.assertTrue(mock_send_verification_email.called) + user = User.objects.get(username="deno") + self.assertTrue(user.is_active) diff --git a/onadata/libs/serializers/user_profile_serializer.py b/onadata/libs/serializers/user_profile_serializer.py index 6290927417..68693b4c0a 100644 --- a/onadata/libs/serializers/user_profile_serializer.py +++ b/onadata/libs/serializers/user_profile_serializer.py @@ -271,21 +271,26 @@ def create(self, validated_data): request = self.context.get("request") metadata = {} username = params.get("username") + password = params.get("password1") site = Site.objects.get(pk=settings.SITE_ID) new_user = None + try: new_user = RegistrationProfile.objects.create_inactive_user( username=username, - password=params.get("password1"), + password=password, email=params.get("email"), site=site, send_email=settings.SEND_EMAIL_ACTIVATION_API, ) - validate_password(params.get("password1"), user=new_user) except IntegrityError as e: raise serializers.ValidationError( _(f"User account {username} already exists") ) from e + + try: + validate_password("" if password is None else password, user=new_user) + except ValidationError as e: # Delete created user object if created # to allow re-registration