From 58d2c85a337e5565a49b7571b8fe67baf2bc7eb9 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Thu, 30 Jul 2020 11:54:14 +0200
Subject: [PATCH 1/2] auth_fc: factorize code checking email unicity (#45199)

---
 src/authentic2_auth_fc/utils.py | 17 +++++++++++++++++
 src/authentic2_auth_fc/views.py | 13 +++----------
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/src/authentic2_auth_fc/utils.py b/src/authentic2_auth_fc/utils.py
index bcb8db23..3d33b1b8 100644
--- a/src/authentic2_auth_fc/utils.py
+++ b/src/authentic2_auth_fc/utils.py
@@ -20,21 +20,24 @@ import json
 import datetime
 import uuid
 
 import requests
 from requests.adapters import HTTPAdapter
 from requests.packages.urllib3.util.retry import Retry
 
 from django.conf import settings
+from django.contrib.auth import get_user_model
 from django.shortcuts import resolve_url
 from django.utils.http import urlencode
 from django.utils.translation import ugettext_lazy as _
 from django.urls import reverse
 
+from authentic2.a2_rbac.utils import get_default_ou
+from authentic2 import app_settings as a2_app_settings
 from . import app_settings
 
 
 def build_logout_url(request, next_url=None):
     """
     For now fc_id_token in request.session is used as the flag of an
     active session on the OP. It is set in the login view and deleted in the
     logout return view.
@@ -137,16 +140,30 @@ def resolve_insee_country(insee_code):
     if not _insee_countries:
         _insee_countries = json.load(
             open(
                 os.path.join(
                     os.path.dirname(__file__), 'insee-countries.json')))
     return _insee_countries.get(insee_code, _('Unknown INSEE code'))
 
 
+def email_is_unique():
+    default_ou = get_default_ou()
+    return a2_app_settings.A2_EMAIL_IS_UNIQUE or default_ou.email_is_unique
+
+
+def users_having_email(email):
+    default_ou = get_default_ou()
+    User = get_user_model()
+    qs = User.objects.filter(email__iexact=email)
+    if not a2_app_settings.A2_EMAIL_IS_UNIQUE and default_ou.email_is_unique:
+        qs = qs.filter(ou=default_ou)
+    return qs
+
+
 def apply_user_info_mappings(user, user_info):
     assert user
     assert user_info
 
     logger = logging.getLogger(__name__)
     mappings = app_settings.user_info_mappings
 
     save_user = False
diff --git a/src/authentic2_auth_fc/views.py b/src/authentic2_auth_fc/views.py
index e64a11ce..38d7df9e 100644
--- a/src/authentic2_auth_fc/views.py
+++ b/src/authentic2_auth_fc/views.py
@@ -20,17 +20,17 @@ import json
 import requests
 
 from requests_oauthlib import OAuth2Session
 
 
 from django.db import IntegrityError
 from django.views.generic import View, FormView
 from django.http import HttpResponseRedirect, Http404
-from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
+from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.contrib import messages
 from django.shortcuts import resolve_url, render
 from django.urls import reverse
 from django.utils.six.moves.urllib import parse as urlparse
 from django.utils.translation import ugettext as _
 from django.utils.http import is_safe_url, urlencode
 from django.conf import settings
 from django.core import signing
@@ -39,17 +39,16 @@ from django.core.exceptions import PermissionDenied
 from django.forms import Form
 try:
     from django.contrib.auth.views import update_session_auth_hash
 except ImportError:
     update_session_auth_hash = None
 
 from authentic2 import app_settings as a2_app_settings
 from authentic2 import utils as a2_utils, hooks, constants
-from authentic2.a2_rbac.utils import get_default_ou
 from authentic2.forms.passwords import SetPasswordForm
 from authentic2.utils import views as views_utils
 
 from . import app_settings, models, utils
 
 
 class LoggerMixin(object):
     def __init__(self, *args, **kwargs):
@@ -388,32 +387,26 @@ class LoginOrLinkView(PopupViewMixin, FcOAuthSessionViewMixin, View):
                 messages.info(request,
                               _('Your FranceConnect account {} has been linked.').format(self.fc_display_name))
                 hooks.call_hooks('event', name='fc-link', user=request.user, sub=self.sub, request=request)
             else:
                 messages.info(request, _('Your local account has been updated.'))
             self.update_user_info()
             return self.redirect(request)
 
-        default_ou = get_default_ou()
-        email_is_unique = a2_app_settings.A2_EMAIL_IS_UNIQUE or default_ou.email_is_unique
         user = a2_utils.authenticate(
             request,
             sub=self.sub,
             user_info=self.user_info,
             token=self.token)
         if user:
             self.fc_account = user.fc_accounts.get(order=0)
-        if not user and self.user_info.get('email') and email_is_unique:
+        if not user and self.user_info.get('email') and utils.email_is_unique():
             email = self.user_info['email']
-            User = get_user_model()
-            qs = User.objects.filter(email__iexact=email)
-            if not a2_app_settings.A2_EMAIL_IS_UNIQUE and default_ou.email_is_unique:
-                qs = qs.filter(ou=default_ou)
-
+            qs = utils.users_having_email(email)
             if qs.exists():
                 # there should not be multiple accounts with the same mail
                 if len(qs) > 1:
                     self.logger.error(u'multiple accounts with the same mail %s, %s', email,
                                       list(qs))
                 # ok we have one account
                 elif len(qs) == 1:
                     user = qs[0]
-- 
2.27.0

