Repository: zestedesavoir/zds-site

Issue:
Il est possible de choisir un pseudo invalide
**Description du bug**

Il est possible de choisir un pseudo un peu farfelu comme par exemple `https://viki53.eu` qui est dans certains cas invalide : la fonction `reverse_lazy('tutorial:find-tutorial', args=(profile.user.username,))` qui permet de retrouver l'URL `'tutoriels/voir/(?P<username>[^/]+)/$'` retourne une erreur `NoReverseMatch`.

**Comment reproduire ?**

La liste des étapes qui permet de reproduire le bug :

1. Se renommer en `https://viki53.eu`
2. Aller sur son profil et constater l'erreur interne

**Comportement attendu**

Aucune erreur interne.

**Solution possible**

Il serait possible d'ajouter une petite vérification lors du changement de pseudo pour refuser les pseudos invalides : 

```py
try:
    reverse_lazy('tutorial:find-tutorial', args=(profile.user.username,))
except NoReverseMatch:
    # Refuser le pseudo
```


Discussion:
Plutôt que cette idée de validation un peu tordue, ne serait-il pas plus pertinent de vérifier le pseudo [à l'aide du validateur qui va bien](https://docs.djangoproject.com/fr/3.1/ref/contrib/auth/#validators), et de s'assurer que ce validateur (qui serait également utilisé à l'inscription, donc) exclue bien les _slashes_ ?
L’avantage de la solution proposée, c’est qu’elle garantit que le système va bien se comporter.

J’avoue que je suis dubitatif devant la description qui est faite de `UnicodeUsernameValidator` dans la documentation : tous les caractères bien formés sont des caractères Unicode (en supposant que le système tourne en Unicode pour les entrées-sorties). Du coup le validateur n’exclut pas grand-chose, et en particulier n’exclut pas l’exemple (qui n’utilise que des caractères ASCII d’ailleurs).
Le problème semble vraiment venir du `/` non ? (en tout cas quand je reproduis ça, ça semble venir du slash). Dans le validateur existant, on interdit déjà l'utilisation de virgule donc on pourrait simplement ajouter une condition interdisant le slash ?

Si ça semble être une bonne idée, je peux faire ça.
> Le problème semble vraiment venir du `/` non ? (en tout cas quand je reproduis ça, ça semble venir du slash). Dans le validateur existant, on interdit déjà l'utilisation de virgule donc on pourrait simplement ajouter une condition interdisant le slash ?
> 
> Si ça semble être une bonne idée, je peux faire ça.

C'est effectivement une solution donc tu peux faire ça ! :)

Resolving patch:
diff --git a/zds/member/validators.py b/zds/member/validators.py
--- a/zds/member/validators.py
+++ b/zds/member/validators.py
@@ -82,6 +82,8 @@ def validate_zds_username(value, check_username_available=True):
     skeleton_user_count = Profile.objects.filter(username_skeleton=Profile.find_username_skeleton(value)).count()
     if "," in value:
         msg = _("Le nom d'utilisateur ne peut contenir de virgules")
+    if "/" in value:
+        msg = _("Le nom d'utilisateur ne peut contenir de barres obliques")
     elif contains_utf8mb4(value):
         msg = _("Le nom d'utilisateur ne peut pas contenir des caractères utf8mb4")
     elif check_username_available and user_count > 0:


Test patch:
diff --git a/zds/member/tests/tests_forms.py b/zds/member/tests/tests_forms.py
--- a/zds/member/tests/tests_forms.py
+++ b/zds/member/tests/tests_forms.py
@@ -176,6 +176,17 @@ def test_username_coma_register_form(self):
         form = RegisterForm(data=data)
         self.assertFalse(form.is_valid())
 
+    def test_username_slash_register_form(self):
+        ProfileFactory()
+        data = {
+            "email": "test@gmail.com",
+            "username": "Ze/Tester",
+            "password": "ZePassword",
+            "password_confirm": "ZePassword",
+        }
+        form = RegisterForm(data=data)
+        self.assertFalse(form.is_valid())
+
 
 class MiniProfileFormTest(TestCase):
     """
@@ -315,6 +326,15 @@ def test_username_coma_register_form(self):
         form = ChangeUserForm(data=data, user=self.user1.user)
         self.assertFalse(form.is_valid())
 
+    def test_username_slash_changeuser_form(self):
+        ProfileFactory()
+        data = {
+            "username": "Ze/Tester",
+            "email": self.user1.user.email,
+        }
+        form = ChangeUserForm(data=data, user=self.user1.user)
+        self.assertFalse(form.is_valid())
+
 
 class ChangePasswordFormTest(TestCase):
     """