migrate A2_ACCEPT_*_AUTHENTICATION to models (#78046)

This commit is contained in:
Paul Marillonnet 2023-06-14 10:38:27 +02:00
parent a1169d83ff
commit eef2e168a7
17 changed files with 145 additions and 73 deletions

View File

@ -142,7 +142,6 @@ def extract_settings_from_environ():
'SHOW_DISCO_IN_MD',
'SSLAUTH_CREATE_USER',
'PUSH_PROFILE_UPDATES',
'A2_ACCEPT_EMAIL_AUTHENTICATION',
'A2_CAN_RESET_PASSWORD',
'A2_REGISTRATION_CAN_DELETE_ACCOUNT',
'A2_REGISTRATION_EMAIL_IS_UNIQUE',

View File

@ -283,8 +283,6 @@ 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_ACCEPT_EMAIL_AUTHENTICATION=Setting(default=True, definition='Enable authentication by email'),
A2_ACCEPT_PHONE_AUTHENTICATION=Setting(default=False, definition='Enable authentication by phone'),
A2_USER_DELETED_KEEP_DATA=Setting(
default=['email', 'uuid', 'phone'], definition='User data to keep after deletion'
),

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.18 on 2023-06-01 15:44
from django.conf import settings
from django.db import migrations
def migrate_a2_accept_authentication_settings(apps, schema_editor):
LoginPasswordAuthenticator = apps.get_model('authenticators', 'LoginPasswordAuthenticator')
authenticator, _ = LoginPasswordAuthenticator.objects.get_or_create(
slug='password-authenticator',
defaults={'enabled': True},
)
authenticator.accept_email_authentication = getattr(settings, 'A2_ACCEPT_EMAIL_AUTHENTICATION', True)
authenticator.accept_phone_authentication = getattr(settings, 'A2_ACCEPT_PHONE_AUTHENTICATION', False)
authenticator.save()
class Migration(migrations.Migration):
dependencies = [
('authenticators', '0010_auto_20230614_1017'),
]
operations = [
migrations.RunPython(
migrate_a2_accept_authentication_settings, reverse_code=migrations.RunPython.noop
),
]

View File

@ -46,7 +46,6 @@ from ldap.dn import escape_dn_chars
from ldap.filter import filter_format
from ldap.ldapobject import ReconnectLDAPObject as NativeLDAPObject
from authentic2 import app_settings
from authentic2.a2_rbac.models import OrganizationalUnit, Role
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.backends import is_user_authenticable
@ -56,7 +55,7 @@ from authentic2.middleware import StoreRequestMiddleware
from authentic2.models import Lock, UserExternalId
from authentic2.user_login_failure import user_login_failure, user_login_success
from authentic2.utils import crypto
from authentic2.utils.misc import PasswordChangeError, to_list
from authentic2.utils.misc import PasswordChangeError, get_password_authenticator, to_list
# code originaly copied from by now merely inspired by
# http://www.amherst.k12.oh.us/django-ldap.html
@ -450,7 +449,6 @@ class LDAPBackend:
'bindsasl': (),
'user_dn_template': '',
'user_filter': 'uid=%s', # will be '(|(mail=%s)(uid=%s))' if
# A2_ACCEPT_EMAIL_AUTHENTICATION is set (see update_default)
'sync_ldap_users_filter': '',
'user_basedn': '',
'group_basedn': '',
@ -1955,10 +1953,11 @@ class LDAPBackend:
if i in block and isinstance(block[i], str):
block[i] = (block[i],)
email_authn = get_password_authenticator().accept_email_authentication
for d, value in cls._DEFAULTS.items():
if d not in block:
block[d] = value
if d == 'user_filter' and app_settings.A2_ACCEPT_EMAIL_AUTHENTICATION:
if d == 'user_filter' and email_authn:
block[d] = '(|(mail=%s)(uid=%s))'
else:
if isinstance(value, str):

View File

@ -24,7 +24,7 @@ from phonenumbers import PhoneNumberFormat, format_number, is_valid_number
from authentic2.backends import get_user_queryset
from authentic2.user_login_failure import user_login_failure, user_login_success
from authentic2.utils.misc import parse_phone_number
from authentic2.utils.misc import get_password_authenticator, parse_phone_number
from .. import app_settings
@ -45,9 +45,10 @@ class ModelBackend(BaseModelBackend):
def get_query(self, username, realm=None, ou=None):
username_field = 'username'
queries = []
if app_settings.A2_ACCEPT_EMAIL_AUTHENTICATION:
password_authenticator = get_password_authenticator()
if password_authenticator.accept_email_authentication:
queries.append(models.Q(**{'email__iexact': username}))
if app_settings.A2_ACCEPT_PHONE_AUTHENTICATION:
if password_authenticator.is_phone_authn_active:
# try with the phone number as user identifier
if (pn := parse_phone_number(username)) and is_valid_number(pn):
query = {'phone': format_number(pn, PhoneNumberFormat.E164)}

View File

@ -61,7 +61,7 @@ class PasswordResetForm(HoneypotForm):
label=_('Email or username'), max_length=254, required=False
)
if not app_settings.A2_ACCEPT_PHONE_AUTHENTICATION or not get_user_model()._meta.get_field('phone'):
if not self.authenticator.is_phone_authn_active:
del self.fields['phone']
if 'email' in self.fields:
self.fields['email'].required = True
@ -95,7 +95,7 @@ class PasswordResetForm(HoneypotForm):
return phone
def clean(self):
if app_settings.A2_ACCEPT_PHONE_AUTHENTICATION and get_user_model()._meta.get_field('phone'):
if self.authenticator.is_phone_authn_active:
if (
not self.cleaned_data['email']
and not self.cleaned_data.get('email_or_username')

View File

@ -30,6 +30,7 @@ from authentic2.forms.fields import CharField, CheckPasswordField, NewPasswordFi
from authentic2.passwords import get_min_password_strength
from .. import app_settings, models
from ..utils import misc as utils_misc
from . import profile as profile_forms
from .fields import PhoneField, ValidatedEmailField
from .honeypot import HoneypotForm
@ -58,7 +59,7 @@ class RegistrationForm(HoneypotForm):
super().__init__(*args, **kwargs)
attributes = {a.name: a for a in models.Attribute.objects.all()}
if not app_settings.A2_ACCEPT_PHONE_AUTHENTICATION or not get_user_model()._meta.get_field('phone'):
if not utils_misc.get_password_authenticator().is_phone_authn_active:
del self.fields['phone']
self.fields['email'].required = True
@ -81,7 +82,7 @@ class RegistrationForm(HoneypotForm):
return email
def clean(self):
if app_settings.A2_ACCEPT_PHONE_AUTHENTICATION and get_user_model()._meta.get_field('phone'):
if utils_misc.get_password_authenticator().is_phone_authn_active:
if not self.cleaned_data.get('email') and not self.cleaned_data.get('phone'):
raise ValidationError(gettext('Please provide an email address or a mobile phone number.'))

View File

@ -748,11 +748,11 @@ def login_password_login(request, authenticator, *args, **kwargs):
if request.user.is_authenticated and request.login_token.get('action'):
form.initial['username'] = request.user.username or request.user.email
form.fields['username'].widget.attrs['readonly'] = True
if app_settings.A2_ACCEPT_EMAIL_AUTHENTICATION:
if authenticator.accept_email_authentication:
form.fields['username'].label = _('Username or email')
if app_settings.A2_ACCEPT_PHONE_AUTHENTICATION:
if authenticator.is_phone_authn_active:
form.fields['username'].label = _('Username, email or phone number')
elif app_settings.A2_ACCEPT_PHONE_AUTHENTICATION:
elif authenticator.is_phone_authn_active:
form.fields['username'].label = _('Username or phone number')
if app_settings.A2_USERNAME_LABEL:
form.fields['username'].label = app_settings.A2_USERNAME_LABEL
@ -858,7 +858,9 @@ class PasswordResetView(FormView):
return super().dispatch(*args, **kwargs)
def get_success_url(self):
if not app_settings.A2_ACCEPT_PHONE_AUTHENTICATION or not self.code: # user input is email
if (
not utils_misc.get_password_authenticator().is_phone_authn_active or not self.code
): # user input is email
return reverse('password_reset_instructions')
else: # user input is phone number
params = {}
@ -1168,7 +1170,7 @@ class BaseRegistrationView(HomeURLMixin, FormView):
if email:
return self.perform_email_registration(form, email)
if app_settings.A2_ACCEPT_PHONE_AUTHENTICATION:
if self.authenticator.is_phone_authn_active:
phone = form.cleaned_data.pop('phone')
return self.perform_phone_registration(form, phone)

View File

@ -3206,10 +3206,7 @@ def test_check_api_client_role_inheritance(app, superuser):
assert set(resp.json['data']['roles']) == {role1.uuid, role2.uuid, role3.uuid}
def test_api_basic_authz_user_phone_number(app, settings, superuser):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
Attribute.objects.get_or_create(name='phone', kind='phone_number')
def test_api_basic_authz_user_phone_number(app, settings, superuser, phone_activated_authn):
headers = {'Authorization': 'Basic abc'}
app.get('/api/users/', headers=headers, status=401)

View File

@ -30,6 +30,7 @@ from django.db.migrations.executor import MigrationExecutor
from authentic2.a2_rbac.models import OrganizationalUnit, Role
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.apps.authenticators.models import LoginPasswordAuthenticator
from authentic2.authentication import OIDCUser
from authentic2.manager.utils import get_ou_count
from authentic2.models import Attribute, Service
@ -329,6 +330,20 @@ def api_user(
return locals().get(request.param)
@pytest.fixture
def phone_activated_authn(db):
phone, dummy = Attribute.objects.get_or_create(
name='phone',
kind='phone_number',
defaults={'label': 'Phone'},
)
LoginPasswordAuthenticator.objects.update(
accept_phone_authentication=True,
phone_identifier_field=phone,
)
return LoginPasswordAuthenticator.objects.get()
@pytest.fixture(autouse=True)
def clear_cache():
cache.clear()

View File

@ -40,16 +40,20 @@ def test_user_filters(settings, db, simple_user, user_ou1, ou1):
assert not is_user_authenticable(user_ou1)
def test_model_backend_phone_number(settings, db, simple_user, nomail_user, ou1):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_model_backend_phone_number(settings, db, simple_user, nomail_user, ou1, phone_activated_authn):
nomail_user.attributes.phone = '+33123456789'
nomail_user.save()
simple_user.attributes.phone = '+33123456789'
simple_user.save()
assert authenticate(username=simple_user.phone, password=simple_user.username)
assert is_user_authenticable(simple_user)
assert authenticate(username=nomail_user.phone, password=nomail_user.username)
assert is_user_authenticable(nomail_user)
def test_model_backend_phone_number_email(settings, db, simple_user):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_model_backend_phone_number_email(settings, db, simple_user, phone_activated_authn):
simple_user.attributes.phone = '+33123456789'
simple_user.save()
# user with both phone number and username can authenticate in two different ways
assert authenticate(username=simple_user.username, password=simple_user.username)
assert authenticate(username=simple_user.phone, password=simple_user.username)

View File

@ -2119,7 +2119,7 @@ def test_get_extra_attributes(slapd, settings, client):
assert {'id': EE_O, 'street': EE_STREET, 'city': EE_CITY, 'postal_code': EE_POSTALCODE} in orgas
def test_config_to_lowercase():
def test_config_to_lowercase(db):
config = {
'fname_field': 'givenName',
'lname_field': 'surName',

View File

@ -36,8 +36,7 @@ def test_success(db, app, simple_user):
assert_event('user.logout', user=simple_user, session=session)
def test_success_email_with_phone_authn_activated(db, app, simple_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_success_email_with_phone_authn_activated(db, app, simple_user, settings, phone_activated_authn):
login(app, simple_user)
assert_event('user.login', user=simple_user, session=app.session, how='password-on-https')
session = app.session
@ -45,8 +44,9 @@ def test_success_email_with_phone_authn_activated(db, app, simple_user, settings
assert_event('user.logout', user=simple_user, session=session)
def test_success_phone_authn_nomail_user(db, app, nomail_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_success_phone_authn_nomail_user(db, app, nomail_user, settings, phone_activated_authn):
nomail_user.attributes.phone = '+33123456789'
nomail_user.save()
login(app, nomail_user, login='123456789', phone_authn=True)
assert_event('user.login', user=nomail_user, session=app.session, how='password-on-https')
session = app.session
@ -54,9 +54,9 @@ def test_success_phone_authn_nomail_user(db, app, nomail_user, settings):
assert_event('user.logout', user=nomail_user, session=session)
def test_success_phone_authn_simple_user(db, app, simple_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_success_phone_authn_simple_user(db, app, simple_user, settings, phone_activated_authn):
simple_user.attributes.phone = '+33123456789'
simple_user.save()
login(app, simple_user, login='123456789', phone_authn=True)
assert_event('user.login', user=simple_user, session=app.session, how='password-on-https')
session = app.session
@ -72,8 +72,7 @@ def test_failure(db, app, simple_user):
assert_event('user.login.failure', username='noone')
def test_failure_no_means_of_authentication(db, app, nomail_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_failure_no_means_of_authentication(db, app, nomail_user, settings, phone_activated_authn):
nomail_user.username = None
nomail_user.phone = None
nomail_user.save()
@ -92,17 +91,25 @@ def test_required_username_identifier(db, app, settings, caplog):
assert not response.pyquery('span.optional')
assert response.pyquery('label[for="id_username"]').text() == 'Username or email:'
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
phone, dummy = models.Attribute.objects.get_or_create(
name='phone',
kind='phone_number',
defaults={'label': 'Phone'},
)
LoginPasswordAuthenticator.objects.update(
accept_phone_authentication=True,
phone_identifier_field=phone,
)
response = app.get('/login/')
assert not response.pyquery('span.optional')
assert response.pyquery('label[for="id_username"]').text() == 'Username, email or phone number:'
settings.A2_ACCEPT_EMAIL_AUTHENTICATION = False
LoginPasswordAuthenticator.objects.update(accept_email_authentication=False)
response = app.get('/login/')
assert not response.pyquery('span.optional')
assert response.pyquery('label[for="id_username"]').text() == 'Username or phone number:'
settings.A2_ACCEPT_PHONE_AUTHENTICATION = False
LoginPasswordAuthenticator.objects.update(accept_phone_authentication=False)
response = app.get('/login/')
assert not response.pyquery('span.optional')
assert response.pyquery('label[for="id_username"]').text() == 'Username:'
@ -117,7 +124,7 @@ def test_login_form_fields_order(db, app, settings, ou1, ou2):
'login-password-submit',
]
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
LoginPasswordAuthenticator.objects.update(accept_phone_authentication=True)
response = app.get('/login/')
assert list(response.form.fields.keys()) == [
@ -572,3 +579,22 @@ def test_password_authenticator_data_migration_new_settings_invalid(migration, s
assert authenticator.login_exponential_retry_timeout_max_duration == 10
assert authenticator.emails_ip_ratelimit == '10/h'
assert authenticator.sms_ip_ratelimit == '10/h'
@pytest.mark.parametrize('email,phone', [(True, True), (True, False), (False, True)])
def test_password_authenticator_migration_accept_authentication_settings(migration, settings, email, phone):
app = 'authenticators'
migrate_from = [(app, '0010_auto_20230614_1017')]
migrate_to = [(app, '0011_migrate_a2_accept_authentication_settings')]
migration.before(migrate_from)
settings.A2_ACCEPT_EMAIL_AUTHENTICATION = email
settings.A2_ACCEPT_PHONE_AUTHENTICATION = phone
new_apps = migration.apply(migrate_to)
LoginPasswordAuthenticator = new_apps.get_model(app, 'LoginPasswordAuthenticator')
authenticator = LoginPasswordAuthenticator.objects.get()
assert authenticator.accept_email_authentication == email
assert authenticator.accept_phone_authentication == phone

View File

@ -80,6 +80,9 @@ 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',
@ -170,6 +173,8 @@ def test_authenticators_password_export(app, superuser):
'sms_number_ratelimit': '10/h',
'ou': None,
'related_objects': [],
'accept_email_authentication': True,
'accept_phone_authentication': False,
}
resp = app.get('/manage/authenticators/')

View File

@ -60,8 +60,11 @@ def test_send_password_reset_email(app, simple_user, mailoutbox):
utils.assert_event('user.password.reset', user=simple_user, session=app.session)
def test_send_password_reset_by_sms_code_improperly_configured(app, nomail_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_send_password_reset_by_sms_code_improperly_configured(
app, nomail_user, settings, phone_activated_authn
):
nomail_user.attributes.phone = nomail_user.phone
nomail_user.save()
settings.SMS_URL = 'https://foo.whatever.none/'
assert not SMSCode.objects.count()
@ -74,8 +77,9 @@ def test_send_password_reset_by_sms_code_improperly_configured(app, nomail_user,
assert 'Something went wrong while trying to send' in resp.pyquery('li.error').text()
def test_send_password_reset_by_sms_code(app, nomail_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_send_password_reset_by_sms_code(app, nomail_user, settings, phone_activated_authn):
nomail_user.attributes.phone = '+33123456789'
nomail_user.save()
settings.SMS_URL = 'https://foo.whatever.none/'
code_length = settings.SMS_CODE_LENGTH
@ -111,8 +115,9 @@ def test_send_password_reset_by_sms_code(app, nomail_user, settings):
app.get(url, status=404)
def test_send_password_reset_by_sms_code_next_url(app, nomail_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_send_password_reset_by_sms_code_next_url(app, nomail_user, settings, phone_activated_authn):
nomail_user.attributes.phone = '+33123456789'
nomail_user.save()
settings.SMS_URL = 'https://foo.whatever.none/'
resp = app.get('/accounts/consents/').follow()
@ -134,8 +139,7 @@ def test_send_password_reset_by_sms_code_next_url(app, nomail_user, settings):
assert "Consent Management" in resp
def test_password_reset_empty_form(app, db, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_password_reset_empty_form(app, db, settings, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
url = reverse('password_reset')
@ -147,8 +151,11 @@ def test_password_reset_empty_form(app, db, settings):
)
def test_password_reset_both_fields_filled_email_precedence(app, simple_user, settings, mailoutbox):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_password_reset_both_fields_filled_email_precedence(
app, simple_user, settings, mailoutbox, phone_activated_authn
):
simple_user.attributes.phone = '+33123456789'
simple_user.save()
settings.SMS_URL = 'https://foo.whatever.none/'
url = reverse('password_reset')
@ -163,8 +170,9 @@ def test_password_reset_both_fields_filled_email_precedence(app, simple_user, se
assert not SMSCode.objects.count()
def test_send_password_reset_by_sms_code_erroneous_phone_number(app, nomail_user, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_send_password_reset_by_sms_code_erroneous_phone_number(
app, nomail_user, settings, phone_activated_authn
):
settings.SMS_URL = 'https://foo.whatever.none/'
assert not SMSCode.objects.count()

View File

@ -951,15 +951,13 @@ def test_registration_completion(db, app, mailoutbox):
assert 'This password is not strong enough' in resp.text
def test_registration_no_identifier(app, db, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_registration_no_identifier(app, db, settings, phone_activated_authn):
resp = app.get(reverse('registration_register'))
resp = resp.form.submit()
assert 'Please provide an email address or a mobile' in resp.text
def test_registration_erroneous_phone_identifier(app, db, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_registration_erroneous_phone_identifier(app, db, settings, phone_activated_authn):
resp = app.get(reverse('registration_register'))
resp.form.set('phone_1', 'thatsnotquiteit')
resp = resp.form.submit()
@ -981,8 +979,7 @@ def sms_service_mock(url, request):
}
def test_phone_registration_wrong_code(app, db, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration_wrong_code(app, db, settings, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
resp = app.get(reverse('registration_register'))
@ -995,8 +992,7 @@ def test_phone_registration_wrong_code(app, db, settings):
assert resp.pyquery('li')[0].text_content() == 'Wrong SMS code.'
def test_phone_registration_expired_code(app, db, settings, freezer):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration_expired_code(app, db, settings, freezer, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
resp = app.get(reverse('registration_register'))
@ -1011,8 +1007,7 @@ def test_phone_registration_expired_code(app, db, settings, freezer):
assert resp.pyquery('li')[0].text_content() == 'The code has expired.'
def test_phone_registration_cancel(app, db, settings, freezer):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration_cancel(app, db, settings, freezer, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
resp = app.get(reverse('registration_register'))
@ -1026,8 +1021,7 @@ def test_phone_registration_cancel(app, db, settings, freezer):
assert not SMSCode.objects.count()
def test_phone_registration_improperly_configured(app, db, settings, freezer, caplog):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration_improperly_configured(app, db, settings, freezer, caplog, phone_activated_authn):
settings.SMS_URL = ''
resp = app.get(reverse('registration_register'))
@ -1043,8 +1037,7 @@ def test_phone_registration_improperly_configured(app, db, settings, freezer, ca
assert caplog.records[0].message == 'settings.SMS_URL is not set'
def test_phone_registration_connection_error(app, db, settings, freezer, caplog):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration_connection_error(app, db, settings, freezer, caplog, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
def mocked_requests_connection_error(*args, **kwargs):
@ -1067,8 +1060,7 @@ def test_phone_registration_connection_error(app, db, settings, freezer, caplog)
)
def test_phone_registration(app, db, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration(app, db, settings, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
code_length = settings.SMS_CODE_LENGTH
@ -1099,8 +1091,7 @@ def test_phone_registration(app, db, settings):
assert user.phone == '+33612345678'
def test_phone_registration_redirect_url(app, db, settings):
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
def test_phone_registration_redirect_url(app, db, settings, phone_activated_authn):
settings.SMS_URL = 'https://foo.whatever.none/'
resp = app.get('/accounts/consents/').follow()

View File

@ -321,10 +321,9 @@ def test_custom_account(settings, app, simple_user):
@pytest.mark.parametrize('view_name', ['registration_register']) # password_lost to be added with #69890
def test_views_sms_ratelimit(app, db, simple_user, settings, freezer, view_name):
def test_views_sms_ratelimit(app, db, simple_user, settings, freezer, view_name, phone_activated_authn):
freezer.move_to('2020-01-01')
LoginPasswordAuthenticator.objects.update(sms_ip_ratelimit='10/h', sms_number_ratelimit='3/d')
settings.A2_ACCEPT_PHONE_AUTHENTICATION = True
settings.SMS_SENDER = 'EO'
settings.SMS_URL = 'https://www.example.com/send'