From 0396fe9e2dd542369fe0da62e731a04e2f7bd269 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Mon, 26 Apr 2021 15:24:43 +0200 Subject: [PATCH 2/2] auth_fc: handle case of multiple FranceConnect accounts with same email (#53409) --- src/authentic2_auth_fc/views.py | 29 +++++++++++++++++++++-------- tests/auth_fc/test_auth_fc.py | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/authentic2_auth_fc/views.py b/src/authentic2_auth_fc/views.py index 554f0640..1d04869f 100644 --- a/src/authentic2_auth_fc/views.py +++ b/src/authentic2_auth_fc/views.py @@ -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,15 @@ 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), - ) + # mandatory for the test to work :/, as test run in a transaction, we need a savepoint + 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 diff --git a/tests/auth_fc/test_auth_fc.py b/tests/auth_fc/test_auth_fc.py index 88607cff..f70c8b0a 100644 --- a/tests/auth_fc/test_auth_fc.py +++ b/tests/auth_fc/test_auth_fc.py @@ -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 -- 2.31.0