Projet

Général

Profil

0001-report-all-password-requirements-at-once-on-password.patch

Benjamin Dauvergne, 31 mars 2015 16:06

Télécharger (6,1 ko)

Voir les différences:

Subject: [PATCH 1/2] report all password requirements at once on password
 input (fixes #6805)

 src/authentic2/registration_backend/forms.py |  9 ++++---
 src/authentic2/validators.py                 | 36 +++++++++++++++++++++++-----
 2 files changed, 36 insertions(+), 9 deletions(-)
src/authentic2/registration_backend/forms.py
67 67
            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
68 68
                      [data['email']], fail_silently=True)
69 69

  
70 70
class RegistrationCompletionForm(forms.UserAttributeFormMixin, Form):
71 71
    error_css_class = 'form-field-error'
72 72
    required_css_class = 'form-field-required'
73 73

  
74 74
    password1 = CharField(widget=PasswordInput, label=_("Password"),
75
            validators=[validators.validate_password])
75
            validators=[validators.validate_password],
76
            help_text=validators.password_help_text())
76 77
    password2 = CharField(widget=PasswordInput, label=_("Password (again)"))
77 78

  
78 79
    def __init__(self, *args, **kwargs):
79 80
        """
80 81
        Inject required fields in registration form
81 82
        """
82 83
        super(RegistrationCompletionForm, self).__init__(*args, **kwargs)
83 84
        User = compat.get_user_model()
......
192 193
                return ret
193 194
            self.user.save = save
194 195
        return ret
195 196

  
196 197

  
197 198
class SetPasswordForm(PasswordResetMixin, auth_forms.SetPasswordForm):
198 199
    new_password1 = CharField(label=_("New password"),
199 200
                                    widget=PasswordInput,
200
                                    validators=[validators.validate_password])
201
                                    validators=[validators.validate_password],
202
                                    help_text=validators.password_help_text())
201 203

  
202 204

  
203 205
class PasswordChangeForm(forms.NextUrlFormMixin, PasswordResetMixin,
204 206
        auth_forms.PasswordChangeForm):
205 207
    new_password1 = CharField(label=_("New password"),
206 208
                                    widget=PasswordInput,
207
                                    validators=[validators.validate_password])
209
                                    validators=[validators.validate_password],
210
                                    help_text=validators.password_help_text())
208 211

  
209 212

  
src/authentic2/validators.py
1 1
from __future__ import unicode_literals
2 2
import string
3 3
import re
4
import six
4 5

  
5 6
import smtplib
6 7

  
7
from django.utils.translation import ugettext_lazy as _
8
from django.utils.translation import ugettext_lazy as _, ugettext
8 9
from django.utils.encoding import force_text
9 10
from django.core.exceptions import ValidationError
11
from django.utils.functional import lazy
10 12

  
11 13
import socket
12 14
import dns.resolver
13 15
import dns.exception
14 16

  
15 17
from . import app_settings
16 18

  
17 19
# copied from http://www.djangotips.com/real-email-validation
......
78 80
email_validator = EmailValidator()
79 81

  
80 82
def validate_password(password):
81 83
    password_set = set(password)
82 84
    digits = set(string.digits)
83 85
    lower = set(string.lowercase)
84 86
    upper = set(string.uppercase)
85 87
    punc = set(string.punctuation)
88
    errors = []
86 89

  
87 90
    if not password:
88 91
        return
89 92
    min_len = app_settings.A2_PASSWORD_POLICY_MIN_LENGTH
90 93
    if len(password) < min_len:
91
        raise ValidationError(_('password must contain at least %d '
92
            'characters') % min_len)
94
        errors.append(ValidationError(_('password must contain at least %d '
95
            'characters') % min_len))
93 96

  
94 97
    class_count = 0
95 98
    for cls in (digits, lower, upper, punc):
96 99
        if not password_set.isdisjoint(cls):
97 100
            class_count += 1
98 101
    min_class_count = app_settings.A2_PASSWORD_POLICY_MIN_CLASSES
99 102
    if class_count < min_class_count:
100
        raise ValidationError(_('password must contain characters '
103
        errors.append(ValidationError(_('password must contain characters '
101 104
            'from at least %d classes among: lowercase letters, '
102
            'uppercase letters, digits, and punctuations') % min_class_count)
105
            'uppercase letters, digits, and punctuations') % min_class_count))
103 106
    if app_settings.A2_PASSWORD_POLICY_REGEX:
104 107
        if not re.match(app_settings.A2_PASSWORD_POLICY_REGEX, password):
105 108
            msg = app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG
106 109
            msg = msg or _('your password dit not match the regular expession %s') % app_settings.A2_PASSWORD_POLICY_REGEX
107
            raise ValidationError(msg)
110
            errors.append(ValidationError(msg))
111
    if errors:
112
        raise ValidationError(errors)
113

  
114
def __password_help_text_helper():
115
    if app_settings.A2_PASSWORD_POLICY_MIN_LENGTH:
116
        yield ugettext('Your password must contain at least %(min_length)d characters.') % {'min_length': app_settings.A2_PASSWORD_POLICY_MIN_LENGTH}
117
    if app_settings.A2_PASSWORD_POLICY_MIN_CLASSES:
118
        yield ugettext('Your password must contain characters from at least %(min_classes)d '
119
                'classes among: lowercase letters, uppercase letters, digits, '
120
                'and punctuations') % {'min_classes': app_settings.A2_PASSWORD_POLICY_MIN_CLASSES}
121
    if app_settings.A2_PASSWORD_POLICY_REGEX:
122
        yield ugettext(app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG) or \
123
                ugettext('Your password must match the regular expression: '
124
                        '%(regexp)s, please change this message using the '
125
                        'A2_PASSWORD_POLICY_REGEX_ERROR_MSG setting.') % \
126
                        {'regexp': app_settings.A2_PASSWORD_POLICY_REGEX}
127

  
128
def password_help_text():
129
    return ' '.join(__password_help_text_helper())
130

  
131
password_help_text = lazy(password_help_text, six.text_type)
108
-