summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/auth/forms.py58
-rw-r--r--django/contrib/auth/tests/forms.py58
-rw-r--r--django/contrib/auth/tests/views.py88
3 files changed, 122 insertions, 82 deletions
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index 6fcf323216..23185a0ece 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -15,6 +15,7 @@ UNMASKED_DIGITS_TO_SHOW = 6
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 @@ class ReadOnlyPasswordHashWidget(forms.Widget):
"masked": masked,
})
+
class ReadOnlyPasswordHashField(forms.Field):
widget = ReadOnlyPasswordHashWidget
@@ -46,10 +48,15 @@ class ReadOnlyPasswordHashField(forms.Field):
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 @@ class UserCreationForm(forms.ModelForm):
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 @@ class UserCreationForm(forms.ModelForm):
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 @@ class UserChangeForm(forms.ModelForm):
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 @@ class AuthenticationForm(forms.Form):
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 @@ class AuthenticationForm(forms.Form):
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 @@ class PasswordResetForm(forms.Form):
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 @@ class PasswordResetForm(forms.Form):
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 @@ class SetPasswordForm(forms.Form):
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 @@ class SetPasswordForm(forms.Form):
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 @@ class PasswordChangeForm(SetPasswordForm):
"""
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 @@ class AdminPasswordChangeForm(forms.Form):
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):
diff --git a/django/contrib/auth/tests/forms.py b/django/contrib/auth/tests/forms.py
index 429967cba0..2bacc8e611 100644
--- a/django/contrib/auth/tests/forms.py
+++ b/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 @@ class UserCreationFormTest(TestCase):
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 @@ class UserCreationFormTest(TestCase):
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 @@ class UserCreationFormTest(TestCase):
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 @@ class AuthenticationFormTest(TestCase):
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 @@ class AuthenticationFormTest(TestCase):
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 @@ class SetPasswordFormTest(TestCase):
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 @@ class PasswordChangeFormTest(TestCase):
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 @@ class PasswordChangeFormTest(TestCase):
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 @@ class PasswordChangeFormTest(TestCase):
self.assertEqual(PasswordChangeForm(user, {}).fields.keys(),
['old_password', 'new_password1', 'new_password2'])
+
class UserChangeFormTest(TestCase):
fixtures = ['authtestdata.json']
@@ -200,7 +209,7 @@ class UserChangeFormTest(TestCase):
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 @@ class PasswordResetFormTest(TestCase):
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 @@ class PasswordResetFormTest(TestCase):
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"}
diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py
index a0be92fc15..1975266f4f 100644
--- a/django/contrib/auth/tests/views.py
+++ b/django/contrib/auth/tests/views.py
@@ -4,15 +4,18 @@ import re
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 @@ class AuthViewsTestCase(TestCase):
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 @@ class PasswordResetTest(AuthViewsTestCase):
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 @@ class PasswordResetTest(AuthViewsTestCase):
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 @@ class PasswordResetTest(AuthViewsTestCase):
# 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 @@ class PasswordResetTest(AuthViewsTestCase):
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 @@ class ChangePasswordTest(AuthViewsTestCase):
'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 @@ class ChangePasswordTest(AuthViewsTestCase):
'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 @@ class ChangePasswordTest(AuthViewsTestCase):
'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 @@ class ChangePasswordTest(AuthViewsTestCase):
'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 @@ class LoginTest(AuthViewsTestCase):
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 @@ class LoginTest(AuthViewsTestCase):
'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 @@ class LoginURLSettings(AuthViewsTestCase):
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 @@ class LogoutTest(AuthViewsTestCase):
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 @@ class LogoutTest(AuthViewsTestCase):
'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)