Skip to content

Commit f0e785a

Browse files
authored
[feature:api] Allow creating users with a verified email via REST API
1 parent e6b2d7a commit f0e785a

File tree

3 files changed

+59
-18
lines changed

3 files changed

+59
-18
lines changed

README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,12 @@ Create User
606606
607607
POST /api/v1/users/user/
608608
609+
**Note**: Passing ``true`` to the optional
610+
``is_verified`` field allows creating users with
611+
their email address flagged as verified. This will
612+
also skip sending the verification link to their
613+
email address.
614+
609615
Get User detail
610616
^^^^^^^^^^^^^^^
611617

openwisp_users/api/serializers.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,15 @@ def to_representation(self, instance):
217217

218218

219219
class SuperUserListSerializer(BaseSuperUserSerializer):
220+
email_verified = serializers.BooleanField(default=False, write_only=True)
221+
220222
class Meta:
221223
model = User
222224
fields = (
223225
'id',
224226
'username',
225227
'email',
228+
'email_verified',
226229
'password',
227230
'first_name',
228231
'last_name',
@@ -248,9 +251,10 @@ def validate_email(self, value):
248251
def create(self, validated_data):
249252
group_data = validated_data.pop('groups', None)
250253
org_user_data = validated_data.pop('organization_users', None)
254+
password = validated_data.pop('password')
255+
email_verified = validated_data.pop('email_verified', False)
251256

252257
instance = self.instance or self.Meta.model(**validated_data)
253-
password = validated_data.pop('password')
254258
instance.set_password(password)
255259
instance.full_clean()
256260
instance.save()
@@ -267,13 +271,17 @@ def create(self, validated_data):
267271

268272
if instance.email:
269273
try:
270-
EmailAddress.objects.add_email(
274+
email = EmailAddress.objects.add_email(
271275
self.context['request'],
272276
user=instance,
273277
email=instance.email,
274-
confirm=True,
278+
confirm=not email_verified,
275279
signup=True,
276280
)
281+
if email_verified:
282+
email.primary = True
283+
email.verified = True
284+
email.save(update_fields=['primary', 'verified'])
277285
except Exception as e:
278286
logger.exception(
279287
'Got exception {} while sending '

openwisp_users/tests/test_api/test_api.py

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.contrib import auth
33
from django.contrib.auth import get_user_model
44
from django.contrib.auth.models import Permission
5+
from django.core import mail
56
from django.test import TestCase
67
from django.urls import reverse
78
from openwisp_utils.tests import AssertNumQueriesSubTestMixin
@@ -500,21 +501,47 @@ def test_get_user_list_api(self):
500501
self.assertEqual(r.data['count'], 1)
501502

502503
def test_create_user_list_api(self):
503-
self.assertEqual(User.objects.count(), 1)
504-
path = reverse('users:user_list')
505-
data = {
506-
'username': 'tester',
507-
'email': 'tester@test.com',
508-
'password': 'password123',
509-
}
510-
r = self.client.post(path, data, content_type='application/json')
511-
self.assertEqual(r.status_code, 201)
512-
self.assertEqual(User.objects.count(), 2)
513-
self.assertEqual(r.data['groups'], [])
514-
self.assertEqual(r.data['organization_users'], [])
515-
self.assertEqual(r.data['username'], 'tester')
516-
self.assertEqual(r.data['email'], 'tester@test.com')
517-
self.assertEqual(r.data['is_active'], True)
504+
with self.subTest('create user, standard case'):
505+
mail_sent = len(mail.outbox)
506+
self.assertEqual(User.objects.count(), 1)
507+
path = reverse('users:user_list')
508+
data = {
509+
'username': 'tester',
510+
'email': 'tester@test.com',
511+
'password': 'password123',
512+
}
513+
r = self.client.post(path, data, content_type='application/json')
514+
self.assertEqual(r.status_code, 201)
515+
self.assertEqual(User.objects.count(), 2)
516+
self.assertEqual(r.data['groups'], [])
517+
self.assertEqual(r.data['organization_users'], [])
518+
self.assertEqual(r.data['username'], 'tester')
519+
self.assertEqual(r.data['email'], 'tester@test.com')
520+
self.assertEqual(r.data['is_active'], True)
521+
# ensure email address object is created but not verified
522+
user = User.objects.filter(email=data['email']).first()
523+
self.assertIsNotNone(user)
524+
self.assertEqual(user.emailaddress_set.count(), 1)
525+
email = user.emailaddress_set.first()
526+
self.assertFalse(email.verified)
527+
self.assertFalse(email.primary)
528+
# ensure the email verification link is sent
529+
self.assertEqual(len(mail.outbox), mail_sent + 1)
530+
531+
with self.subTest('create user and flag email as verified'):
532+
mail_sent = len(mail.outbox)
533+
User.objects.filter(email=data['email']).delete()
534+
data['email_verified'] = True
535+
r = self.client.post(path, data, content_type='application/json')
536+
self.assertEqual(r.status_code, 201)
537+
user = User.objects.filter(email=data['email']).first()
538+
self.assertIsNotNone(user)
539+
self.assertEqual(user.emailaddress_set.count(), 1)
540+
email = user.emailaddress_set.first()
541+
self.assertTrue(email.verified)
542+
self.assertTrue(email.primary)
543+
# ensure the email verification link is not sent
544+
self.assertEqual(len(mail.outbox), mail_sent)
518545

519546
def test_post_with_empty_form_api_400(self):
520547
path = reverse('users:user_list')

0 commit comments

Comments
 (0)