Projet

Général

Profil

0001-create-password-validation-in-registration-completio.patch

version provisoire sans tests et sans traductions - Anonyme, 14 juin 2018 18:12

Télécharger (11,4 ko)

Voir les différences:

Subject: [PATCH] create password validation in registration completion
 (#24439)

 src/authentic2/registration_backend/forms.py  | 13 ++-
 src/authentic2/registration_backend/views.py  |  6 ++
 .../static/authentic2/css/style.css           | 67 ++++++++++++++
 .../static/authentic2/js/password.js          | 88 +++++++++++++++++++
 .../registration_completion_form.html         |  3 +-
 src/authentic2/validators.py                  | 40 +++++----
 6 files changed, 197 insertions(+), 20 deletions(-)
 create mode 100644 src/authentic2/static/authentic2/js/password.js
src/authentic2/registration_backend/forms.py
115 115

  
116 116

  
117 117
class RegistrationCompletionForm(RegistrationCompletionFormNoPassword):
118
    password1 = CharField(widget=PasswordInput, label=_("Password"),
118
    class Media:
119
        js = ('authentic2/js/password.js',)
120

  
121
    password1 = CharField(widget=PasswordInput(attrs={'class': 'validatePassword showPassword passwordEquality'}),
122
            label=_("Password"),
119 123
            validators=[validators.validate_password],
120
            help_text=validators.password_help_text())
121
    password2 = CharField(widget=PasswordInput, label=_("Password (again)"))
124
            help_text='%s <span class="a2-policy-block">%s</span>' % ('<i class="show-password-button" aria-hidden="true"></i>', validators.password_help_text()))
125
    password2 = CharField(widget=PasswordInput(attrs={'class': 'passwordEquality'}),
126
            label=_("Password (again)"),
127
            help_text='<span class="a2-passwords-matched">%s</span><span class="a2-passwords-unmatched">%s</span>' % \
128
                (ugettext('Passwords match.'), ugettext('Passwords does not match.')))
122 129

  
123 130
    def clean(self):
124 131
        """
src/authentic2/registration_backend/views.py
2 2
import collections
3 3
import logging
4 4
import random
5
import json
5 6

  
6 7
from django.conf import settings
7 8
from django.shortcuts import get_object_or_404
......
266 267
        ctx['email'] = self.email
267 268
        ctx['email_is_unique'] = self.email_is_unique
268 269
        ctx['create'] = 'create' in self.request.GET
270
        ctx['passwordpolicy'] = json.dumps({
271
            'A2_PASSWORD_POLICY_MIN_LENGTH': app_settings.A2_PASSWORD_POLICY_MIN_LENGTH,
272
            'A2_PASSWORD_POLICY_MIN_CLASSES': app_settings.A2_PASSWORD_POLICY_MIN_CLASSES,
273
            'A2_PASSWORD_POLICY_REGEX': app_settings.A2_PASSWORD_POLICY_REGEX,
274
        })
269 275
        return ctx
270 276

  
271 277
    def get(self, request, *args, **kwargs):
src/authentic2/static/authentic2/css/style.css
76 76
.a2-log-message {
77 77
  white-space: pre-wrap;
78 78
}
79

  
80
.a2-policy-block {
81
  display: block;
82
}
83

  
84
.a2-min-class-policy, .a2-min-length-policy, .a2-regexp-policy {
85
  display: inline;
86
}
87

  
88
.password-error {
89
  font-weight: bold;
90
  color: red;
91
}
92

  
93
.password-error:before {
94
  content: "\f071";
95
  margin-right: 0.3em;
96
  font-family: FontAwesome;
97
  font-size: 100%;
98
  color: red;
99
}
100

  
101
.password-ok:before {
102
  content: "\f00c";
103
  font-family: FontAwesome;
104
  font-size: 100%;
105
  color: green;
106
}
107

  
108
.showPassword ~ .helptext{
109
  display: inline;
110
  margin-left: 0.5em;
111
}
112

  
113
.show-password-button {
114
  cursor: pointer;
115
}
116

  
117
.show-password-button:after {
118
	content: "\f06e";
119
	font-family: FontAwesome;
120
	font-size: 150%;
121
}
122

  
123
.hide-password-button:after {
124
	content: "\f070";
125
	font-family: FontAwesome;
126
	font-size: 150%;
127
}
128

  
129
.a2-passwords-unmatched {
130
  display: none;
131
  color: red;
132
}
133

  
134
.a2-passwords-matched {
135
  display: none;
136
  color: green;
137
}
138

  
139
.password-error ~ .helptext .a2-passwords-unmatched {
140
  display: block;
141
}
142

  
143
.password-ok ~ .helptext .a2-passwords-matched {
144
  display: block;
145
}
src/authentic2/static/authentic2/js/password.js
1
"use strict";
2
/* globals $, console, window */
3

  
4
$(function () {
5
	var toggleError = function($elt) {
6
		$elt.removeClass('password-ok');
7
		$elt.addClass('password-error');
8
	}
9
	var toggleOk = function($elt) {
10
		$elt.removeClass('password-error');
11
		$elt.addClass('password-ok');
12
	}
13
	var validatePassword = function () {
14
		var settings = $(this).data('passwordPolicy');
15
		var minClassElt = $(this).find('.a2-min-class-policy');
16
		var minLengthElt = $(this).find('.a2-min-length-policy');
17
		var regexpElt = $(this).find('.a2-regexp-policy');
18
		$(this)
19
			.find('.validatePassword')
20
			.each(function () {
21
				var password = $(this).val();
22
				var min_len = settings.A2_PASSWORD_POLICY_MIN_LENGTH;
23
				if (min_len && password.length < min_len) {
24
					toggleError(minLengthElt);
25
				} else {
26
					toggleOk(minLengthElt);
27
				}
28
				var digits = /\d/g;
29
				var lowerCase = /[a-z]/g;
30
				var upperCase = /[A-Z]/g;
31
				var punctuation = /'!"#\$%&\\'\(\)\*\+,-\.\/:;<=>\?@\[\]\^_`\{\|\}~'/g;
32
				var minClassCount = settings.A2_PASSWORD_POLICY_MIN_CLASSES;
33
				var classCount = 0;
34
				if (minClassCount) {
35
					[digits, lowerCase, upperCase, punctuation].forEach(function (cls) {
36
						if (cls.test(password)) classCount++;
37
					})
38
					if (classCount < minClassCount) {
39
						toggleError(minClassElt);
40
					} else {
41
						toggleOk(minClassElt);
42
					}
43
				}
44
				if (settings.A2_PASSWORD_POLICY_REGEX) {
45
					var regExpObject = new RegExp(settings.A2_PASSWORD_POLICY_REGEX, 'g');
46
					if (!regExpObject.test(password)) {
47
						toggleError(regexpElt);
48
					} else {
49
						toggleOk(regexpElt);
50
					}
51
				}
52
			});
53
	}
54
	var passwordEquality = function () {
55
		var form = $(this).parents('form');
56
		var input0 = form.find('.passwordEquality').eq(0);
57
		var input1 = form.find('.passwordEquality').eq(1);
58
		if (!input1.val() || !input0.val()) return;
59
		if (input0.val() !== input1.val()) {
60
			toggleError(input1);
61
		} else {
62
			toggleOk(input1);
63
		}
64
	}
65
	var showPassword = function () {
66
		$(this).addClass('hide-password-button');
67
		$(this)
68
			.parents('form')
69
			.find('input.showPassword')
70
			.attr('type', 'text');
71
	}
72
	var hidePassword = function () {
73
		$(this).removeClass('hide-password-button');
74
		$(this)
75
			.parents('form')
76
			.find('input.showPassword')
77
			.attr('type', 'password');
78
	}
79
	// TODO check both passwords are equal
80
	$('form[data-password-policy]')
81
		.keyup(validatePassword);
82
	$('form[data-password-policy] input[type=password].passwordEquality')
83
		.keyup(passwordEquality);
84
	$('form.showPassword')
85
		.on('mousedown', '.show-password-button', showPassword);
86
	$('form.showPassword')
87
		.on('mouseup', '.show-password-button', hidePassword);
88
});
src/authentic2/templates/registration/registration_completion_form.html
25 25
{% block content %}
26 26
      <h2>{% trans "Registration" %}</h2>
27 27
      <p>{% trans "Please fill the form to complete your registration" %}</p>
28
      <form method="post">
28
      <form method="post" class="showPassword passwordEquality"
29
        data-password-policy='{% autoescape off %}{{ passwordpolicy }}{% endautoescape %}'>
29 30
        {% csrf_token %}
30 31
        {{ form.as_p }}
31 32
        <button class="submit-button">{% trans 'Submit' %}</button>
src/authentic2/validators.py
120 120

  
121 121

  
122 122
def __password_help_text_helper():
123
    if app_settings.A2_PASSWORD_POLICY_MIN_LENGTH and \
124
            app_settings.A2_PASSWORD_POLICY_MIN_CLASSES:
125
        yield ugettext('Your password must contain at least %(min_length)d characters from at '
126
                'least %(min_classes)d classes among: lowercase letters, uppercase letters, '
127
                'digits and punctuations.') % {
128
                        'min_length': app_settings.A2_PASSWORD_POLICY_MIN_LENGTH,
129
                        'min_classes': app_settings.A2_PASSWORD_POLICY_MIN_CLASSES}
123
    '''
124
    Password fields help_text
125
    '''
126
    min_length_html = '<span class="a2-min-length-policy">%s</span>' %\
127
        ugettext('Your password must contain at least %(min_length)d characters.' %
128
        {'min_length': app_settings.A2_PASSWORD_POLICY_MIN_LENGTH})
129

  
130
    min_class_html = '<span class="a2-min-class-policy">%s</span>' %\
131
        ugettext(('at least %(min_classes)d classes among: lowercase letters, uppercase letters, digits and punctuations.') %
132
        {'min_classes': app_settings.A2_PASSWORD_POLICY_MIN_CLASSES})
133
    if app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG:
134
        regexp_html = '<span class="a2-regexp-policy">%s</span>' %\
135
            ugettext(app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG)
136
    else:
137
        regexp_html = '<span class="a2-regexp-policy">%s</span>' %\
138
            ugettext('Your password must match the regular expression: %(regexp)s, please change this message using the A2_PASSWORD_POLICY_REGEX_ERROR_MSG setting.') % \
139
                {'regexp': app_settings.A2_PASSWORD_POLICY_REGEX}
140

  
141
    if app_settings.A2_PASSWORD_POLICY_MIN_LENGTH and app_settings.A2_PASSWORD_POLICY_MIN_CLASSES:
142
        yield '%s %s %s' % (min_length_html, _('from'), min_class_html)
130 143
    else:
131 144
        if app_settings.A2_PASSWORD_POLICY_MIN_LENGTH:
132
            yield ugettext('Your password must contain at least %(min_length)d characters.') % {'min_length': app_settings.A2_PASSWORD_POLICY_MIN_LENGTH}
145
            yield min_length_html
133 146
        if app_settings.A2_PASSWORD_POLICY_MIN_CLASSES:
134
            yield ugettext('Your password must contain characters from at least %(min_classes)d '
135
                    'classes among: lowercase letters, uppercase letters, digits '
136
                    'and punctuations.') % {'min_classes': app_settings.A2_PASSWORD_POLICY_MIN_CLASSES}
147
            yield "%s %s" % (ugettext('Your password must contain characters from'), min_class_html)
137 148
    if app_settings.A2_PASSWORD_POLICY_REGEX:
138
        yield ugettext(app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG) or \
139
                ugettext('Your password must match the regular expression: '
140
                        '%(regexp)s, please change this message using the '
141
                        'A2_PASSWORD_POLICY_REGEX_ERROR_MSG setting.') % \
142
                        {'regexp': app_settings.A2_PASSWORD_POLICY_REGEX}
149
        yield regexp_html
150

  
143 151

  
144 152
def password_help_text():
145 153
    return ' '.join(__password_help_text_helper())
146
-