registration: decide on which phone attribute to use (#78046)

This commit is contained in:
Paul Marillonnet 2023-06-19 16:40:32 +02:00
parent f9b9d9ac88
commit 795f04ea7d
2 changed files with 64 additions and 5 deletions

View File

@ -27,6 +27,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import PasswordChangeView as DjPasswordChangeView
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldDoesNotExist, ValidationError
from django.db.models import Count
from django.db.models.query import Q
@ -1439,6 +1440,8 @@ class RegistrationCompletionView(CreateView):
@atomic(savepoint=False)
def dispatch(self, request, *args, **kwargs):
registration_token = kwargs['registration_token'].replace(' ', '')
self.authenticator = utils_misc.get_password_authenticator()
user_ct = ContentType.objects.get_for_model(get_user_model())
try:
token = models.Token.use('registration', registration_token, delete=False)
except models.Token.DoesNotExist:
@ -1463,9 +1466,15 @@ class RegistrationCompletionView(CreateView):
self.email = self.token['email']
qs_filter = {'email__iexact': self.email}
Lock.lock_email(self.email)
elif self.token.get('phone', None):
elif self.token.get('phone', None) and self.authenticator.is_phone_authn_active:
self.phone = self.token['phone']
qs_filter = {'phone': self.phone}
user_ids = models.AttributeValue.objects.filter(
attribute=self.authenticator.phone_identifier_field,
content=self.phone,
content_type=user_ct,
).values_list('object_id', flat=True)
qs_filter = {'id__in': user_ids}
Lock.lock_identifier(self.phone)
else:
messages.warning(request, _('Activation failed'))
@ -1574,7 +1583,9 @@ class RegistrationCompletionView(CreateView):
kwargs['instance'] = User.objects.get(id=self.token.get('user_id'))
else:
init_kwargs = {}
for key in ('email', 'first_name', 'last_name', 'ou', 'phone'):
keys = ['email', 'first_name', 'last_name', 'ou']
# phone identifier is a separate attribute and is set post user-creation
for key in keys:
if key in attributes:
init_kwargs[key] = attributes[key]
kwargs['instance'] = get_user_model()(**init_kwargs)
@ -1680,6 +1691,18 @@ class RegistrationCompletionView(CreateView):
if count:
super().form_valid(form) # user creation happens here
user = form.instance
if (
(phone := getattr(self, 'phone', None))
and (authn := utils_misc.get_password_authenticator())
and authn.is_phone_authn_active
):
# phone identifier set post user-creation
models.AttributeValue.objects.create(
content_type=ContentType.objects.get_for_model(get_user_model()),
object_id=user.id,
content=phone,
attribute=authn.phone_identifier_field,
)
self.process_registration(self.request, user, form)
else:
try:

View File

@ -26,6 +26,7 @@ from httmock import HTTMock, remember_called, urlmatch
from authentic2 import models
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.apps.authenticators.models import LoginPasswordAuthenticator
from authentic2.apps.journal.models import Event
from authentic2.forms.profile import modelform_factory
from authentic2.forms.registration import RegistrationCompletionForm
@ -1088,7 +1089,42 @@ def test_phone_registration(app, db, settings, phone_activated_authn):
assert "You have just created an account" in resp.text
user = User.objects.get(first_name='John', last_name='Doe')
assert user.phone == '+33612345678'
assert user.attributes.phone == '+33612345678'
def test_phone_registration_nondefault_attribute(app, db, settings):
phone, dummy = models.Attribute.objects.get_or_create(
name='another_phone',
kind='phone_number',
defaults={'label': 'Another phone'},
)
LoginPasswordAuthenticator.objects.update(
accept_phone_authentication=True,
phone_identifier_field=phone,
)
settings.SMS_URL = 'https://foo.whatever.none/'
resp = app.get(reverse('registration_register'))
resp.form.set('phone_1', '612345678')
with HTTMock(sms_service_mock):
resp = resp.form.submit().follow()
json.loads(sms_service_mock.call['requests'][0].body)
code = SMSCode.objects.get()
resp.form.set('sms_code', code.value)
resp = resp.form.submit().follow()
resp.form.set('password1', 'Password0')
resp.form.set('password2', 'Password0')
resp.form.set('first_name', 'John')
resp.form.set('last_name', 'Doe')
resp = resp.form.submit().follow()
assert "You have just created an account" in resp.text
user = User.objects.get(first_name='John', last_name='Doe')
assert user.attributes.another_phone == '+33612345678'
# no standard attribute set nor even created
assert not getattr(user.attributes, 'phone', None)
assert not user.phone
def test_phone_registration_redirect_url(app, db, settings, phone_activated_authn):
@ -1111,4 +1147,4 @@ def test_phone_registration_redirect_url(app, db, settings, phone_activated_auth
assert resp.location == '/accounts/consents/'
resp.follow()
user = User.objects.get(first_name='John', last_name='Doe')
assert user.phone == '+33612345678'
assert user.attributes.phone == '+33612345678'