auth_fc: add flag to disable link by email (#68360)

This commit is contained in:
Benjamin Dauvergne 2022-11-23 14:42:32 +01:00
parent e524c5f94d
commit 0848d1cb3b
6 changed files with 77 additions and 18 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2022-11-23 13:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentic2_auth_fc', '0007_auto_20220615_1002'),
]
operations = [
migrations.AddField(
model_name='fcauthenticator',
name='link_by_email',
field=models.BooleanField(default=True, verbose_name='Link by email address'),
),
]

View File

@ -66,11 +66,19 @@ class FcAuthenticator(BaseAuthenticator):
verbose_name=_('Scopes'),
default=get_default_scopes,
)
link_by_email = models.BooleanField(_('Link by email address'), default=True)
type = 'fc'
how = ['france-connect']
unique = True
description_fields = ['show_condition', 'platform', 'client_id', 'client_secret', 'scopes']
description_fields = [
'show_condition',
'platform',
'client_id',
'client_secret',
'scopes',
'link_by_email',
]
class Meta:
verbose_name = _('FranceConnect')

View File

@ -60,7 +60,7 @@ logger = logging.getLogger(__name__)
User = get_user_model()
class UserOutsideDefaultOu(Exception):
class EmailExistsError(Exception):
pass
@ -435,9 +435,7 @@ class LoginOrLinkView(View):
# try to create or find an user with this email
try:
user, created = self.get_or_create_user_with_email(email)
except UserOutsideDefaultOu:
user = None
except User.MultipleObjectsReturned:
except EmailExistsError:
user = None
if not user:
messages.warning(
@ -557,11 +555,15 @@ class LoginOrLinkView(View):
Lock.lock_email(email)
try:
user = qs.get_by_email(email)
if not self.authenticator.link_by_email:
raise EmailExistsError
except User.DoesNotExist:
return User.objects.create(ou=ou, email=email), True
except User.MultipleObjectsReturned:
raise EmailExistsError
if user.ou != ou:
raise UserOutsideDefaultOu
raise EmailExistsError
return user, False

View File

@ -166,8 +166,8 @@ def service(db):
@pytest.fixture
def franceconnect(settings, service, db):
FcAuthenticator.objects.create(
def authenticator(db):
return FcAuthenticator.objects.create(
enabled=True,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
@ -175,6 +175,9 @@ def franceconnect(settings, service, db):
scopes=['profile', 'email'],
)
@pytest.fixture
def franceconnect(settings, authenticator, service, db):
mock_object = FranceConnectMock()
with mock_object():
yield mock_object

View File

@ -200,17 +200,44 @@ def test_create_expired(settings, app, franceconnect, hooks):
assert User.objects.count() == 0
def test_login_email_is_unique(settings, app, franceconnect, caplog):
settings.A2_EMAIL_IS_UNIQUE = True
user = User(email='john.doe@example.com', first_name='John', last_name='Doe', ou=get_default_ou())
user.set_password('toto')
user.save()
franceconnect.user_info['email'] = user.email
class TestLinkByEmail:
@pytest.fixture
def franceconnect(self, franceconnect):
franceconnect.callback_params = {'next': '/accounts/'}
return franceconnect
assert User.objects.count() == 1
franceconnect.login_with_fc_fixed_params(app)
assert User.objects.count() == 1
assert app.session['_auth_user_id'] == str(user.pk)
def test_enabled(self, settings, app, franceconnect, authenticator, caplog):
authenticator.link_by_email = True
authenticator.save()
user = User(email='john.doe@example.com', first_name='John', last_name='Doe', ou=get_default_ou())
user.set_password('toto')
user.save()
franceconnect.user_info['email'] = user.email
assert User.objects.count() == 1
franceconnect.login_with_fc_fixed_params(app)
assert User.objects.count() == 1
assert '_auth_user_id' in app.session
def test_disabled(self, settings, app, franceconnect, authenticator, caplog):
authenticator.link_by_email = False
authenticator.save()
user = User(email='john.doe@example.com', first_name='John', last_name='Doe', ou=get_default_ou())
user.set_password('toto')
user.save()
franceconnect.user_info['email'] = user.email
assert User.objects.count() == 1
response = franceconnect.login_with_fc_fixed_params(app)
assert User.objects.count() == 1
assert '_auth_user_id' not in app.session
# no login, so we must have produced a logout request toward FC
response = franceconnect.handle_logout(app, response.location)
response = response.maybe_follow()
assert 'Your FranceConnect email address' in response.pyquery('.messages .warning').text()
def test_link_after_login_with_password(app, franceconnect, simple_user):

View File

@ -418,6 +418,7 @@ def test_authenticators_fc(app, superuser):
'client_id',
'client_secret',
'scopes',
'link_by_email',
None,
]
assert 'phone' not in resp.pyquery('#id_scopes').html()