custom_user: set email verification sources (#66054)

This commit is contained in:
Paul Marillonnet 2022-06-08 10:29:21 +02:00
parent bf85976e04
commit 43ccdfea68
9 changed files with 88 additions and 14 deletions

View File

@ -736,7 +736,7 @@ class UserCsvImporter:
if getattr(user, cell.header.name) != cell.value:
setattr(user, cell.header.name, cell.value)
if cell.header.name == 'email' and cell.header.verified:
user.set_email_verified(True)
user.set_email_verified(True, source='csv')
if cell.header.name == 'phone' and cell.header.verified:
user.phone_verified_on = now()
cell.action = 'updated'

View File

@ -0,0 +1,26 @@
# Generated by Django 2.2.26 on 2022-06-08 12:33
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('custom_user', '0033_user_keepalive'),
]
operations = [
migrations.AddField(
model_name='user',
name='email_verified_sources',
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=63),
default=list,
size=None,
null=True,
blank=True,
verbose_name='email verification sources',
),
),
]

View File

@ -28,6 +28,7 @@ from django.contrib.auth.models import AbstractBaseUser, Group
from django.contrib.auth.models import Permission as AuthPermission
from django.contrib.auth.models import _user_has_module_perms, _user_has_perm
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.core.mail import send_mail
from django.db import models, transaction
@ -183,6 +184,13 @@ class User(AbstractBaseUser):
email_verified_date = models.DateTimeField(
default=None, blank=True, null=True, verbose_name=_('email verified date')
)
email_verified_sources = ArrayField(
verbose_name=_('email verification sources'),
base_field=models.CharField(max_length=63),
default=list,
null=True,
blank=True,
)
is_superuser = models.BooleanField(
_('superuser status'),
default=False,
@ -607,16 +615,22 @@ class User(AbstractBaseUser):
def get_absolute_url(self):
return reverse('a2-manager-user-detail', kwargs={'pk': self.pk})
def set_email_verified(self, value):
if isinstance(value, datetime.datetime):
self.email_verified = True
self.email_verified_date = value
elif bool(value):
self.email_verified = True
self.email_verified_date = timezone.now()
def set_email_verified(self, value, source=None):
if bool(value):
if isinstance(value, datetime.datetime):
self.email_verified = True
self.email_verified_date = value
else:
self.email_verified = True
self.email_verified_date = timezone.now()
if source and source not in self.email_verified_sources:
self.email_verified_sources.append(source)
else:
self.email_verified = False
self.email_verified_date = None
if source and source in self.email_verified_sources:
self.email_verified_sources.remove(source)
if not source or not self.email_verified_sources:
self.email_verified = False
self.email_verified_date = None
class DeletedUser(models.Model):

View File

@ -139,7 +139,7 @@ class RegistrationCompletionFormNoPassword(profile_forms.BaseUserForm):
return BaseUserManager.normalize_email(email)
def save(self, commit=True):
self.instance.set_email_verified(True)
self.instance.set_email_verified(True, source='registration')
self.instance.is_active = True
user = super().save(commit=commit)
if commit and app_settings.A2_REGISTRATION_GROUPS:

View File

@ -294,7 +294,7 @@ class EmailChangeVerifyView(TemplateView):
raise ValidationError(_('This email is already used by another account.'))
old_email = user.email
user.email = email
user.set_email_verified(True)
user.set_email_verified(True, source='user')
user.save()
messages.info(
request, _('your request for changing your email for {0} is successful').format(email)
@ -994,7 +994,7 @@ class PasswordResetConfirmView(cbv.RedirectToNextURLViewMixin, FormView):
def form_valid(self, form):
# Changing password by mail validate the email
form.user.set_email_verified(True)
form.user.set_email_verified(True, source='user')
form.save()
hooks.call_hooks('event', name='password-reset-confirm', user=form.user, token=self.token, form=form)
logger.info('password reset for user %s with token %r', self.user, self.token.uuid)

View File

@ -368,7 +368,7 @@ class OIDCBackend(ModelBackend):
logger.info('auth_oidc: set user %s attribute %s to value %s', user, attribute, value)
setattr(user, attribute, value)
if attribute == 'email':
user.set_email_verified(verified)
user.set_email_verified(verified, source='oidc')
save_user = True
if user.ou != user_ou:

View File

@ -63,6 +63,7 @@ USER_ATTRIBUTES_SET = {
'modified',
'email_verified',
'email_verified_date',
'email_verified_sources',
'phone',
'phone_verified_on',
'last_account_deletion_alert',

View File

@ -71,6 +71,7 @@ class SerializerTests(TestCase):
'uuid': u.uuid,
'email_verified': False,
'email_verified_date': None,
'email_verified_sources': '[]', # weird ArrayField serialization behavior
'username': 'john.doe',
'email': '',
'phone': None,

View File

@ -165,3 +165,35 @@ def test_service_profile_type(db):
ServiceProfileType.objects.create(service=service, profile_type=pft)
assert list(service.profile_types.all()) == [pft]
assert list(pft.services.all()) == [service]
def test_user_email_verified(app, simple_user, superuser_or_admin):
simple_user.set_email_verified(True, source='tests')
simple_user.save()
user = User.objects.get(id=simple_user.id)
assert user.email_verified
assert user.email_verified_sources == ['tests']
simple_user.set_email_verified(True, source='other')
simple_user.save()
user = User.objects.get(id=simple_user.id)
assert user.email_verified
assert user.email_verified_sources == ['tests', 'other']
simple_user.set_email_verified(False, source='tests')
simple_user.save()
user = User.objects.get(id=simple_user.id)
assert user.email_verified
assert user.email_verified_sources == ['other']
simple_user.set_email_verified(True, source='other')
simple_user.save()
user = User.objects.get(id=simple_user.id)
assert user.email_verified
assert user.email_verified_sources == ['other']
simple_user.set_email_verified(False, source='other')
simple_user.save()
user = User.objects.get(id=simple_user.id)
assert not user.email_verified
assert user.email_verified_sources == []