/manage/: make phone-authn config a flagged feature (#78046)
gitea/authentic/pipeline/head This commit looks good Details

This commit is contained in:
Paul Marillonnet 2023-06-20 11:13:31 +02:00
parent 053899cb4f
commit e4cb69c1cb
4 changed files with 61 additions and 10 deletions

View File

@ -283,6 +283,10 @@ default_settings = dict(
),
A2_ACCOUNTS_URL=Setting(default=None, definition='IdP has no account page, redirect to this one.'),
A2_CACHE_ENABLED=Setting(default=True, definition='Disable all cache decorators for testing purpose.'),
A2_ALLOW_PHONE_AUTHN_MANAGEMENT=Setting(
default=False,
definition='Allow phone-authentication backoffice-management by authentic\'s administrators',
),
A2_USER_DELETED_KEEP_DATA=Setting(
default=['email', 'uuid', 'phone'], definition='User data to keep after deletion'
),

View File

@ -21,7 +21,9 @@ from django.core.exceptions import ValidationError
from django.db.models import Max
from django.utils.translation import gettext as _
from authentic2 import app_settings
from authentic2.forms.mixins import SlugMixin
from authentic2.models import Attribute
from .models import BaseAuthenticator, LoginPasswordAuthenticator
@ -72,6 +74,25 @@ class AuthenticatorImportForm(forms.Form):
class LoginPasswordAuthenticatorAdvancedForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['phone_identifier_field'].choices = Attribute.objects.filter(
disabled=False,
multiple=False,
kind__in=('phone_number', 'fr_phone_number'),
).values_list('id', 'label')
# TODO drop temporary feature-flag app setting once phone number
# verification is enforced everywhere in /accounts/
if not app_settings.A2_ALLOW_PHONE_AUTHN_MANAGEMENT:
for field in (
'accept_email_authentication',
'accept_phone_authentication',
'phone_identifier_field',
):
del self.fields[field]
class Meta:
model = LoginPasswordAuthenticator
fields = (
@ -87,6 +108,9 @@ class LoginPasswordAuthenticatorAdvancedForm(forms.ModelForm):
'sms_ip_ratelimit',
'emails_address_ratelimit',
'sms_number_ratelimit',
'accept_email_authentication',
'accept_phone_authentication',
'phone_identifier_field',
)

View File

@ -1693,17 +1693,13 @@ 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
):
if (phone := getattr(self, 'phone', None)) and self.authenticator.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,
attribute=self.authenticator.phone_identifier_field,
)
self.process_registration(self.request, user, form)
else:

View File

@ -61,7 +61,7 @@ def test_authenticators_authorization(app, simple_user, simple_role, admin, supe
assert 'Authenticators' in resp.text
def test_authenticators_password(app, superuser_or_admin):
def test_authenticators_password(app, superuser_or_admin, settings):
resp = login(app, superuser_or_admin, path='/manage/authenticators/')
# Password authenticator already exists
assert 'Password' in resp.text
@ -80,9 +80,6 @@ def test_authenticators_password(app, superuser_or_admin):
'show_condition',
'button_description',
'registration_open',
'accept_email_authentication',
'accept_phone_authentication',
'phone_identifier_field',
'password_min_length',
'remember_me',
'include_ou_selector',
@ -135,6 +132,36 @@ def test_authenticators_password(app, superuser_or_admin):
resp = app.get('/manage/authenticators/add/')
assert 'Password' not in resp.text
# phone authn management feature flag is activated
settings.A2_ALLOW_PHONE_AUTHN_MANAGEMENT = True
phone1 = Attribute.objects.create(
name='another_phone',
kind='phone_number',
label='Another phone',
)
phone2 = Attribute.objects.create(
name='yet_another_phone',
kind='fr_phone_number',
label='Yet another phone',
)
resp = app.get('/manage/authenticators/%s/edit/' % authenticator.pk)
resp.form['accept_email_authentication'] = False
resp.form['accept_phone_authentication'] = True
assert resp.form['phone_identifier_field'].options == [
(str(phone1.id), False, 'Another phone'),
(str(phone2.id), False, 'Yet another phone'),
]
resp.form['phone_identifier_field'] = phone2.id
resp.form.submit()
authenticator.refresh_from_db()
assert authenticator.accept_email_authentication is False
assert authenticator.accept_phone_authentication is True
assert authenticator.phone_identifier_field == phone2
@pytest.mark.freeze_time('2022-04-19 14:00')
def test_authenticators_password_export(app, superuser):