From 3feda537e3b900630dcb3e5305862526b5f09f40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sun, 2 Aug 2020 16:02:56 +0200
Subject: [PATCH] misc: improve invalid login error message (#19944)

---
 src/authentic2/forms/authentication.py | 24 ++++++++++++++++
 tests/test_login.py                    | 39 ++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/src/authentic2/forms/authentication.py b/src/authentic2/forms/authentication.py
index b74c89a9..d970134a 100644
--- a/src/authentic2/forms/authentication.py
+++ b/src/authentic2/forms/authentication.py
@@ -14,13 +14,16 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import copy
 import math
 
 from django import forms
+from django.conf import settings
 from django.forms.widgets import Media
 from django.utils.translation import ugettext_lazy as _, ugettext
 from django.contrib.auth import forms as auth_forms
 from django.utils import html
+from django.utils.encoding import force_text
 
 from authentic2.forms.fields import PasswordField
 from authentic2.utils.lazy import lazy_label
@@ -129,3 +132,24 @@ class AuthenticationForm(auth_forms.AuthenticationForm):
         if app_settings.A2_LOGIN_FORM_OU_SELECTOR:
             media = media + Media(js=['authentic2/js/ou_selector.js'])
         return media
+
+    @property
+    def error_messages(self):
+        error_messages = copy.copy(auth_forms.AuthenticationForm.error_messages)
+        username_label = _('Username')
+        if app_settings.A2_USERNAME_LABEL:
+            username_label = app_settings.A2_USERNAME_LABEL
+        invalid_login_message = [
+                _('Incorrect %(username_label)s or password.') % {'username_label': username_label},
+        ]
+        if app_settings.A2_USER_CAN_RESET_PASSWORD is not False and getattr(settings, 'REGISTRATION_OPEN', True):
+            invalid_login_message.append(
+                    _('Try again, use the forgotten password link below, or create an account.'))
+        elif app_settings.A2_USER_CAN_RESET_PASSWORD is not False:
+            invalid_login_message.append(
+                    _('Try again or use the forgotten password link below.'))
+        elif getattr(settings, 'REGISTRATION_OPEN', True):
+            invalid_login_message.append(
+                    _('Try again or create an account.'))
+        error_messages['invalid_login'] = ' '.join([force_text(x) for x in invalid_login_message])
+        return error_messages
diff --git a/tests/test_login.py b/tests/test_login.py
index 525b0c6c..a1578657 100644
--- a/tests/test_login.py
+++ b/tests/test_login.py
@@ -239,3 +239,42 @@ def test_login_test_cookie(app, simple_user):
     resp = resp.form.submit(name='login-password-submit')
     # CSRF and test cookie checks failed
     assert 'Cookies are disabled' in resp
+
+
+def test_login_error_messages(app, settings, simple_user):
+    settings.A2_USER_CAN_RESET_PASSWORD = True
+    settings.REGISTRATION_OPEN = True
+    resp = app.get('/login/')
+    resp.form.set('username', 'x')
+    resp.form.set('password', 'y')
+    resp = resp.form.submit(name='login-password-submit')
+    assert 'Incorrect Username or password.' in resp
+    assert 'use the forgotten password link below' in resp
+    assert 'or create an account.' in resp
+
+    settings.A2_USER_CAN_RESET_PASSWORD = False
+    settings.REGISTRATION_OPEN = False
+    resp.form.set('username', 'x')
+    resp.form.set('password', 'y')
+    resp = resp.form.submit(name='login-password-submit')
+    assert 'Incorrect Username or password.' in resp
+    assert 'use the forgotten password link below' not in resp
+    assert 'or create an account.' not in resp
+
+    settings.A2_USER_CAN_RESET_PASSWORD = True
+    settings.REGISTRATION_OPEN = False
+    resp.form.set('username', 'x')
+    resp.form.set('password', 'y')
+    resp = resp.form.submit(name='login-password-submit')
+    assert 'Incorrect Username or password.' in resp
+    assert 'use the forgotten password link below' in resp
+    assert 'or create an account.' not in resp
+
+    settings.A2_USER_CAN_RESET_PASSWORD = False
+    settings.REGISTRATION_OPEN = True
+    resp.form.set('username', 'x')
+    resp.form.set('password', 'y')
+    resp = resp.form.submit(name='login-password-submit')
+    assert 'Incorrect Username or password.' in resp
+    assert 'use the forgotten password link below' not in resp
+    assert 'or create an account.' in resp
-- 
2.28.0

