From f372225e6d324310edeb17ff141647221b690b25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Corentin=20S=C3=A9chet?= <csechet@entrouvert.com>
Date: Wed, 31 Aug 2022 14:12:09 +0200
Subject: [PATCH 1/3] misc: use a template for NewPasswordInput (#63831)

---
 src/authentic2/forms/fields.py                |  6 +----
 src/authentic2/forms/widgets.py               | 10 +++++++++
 src/authentic2/passwords.py                   | 22 +++----------------
 .../static/authentic2/css/password.scss       |  5 +++++
 .../static/authentic2/js/password.js          |  4 ++--
 .../authentic2/widgets/new_password.html      | 10 +++++++++
 src/authentic2/validators.py                  |  2 +-
 7 files changed, 32 insertions(+), 27 deletions(-)
 create mode 100644 src/authentic2/templates/authentic2/widgets/new_password.html

diff --git a/src/authentic2/forms/fields.py b/src/authentic2/forms/fields.py
index ae3f9f05..faf88138 100644
--- a/src/authentic2/forms/fields.py
+++ b/src/authentic2/forms/fields.py
@@ -31,7 +31,7 @@ from authentic2.forms.widgets import (
     PasswordInput,
     ProfileImageInput,
 )
-from authentic2.passwords import password_help_text, validate_password
+from authentic2.passwords import validate_password
 from authentic2.validators import email_validator
 
 
@@ -43,10 +43,6 @@ class NewPasswordField(CharField):
     widget = NewPasswordInput
     default_validators = [validate_password]
 
-    def __init__(self, *args, **kwargs):
-        kwargs['help_text'] = password_help_text()
-        super().__init__(*args, **kwargs)
-
 
 class CheckPasswordField(CharField):
     widget = CheckPasswordInput
diff --git a/src/authentic2/forms/widgets.py b/src/authentic2/forms/widgets.py
index c7c9405c..90150967 100644
--- a/src/authentic2/forms/widgets.py
+++ b/src/authentic2/forms/widgets.py
@@ -41,6 +41,7 @@ from django.utils.translation import ugettext_lazy as _
 from gadjo.templatetags.gadjo import xstatic
 
 from authentic2 import app_settings
+from authentic2.passwords import get_password_checker
 
 DATE_FORMAT_JS_PY_MAPPING = {
     'P': '%p',
@@ -270,6 +271,15 @@ class PasswordInput(BasePasswordInput):
 
 
 class NewPasswordInput(PasswordInput):
+    template_name = 'authentic2/widgets/new_password.html'
+
+    def get_context(self, *args, **kwargs):
+        context = super().get_context(*args, **kwargs)
+        password_checker = get_password_checker()
+        checks = list(password_checker(''))
+        context['checks'] = checks
+        return context
+
     def render(self, name, value, attrs=None, renderer=None):
         if attrs is None:
             attrs = {}
diff --git a/src/authentic2/passwords.py b/src/authentic2/passwords.py
index 41623d38..29cb3440 100644
--- a/src/authentic2/passwords.py
+++ b/src/authentic2/passwords.py
@@ -20,7 +20,6 @@ import re
 import string
 
 from django.core.exceptions import ValidationError
-from django.utils.functional import lazy
 from django.utils.module_loading import import_string
 from django.utils.translation import ugettext as _
 from zxcvbn import zxcvbn
@@ -127,22 +126,7 @@ def get_password_checker(*args, **kwargs):
 
 
 def validate_password(password):
-    error = password_help_text(password, only_errors=True)
-    if error:
-        raise ValidationError(_('This password is not accepted.'))
-
-
-def password_help_text(password='', only_errors=False):
     password_checker = get_password_checker()
-    criteria = [check.label for check in password_checker(password) if not (only_errors and check.result)]
-    if criteria:
-        html_criteria = ['<span class="a2-password-policy-rule">%s</span>' % criter for criter in criteria]
-        return _(
-            'In order to create a secure password, please use at least: <span'
-            ' class="a2-password-policy-container">%s</span>'
-        ) % ''.join(html_criteria)
-    else:
-        return ''
-
-
-password_help_text = lazy(password_help_text, str)
+    errors = [not check.result for check in password_checker(password)]
+    if any(errors):
+        raise ValidationError(_('This password is not accepted.'))
diff --git a/src/authentic2/static/authentic2/css/password.scss b/src/authentic2/static/authentic2/css/password.scss
index 1bd43853..3f729d66 100644
--- a/src/authentic2/static/authentic2/css/password.scss
+++ b/src/authentic2/static/authentic2/css/password.scss
@@ -1,3 +1,8 @@
+.a2-password-policy-hint {
+  display: block;
+  font-size: 80%;
+}
+
 /* position span to show last char */
 .a2-password-show-last-char {
   text-align: center;
diff --git a/src/authentic2/static/authentic2/js/password.js b/src/authentic2/static/authentic2/js/password.js
index f782b13a..f83367e8 100644
--- a/src/authentic2/static/authentic2/js/password.js
+++ b/src/authentic2/static/authentic2/js/password.js
@@ -3,7 +3,7 @@ a2_password_check_equality = (function () {
         $(function () {
             function check_equality() {
                 setTimeout(function () {
-                    var $help_text = $input2.parent().find('.hint');
+                    var $help_text = $input2.parent().find('.a2-password-policy-hint');
                     var password1 = $input1.val();
                     var password2 = $input2.val();
 
@@ -36,7 +36,7 @@ a2_password_validate = (function () {
     }
     function get_validation($input) {
         var password = $input.val();
-        var $help_text = $input.parent().find('.hint');
+        var $help_text = $input.parent().find('.a2-password-policy-hint');
         var $policyContainer = $help_text.find('.a2-password-policy-container');
         $.ajax({
             method: 'POST',
diff --git a/src/authentic2/templates/authentic2/widgets/new_password.html b/src/authentic2/templates/authentic2/widgets/new_password.html
new file mode 100644
index 00000000..acbca015
--- /dev/null
+++ b/src/authentic2/templates/authentic2/widgets/new_password.html
@@ -0,0 +1,10 @@
+{% load i18n %}
+{% include "django/forms/widgets/input.html" %}
+<div class="a2-password-policy-hint">
+  {% trans "In order to create a secure password, please use at least :" %}
+  <div class="a2-password-policy-container">
+    {% for check in checks %}
+      <span class="a2-password-policy-rule">{{ check.label }}</span>
+    {% endfor %}
+  </div>
+</div>
diff --git a/src/authentic2/validators.py b/src/authentic2/validators.py
index 0cf10367..414166e8 100644
--- a/src/authentic2/validators.py
+++ b/src/authentic2/validators.py
@@ -29,7 +29,7 @@ from django.utils.translation import gettext_lazy as _
 from . import app_settings
 
 # keep those symbols here for retrocompatibility
-from .passwords import password_help_text, validate_password  # pylint: disable=unused-import
+from .passwords import validate_password  # pylint: disable=unused-import
 
 
 # copied from http://www.djangotips.com/real-email-validation
-- 
2.35.1

