Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #17194 -- Made sure the auth form tests work if a language othe…

…r than English is activated by moving the error message translation strings into class level dictionaries. Many thanks to Claude Paroz, rabio and Bas Peschier for their initial work on this.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17204 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 5df31c0164e9477a3ebb6b1bbde8604e06fbefd4 1 parent e7b5e22
@jezdez jezdez authored
View
58 django/contrib/auth/forms.py
@@ -15,6 +15,7 @@
mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0))
+
class ReadOnlyPasswordHashWidget(forms.Widget):
def render(self, name, value, attrs):
if not value:
@@ -39,6 +40,7 @@ def render(self, name, value, attrs):
"masked": masked,
})
+
class ReadOnlyPasswordHashField(forms.Field):
widget = ReadOnlyPasswordHashWidget
@@ -46,10 +48,15 @@ def __init__(self, *args, **kwargs):
kwargs.setdefault("required", False)
super(ReadOnlyPasswordHashField, self).__init__(*args, **kwargs)
+
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and password.
"""
+ error_messages = {
+ 'duplicate_username': _("A user with that username already exists."),
+ 'password_mismatch': _("The two password fields didn't match."),
+ }
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^[\w.@+-]+$',
help_text = _("Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."),
error_messages = {'invalid': _("This value may contain only letters, numbers and @/./+/-/_ characters.")})
@@ -67,13 +74,13 @@ def clean_username(self):
User.objects.get(username=username)
except User.DoesNotExist:
return username
- raise forms.ValidationError(_("A user with that username already exists."))
+ raise forms.ValidationError(self.error_messages['duplicate_username'])
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
- raise forms.ValidationError(_("The two password fields didn't match."))
+ raise forms.ValidationError(self.error_messages['password_mismatch'])
return password2
def save(self, commit=True):
@@ -83,6 +90,7 @@ def save(self, commit=True):
user.save()
return user
+
class UserChangeForm(forms.ModelForm):
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^[\w.@+-]+$',
help_text = _("Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."),
@@ -101,6 +109,7 @@ def __init__(self, *args, **kwargs):
if f is not None:
f.queryset = f.queryset.select_related('content_type')
+
class AuthenticationForm(forms.Form):
"""
Base class for authenticating users. Extend this to get a form that accepts
@@ -109,6 +118,14 @@ class AuthenticationForm(forms.Form):
username = forms.CharField(label=_("Username"), max_length=30)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
+ error_messages = {
+ 'invalid_login': _("Please enter a correct username and password. "
+ "Note that both fields are case-sensitive."),
+ 'no_cookies': _("Your Web browser doesn't appear to have cookies "
+ "enabled. Cookies are required for logging in."),
+ 'inactive': _("This account is inactive."),
+ }
+
def __init__(self, request=None, *args, **kwargs):
"""
If request is passed in, the form will validate that cookies are
@@ -127,17 +144,15 @@ def clean(self):
if username and password:
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
- raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive."))
+ raise forms.ValidationError(self.error_messages['invalid_login'])
elif not self.user_cache.is_active:
- raise forms.ValidationError(_("This account is inactive."))
+ raise forms.ValidationError(self.error_messages['inactive'])
self.check_for_test_cookie()
return self.cleaned_data
def check_for_test_cookie(self):
if self.request and not self.request.session.test_cookie_worked():
- raise forms.ValidationError(
- _("Your Web browser doesn't appear to have cookies enabled. "
- "Cookies are required for logging in."))
+ raise forms.ValidationError(self.error_messages['no_cookies'])
def get_user_id(self):
if self.user_cache:
@@ -147,7 +162,14 @@ def get_user_id(self):
def get_user(self):
return self.user_cache
+
class PasswordResetForm(forms.Form):
+ error_messages = {
+ 'unknown': _("That e-mail address doesn't have an associated "
+ "user account. Are you sure you've registered?"),
+ 'unusable': _("The user account associated with this e-mail "
+ "address cannot reset the password."),
+ }
email = forms.EmailField(label=_("E-mail"), max_length=75)
def clean_email(self):
@@ -159,9 +181,9 @@ def clean_email(self):
email__iexact=email,
is_active=True)
if not len(self.users_cache):
- raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))
+ raise forms.ValidationError(self.error_messages['unknown'])
if any((user.password == UNUSABLE_PASSWORD) for user in self.users_cache):
- raise forms.ValidationError(_("The user account associated with this e-mail address cannot reset the password."))
+ raise forms.ValidationError(self.error_messages['unusable'])
return email
def save(self, domain_override=None,
@@ -195,11 +217,15 @@ def save(self, domain_override=None,
email = loader.render_to_string(email_template_name, c)
send_mail(subject, email, from_email, [user.email])
+
class SetPasswordForm(forms.Form):
"""
A form that lets a user change set his/her password without
entering the old password
"""
+ error_messages = {
+ 'password_mismatch': _("The two password fields didn't match."),
+ }
new_password1 = forms.CharField(label=_("New password"), widget=forms.PasswordInput)
new_password2 = forms.CharField(label=_("New password confirmation"), widget=forms.PasswordInput)
@@ -212,7 +238,7 @@ def clean_new_password2(self):
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
- raise forms.ValidationError(_("The two password fields didn't match."))
+ raise forms.ValidationError(self.error_messages['password_mismatch'])
return password2
def save(self, commit=True):
@@ -221,11 +247,15 @@ def save(self, commit=True):
self.user.save()
return self.user
+
class PasswordChangeForm(SetPasswordForm):
"""
A form that lets a user change his/her password by entering
their old password.
"""
+ error_messages = dict(SetPasswordForm.error_messages, **{
+ 'password_incorrect': _("Your old password was entered incorrectly. Please enter it again."),
+ })
old_password = forms.CharField(label=_("Old password"), widget=forms.PasswordInput)
def clean_old_password(self):
@@ -234,14 +264,18 @@ def clean_old_password(self):
"""
old_password = self.cleaned_data["old_password"]
if not self.user.check_password(old_password):
- raise forms.ValidationError(_("Your old password was entered incorrectly. Please enter it again."))
+ raise forms.ValidationError(self.error_messages['password_incorrect'])
return old_password
PasswordChangeForm.base_fields.keyOrder = ['old_password', 'new_password1', 'new_password2']
+
class AdminPasswordChangeForm(forms.Form):
"""
A form used to change the password of a user in the admin interface.
"""
+ error_messages = {
+ 'password_mismatch': _("The two password fields didn't match."),
+ }
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput)
@@ -254,7 +288,7 @@ def clean_password2(self):
password2 = self.cleaned_data.get('password2')
if password1 and password2:
if password1 != password2:
- raise forms.ValidationError(_("The two password fields didn't match."))
+ raise forms.ValidationError(self.error_messages['password_mismatch'])
return password2
def save(self, commit=True):
View
60 django/contrib/auth/tests/forms.py
@@ -1,9 +1,12 @@
from __future__ import with_statement
import os
from django.core import mail
+from django.forms.fields import Field, EmailField
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm
from django.test import TestCase
+from django.utils.encoding import force_unicode
+from django.utils import translation
class UserCreationFormTest(TestCase):
@@ -19,7 +22,7 @@ def test_user_already_exists(self):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["username"].errors,
- [u'A user with that username already exists.'])
+ [force_unicode(form.error_messages['duplicate_username'])])
def test_invalid_data(self):
data = {
@@ -30,8 +33,7 @@ def test_invalid_data(self):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["username"].errors,
- [u'This value may contain only letters, numbers and @/./+/-/_ characters.'])
-
+ [force_unicode(form.fields['username'].error_messages['invalid'])])
def test_password_verification(self):
# The verification password is incorrect.
@@ -43,25 +45,21 @@ def test_password_verification(self):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["password2"].errors,
- [u"The two password fields didn't match."])
-
+ [force_unicode(form.error_messages['password_mismatch'])])
def test_both_passwords(self):
# One (or both) passwords weren't given
data = {'username': 'jsmith'}
form = UserCreationForm(data)
+ required_error = [force_unicode(Field.default_error_messages['required'])]
self.assertFalse(form.is_valid())
- self.assertEqual(form['password1'].errors,
- [u'This field is required.'])
- self.assertEqual(form['password2'].errors,
- [u'This field is required.'])
-
+ self.assertEqual(form['password1'].errors, required_error)
+ self.assertEqual(form['password2'].errors, required_error)
data['password2'] = 'test123'
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
- self.assertEqual(form['password1'].errors,
- [u'This field is required.'])
+ self.assertEqual(form['password1'].errors, required_error)
def test_success(self):
# The success case.
@@ -91,7 +89,7 @@ def test_invalid_username(self):
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(),
- [u'Please enter a correct username and password. Note that both fields are case-sensitive.'])
+ [force_unicode(form.error_messages['invalid_login'])])
def test_inactive_user(self):
# The user is inactive.
@@ -102,8 +100,20 @@ def test_inactive_user(self):
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(),
- [u'This account is inactive.'])
-
+ [force_unicode(form.error_messages['inactive'])])
+
+ def test_inactive_user_i18n(self):
+ with self.settings(USE_I18N=True):
+ with translation.override('pt-br', deactivate=True):
+ # The user is inactive.
+ data = {
+ 'username': 'inactive',
+ 'password': 'password',
+ }
+ form = AuthenticationForm(None, data)
+ self.assertFalse(form.is_valid())
+ self.assertEqual(form.non_field_errors(),
+ [force_unicode(form.error_messages['inactive'])])
def test_success(self):
# The success case
@@ -130,7 +140,7 @@ def test_password_verification(self):
form = SetPasswordForm(user, data)
self.assertFalse(form.is_valid())
self.assertEqual(form["new_password2"].errors,
- [u"The two password fields didn't match."])
+ [force_unicode(form.error_messages['password_mismatch'])])
def test_success(self):
user = User.objects.get(username='testclient')
@@ -156,8 +166,7 @@ def test_incorrect_password(self):
form = PasswordChangeForm(user, data)
self.assertFalse(form.is_valid())
self.assertEqual(form["old_password"].errors,
- [u'Your old password was entered incorrectly. Please enter it again.'])
-
+ [force_unicode(form.error_messages['password_incorrect'])])
def test_password_verification(self):
# The two new passwords do not match.
@@ -170,8 +179,7 @@ def test_password_verification(self):
form = PasswordChangeForm(user, data)
self.assertFalse(form.is_valid())
self.assertEqual(form["new_password2"].errors,
- [u"The two password fields didn't match."])
-
+ [force_unicode(form.error_messages['password_mismatch'])])
def test_success(self):
# The success case.
@@ -190,6 +198,7 @@ def test_field_order(self):
self.assertEqual(PasswordChangeForm(user, {}).fields.keys(),
['old_password', 'new_password1', 'new_password2'])
+
class UserChangeFormTest(TestCase):
fixtures = ['authtestdata.json']
@@ -200,7 +209,7 @@ def test_username_validity(self):
form = UserChangeForm(data, instance=user)
self.assertFalse(form.is_valid())
self.assertEqual(form['username'].errors,
- [u'This value may contain only letters, numbers and @/./+/-/_ characters.'])
+ [force_unicode(form.fields['username'].error_messages['invalid'])])
def test_bug_14242(self):
# A regression test, introduce by adding an optimization for the
@@ -232,19 +241,19 @@ def create_dummy_user(self):
return (user, username, email)
def test_invalid_email(self):
- data = {'email':'not valid'}
+ data = {'email': 'not valid'}
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form['email'].errors,
- [u'Enter a valid e-mail address.'])
+ [force_unicode(EmailField.default_error_messages['invalid'])])
def test_nonexistant_email(self):
# Test nonexistant email address
- data = {'email':'foo@bar.com'}
+ data = {'email': 'foo@bar.com'}
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form.errors,
- {'email': [u"That e-mail address doesn't have an associated user account. Are you sure you've registered?"]})
+ {'email': [force_unicode(form.error_messages['unknown'])]})
def test_cleaned_data(self):
# Regression test
@@ -284,7 +293,6 @@ def test_inactive_user(self):
form = PasswordResetForm({'email': email})
self.assertFalse(form.is_valid())
-
def test_unusable_password(self):
user = User.objects.create_user('testuser', 'test@example.com', 'test')
data = {"email": "test@example.com"}
View
88 django/contrib/auth/tests/views.py
@@ -4,15 +4,18 @@
import urllib
from django.conf import settings
-from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
-from django.contrib.auth.forms import AuthenticationForm
from django.contrib.sites.models import Site, RequestSite
from django.contrib.auth.models import User
-from django.core.urlresolvers import NoReverseMatch
-from django.test import TestCase
from django.core import mail
-from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import QueryDict
+from django.utils.encoding import force_unicode
+from django.utils.html import escape
+from django.test import TestCase
+
+from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
+from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
+ SetPasswordForm, PasswordResetForm)
class AuthViewsTestCase(TestCase):
@@ -40,13 +43,15 @@ def tearDown(self):
def login(self, password='password'):
response = self.client.post('/login/', {
'username': 'testclient',
- 'password': password
- }
- )
+ 'password': password,
+ })
self.assertEqual(response.status_code, 302)
self.assertTrue(response['Location'].endswith(settings.LOGIN_REDIRECT_URL))
self.assertTrue(SESSION_KEY in self.client.session)
+ def assertContainsEscaped(self, response, text, **kwargs):
+ return self.assertContains(response, escape(force_unicode(text)), **kwargs)
+
class AuthViewNamedURLTests(AuthViewsTestCase):
urls = 'django.contrib.auth.urls'
@@ -80,7 +85,7 @@ def test_email_not_found(self):
response = self.client.get('/password_reset/')
self.assertEqual(response.status_code, 200)
response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
- self.assertContains(response, "That e-mail address doesn't have an associated user account")
+ self.assertContainsEscaped(response, PasswordResetForm.error_messages['unknown'])
self.assertEqual(len(mail.outbox), 0)
def test_email_found(self):
@@ -121,7 +126,7 @@ def test_confirm_invalid(self):
url, path = self._test_confirm_start()
# Let's munge the token in the path, but keep the same length,
# in case the URLconf will reject a different length.
- path = path[:-5] + ("0"*4) + path[-1]
+ path = path[:-5] + ("0" * 4) + path[-1]
response = self.client.get(path)
self.assertEqual(response.status_code, 200)
@@ -143,10 +148,12 @@ def test_confirm_invalid_post(self):
# Same as test_confirm_invalid, but trying
# to do a POST instead.
url, path = self._test_confirm_start()
- path = path[:-5] + ("0"*4) + path[-1]
+ path = path[:-5] + ("0" * 4) + path[-1]
- response = self.client.post(path, {'new_password1': 'anewpassword',
- 'new_password2':' anewpassword'})
+ self.client.post(path, {
+ 'new_password1': 'anewpassword',
+ 'new_password2': ' anewpassword',
+ })
# Check the password has not been changed
u = User.objects.get(email='staffmember@example.com')
self.assertTrue(not u.check_password("anewpassword"))
@@ -169,20 +176,20 @@ def test_confirm_complete(self):
def test_confirm_different_passwords(self):
url, path = self._test_confirm_start()
response = self.client.post(path, {'new_password1': 'anewpassword',
- 'new_password2':' x'})
+ 'new_password2': 'x'})
self.assertEqual(response.status_code, 200)
- self.assertTrue("The two password fields didn't match" in response.content)
+ self.assertContainsEscaped(response, SetPasswordForm.error_messages['password_mismatch'])
+
class ChangePasswordTest(AuthViewsTestCase):
def fail_login(self, password='password'):
response = self.client.post('/login/', {
'username': 'testclient',
- 'password': password
- }
- )
+ 'password': password,
+ })
self.assertEqual(response.status_code, 200)
- self.assertTrue("Please enter a correct username and password. Note that both fields are case-sensitive." in response.content)
+ self.assertContainsEscaped(response, AuthenticationForm.error_messages['invalid_login'])
def logout(self):
response = self.client.get('/logout/')
@@ -193,10 +200,9 @@ def test_password_change_fails_with_invalid_old_password(self):
'old_password': 'donuts',
'new_password1': 'password1',
'new_password2': 'password1',
- }
- )
+ })
self.assertEqual(response.status_code, 200)
- self.assertTrue("Your old password was entered incorrectly. Please enter it again." in response.content)
+ self.assertContainsEscaped(response, PasswordChangeForm.error_messages['password_incorrect'])
def test_password_change_fails_with_mismatched_passwords(self):
self.login()
@@ -204,10 +210,9 @@ def test_password_change_fails_with_mismatched_passwords(self):
'old_password': 'password',
'new_password1': 'password1',
'new_password2': 'donuts',
- }
- )
+ })
self.assertEqual(response.status_code, 200)
- self.assertTrue("The two password fields didn't match." in response.content)
+ self.assertContainsEscaped(response, SetPasswordForm.error_messages['password_mismatch'])
def test_password_change_succeeds(self):
self.login()
@@ -215,8 +220,7 @@ def test_password_change_succeeds(self):
'old_password': 'password',
'new_password1': 'password1',
'new_password2': 'password1',
- }
- )
+ })
self.assertEqual(response.status_code, 302)
self.assertTrue(response['Location'].endswith('/password_change/done/'))
self.fail_login()
@@ -228,8 +232,7 @@ def test_password_change_done_succeeds(self):
'old_password': 'password',
'new_password1': 'password1',
'new_password2': 'password1',
- }
- )
+ })
self.assertEqual(response.status_code, 302)
self.assertTrue(response['Location'].endswith('/password_change/done/'))
@@ -266,13 +269,12 @@ def test_security_check(self, password='password'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url,
'next': REDIRECT_FIELD_NAME,
- 'bad_url': urllib.quote(bad_url)
+ 'bad_url': urllib.quote(bad_url),
}
response = self.client.post(nasty_url, {
'username': 'testclient',
'password': password,
- }
- )
+ })
self.assertEqual(response.status_code, 302)
self.assertFalse(bad_url in response['Location'],
"%s should be blocked" % bad_url)
@@ -284,18 +286,16 @@ def test_security_check(self, password='password'):
'view/?param=//example.com',
'https:///',
'//testserver/',
- '/url%20with%20spaces/', # see ticket #12534
- ):
+ '/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
'url': login_url,
'next': REDIRECT_FIELD_NAME,
- 'good_url': urllib.quote(good_url)
+ 'good_url': urllib.quote(good_url),
}
response = self.client.post(safe_url, {
'username': 'testclient',
'password': password,
- }
- )
+ })
self.assertEqual(response.status_code, 302)
self.assertTrue(good_url in response['Location'],
"%s should be allowed" % good_url)
@@ -322,8 +322,8 @@ def test_standard_login_url(self):
login_required_url = self.get_login_required_url(login_url)
querystring = QueryDict('', mutable=True)
querystring['next'] = '/login_required/'
- self.assertEqual(login_required_url,
- 'http://testserver%s?%s' % (login_url, querystring.urlencode('/')))
+ self.assertEqual(login_required_url, 'http://testserver%s?%s' %
+ (login_url, querystring.urlencode('/')))
def test_remote_login_url(self):
login_url = 'http://remote.example.com/login'
@@ -422,12 +422,11 @@ def test_security_check(self, password='password'):
for bad_url in ('http://example.com',
'https://example.com',
'ftp://exampel.com',
- '//example.com'
- ):
+ '//example.com'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
- 'bad_url': urllib.quote(bad_url)
+ 'bad_url': urllib.quote(bad_url),
}
self.login()
response = self.client.get(nasty_url)
@@ -443,12 +442,11 @@ def test_security_check(self, password='password'):
'view/?param=//example.com',
'https:///',
'//testserver/',
- '/url%20with%20spaces/', # see ticket #12534
- ):
+ '/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
- 'good_url': urllib.quote(good_url)
+ 'good_url': urllib.quote(good_url),
}
self.login()
response = self.client.get(safe_url)
Please sign in to comment.
Something went wrong with that request. Please try again.