handle phone-uniqueness settings at registration time (#82737)
gitea/authentic/pipeline/head This commit looks good
Details
gitea/authentic/pipeline/head This commit looks good
Details
This commit is contained in:
parent
fe87d258cc
commit
b9da1b5764
|
@ -68,6 +68,7 @@ class OrganizationalUnitAdmin(admin.ModelAdmin):
|
|||
'description',
|
||||
'username_is_unique',
|
||||
'email_is_unique',
|
||||
'phone_is_unique',
|
||||
'default',
|
||||
'validate_emails',
|
||||
'user_can_reset_password',
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.18 on 2023-11-06 15:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('a2_rbac', '0037_remove_organizationalunit_min_password_strength'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='organizationalunit',
|
||||
name='phone_is_unique',
|
||||
field=models.BooleanField(blank=True, default=False, verbose_name='Phone is unique'),
|
||||
),
|
||||
]
|
|
@ -104,6 +104,7 @@ class OrganizationalUnit(AbstractBase):
|
|||
|
||||
username_is_unique = models.BooleanField(blank=True, default=False, verbose_name=_('Username is unique'))
|
||||
email_is_unique = models.BooleanField(blank=True, default=False, verbose_name=_('Email is unique'))
|
||||
phone_is_unique = models.BooleanField(blank=True, default=False, verbose_name=_('Phone is unique'))
|
||||
default = fields.UniqueBooleanField(verbose_name=_('Default organizational unit'))
|
||||
|
||||
validate_emails = models.BooleanField(blank=True, default=False, verbose_name=_('Validate emails'))
|
||||
|
@ -234,6 +235,7 @@ class OrganizationalUnit(AbstractBase):
|
|||
'description': self.description,
|
||||
'default': self.default,
|
||||
'email_is_unique': self.email_is_unique,
|
||||
'phone_is_unique': self.phone_is_unique,
|
||||
'username_is_unique': self.username_is_unique,
|
||||
'validate_emails': self.validate_emails,
|
||||
}
|
||||
|
|
|
@ -138,9 +138,13 @@ default_settings = dict(
|
|||
A2_USER_CAN_RESET_PASSWORD_BY_USERNAME=Setting(
|
||||
default=False, definition='Allow password reset request by username'
|
||||
),
|
||||
A2_EMAIL_IS_UNIQUE=Setting(default=False, definition='Email of users must be unique'),
|
||||
A2_EMAIL_IS_UNIQUE=Setting(default=False, definition="Users' email address must be unique"),
|
||||
A2_REGISTRATION_EMAIL_IS_UNIQUE=Setting(
|
||||
default=False, definition='Email of registered accounts must be unique'
|
||||
default=False, definition='Email address declared at registration time must be unique'
|
||||
),
|
||||
A2_PHONE_IS_UNIQUE=Setting(default=False, definition="Users' phone number must be unique"),
|
||||
A2_REGISTRATION_PHONE_IS_UNIQUE=Setting(
|
||||
default=False, definition='Phone number declared at registration time must be unique'
|
||||
),
|
||||
A2_REGISTRATION_FORM_USERNAME_REGEX=Setting(
|
||||
default=r'^[\w.@+-]+$', definition='Regex to validate usernames'
|
||||
|
|
|
@ -1796,9 +1796,19 @@ class RegistrationCompletionView(CreateView):
|
|||
self.users = User.objects.filter(**qs_filter).order_by('date_joined')
|
||||
if self.ou:
|
||||
self.users = self.users.filter(ou=self.ou)
|
||||
self.email_is_unique = app_settings.A2_EMAIL_IS_UNIQUE or app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE
|
||||
if self.ou:
|
||||
self.email_is_unique |= self.ou.email_is_unique
|
||||
self.email_is_unique = self.phone_is_unique = False
|
||||
if self.token.get('email', None):
|
||||
self.email_is_unique = (
|
||||
app_settings.A2_EMAIL_IS_UNIQUE or app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE
|
||||
)
|
||||
if self.ou:
|
||||
self.email_is_unique |= self.ou.email_is_unique
|
||||
elif self.token.get('phone', None) and self.authenticator.is_phone_authn_active:
|
||||
self.phone_is_unique = (
|
||||
app_settings.A2_PHONE_IS_UNIQUE or app_settings.A2_REGISTRATION_PHONE_IS_UNIQUE
|
||||
)
|
||||
if self.ou:
|
||||
self.phone_is_unique |= self.ou.phone_is_unique
|
||||
self.init_fields_labels_and_help_texts()
|
||||
set_home_url(request, self.get_success_url())
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
@ -1921,12 +1931,18 @@ class RegistrationCompletionView(CreateView):
|
|||
if hasattr(self, 'phone'):
|
||||
ctx['phone'] = self.phone
|
||||
ctx['email_is_unique'] = self.email_is_unique
|
||||
ctx['phone_is_unique'] = self.phone_is_unique
|
||||
ctx['create'] = 'create' in self.request.GET
|
||||
return ctx
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if len(self.users) == 1 and self.email_is_unique:
|
||||
# Found one user, EMAIL is unique, log her in
|
||||
if len(self.users) == 1 and (
|
||||
self.email_is_unique
|
||||
and self.token.get('email', None)
|
||||
or self.phone_is_unique
|
||||
and self.token.get('phone', None)
|
||||
):
|
||||
# Found one user whose identifier is unique, log her in
|
||||
utils_misc.simulate_authentication(request, self.users[0], method=self.authentication_method)
|
||||
return utils_misc.redirect(request, self.get_success_url())
|
||||
confirm_data = self.token.get('confirm_data', False)
|
||||
|
@ -1956,8 +1972,13 @@ class RegistrationCompletionView(CreateView):
|
|||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if self.users and self.email_is_unique:
|
||||
# email is unique, users already exist, creating a new one is forbidden !
|
||||
if self.users and (
|
||||
self.email_is_unique
|
||||
and self.token.get('email', None)
|
||||
or self.phone_is_unique
|
||||
and self.token.get('phone', None)
|
||||
):
|
||||
# identifier is unique, users already exist, creating a new one is forbidden !
|
||||
return utils_misc.redirect(
|
||||
request, request.resolver_match.view_name, args=self.args, kwargs=self.kwargs
|
||||
)
|
||||
|
|
|
@ -277,6 +277,7 @@ def test_ou_export_json(db):
|
|||
description='basic ou description',
|
||||
username_is_unique=True,
|
||||
email_is_unique=True,
|
||||
phone_is_unique=True,
|
||||
default=False,
|
||||
validate_emails=True,
|
||||
)
|
||||
|
@ -287,6 +288,7 @@ def test_ou_export_json(db):
|
|||
assert ou_dict['description'] == ou.description
|
||||
assert ou_dict['username_is_unique'] == ou.username_is_unique
|
||||
assert ou_dict['email_is_unique'] == ou.email_is_unique
|
||||
assert ou_dict['phone_is_unique'] == ou.phone_is_unique
|
||||
assert ou_dict['default'] == ou.default
|
||||
assert ou_dict['validate_emails'] == ou.validate_emails
|
||||
|
||||
|
|
|
@ -1185,6 +1185,108 @@ def test_phone_registration_number_already_existing_select(app, db, settings, ph
|
|||
assert app.session['_auth_user_id'] == str(user_ids[1])
|
||||
|
||||
|
||||
def test_phone_registration_number_already_existing_phone_is_unique(app, db, settings, phone_activated_authn):
|
||||
settings.SMS_URL = 'https://foo.whatever.none/'
|
||||
settings.A2_PHONE_IS_UNIQUE = True
|
||||
settings.A2_REGISTRATION_PHONE_IS_UNIQUE = True
|
||||
|
||||
# create duplicate
|
||||
user = User.objects.create(
|
||||
first_name='John',
|
||||
last_name='Doe',
|
||||
email='john@example.com',
|
||||
username='john',
|
||||
ou=get_default_ou(),
|
||||
)
|
||||
user.attributes.phone = '+33612345678'
|
||||
user.save()
|
||||
|
||||
resp = app.get(reverse('registration_register'))
|
||||
resp.form.set('phone_1', '612345678')
|
||||
with HTTMock(sms_service_mock):
|
||||
resp = resp.form.submit().follow()
|
||||
code = SMSCode.objects.get()
|
||||
resp.form.set('sms_code', code.value)
|
||||
resp = resp.form.submit().follow()
|
||||
|
||||
assert app.session['_auth_user_id'] == str(user.id)
|
||||
# logged in user is redirected to their homepage
|
||||
assert resp.location == '/'
|
||||
|
||||
|
||||
def test_phone_registration_number_already_existing_registration_phone_is_unique(
|
||||
app, db, settings, phone_activated_authn
|
||||
):
|
||||
settings.SMS_URL = 'https://foo.whatever.none/'
|
||||
settings.A2_PHONE_IS_UNIQUE = False
|
||||
settings.A2_REGISTRATION_PHONE_IS_UNIQUE = True
|
||||
|
||||
user_ids = []
|
||||
|
||||
# create duplicate
|
||||
user = User.objects.create(
|
||||
first_name='John',
|
||||
last_name='Doe',
|
||||
email='john@example.com',
|
||||
username='john',
|
||||
ou=get_default_ou(),
|
||||
)
|
||||
user.attributes.phone = '+33612345678'
|
||||
user.save()
|
||||
user_ids.append(user.id)
|
||||
|
||||
resp = app.get(reverse('registration_register'))
|
||||
resp.form.set('phone_1', '612345678')
|
||||
with HTTMock(sms_service_mock):
|
||||
resp = resp.form.submit().follow()
|
||||
code = SMSCode.objects.get()
|
||||
resp.form.set('sms_code', code.value)
|
||||
resp = resp.form.submit().follow()
|
||||
assert Token.objects.count() == 1
|
||||
|
||||
assert app.session['_auth_user_id'] == str(user.id)
|
||||
# logged in user is redirected to their homepage
|
||||
assert resp.location == '/'
|
||||
|
||||
|
||||
def test_phone_registration_number_already_existing_ou_phone_is_unique(
|
||||
app, db, settings, phone_activated_authn
|
||||
):
|
||||
settings.SMS_URL = 'https://foo.whatever.none/'
|
||||
settings.A2_PHONE_IS_UNIQUE = False
|
||||
settings.A2_REGISTRATION_PHONE_IS_UNIQUE = False
|
||||
ou = get_default_ou()
|
||||
ou.phone_is_unique = True
|
||||
ou.save()
|
||||
|
||||
user_ids = []
|
||||
|
||||
# create duplicate
|
||||
user = User.objects.create(
|
||||
first_name='John',
|
||||
last_name='Doe',
|
||||
email='john@example.com',
|
||||
username='john',
|
||||
ou=ou,
|
||||
)
|
||||
user.attributes.phone = '+33612345678'
|
||||
user.save()
|
||||
user_ids.append(user.id)
|
||||
|
||||
resp = app.get(reverse('registration_register'))
|
||||
resp.form.set('phone_1', '612345678')
|
||||
with HTTMock(sms_service_mock):
|
||||
resp = resp.form.submit().follow()
|
||||
code = SMSCode.objects.get()
|
||||
resp.form.set('sms_code', code.value)
|
||||
resp = resp.form.submit().follow()
|
||||
assert Token.objects.count() == 1
|
||||
|
||||
assert app.session['_auth_user_id'] == str(user.id)
|
||||
# logged in user is redirected to their homepage
|
||||
assert resp.location == '/'
|
||||
|
||||
|
||||
def test_phone_registration(app, db, settings, phone_activated_authn):
|
||||
settings.SMS_URL = 'https://foo.whatever.none/'
|
||||
code_length = settings.SMS_CODE_LENGTH
|
||||
|
|
Loading…
Reference in New Issue