0001-create-assisted-password-input-widgets-24438.patch
src/authentic2/app_settings.py | ||
---|---|---|
143 | 143 |
A2_PASSWORD_POLICY_MIN_LENGTH=Setting(default=8, definition='Minimum number of characters in a password'), |
144 | 144 |
A2_PASSWORD_POLICY_REGEX=Setting(default=None, definition='Regular expression for validating passwords'), |
145 | 145 |
A2_PASSWORD_POLICY_REGEX_ERROR_MSG=Setting(default=None, definition='Error message to show when the password do not validate the regular expression'), |
146 |
A2_PASSWORD_WIDGET_SHOW_ALL_BUTTON=Setting(default=False, definition='Show a button on BasePasswordInput for the user to see password input text'), |
|
146 | 147 |
A2_PASSWORD_POLICY_CLASS=Setting( |
147 | 148 |
default='authentic2.passwords.DefaultPasswordChecker', |
148 | 149 |
definition='path of a class to validate passwords'), |
src/authentic2/locale/fr/LC_MESSAGES/django.po | ||
---|---|---|
7 | 7 |
msgstr "" |
8 | 8 |
"Project-Id-Version: Authentic\n" |
9 | 9 |
"Report-Msgid-Bugs-To: \n" |
10 |
"POT-Creation-Date: 2018-07-05 16:39+0200\n"
|
|
10 |
"POT-Creation-Date: 2018-07-06 16:53+0200\n"
|
|
11 | 11 |
"PO-Revision-Date: 2018-07-05 14:08+0200\n" |
12 | 12 |
"Last-Translator: Mikaël Ates <mates@entrouvert.com>\n" |
13 | 13 |
"Language-Team: None\n" |
... | ... | |
17 | 17 |
"Content-Transfer-Encoding: 8bit\n" |
18 | 18 |
"Plural-Forms: nplurals=2; plural=n>1;\n" |
19 | 19 | |
20 |
#: authentic2/admin.py:26 |
|
20 |
#: debian-jessie/multitenant/debian_config.py:37 |
|
21 |
#: debian-wheezy/multitenant/debian_config.py:38 |
|
22 |
#: authentic2/profile_forms.py:19 |
|
23 |
#: authentic2/registration_backend/forms.py:33 |
|
24 |
#: authentic2/templates/authentic2/api_user_create_registration_email_body.txt:4 |
|
25 |
msgid "Email" |
|
26 |
msgstr "Courriel" |
|
21 | 27 |
msgid "Cleanup expired objects" |
22 | 28 |
msgstr "Nettoyer les objets qui ont expiré" |
23 | 29 | |
... | ... | |
63 | 69 |
msgid "You must at least give a username or an email to your user" |
64 | 70 |
msgstr "Un utilisateur doit au minimum posséder un courriel ou un identifiant." |
65 | 71 | |
66 |
#: authentic2/admin.py:167 authentic2/admin.py:204
|
|
67 |
#: authentic2/auth_frontends.py:14 authentic2/forms.py:24
|
|
68 |
#: authentic2/registration_backend/forms.py:118
|
|
69 |
#: authentic2/registration_backend/forms.py:204
|
|
70 |
#: authentic2/templates/authentic2/login_password_profile.html:4
|
|
72 |
#: admin.py:167 admin.py:204
|
|
73 |
#: auth_frontends.py:14 forms.py:24
|
|
74 |
#: registration_backend/forms.py:120
|
|
75 |
#: registration_backend/forms.py:206
|
|
76 |
#: templates/authentic2/login_password_profile.html:4 |
|
71 | 77 |
msgid "Password" |
72 | 78 |
msgstr "Mot de passe" |
73 | 79 | |
... | ... | |
82 | 88 |
"modifier le mot de passe de l'usager en utilisant <a href=\"password/\">ce " |
83 | 89 |
"formulaire</a>." |
84 | 90 | |
85 |
#: authentic2/admin.py:201 authentic2/registration_backend/forms.py:132
|
|
91 |
#: admin.py:201 registration_backend/forms.py:134
|
|
86 | 92 |
msgid "The two password fields didn't match." |
87 | 93 |
msgstr "Les deux champs mot de passe ne correspondent pas." |
88 | 94 | |
... | ... | |
114 | 120 |
msgid "you are not authorized to create users in this ou" |
115 | 121 |
msgstr "Vous n'êtes pas autorisé à vous inscrire dans cette collectivité." |
116 | 122 | |
117 |
#: authentic2/api_views.py:119 authentic2/api_views.py:126
|
|
123 |
#: authentic2/api_views.py:120 authentic2/api_views.py:127
|
|
118 | 124 |
msgid "You already have an account" |
119 | 125 |
msgstr "Vous avez déjà un compte." |
120 | 126 | |
121 |
#: authentic2/api_views.py:123
|
|
127 |
#: authentic2/api_views.py:124 tests/test_all.py:530 tests/test_all.py:675
|
|
122 | 128 |
msgid "Username is required in this ou" |
123 | 129 |
msgstr "L'identifiant est requis dans cette collectivité." |
124 | 130 | |
125 |
#: authentic2/api_views.py:654
|
|
131 |
#: authentic2/api_views.py:655
|
|
126 | 132 |
msgid "User successfully added to role" |
127 | 133 |
msgstr "Utilisateur ajouté au rôle" |
128 | 134 | |
129 |
#: authentic2/api_views.py:659
|
|
135 |
#: authentic2/api_views.py:660
|
|
130 | 136 |
msgid "User successfully removed from role" |
131 | 137 |
msgstr "Utilisateur retiré du rôle" |
132 | 138 | |
... | ... | |
596 | 602 |
msgid "base service models" |
597 | 603 |
msgstr "applications" |
598 | 604 | |
599 |
#: authentic2/profile_forms.py:19 authentic2/registration_backend/forms.py:32 |
|
600 |
#: authentic2/templates/authentic2/api_user_create_registration_email_body.txt:4 |
|
601 |
msgid "Email" |
|
602 |
msgstr "Courriel" |
|
605 |
#: authentic2/passwords.py:80 |
|
606 |
#, python-format |
|
607 |
msgid "%s characters" |
|
608 |
msgstr "%s caractères" |
|
609 | ||
610 |
#: authentic2/passwords.py:85 |
|
611 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:12 |
|
612 |
msgid "1 lowercase letter" |
|
613 |
msgstr "1 minuscule" |
|
614 | ||
615 |
#: authentic2/passwords.py:90 |
|
616 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:15 |
|
617 |
msgid "1 digit" |
|
618 |
msgstr "1 chiffre" |
|
619 | ||
620 |
#: authentic2/passwords.py:95 |
|
621 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:18 |
|
622 |
msgid "1 uppercase letter" |
|
623 |
msgstr "1 majuscule" |
|
603 | 624 | |
604 | 625 |
#: authentic2/profile_urls.py:41 |
605 | 626 |
#: authentic2/templates/registration/password_change_done.html:9 |
... | ... | |
646 | 667 |
msgid "Enter new password" |
647 | 668 |
msgstr "Entrez un nouveau mot de passe" |
648 | 669 | |
649 |
#: authentic2/registration_backend/forms.py:52
|
|
670 |
#: authentic2/registration_backend/forms.py:53
|
|
650 | 671 |
msgid "You cannot register with this email." |
651 | 672 |
msgstr "Vous ne pouvez pas vous inscrire avec cette adresse de courriel." |
652 | 673 | |
653 |
#: authentic2/registration_backend/forms.py:81
|
|
674 |
#: authentic2/registration_backend/forms.py:82 tests/test_all.py:763
|
|
654 | 675 |
msgid "This username is already in use. Please supply a different username." |
655 | 676 |
msgstr "" |
656 | 677 |
"Cet identifiant est déjà utilisé. Utilisez s'il vous plait un autre " |
657 | 678 |
"identifiant." |
658 | 679 | |
659 |
#: authentic2/registration_backend/forms.py:100
|
|
680 |
#: authentic2/registration_backend/forms.py:101
|
|
660 | 681 |
msgid "" |
661 | 682 |
"This email address is already in use. Please supply a different email " |
662 | 683 |
"address." |
... | ... | |
664 | 685 |
"Cette adresse de courriel est déjà utilisée. Utilisez s'il vous plait une " |
665 | 686 |
"autre adresse de courriel." |
666 | 687 | |
667 |
#: authentic2/registration_backend/forms.py:121
|
|
688 |
#: authentic2/registration_backend/forms.py:123
|
|
668 | 689 |
msgid "Password (again)" |
669 | 690 |
msgstr "Confirmation du mot de passe" |
670 | 691 | |
671 |
#: authentic2/registration_backend/forms.py:168
|
|
672 |
#: authentic2/registration_backend/forms.py:182
|
|
692 |
#: authentic2/registration_backend/forms.py:170
|
|
693 |
#: authentic2/registration_backend/forms.py:184
|
|
673 | 694 |
msgid "New password" |
674 | 695 |
msgstr "Nouveau mot de passe" |
675 | 696 | |
676 |
#: authentic2/registration_backend/forms.py:176
|
|
677 |
#: authentic2/registration_backend/forms.py:191
|
|
697 |
#: authentic2/registration_backend/forms.py:178
|
|
698 |
#: authentic2/registration_backend/forms.py:193
|
|
678 | 699 |
msgid "New password must differ from old password" |
679 | 700 |
msgstr "Le nouveau mot de passe doit être différent de l'ancien." |
680 | 701 | |
681 |
#: authentic2/registration_backend/forms.py:213
|
|
702 |
#: authentic2/registration_backend/forms.py:215
|
|
682 | 703 |
msgid "Password is invalid" |
683 | 704 |
msgstr "Le mot de passe est invalide" |
684 | 705 | |
685 |
#: authentic2/registration_backend/views.py:44
|
|
706 |
#: authentic2/registration_backend/views.py:45
|
|
686 | 707 |
msgid "Your activation key is expired" |
687 | 708 |
msgstr "Votre clé d'activation a expiré" |
688 | 709 | |
689 |
#: authentic2/registration_backend/views.py:47
|
|
710 |
#: authentic2/registration_backend/views.py:48
|
|
690 | 711 |
msgid "Activation failed" |
691 | 712 |
msgstr "Échec à l'activation du compte" |
692 | 713 | |
693 |
#: authentic2/registration_backend/views.py:56
|
|
714 |
#: authentic2/registration_backend/views.py:57
|
|
694 | 715 |
#: authentic2/templates/registration/registration_completion_choose.html:6 |
695 | 716 |
#: authentic2/templates/registration/registration_completion_form.html:17 |
696 | 717 |
#: authentic2/templates/registration/registration_completion_form.html:26 |
697 | 718 |
msgid "Registration" |
698 | 719 |
msgstr "Création d'un compte" |
699 | 720 | |
700 |
#: authentic2/registration_backend/views.py:344
|
|
721 |
#: authentic2/registration_backend/views.py:345
|
|
701 | 722 |
msgid "You have just created an account." |
702 | 723 |
msgstr "Vous venez de créer un compte." |
703 | 724 | |
704 |
#: authentic2/registration_backend/views.py:369
|
|
725 |
#: authentic2/registration_backend/views.py:370
|
|
705 | 726 |
#: authentic2/templates/authentic2/accounts.html:41 |
706 | 727 |
msgid "Delete account" |
707 | 728 |
msgstr "Supprimer votre compte" |
708 | 729 | |
709 |
#: authentic2/registration_backend/views.py:400
|
|
730 |
#: authentic2/registration_backend/views.py:401
|
|
710 | 731 |
msgid "" |
711 | 732 |
"Your account has been scheduled for deletion. You cannot use it anymore." |
712 | 733 |
msgstr "" |
... | ... | |
1133 | 1154 |
msgid "Notification: %(user)s, your account has been deleted" |
1134 | 1155 |
msgstr "Notification: %(user)s, votre compte a été supprimé" |
1135 | 1156 | |
1136 |
#: authentic2/templates/error_ssl.html:4 |
|
1157 |
#: templates/authentic2/widgets/assisted_password.html:4 |
|
1158 |
msgid "In order to create a secure password, please use <i>at least</i> : " |
|
1159 |
msgstr "" |
|
1160 |
"Pour avoir un mot de passe sécurisé, veuillez utiliser <i>à minima</i> :" |
|
1161 | ||
1162 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:9 |
|
1163 |
#, python-format |
|
1164 |
msgid "%(A2_PASSWORD_POLICY_MIN_LENGTH)s characters" |
|
1165 |
msgstr "%(A2_PASSWORD_POLICY_MIN_LENGTH)s caratères" |
|
1166 | ||
1167 |
#: templates/authentic2/widgets/assisted_password.html:16 |
|
1168 |
#, python-format |
|
1169 |
msgid "%(A2_PASSWORD_POLICY_REGEX_ERROR_MSG)s" |
|
1170 |
msgstr "%(A2_PASSWORD_POLICY_REGEX_ERROR_MSG)s" |
|
1171 | ||
1172 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:24 |
|
1173 |
#, python-format |
|
1174 |
msgid "" |
|
1175 |
"Match the regular expression: %(A2_PASSWORD_POLICY_REGEX)s, please change " |
|
1176 |
"this message using the A2_PASSWORD_POLICY_REGEX_ERROR_MSG setting.'" |
|
1177 |
msgstr "" |
|
1178 |
"Valider l'expression régulière %(A2_PASSWORD_POLICY_REGEX)s. Veuillez " |
|
1179 |
"changer ce message en modifiant le paramètre " |
|
1180 |
"A2_PASSWORD_POLICY_REGEX_ERROR_MSG." |
|
1181 | ||
1182 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:31 |
|
1183 |
msgid "Both passwords must match." |
|
1184 |
msgstr "Les deux mots de passe doivent être identiques." |
|
1185 | ||
1186 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:32 |
|
1187 |
msgid "Passwords match." |
|
1188 |
msgstr "Les mots de passe sont identiques." |
|
1189 | ||
1190 |
#: authentic2/templates/authentic2/widgets/assisted_password.html:33 |
|
1191 |
msgid "Passwords do not match." |
|
1192 |
msgstr "Les deux mots de passe ne sont pas identiques." |
|
1193 | ||
1194 |
#: templates/error_ssl.html:4 |
|
1137 | 1195 |
msgid "Error: authentication failure" |
1138 | 1196 |
msgstr "Erreur: échec de l'authentification" |
1139 | 1197 | |
... | ... | |
1488 | 1546 | |
1489 | 1547 |
#: authentic2/validators.py:95 |
1490 | 1548 |
#, python-format |
1491 |
msgid "password must contain at least %d characters" |
|
1492 |
msgstr "Le mot de passe doit contenir au moins %d caractères." |
|
1493 | ||
1494 |
#: authentic2/validators.py:104 |
|
1495 |
#, python-format |
|
1496 |
msgid "" |
|
1497 |
"password must contain characters from at least %d classes among: lowercase " |
|
1498 |
"letters, uppercase letters, digits, and punctuations" |
|
1499 |
msgstr "" |
|
1500 |
"Le mot de passe doit contenir des caractères d'au moins %d types parmi: " |
|
1501 |
"minuscules, majuscules, chiffres et ponctuations." |
|
1502 | ||
1503 |
#: authentic2/validators.py:110 |
|
1504 |
#, python-format |
|
1505 |
msgid "your password dit not match the regular expession %s" |
|
1506 |
msgstr "Votre mot de passe ne valide pas l'expression régulière %s." |
|
1507 | ||
1508 |
#: authentic2/validators.py:125 |
|
1509 |
#, python-format |
|
1510 |
msgid "" |
|
1511 |
"Your password must contain at least %(min_length)d characters from at least " |
|
1512 |
"%(min_classes)d classes among: lowercase letters, uppercase letters, digits " |
|
1513 |
"and punctuations." |
|
1514 |
msgstr "" |
|
1515 |
"Le mot de passe doit contenir au moins %(min_length)d caractères d'au moins " |
|
1516 |
"%(min_classes)d types parmi : minuscules, majuscules, chiffres et " |
|
1517 |
"ponctuation." |
|
1518 | ||
1519 |
#: authentic2/validators.py:132 |
|
1520 |
#, python-format |
|
1521 |
msgid "Your password must contain at least %(min_length)d characters." |
|
1522 |
msgstr "Le mot de passe doit contenir au moins %(min_length)d caractères." |
|
1523 | ||
1524 |
#: authentic2/validators.py:134 |
|
1525 |
#, python-format |
|
1526 |
msgid "" |
|
1527 |
"Your password must contain characters from at least %(min_classes)d classes " |
|
1528 |
"among: lowercase letters, uppercase letters, digits and punctuations." |
|
1549 |
msgid "Password must obey the rule: %s" |
|
1529 | 1550 |
msgstr "" |
1530 |
"Le mot de passe doit contenir des caractères d'au moins %(min_classes)d " |
|
1531 |
"types parmi: minuscules, majuscules, chiffres et ponctuations." |
|
1532 | 1551 | |
1533 |
#: authentic2/validators.py:139 |
|
1534 |
#, python-format |
|
1535 |
msgid "" |
|
1536 |
"Your password must match the regular expression: %(regexp)s, please change " |
|
1537 |
"this message using the A2_PASSWORD_POLICY_REGEX_ERROR_MSG setting." |
|
1552 |
#: authentic2/validators.py:102 |
|
1553 |
msgid "Password must obey the rules: " |
|
1538 | 1554 |
msgstr "" |
1539 |
"Votre mot de passe ne valide pas l'expression régulière %(regexp)s. Veuillez " |
|
1540 |
"changer ce message en modifiant le paramètre " |
|
1541 |
"A2_PASSWORD_POLICY_REGEX_ERROR_MSG." |
|
1542 | 1555 | |
1543 | 1556 |
#: authentic2/views.py:175 |
1544 | 1557 |
msgid "Email Change" |
... | ... | |
1604 | 1617 |
msgid "Format:" |
1605 | 1618 |
msgstr "Format :" |
1606 | 1619 | |
1620 |
#~ msgid "password must contain at least %d characters" |
|
1621 |
#~ msgstr "Le mot de passe doit contenir au moins %d caractères." |
|
1622 | ||
1623 |
#~ msgid "" |
|
1624 |
#~ "password must contain characters from at least %d classes among: " |
|
1625 |
#~ "lowercase letters, uppercase letters, digits, and punctuations" |
|
1626 |
#~ msgstr "" |
|
1627 |
#~ "Le mot de passe doit contenir des caractères d'au moins %d types parmi: " |
|
1628 |
#~ "minuscules, majuscules, chiffres et ponctuations." |
|
1629 | ||
1630 |
#~ msgid "your password dit not match the regular expression %s" |
|
1631 |
#~ msgstr "Votre mot de passe ne valide pas l'expression régulière %s." |
|
1632 | ||
1633 |
#~ msgid "" |
|
1634 |
#~ "Your password must contain at least %(min_length)d characters from at " |
|
1635 |
#~ "least %(min_classes)d classes among: lowercase letters, uppercase " |
|
1636 |
#~ "letters, digits and punctuations." |
|
1637 |
#~ msgstr "" |
|
1638 |
#~ "Le mot de passe doit contenir au moins %(min_length)d caractères d'au " |
|
1639 |
#~ "moins %(min_classes)d types parmi : minuscules, majuscules, chiffres et " |
|
1640 |
#~ "ponctuation." |
|
1641 | ||
1642 |
#~ msgid "Your password must contain at least %(min_length)d characters." |
|
1643 |
#~ msgstr "Le mot de passe doit contenir au moins %(min_length)d caractères." |
|
1644 | ||
1645 |
#~ msgid "" |
|
1646 |
#~ "Your password must contain characters from at least %(min_classes)d " |
|
1647 |
#~ "classes among: lowercase letters, uppercase letters, digits and " |
|
1648 |
#~ "punctuations." |
|
1649 |
#~ msgstr "" |
|
1650 |
#~ "Le mot de passe doit contenir des caractères d'au moins %(min_classes)d " |
|
1651 |
#~ "types parmi: minuscules, majuscules, chiffres et ponctuations." |
|
1652 | ||
1607 | 1653 |
#~ msgid "Modify" |
1608 | 1654 |
#~ msgstr "Modifier" |
1609 | 1655 |
src/authentic2/passwords.py | ||
---|---|---|
76 | 76 |
if self.min_length: |
77 | 77 |
yield self.Check( |
78 | 78 |
result=len(password) >= self.min_length, |
79 |
label=_('at least %s characters') % self.min_length)
|
|
79 |
label=_('%s characters') % self.min_length) |
|
80 | 80 | |
81 | 81 |
if self.at_least_one_lowercase: |
82 | 82 |
yield self.Check( |
83 | 83 |
result=any(c.islower() for c in password), |
84 |
label=_('at least 1 lowercase letter'))
|
|
84 |
label=_('1 lowercase letter')) |
|
85 | 85 | |
86 | 86 |
if self.at_least_one_digit: |
87 | 87 |
yield self.Check( |
88 | 88 |
result=any(c.isdigit() for c in password), |
89 |
label=_('at least 1 digit'))
|
|
89 |
label=_('1 digit')) |
|
90 | 90 | |
91 | 91 |
if self.at_least_one_uppercase: |
92 | 92 |
yield self.Check( |
93 | 93 |
result=any(c.isupper() for c in password), |
94 |
label=_('at least 1 uppercase letter'))
|
|
94 |
label=_('1 uppercase letter')) |
|
95 | 95 | |
96 | 96 |
if self.regexp and self.regexp_label: |
97 | 97 |
yield self.Check( |
src/authentic2/registration_backend/forms.py | ||
---|---|---|
1 | 1 |
import re |
2 | 2 |
import copy |
3 | 3 |
from collections import OrderedDict |
4 |
import json |
|
4 | 5 | |
5 | 6 |
from django.conf import settings |
6 | 7 |
from django.core.exceptions import ValidationError |
... | ... | |
15 | 16 |
from django.core.mail import send_mail |
16 | 17 |
from django.core import signing |
17 | 18 |
from django.template import RequestContext |
18 |
from django.template.loader import render_to_string |
|
19 | 19 |
from django.core.urlresolvers import reverse |
20 | 20 |
from django.core.validators import RegexValidator |
21 | 21 | |
22 |
from .widgets import CheckPasswordInput, NewPasswordInput |
|
22 | 23 |
from .. import app_settings, compat, forms, utils, validators, models, middleware, hooks |
23 | 24 |
from authentic2.a2_rbac.models import OrganizationalUnit |
24 | 25 | |
... | ... | |
115 | 116 | |
116 | 117 | |
117 | 118 |
class RegistrationCompletionForm(RegistrationCompletionFormNoPassword): |
118 |
password1 = CharField(widget=PasswordInput, label=_("Password"), |
|
119 |
validators=[validators.validate_password], |
|
120 |
help_text=validators.password_help_text()) |
|
121 |
password2 = CharField(widget=PasswordInput, label=_("Password (again)")) |
|
119 | ||
120 |
password1 = CharField(widget=NewPasswordInput(), label=_("Password"), |
|
121 |
validators=[validators.validate_password], |
|
122 |
help_text=validators.password_help_text()) |
|
123 |
password2 = CharField(widget=CheckPasswordInput(), label=_("Password (again)")) |
|
122 | 124 | |
123 | 125 |
def clean(self): |
124 | 126 |
""" |
src/authentic2/registration_backend/widgets.py | ||
---|---|---|
1 |
from django.forms import PasswordInput |
|
2 |
from django.template.loader import render_to_string |
|
3 |
from django.utils.encoding import force_text |
|
4 |
from django.utils.safestring import mark_safe |
|
5 | ||
6 |
from .. import app_settings |
|
7 | ||
8 | ||
9 |
class BasePasswordInput(PasswordInput): |
|
10 |
""" |
|
11 |
a password Input with some features to help the user choosing a new password |
|
12 |
Inspired by Django >= 1.11 new-style rendering |
|
13 |
(cf. https://docs.djangoproject.com/fr/1.11/ref/forms/renderers) |
|
14 |
""" |
|
15 |
template_name = 'authentic2/widgets/assisted_password.html' |
|
16 |
features = {} |
|
17 | ||
18 |
class Media: |
|
19 |
js = ('authentic2/js/password.js',) |
|
20 |
css = { |
|
21 |
'all': ('authentic2/css/password.css',) |
|
22 |
} |
|
23 | ||
24 |
def get_context(self, name, value, attrs): |
|
25 |
""" |
|
26 |
Base get_context |
|
27 |
""" |
|
28 |
context = { |
|
29 |
'app_settings': { |
|
30 |
'A2_PASSWORD_POLICY_MIN_LENGTH': app_settings.A2_PASSWORD_POLICY_MIN_LENGTH, |
|
31 |
'A2_PASSWORD_POLICY_MIN_CLASSES': app_settings.A2_PASSWORD_POLICY_MIN_CLASSES, |
|
32 |
'A2_PASSWORD_POLICY_REGEX': app_settings.A2_PASSWORD_POLICY_REGEX, |
|
33 |
}, |
|
34 |
'features': self.features |
|
35 |
} |
|
36 |
# attach data-* attributes for password.js to activate events |
|
37 |
attrs.update(dict([('data-%s' % feat.replace('_', '-'), is_active) for feat, is_active in self.features.items()])) |
|
38 | ||
39 |
context['widget'] = { |
|
40 |
'name': name, |
|
41 |
'is_hidden': self.is_hidden, |
|
42 |
'required': self.is_required, |
|
43 |
'template_name': self.template_name, |
|
44 |
'attrs': self.build_attrs(extra_attrs=attrs, name=name, type=self.input_type) |
|
45 |
} |
|
46 |
# Only add the 'value' attribute if a value is non-empty. |
|
47 |
if value is None: |
|
48 |
value = '' |
|
49 |
if value != '': |
|
50 |
context['widget']['value'] = force_text(self._format_value(value)) |
|
51 | ||
52 |
return context |
|
53 | ||
54 |
def render(self, name, value, attrs=None, **kwargs): |
|
55 |
""" |
|
56 |
Override render with a template-based system |
|
57 |
Remove this line when dropping Django 1.8, 1.9, 1.10 compatibility |
|
58 |
""" |
|
59 |
return mark_safe(render_to_string(self.template_name, |
|
60 |
self.get_context(name, value, attrs))) |
|
61 | ||
62 | ||
63 |
class CheckPasswordInput(BasePasswordInput): |
|
64 |
""" |
|
65 |
Password typing assistance widget (eg. password2) |
|
66 |
""" |
|
67 |
features = { |
|
68 |
'check_equality': True, |
|
69 |
'show_all': app_settings.A2_PASSWORD_WIDGET_SHOW_ALL_BUTTON, |
|
70 |
'show_last': True, |
|
71 |
} |
|
72 | ||
73 |
def get_context(self, name, value, attrs): |
|
74 |
context = super(CheckPasswordInput, self).get_context( |
|
75 |
name, value, attrs) |
|
76 |
return context |
|
77 | ||
78 | ||
79 |
class NewPasswordInput(CheckPasswordInput): |
|
80 |
""" |
|
81 |
Password creation assistance widget with policy (eg. password1) |
|
82 |
""" |
|
83 |
features = { |
|
84 |
'check_equality': False, |
|
85 |
'show_all': app_settings.A2_PASSWORD_WIDGET_SHOW_ALL_BUTTON, |
|
86 |
'show_last': True, |
|
87 |
'check_policy': True, |
|
88 |
} |
|
89 | ||
90 |
def get_context(self, name, value, attrs): |
|
91 |
context = super(NewPasswordInput, self).get_context(name, value, attrs) |
|
92 |
return context |
src/authentic2/static/authentic2/css/password.css | ||
---|---|---|
1 |
/* required in order to position a2-password-show-all and a2-password-show-last */ |
|
2 |
input[type=password].a2-password-assisted { |
|
3 |
padding-right: 60px; |
|
4 |
width: 100%; |
|
5 |
} |
|
6 | ||
7 |
.a2-password-icon { |
|
8 |
display: inline-block; |
|
9 |
width: calc(18em / 14); |
|
10 |
text-align: center; |
|
11 |
font-style: normal; |
|
12 |
padding-right: 1em; |
|
13 |
} |
|
14 | ||
15 |
/* default circle icon */ |
|
16 |
.a2-password-icon:before { |
|
17 |
font-family: FontAwesome; |
|
18 |
content: "\f111"; /* right hand icon */ |
|
19 |
font-size: 50%; |
|
20 |
} |
|
21 | ||
22 |
.a2-password-policy-helper { |
|
23 |
display: flex; |
|
24 |
height: auto; |
|
25 |
flex-direction: row; |
|
26 |
flex-wrap: wrap; |
|
27 |
position: relative; |
|
28 |
padding: 0.5rem 1rem; |
|
29 |
width: 90%; |
|
30 |
} |
|
31 | ||
32 |
/* we don't want helptext when a2-password-policy-helper is here */ |
|
33 |
.a2-password-policy-helper ~ .helptext { |
|
34 |
display: none; |
|
35 |
} |
|
36 | ||
37 |
.a2-password-policy-rule { |
|
38 |
flex: 1 1 50%; |
|
39 |
list-style: none; |
|
40 |
} |
|
41 | ||
42 |
.password-error { |
|
43 |
color: black; |
|
44 |
} |
|
45 | ||
46 |
.password-ok { |
|
47 |
color: green; |
|
48 |
} |
|
49 | ||
50 |
.password-error .a2-password-icon:before { |
|
51 |
content: "\f00d"; /* cross icon */ |
|
52 |
color: red; |
|
53 |
} |
|
54 | ||
55 |
.password-ok .a2-password-icon::before { |
|
56 |
content: "\f00c"; /* ok icon */ |
|
57 |
color: green; |
|
58 |
} |
|
59 | ||
60 |
.a2-password-show-last { |
|
61 |
position: relative; |
|
62 |
display: inline-block; |
|
63 |
float: right; |
|
64 |
opacity: 0; |
|
65 |
text-align: center; |
|
66 |
right: 10px; |
|
67 |
top: -4.5ex; |
|
68 |
width: 20px; |
|
69 |
} |
|
70 | ||
71 |
.a2-password-show-button { |
|
72 |
position: relative; |
|
73 |
display: inline-block; |
|
74 |
float: right; |
|
75 |
padding: 0; |
|
76 |
right: 10px; |
|
77 |
top: -4.4ex; |
|
78 |
cursor: pointer; |
|
79 |
width: 20px; |
|
80 |
} |
|
81 | ||
82 |
.a2-password-show-button:after { |
|
83 |
content: "\f06e"; /* eye */ |
|
84 |
font-family: FontAwesome; |
|
85 |
font-size: 125%; |
|
86 |
} |
|
87 | ||
88 |
.hide-password-button:after { |
|
89 |
content: "\f070"; /* crossed eye */ |
|
90 |
font-family: FontAwesome; |
|
91 |
font-size: 125%; |
|
92 |
} |
|
93 | ||
94 |
.a2-passwords-messages { |
|
95 |
display: block; |
|
96 |
padding: 0.5rem 1rem; |
|
97 |
} |
|
98 | ||
99 |
.a2-passwords-default { |
|
100 |
list-style: none; |
|
101 |
opacity: 0; |
|
102 |
} |
|
103 | ||
104 |
.password-error .a2-passwords-default, |
|
105 |
.password-ok .a2-passwords-default { |
|
106 |
display: none; |
|
107 |
} |
|
108 | ||
109 |
.a2-passwords-matched, |
|
110 |
.a2-passwords-unmatched { |
|
111 |
display: none; |
|
112 |
list-style: none; |
|
113 |
opacity: 0; |
|
114 |
transition: all 0.3s ease; |
|
115 |
} |
|
116 | ||
117 |
.password-error.a2-passwords-messages:before, |
|
118 |
.password-ok.a2-passwords-messages:before { |
|
119 |
display: none; |
|
120 |
} |
|
121 | ||
122 |
.password-error .a2-passwords-unmatched, |
|
123 |
.password-ok .a2-passwords-matched { |
|
124 |
display: block; |
|
125 |
opacity: 1; |
|
126 |
} |
|
127 | ||
128 |
.password-error .a2-passwords-unmatched .a2-password-icon:before { |
|
129 |
content: "\f00d"; /* cross icon */ |
|
130 |
color: red; |
|
131 |
} |
|
132 | ||
133 |
.password-ok .a2-passwords-matched .a2-password-icon:before { |
|
134 |
content: "\f00c"; /* ok icon */ |
|
135 |
color: green; |
|
136 |
} |
|
137 | ||
138 |
.a2-password-policy-intro { |
|
139 |
margin: 0; |
|
140 |
} |
src/authentic2/static/authentic2/css/style.css | ||
---|---|---|
76 | 76 |
.a2-log-message { |
77 | 77 |
white-space: pre-wrap; |
78 | 78 |
} |
79 | ||
80 |
.a2-registration-completion { |
|
81 |
padding: 1rem; |
|
82 |
min-width: 320px; |
|
83 |
width: 50%; |
|
84 |
} |
|
85 | ||
86 |
@media screen and (max-width: 800px) { |
|
87 |
.a2-registration-completion { |
|
88 |
width: 100%; |
|
89 |
} |
|
90 |
} |
|
91 | ||
92 |
.a2-registration-completion input, |
|
93 |
.a2-registration-completion select, |
|
94 |
.a2-registration-completion textarea |
|
95 |
{ |
|
96 |
width: 100%; |
|
97 |
} |
src/authentic2/static/authentic2/js/password.js | ||
---|---|---|
1 |
"use strict"; |
|
2 |
/* globals $, window, console */ |
|
3 | ||
4 |
$(function () { |
|
5 |
var debounce = function (func, milliseconds) { |
|
6 |
var timer; |
|
7 |
return function() { |
|
8 |
window.clearTimeout(timer); |
|
9 |
timer = window.setTimeout(function() { |
|
10 |
func(); |
|
11 |
}, milliseconds); |
|
12 |
}; |
|
13 |
} |
|
14 |
var toggleError = function($elt) { |
|
15 |
$elt.removeClass('password-ok'); |
|
16 |
$elt.addClass('password-error'); |
|
17 |
} |
|
18 |
var toggleOk = function($elt) { |
|
19 |
$elt.removeClass('password-error'); |
|
20 |
$elt.addClass('password-ok'); |
|
21 |
} |
|
22 |
/* |
|
23 |
* toggle error/ok on element with class names same as the validation code names |
|
24 |
* (cf. error_codes in authentic2.validators.validate_password) |
|
25 |
*/ |
|
26 |
var validatePassword = function(event) { |
|
27 |
var $this = $(event.target); |
|
28 |
if (!$this.val()) return; |
|
29 |
var password = $this.val(); |
|
30 |
var inputName = $this.attr('name'); |
|
31 |
getValidation(password, inputName); |
|
32 |
} |
|
33 |
var getValidation = function(password, inputName) { |
|
34 |
var policyContainer = $('#a2-password-policy-helper-' + inputName); |
|
35 |
$.ajax({ |
|
36 |
method: 'POST', |
|
37 |
url: '/api/validate-password/', |
|
38 |
data: JSON.stringify({'password': password}), |
|
39 |
dataType: 'json', |
|
40 |
contentType: 'application/json; charset=utf-8', |
|
41 |
success: function(data) { |
|
42 |
if (data.result) { |
|
43 |
policyContainer |
|
44 |
.empty() |
|
45 |
.removeClass('password-error password-ok'); |
|
46 |
data.checks.forEach(function (error) { |
|
47 |
var $li = $('<li class="a2-password-policy-rule"></li>') |
|
48 |
.html('<i class="a2-password-icon"></i>' + error.label) |
|
49 |
.appendTo(policyContainer); |
|
50 |
if (!error.result) { |
|
51 |
toggleError($li); |
|
52 |
} else { |
|
53 |
toggleOk($li); |
|
54 |
} |
|
55 |
}); |
|
56 |
} |
|
57 |
} |
|
58 |
}); |
|
59 |
} |
|
60 |
/* |
|
61 |
* Check password equality |
|
62 |
*/ |
|
63 |
var displayPasswordEquality = function($input, $inputTarget) { |
|
64 |
var messages = $('#a2-password-equality-helper-' + $input.attr('name')); |
|
65 |
var form = $input.parents('form'); |
|
66 |
if ($inputTarget === undefined) { |
|
67 |
$inputTarget = form.find('input[type=password]:not(input[name='+$input.attr('name')+'])'); |
|
68 |
} |
|
69 |
if (!$input.val() || !$inputTarget.val()) return; |
|
70 |
if ($inputTarget.val() !== $input.val()) { |
|
71 |
toggleError(messages); |
|
72 |
} else { |
|
73 |
toggleOk(messages); |
|
74 |
} |
|
75 |
} |
|
76 |
var passwordEquality = function () { |
|
77 |
var $this = $(this); |
|
78 |
displayPasswordEquality($this); |
|
79 |
} |
|
80 |
/* |
|
81 |
* Hide and show password handlers |
|
82 |
*/ |
|
83 |
var showPassword = function (event) { |
|
84 |
var $this = $(event.target); |
|
85 |
$this.addClass('hide-password-button'); |
|
86 |
var name = $this.attr('id').split('a2-password-show-button-')[1]; |
|
87 |
$('[name='+name+']').attr('type', 'text'); |
|
88 |
event.preventDefault(); |
|
89 |
} |
|
90 |
var hidePassword = function (event) { |
|
91 |
var $this = $(event.target); |
|
92 |
window.setTimeout(function () { |
|
93 |
$this.removeClass('hide-password-button'); |
|
94 |
var name = $this.attr('id').split('a2-password-show-button-')[1]; |
|
95 |
$('[name='+name+']').attr('type', 'password'); |
|
96 |
}, 3000); |
|
97 |
} |
|
98 |
/* |
|
99 |
* Show the last character |
|
100 |
*/ |
|
101 |
var showLastChar = function(event) { |
|
102 |
if (event.keyCode == 32 || event.key === undefined || event.key == "" |
|
103 |
|| event.key == "Unidentified" || event.key.length > 1) { |
|
104 |
return; |
|
105 |
} |
|
106 |
var duration = 1000; |
|
107 |
$('#a2-password-show-last-'+$(event.target).attr('name')) |
|
108 |
.text(event.key) |
|
109 |
.animate({'opacity': 1}, { |
|
110 |
duration: 50, |
|
111 |
queue: false, |
|
112 |
complete: function () { |
|
113 |
var $this = $(this); |
|
114 |
window.setTimeout( |
|
115 |
debounce(function () { |
|
116 |
$this.animate({'opacity': 0}, { |
|
117 |
duration: 50 |
|
118 |
}); |
|
119 |
}, duration), duration); |
|
120 |
} |
|
121 |
}); |
|
122 |
} |
|
123 |
/* |
|
124 |
* Init events |
|
125 |
*/ |
|
126 |
/* add password validation and equality check event handlers */ |
|
127 |
$('form input[type=password]:not(input[data-check-policy])').each(function () { |
|
128 |
$('#a2-password-policy-helper-' + $(this).attr('name')).hide(); |
|
129 |
}); |
|
130 |
$('body').on('keyup', 'form input[data-check-policy]', validatePassword); |
|
131 |
$('body').on('keyup', 'form input[data-check-equality]', passwordEquality); |
|
132 |
/* |
|
133 |
* Add event to handle displaying error/OK |
|
134 |
* while editing the first password |
|
135 |
* only if the second one is not empty |
|
136 |
*/ |
|
137 |
$('input[data-check-equality]') |
|
138 |
.each(function () { |
|
139 |
var $input2 = $(this); |
|
140 |
$('body') |
|
141 |
.on('keyup', 'form input[type=password]:not([name=' + $input2.attr('name') + '])', |
|
142 |
function (event) { |
|
143 |
var $input1 = $(event.target); |
|
144 |
if ($input2.val().length) { |
|
145 |
displayPasswordEquality($input2, $input1); |
|
146 |
} |
|
147 |
}); |
|
148 |
}); |
|
149 |
/* add the a2-password-show-button after the first input */ |
|
150 |
$('input[data-show-all]') |
|
151 |
.each(function () { |
|
152 |
var $this = $(this); |
|
153 |
if (!$('#a2-password-show-button-' + $this.attr('name')).length) { |
|
154 |
$(this).after($('<i class="a2-password-show-button" id="a2-password-show-button-' |
|
155 |
+ $this.attr('name') + '"></i>') |
|
156 |
.on('mousedown', showPassword) |
|
157 |
.on('mouseup mouseleave', hidePassword) |
|
158 |
); |
|
159 |
} |
|
160 |
}); |
|
161 |
/* show the last character on keypress */ |
|
162 |
$('input[data-show-last]') |
|
163 |
.each(function () { |
|
164 |
var $this = $(this); |
|
165 |
if (!$('#a2-password-show-last-' + $this.attr('name')).length) { |
|
166 |
// on crée un div placé dans le padding-right de l'input |
|
167 |
var $span = $('<span class="a2-password-show-last" id="a2-password-show-last-' |
|
168 |
+ $this.attr('name') + '"></span>)') |
|
169 |
$span.css({ |
|
170 |
'font-size': $this.css('font-size'), |
|
171 |
'font-family': $this.css('font-family'), |
|
172 |
'line-height': parseInt($this.css('line-height').replace('px', '')) - parseInt($this.css('padding-bottom').replace('px', '')) + 'px', |
|
173 |
'vertical-align': $this.css('vertical-align'), |
|
174 |
'padding-top': $this.css('padding-top'), |
|
175 |
'padding-bottom': $this.css('padding-bottom') |
|
176 |
}); |
|
177 |
$this.after($span); |
|
178 |
} |
|
179 |
}); |
|
180 |
$('body').on('keyup', 'form input[data-show-last]', showLastChar); |
|
181 |
}); |
src/authentic2/templates/authentic2/widgets/assisted_password.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
<input class="a2-password-assisted" {% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "authentic2/widgets/attrs.html" %}> |
|
3 |
{% if features.check_policy %} |
|
4 |
<p class="a2-password-policy-intro">{% blocktrans %}In order to create a secure password, please use <i>at least</i> : {% endblocktrans %}</p> |
|
5 |
<ul class="a2-password-policy-helper" id="a2-password-policy-helper-{{ widget.attrs.name }}"> |
|
6 |
{% comment %}Required to display the initial rules on page load{% endcomment %} |
|
7 |
{% if app_settings.A2_PASSWORD_POLICY_MIN_LENGTH %} |
|
8 |
<li class="a2-password-policy-rule"><i class="a2-password-icon"></i>{% blocktrans with A2_PASSWORD_POLICY_MIN_LENGTH=app_settings.A2_PASSWORD_POLICY_MIN_LENGTH %}{{ A2_PASSWORD_POLICY_MIN_LENGTH }} characters{% endblocktrans %}</li> |
|
9 |
{% endif %} |
|
10 |
{% if app_settings.A2_PASSWORD_POLICY_MIN_CLASSES > 0 %} |
|
11 |
<li class="a2-password-policy-rule"><i class="a2-password-icon"></i>{% trans "1 lowercase letter" %}</li> |
|
12 |
{% endif %} |
|
13 |
{% if app_settings.A2_PASSWORD_POLICY_MIN_CLASSES > 1 %} |
|
14 |
<li class="a2-password-policy-rule"><i class="a2-password-icon"></i>{% trans "1 digit" %}</li> |
|
15 |
{% endif %} |
|
16 |
{% if app_settings.A2_PASSWORD_POLICY_MIN_CLASSES > 2 %} |
|
17 |
<li class="a2-password-policy-rule"><i class="a2-password-icon"></i>{% trans "1 uppercase letter" %}</li> |
|
18 |
{% endif %} |
|
19 |
{% if app_settings.A2_PASSWORD_POLICY_REGEX %} |
|
20 |
{% if app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG %} |
|
21 |
<li class="a2-password-policy-rule"><i class="a2-password-icon"></i>{% blocktrans with A2_PASSWORD_POLICY_REGEX_ERROR_MSG=app_settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG %}{{ A2_PASSWORD_POLICY_REGEX_ERROR_MSG }}{% endblocktrans %}</li> |
|
22 |
{% else %} |
|
23 |
<li class="a2-password-policy-rule"><i class="a2-password-icon"></i>{% blocktrans with A2_PASSWORD_POLICY_REGEX=app_settings.A2_PASSWORD_POLICY_REGEX %}Match the regular expression: {{ A2_PASSWORD_POLICY_REGEX }}, please change this message using the A2_PASSWORD_POLICY_REGEX_ERROR_MSG setting.'{% endblocktrans %}</li> |
|
24 |
{% endif %} |
|
25 |
{% endif %} |
|
26 |
</ul> |
|
27 |
{% endif %} |
|
28 |
{% if features.check_equality %} |
|
29 |
<ul class="a2-passwords-messages" id="a2-password-equality-helper-{{ widget.attrs.name }}"> |
|
30 |
<li class="a2-passwords-default"><i class="a2-password-icon"></i>{% trans 'Both passwords must match.' %}</li> |
|
31 |
<li class="a2-passwords-matched"><i class="a2-password-icon"></i>{% trans 'Passwords match.' %}</li> |
|
32 |
<li class="a2-passwords-unmatched"><i class="a2-password-icon"></i>{% trans 'Passwords do not match.' %}</li> |
|
33 |
</ul> |
|
34 |
{% endif %} |
src/authentic2/templates/authentic2/widgets/attrs.html | ||
---|---|---|
1 |
{% comment %}Will be deprecated in Django 1.11 : replace with django/forms/widgets/attrs.html{% endcomment %} |
|
2 |
{% for name, value in widget.attrs.items %}{% if value != False %} {{ name }}{% if value != True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %} |
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="a2-registration-completion">
|
|
29 | 29 |
{% csrf_token %} |
30 | 30 |
{{ form.as_p }} |
31 | 31 |
<button class="submit-button">{% trans 'Submit' %}</button> |
tests/test_api.py | ||
---|---|---|
834 | 834 |
('x' * 8, False, True, True, False, False), |
835 | 835 |
('x' * 8 + '1', False, True, True, True, False), |
836 | 836 |
('x' * 8 + '1X', True, True, True, True, True)): |
837 |
response = app.post_json('/api/validate-password/', params={'password': password})
|
|
837 |
response = app.post_json('/api/validate-password/', params={'password': password}) |
|
838 | 838 |
assert response.json['result'] == 1 |
839 | 839 |
assert response.json['ok'] is ok |
840 | 840 |
assert len(response.json['checks']) == 4 |
tests/test_registration.py | ||
---|---|---|
1 | 1 |
# -*- coding: utf-8 -*- |
2 | 2 | |
3 |
import re |
|
3 | 4 |
from urlparse import urlparse |
4 | 5 | |
5 | 6 |
from django.core.urlresolvers import reverse |
... | ... | |
585 | 586 |
response = response.form.submit() |
586 | 587 |
assert new_next_url in response.content |
587 | 588 | |
589 | ||
590 |
def test_registration_activate_passwords_not_equal(app, db, settings, mailoutbox): |
|
591 |
settings.LANGUAGE_CODE = 'en-us' |
|
592 |
settings.A2_VALIDATE_EMAIL_DOMAIN = can_resolve_dns() |
|
593 |
settings.A2_EMAIL_IS_UNIQUE = True |
|
594 | ||
595 |
response = app.get(reverse('registration_register')) |
|
596 |
response.form.set('email', 'testbot@entrouvert.com') |
|
597 |
response = response.form.submit() |
|
598 |
response = response.follow() |
|
599 |
link = get_link_from_mail(mailoutbox[0]) |
|
600 |
response = app.get(link) |
|
601 |
response.form.set('password1', 'azerty12AZ') |
|
602 |
response.form.set('password2', 'AAAazerty12AZ') |
|
603 |
response = response.form.submit() |
|
604 |
assert "The two password fields didn't match." in response.content |
|
605 | ||
606 | ||
607 |
def test_registration_activate_assisted_password(app, db, settings, mailoutbox): |
|
608 |
response = app.get(reverse('registration_register')) |
|
609 |
response.form.set('email', 'testbot@entrouvert.com') |
|
610 |
response = response.form.submit() |
|
611 |
response = response.follow() |
|
612 |
link = get_link_from_mail(mailoutbox[0]) |
|
613 |
response = app.get(link) |
|
614 |
# check presence of the script and css for RegistrationCompletionForm to work |
|
615 |
assert "password.js" in response.content |
|
616 |
assert "password.css" in response.content |
|
617 |
# check default attributes for password.js and css to work |
|
618 |
assert re.search('<input class="a2-password-assisted".*data-show-last.*>', response.content, re.I | re.M | re.S) |
|
619 |
assert re.search('<input class="a2-password-assisted".*data-check-equality.*>', response.content, re.I | re.M | re.S) |
|
620 |
assert re.search('<input class="a2-password-assisted".*data-check-policy.*>', response.content, re.I | re.M | re.S) |
|
621 |
# check template containers for password.js to display its results |
|
622 |
assert re.search('class="a2-passwords-messages" id="a2-password-equality-helper-', response.content, re.I | re.M | re.S) |
|
623 |
assert re.search('class="a2-password-policy-helper" id="a2-password-policy-helper-', response.content, re.I | re.M | re.S) |
|
624 |
assert re.search('class="a2-password-policy-rule"', response.content, re.I | re.M | re.S) |
|
625 | ||
626 | ||
627 |
def test_registration_activate_password_no_show_all_button(app, db, settings, mailoutbox): |
|
628 |
response = app.get(reverse('registration_register')) |
|
629 |
response.form.set('email', 'testbot@entrouvert.com') |
|
630 |
response = response.form.submit() |
|
631 |
response = response.follow() |
|
632 |
link = get_link_from_mail(mailoutbox[0]) |
|
633 |
response = app.get(link) |
|
634 |
assert not re.search('<input class="a2-password-assisted".*data-show-all.*>', response.content, re.I | re.M | re.S) |
|
588 |
- |