From 2da324a6ecf8d45e007e260e4ec4cc34ef87fc10 Mon Sep 17 00:00:00 2001
From: Situphen <Situphen@users.noreply.github.com>
Date: Mon, 11 Mar 2024 21:48:29 +0100
Subject: [PATCH] =?UTF-8?q?Am=C3=A9lioration=20de=20la=20connexion=20via?=
 =?UTF-8?q?=20les=20r=C3=A9seaux=20sociaux=20(#6429)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (Connexion OAuth) On n'enlève plus les lettres accentuées des pseudos
* (Connexion OAuth) On récupère l'adresse de courriel des comptes Facebook
---
 zds/member/validators.py                   | 14 +++++++++++++-
 zds/settings/abstract_base/requirements.py |  3 +++
 zds/utils/misc.py                          | 13 ++++++++++---
 zds/utils/tests/test_misc.py               | 12 ++++++++++--
 4 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/zds/member/validators.py b/zds/member/validators.py
index e959bcac66..6140deeb78 100644
--- a/zds/member/validators.py
+++ b/zds/member/validators.py
@@ -4,7 +4,7 @@
 from django.utils.encoding import force_str
 from django.utils.translation import gettext_lazy as _
 
-from zds.utils.misc import contains_utf8mb4
+from zds.utils.misc import contains_utf8mb4, remove_utf8mb4
 from zds.member.models import BannedEmailProvider, Profile
 
 
@@ -70,6 +70,15 @@ def __call__(self, value, check_username_available=True):
 validate_zds_email = ZdSEmailValidator()
 
 
+def clean_username_social_auth(username):
+    """
+    Clean username of accounts created using social auth.
+    """
+    # These three conditions are the same as the first three in the "validate_zds_username" function below.
+    # If you modify one of them here, make sure you do the same there!
+    return remove_utf8mb4(username).replace(",", "").replace("/", "")
+
+
 def validate_zds_username(value, check_username_available=True):
     """
     Check if username is used by another user
@@ -88,6 +97,9 @@ def validate_zds_username(value, check_username_available=True):
     msg = None
     user_count = User.objects.filter(username=value).count()
     skeleton_user_count = Profile.objects.filter(username_skeleton=Profile.find_username_skeleton(value)).count()
+
+    # These first three conditions are the same as those in the "clean_username_social_auth" function above.
+    # If you modify one of them here, make sure you do the same there!
     if "," in value:
         msg = _("Le nom d'utilisateur ne peut contenir de virgules")
     elif "/" in value:
diff --git a/zds/settings/abstract_base/requirements.py b/zds/settings/abstract_base/requirements.py
index 75a3981238..9294562e50 100644
--- a/zds/settings/abstract_base/requirements.py
+++ b/zds/settings/abstract_base/requirements.py
@@ -10,9 +10,12 @@
 
 social_auth_config = config.get("social_auth", {})
 
+SOCIAL_AUTH_CLEAN_USERNAME_FUNCTION = "zds.member.validators.clean_username_social_auth"
+
 SOCIAL_AUTH_RAISE_EXCEPTIONS = False
 
 SOCIAL_AUTH_FACEBOOK_SCOPE = ["email"]
+SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {"fields": "name,email"}
 
 SOCIAL_AUTH_PIPELINE = (
     "social_core.pipeline.social_auth.social_details",
diff --git a/zds/utils/misc.py b/zds/utils/misc.py
index 60e6d39fc5..29899c2ad7 100644
--- a/zds/utils/misc.py
+++ b/zds/utils/misc.py
@@ -49,14 +49,21 @@ def convert_camel_to_underscore(camel_case):
     return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
 
 
-def contains_utf8mb4(s):
+def remove_utf8mb4(s):
     """
-    This string contains at least one character of more than 3 bytes
+    Remove characters of more than 3 bytes.
     """
     if not isinstance(s, str):
         s = str(s, "utf-8")
     re_pattern = re.compile("[^\u0000-\uD7FF\uE000-\uFFFF]", re.UNICODE)
-    return s != re_pattern.sub("\uFFFD", s)
+    return re_pattern.sub("", s)
+
+
+def contains_utf8mb4(s):
+    """
+    Check if this string contains at least one character of more than 3 bytes.
+    """
+    return s != remove_utf8mb4(s)
 
 
 def check_essential_accounts():
diff --git a/zds/utils/tests/test_misc.py b/zds/utils/tests/test_misc.py
index b33b0c268b..f97055def5 100644
--- a/zds/utils/tests/test_misc.py
+++ b/zds/utils/tests/test_misc.py
@@ -3,13 +3,21 @@
 from django.test import TestCase
 from zds.member.tests.factories import ProfileFactory, StaffProfileFactory, UserFactory
 from zds.tutorialv2.tests.factories import PublishedContentFactory
-from zds.utils.misc import contains_utf8mb4, check_essential_accounts
+from zds.utils.misc import contains_utf8mb4, check_essential_accounts, remove_utf8mb4
 from zds.utils.models import Alert
 from zds.utils.context_processor import get_header_notifications
 
 
 class Misc(TestCase):
-    def test_utf8mb4(self):
+    def test_remove_utf8mb4(self):
+        self.assertEqual("abc", remove_utf8mb4("abc"))
+        self.assertEqual("abc", remove_utf8mb4("abc"))
+        self.assertEqual("abc€", remove_utf8mb4("abc€"))
+        self.assertEqual("abc€", remove_utf8mb4("abc€"))
+        self.assertEqual("atbc€", remove_utf8mb4("a🐙tbc€"))
+        self.assertEqual("atbc€", remove_utf8mb4("a🐙tbc€"))
+
+    def test_contains_utf8mb4(self):
         self.assertFalse(contains_utf8mb4("abc"))
         self.assertFalse(contains_utf8mb4("abc"))
         self.assertFalse(contains_utf8mb4("abc€"))