Repository: zestedesavoir/zds-site

Issue:
Inscription, sécurité mdp
Manque de sécu sur le mot de passe : on peut mettre son login, pas de limite min de taille apparemment, et on n'est pas obligé de mixer plusieurs types parmi minuscules, majuscules, chiffres et caractères spéciaux



Discussion:
J'ai horreur des sites qui me forcent des restrictions sur le mot de passe
que je veux mettre.

2014-05-16 15:35 GMT+01:00 Eskimon notifications@github.com:

> Manque de sécu sur le mot de passe : on peut mettre son login, pas de
> limite min de taille apparemment, et on n'est pas obligé de mixer plusieurs
> types parmi minuscules, majuscules, chiffres et caractères spéciaux
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/Taluu/ZesteDeSavoir/issues/541
> .

Je ne fais que rapporter les propos de mascotte sur ce point... Mais c'est vrai que pas bridé au moins la taille et la copie du pseudo lui-même c'est un peu juste non ? 

Moi je suis partisans de laisser l'utilisateur mettre ce qu'il veut. On
peut effectivement limiter la "taille" du mdp et vérifier si mdp != pseudo,
apres le reste je pense surtout que c'est pas notre "business"; si le
membre veut mettre "password" comme mot de passe, c'est son problème. Ya
pas vraiment de données importantes a securiser deriere ce mot de passe,
donc forcer des P@sS\/\/0rDs du gerne j'aime pas l’idée.

2014-05-16 15:44 GMT+01:00 Eskimon notifications@github.com:

> Je ne fais que rapporter les propos de mascotte sur ce point... Mais c'est
> vrai que pas bridé au moins la taille et la copie du pseudo lui-même c'est
> un peu juste non ?
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/Taluu/ZesteDeSavoir/issues/541#issuecomment-43339358
> .

Tu rigoles mais en ce moment mon mot de passe au boulot est : p@Ss2014

Au boulot, chez nous sur les réseaux internes la quasi totalité des mots de passe c'est root/root, admin/p@ssw0rd et compagnie...

Voici la branche qui va bien https://github.com/Taluu/ZesteDeSavoir/tree/fix_541

J'ai presqur fini (en bridant simplement la longeur du mot de passe a 6 car. et que le mot de passe soit différent du pseudo).
Il reste un truc que je vois pas comment faire...
Lorsque l'utilisateur veut réinitialiser son mot de passe, il est redirigé vers une page contenant les deux champs. Ca ca va. Par contre dans le formulaire on a pas l'utilisateur et je vois pas comment aller le chercher (pour véirfier mdp != pseudo)...
Si quelqu'un sait comment faire, qu'il le fasse et la pull request sera prête...

@Eskimon: Ton lien est mort (404). La branche a été mergée et l'issue résolue ?

Erf faudra voir avec @firm1 qui a fait un gros refactoring dans l'apres midi il me semble :s

Je pense qu'il faudra que tu repousses ta branche après le feu vert de @firm1 (il y a un soucis sur l'historique du projet et des bugs apparus suite à l'allègement du dépôt).

Erf ca ca tombe mal car j'ai viré pour resynchro endébut de soirée comme demandé par firm1... BOn ca va qu'il y avait pas trop de changement...

J'en suis tristement désolé, j'ai du retravailler sur le nettoyage du dépot. C'est vraiment dommage que ta branche soit perdue.

Pourrais-tu nous refaire une correction ?

Ouai c'est pas un probleme c'etait pas trop dur non plus donc ca ira ^^ (par contre il restera toujours le point "Comment recuperer l'utilisateur sur la page de reinit. du mot de passe)

Bon voila, j'ai repoussé [une branche](https://github.com/Taluu/ZesteDeSavoir/tree/fix_541) avec le début du fix...
Il reste une seule chose à faire, faire en sorte que le mdp soit différent du pseudo lors de l'action "mot de passe oublié". C'est le dernier test, écris en commentaire à la fin du code (le hasard fait bien les choses c'est à la fin du fichier uniquement modifié dans les commits). Le code marchera quand il y aura une variable "username" de peupler avec le pseudo.

J'ai fait en sorte que la taille minimum du mdp soit reglable via une simple constante au début du fichier (et c'est cette constante qui s'affiche dans les chaines de caractères d'avertissements).

Bref, plus qu'une bricole à faire et le code sera prêt pour une PR.

Bon bah j'ai complété et mergé tes commits. 

N'hésite pas a aller voir comment j'ai fait, c'est typiquement le genre de chose qu'il faudrait savoir faire pour d'autres issues.

PS : bon boulot

> PS : bon boulot

Merci :)

> N'hésite pas a aller voir comment j'ai fait, c'est typiquement le genre de chose qu'il faudrait savoir faire pour d'autres issues.

J'y compte bien !!

Je réouvre suite à une découverte...
Si l'utilisateur ne fournit pas de mot de passe (champs vides mais autres champs correctes) on a une erreur 500
Idem si l'utilisateur ne fournit pas d'adresse email (seul le champ email est laissé vide)

Par contre pour le champ username pas de souci...

Conclusion, faut des tests :D


Resolving patch:
diff --git a/zds/member/forms.py b/zds/member/forms.py
--- a/zds/member/forms.py
+++ b/zds/member/forms.py
@@ -111,6 +111,7 @@ class RegisterForm(forms.Form):
     password = forms.CharField(
         label='Mot de passe',
         max_length=MAX_PASSWORD_LENGTH,
+        min_length=MIN_PASSWORD_LENGTH,
         required=True,
         widget=forms.PasswordInput
     )
@@ -118,6 +119,7 @@ class RegisterForm(forms.Form):
     password_confirm = forms.CharField(
         label='Confirmation du mot de passe',
         max_length=MAX_PASSWORD_LENGTH,
+        min_length=MIN_PASSWORD_LENGTH,
         required=True,
         widget=forms.PasswordInput
     )
@@ -150,7 +152,7 @@ def clean(self):
 
         if not password_confirm == password:
             msg = u'Les mots de passe sont différents'
-            self._errors['password'] = self.error_class([''])
+            self._errors['password'] = self.error_class([msg])
             self._errors['password_confirm'] = self.error_class([msg])
 
             if 'password' in cleaned_data:
@@ -159,15 +161,6 @@ def clean(self):
             if 'password_confirm' in cleaned_data:
                 del cleaned_data['password_confirm']
 
-        # Check that the password is at least MIN_PASSWORD_LENGTH
-        if len(password) < MIN_PASSWORD_LENGTH:
-            msg = u'Le mot de passe doit faire au moins {0} caractères'.format(MIN_PASSWORD_LENGTH)
-            self._errors['password'] = self.error_class([msg])
-            if 'password' in cleaned_data:
-                del cleaned_data['password']
-            if 'password_confirm' in cleaned_data:
-                del cleaned_data['password_confirm']
-
         # Check that the user doesn't exist yet
         username = cleaned_data.get('username')
         if User.objects.filter(username=username).count() > 0:
@@ -184,14 +177,15 @@ def clean(self):
                 del cleaned_data['password_confirm']
 
         email = cleaned_data.get('email')
-        # Chech if email provider is authorized
-        with open(os.path.join(SITE_ROOT,
-                               'forbidden_email_providers.txt'), 'r') as fh:
-            for provider in fh:
-                if provider.strip() in email:
-                    msg = u'Utilisez un autre fournisseur d\'adresses mail.'
-                    self._errors['email'] = self.error_class([msg])
-                    break
+        if email:
+            # Chech if email provider is authorized
+            with open(os.path.join(SITE_ROOT,
+                                   'forbidden_email_providers.txt'), 'r') as fh:
+                for provider in fh:
+                    if provider.strip() in email:
+                        msg = u'Utilisez un autre fournisseur d\'adresses mail.'
+                        self._errors['email'] = self.error_class([msg])
+                        break
 
         # Check that the email is unique
         if User.objects.filter(email=email).count() > 0:


Test patch:
diff --git a/zds/member/tests.py b/zds/member/tests.py
--- a/zds/member/tests.py
+++ b/zds/member/tests.py
@@ -7,6 +7,7 @@
 from django.test import TestCase
 
 from zds.member.factories import ProfileFactory, StaffProfileFactory
+from zds.member.forms import RegisterForm
 from zds.member.models import Profile
 
 from .models import TokenRegister, Ban
@@ -73,6 +74,80 @@ def test_register(self):
 
         self.assertEquals(User.objects.get(username='firm1').is_active, True)
 
+        # From this point we have a valid user (firm1 / firm1@zestedesavoir.com)
+
+        # Now let's test all the failing cases !!
+
+        # 1. If passwords don't match
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'password',
+            'password_confirm': 'flavour',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 2. If passwords are too shorts
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'pass',
+            'password_confirm': 'pass',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 3. If passwords are equal to username
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'TheUsername',
+            'password_confirm': 'TheUsername',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 4. If there is no username
+        form_data = {
+            'username': '',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 5. If there is no email
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': ''
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 6. If the username is already taken
+        form_data = {
+            'username': 'firm1',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': 'email@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
+        # 7. If the email is already taken
+        form_data = {
+            'username': 'TheUsername',
+            'password': 'password',
+            'password_confirm': 'password',
+            'email': 'firm1@zestedesavoir.com'
+        }
+        form = RegisterForm(data=form_data)
+        self.assertEqual(form.is_valid(), False)
+
     def test_sanctions(self):
         """Test various sanctions."""