auth_fc: handle case of multiple FranceConnect accounts with same email (#53409)

This commit is contained in:
Benjamin Dauvergne 2021-04-26 15:24:43 +02:00
parent 86219adc82
commit 7f8353561d
2 changed files with 47 additions and 8 deletions

View File

@ -24,7 +24,7 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.views import update_session_auth_hash
from django.core.cache import cache
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError
from django.db import IntegrityError, transaction
from django.forms import Form
from django.http import Http404, HttpResponseRedirect
from django.urls import reverse
@ -387,6 +387,17 @@ class LoginOrLinkView(View):
% email,
)
return None
if not created and user.fc_accounts.exists():
messages.warning(
request,
_(
'Your FranceConnect email address "%s" is already used by the '
'FranceConnect account of "%s", so we cannot create an account for you. Please create '
'an account with another email address then link it to FranceConnect '
'using your account management page.'
)
% (email, user.get_full_name()),
)
else: # no email, we cannot disembiguate users, let's create it anyway
user = User.objects.create()
created = True
@ -396,13 +407,17 @@ class LoginOrLinkView(View):
user.set_unusable_password()
user.save()
models.FcAccount.objects.create(
user=user,
sub=sub,
order=0,
token=json.dumps(token),
user_info=json.dumps(user_info),
)
# As we intercept IntegrityError and we can never be sure if we are
# in a transaction or not, we must use one to prevent later SQL
# queries to fail.
with transaction.atomic():
models.FcAccount.objects.create(
user=user,
sub=sub,
order=0,
token=json.dumps(token),
user_info=json.dumps(user_info),
)
except IntegrityError:
# uniqueness check failed, as the user is new, it can only mean that the sub is not unique
# let's try again

View File

@ -28,6 +28,7 @@ from django.core.exceptions import PermissionDenied
from django.urls import reverse
from django.utils.timezone import now
from authentic2.a2_rbac.models import OrganizationalUnit as OU
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.apps.journal.models import Event
from authentic2.custom_user.models import DeletedUser
@ -550,3 +551,26 @@ def test_registration_page(settings, app, franceconnect, hooks):
# hook must have been called
assert hooks.calls['event'][0]['kwargs']['name'] == 'fc-create'
def test_same_email_different_sub(app, franceconnect):
OU.objects.all().update(email_is_unique=True)
assert User.objects.count() == 0
franceconnect.callback_params = {}
franceconnect.login_with_fc_fixed_params(app)
# ok user created
assert User.objects.count() == 1
# logout
app.session.flush()
# change sub
franceconnect.sub = '4567'
resp = franceconnect.login_with_fc_fixed_params(app).maybe_follow()
# email collision, sub is different, no new user created
assert User.objects.count() == 1
assert 'another email address' in resp