Fixed #14674 -- Prevent user accounts with an unusable password from resetting passwords. Thanks, summerisgone, thejaswi_puthraya and lrekucki.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16455 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2011-06-26 16:51:34 +00:00
parent 821d8aaaaa
commit 2619dc8285
4 changed files with 81 additions and 52 deletions

View File

@ -1,11 +1,14 @@
from django.contrib.auth.models import User from django import forms
from django.template import Context, loader
from django.utils.http import int_to_base36
from django.utils.itercompat import any
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User, UNUSABLE_PASSWORD
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site from django.contrib.sites.models import get_current_site
from django.template import Context, loader
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.http import int_to_base36
class UserCreationForm(forms.ModelForm): class UserCreationForm(forms.ModelForm):
""" """
@ -114,10 +117,11 @@ class PasswordResetForm(forms.Form):
email = self.cleaned_data["email"] email = self.cleaned_data["email"]
self.users_cache = User.objects.filter( self.users_cache = User.objects.filter(
email__iexact=email, email__iexact=email,
is_active=True is_active=True)
) if not len(self.users_cache):
if len(self.users_cache) == 0:
raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?")) raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))
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."))
return email return email
def save(self, domain_override=None, def save(self, domain_override=None,

View File

@ -4,7 +4,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django\n" "Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-06-19 13:08+0200\n" "POT-Creation-Date: 2011-06-25 20:39+0200\n"
"PO-Revision-Date: 2010-05-13 15:35+0200\n" "PO-Revision-Date: 2010-05-13 15:35+0200\n"
"Last-Translator: Django team\n" "Last-Translator: Django team\n"
"Language-Team: English <en@li.org>\n" "Language-Team: English <en@li.org>\n"
@ -37,190 +37,196 @@ msgstr ""
msgid "Change password: %s" msgid "Change password: %s"
msgstr "" msgstr ""
#: forms.py:14 forms.py:48 forms.py:66 #: forms.py:17 forms.py:51 forms.py:69
msgid "Username" msgid "Username"
msgstr "" msgstr ""
#: forms.py:15 forms.py:49 #: forms.py:18 forms.py:52
msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only." msgid "Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."
msgstr "" msgstr ""
#: forms.py:16 forms.py:50 #: forms.py:19 forms.py:53
msgid "This value may contain only letters, numbers and @/./+/-/_ characters." msgid "This value may contain only letters, numbers and @/./+/-/_ characters."
msgstr "" msgstr ""
#: forms.py:17 forms.py:67 forms.py:201 #: forms.py:20 forms.py:70 forms.py:205
msgid "Password" msgid "Password"
msgstr "" msgstr ""
#: forms.py:18 #: forms.py:21
msgid "Password confirmation" msgid "Password confirmation"
msgstr "" msgstr ""
#: forms.py:19 #: forms.py:22
msgid "Enter the same password as above, for verification." msgid "Enter the same password as above, for verification."
msgstr "" msgstr ""
#: forms.py:31 #: forms.py:34
msgid "A user with that username already exists." msgid "A user with that username already exists."
msgstr "" msgstr ""
#: forms.py:37 forms.py:171 forms.py:213 #: forms.py:40 forms.py:175 forms.py:217
msgid "The two password fields didn't match." msgid "The two password fields didn't match."
msgstr "" msgstr ""
#: forms.py:87 #: forms.py:90
msgid "" msgid ""
"Please enter a correct username and password. Note that both fields are case-" "Please enter a correct username and password. Note that both fields are case-"
"sensitive." "sensitive."
msgstr "" msgstr ""
#: forms.py:89 #: forms.py:92
msgid "This account is inactive." msgid "This account is inactive."
msgstr "" msgstr ""
#: forms.py:96 #: forms.py:99
msgid "" msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are " "Your Web browser doesn't appear to have cookies enabled. Cookies are "
"required for logging in." "required for logging in."
msgstr "" msgstr ""
#: forms.py:108 #: forms.py:111
msgid "E-mail" msgid "E-mail"
msgstr "" msgstr ""
#: forms.py:120 #: forms.py:122
msgid "" msgid ""
"That e-mail address doesn't have an associated user account. Are you sure " "That e-mail address doesn't have an associated user account. Are you sure "
"you've registered?" "you've registered?"
msgstr "" msgstr ""
#: forms.py:159 #: forms.py:124
msgid ""
"The user account associated with this e-mail address cannot reset the "
"password."
msgstr ""
#: forms.py:163
msgid "New password" msgid "New password"
msgstr "" msgstr ""
#: forms.py:160 #: forms.py:164
msgid "New password confirmation" msgid "New password confirmation"
msgstr "" msgstr ""
#: forms.py:185 #: forms.py:189
msgid "Old password" msgid "Old password"
msgstr "" msgstr ""
#: forms.py:193 #: forms.py:197
msgid "Your old password was entered incorrectly. Please enter it again." msgid "Your old password was entered incorrectly. Please enter it again."
msgstr "" msgstr ""
#: forms.py:202 #: forms.py:206
msgid "Password (again)" msgid "Password (again)"
msgstr "" msgstr ""
#: models.py:77 models.py:105 #: models.py:94 models.py:122
msgid "name" msgid "name"
msgstr "" msgstr ""
#: models.py:79 #: models.py:96
msgid "codename" msgid "codename"
msgstr "" msgstr ""
#: models.py:83 #: models.py:100
msgid "permission" msgid "permission"
msgstr "" msgstr ""
#: models.py:84 models.py:106 #: models.py:101 models.py:123
msgid "permissions" msgid "permissions"
msgstr "" msgstr ""
#: models.py:109 #: models.py:126
msgid "group" msgid "group"
msgstr "" msgstr ""
#: models.py:110 models.py:217 #: models.py:127 models.py:236
msgid "groups" msgid "groups"
msgstr "" msgstr ""
#: models.py:207 #: models.py:226
msgid "username" msgid "username"
msgstr "" msgstr ""
#: models.py:207 #: models.py:226
msgid "" msgid ""
"Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters" "Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"
msgstr "" msgstr ""
#: models.py:208 #: models.py:227
msgid "first name" msgid "first name"
msgstr "" msgstr ""
#: models.py:209 #: models.py:228
msgid "last name" msgid "last name"
msgstr "" msgstr ""
#: models.py:210 #: models.py:229
msgid "e-mail address" msgid "e-mail address"
msgstr "" msgstr ""
#: models.py:211 #: models.py:230
msgid "password" msgid "password"
msgstr "" msgstr ""
#: models.py:211 #: models.py:230
msgid "" msgid ""
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change " "Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
"password form</a>." "password form</a>."
msgstr "" msgstr ""
#: models.py:212 #: models.py:231
msgid "staff status" msgid "staff status"
msgstr "" msgstr ""
#: models.py:212 #: models.py:231
msgid "Designates whether the user can log into this admin site." msgid "Designates whether the user can log into this admin site."
msgstr "" msgstr ""
#: models.py:213 #: models.py:232
msgid "active" msgid "active"
msgstr "" msgstr ""
#: models.py:213 #: models.py:232
msgid "" msgid ""
"Designates whether this user should be treated as active. Unselect this " "Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts." "instead of deleting accounts."
msgstr "" msgstr ""
#: models.py:214 #: models.py:233
msgid "superuser status" msgid "superuser status"
msgstr "" msgstr ""
#: models.py:214 #: models.py:233
msgid "" msgid ""
"Designates that this user has all permissions without explicitly assigning " "Designates that this user has all permissions without explicitly assigning "
"them." "them."
msgstr "" msgstr ""
#: models.py:215 #: models.py:234
msgid "last login" msgid "last login"
msgstr "" msgstr ""
#: models.py:216 #: models.py:235
msgid "date joined" msgid "date joined"
msgstr "" msgstr ""
#: models.py:218 #: models.py:237
msgid "" msgid ""
"In addition to the permissions manually assigned, this user will also get " "In addition to the permissions manually assigned, this user will also get "
"all permissions granted to each group he/she is in." "all permissions granted to each group he/she is in."
msgstr "" msgstr ""
#: models.py:219 #: models.py:238
msgid "user permissions" msgid "user permissions"
msgstr "" msgstr ""
#: models.py:223 #: models.py:242
msgid "user" msgid "user"
msgstr "" msgstr ""
#: models.py:224 #: models.py:243
msgid "users" msgid "users"
msgstr "" msgstr ""

View File

@ -281,3 +281,16 @@ class PasswordResetFormTest(TestCase):
user.save() user.save()
form = PasswordResetForm({'email': email}) form = PasswordResetForm({'email': email})
self.assertFalse(form.is_valid()) 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"}
form = PasswordResetForm(data)
self.assertTrue(form.is_valid())
user.set_unusable_password()
user.save()
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["email"].errors,
[u"The user account associated with this e-mail address cannot reset the password."])

View File

@ -1011,6 +1011,12 @@ includes a few other useful built-in views located in
* ``form``: The form for resetting the user's password. * ``form``: The form for resetting the user's password.
.. versionchanged:: 1.4
Users flagged with an unusable password (see
:meth:`~django.contrib.auth.models.User.set_unusable_password()`
will not be able to request a password reset to prevent misuse
when using an external authentication source like LDAP.
.. function:: password_reset_done(request[, template_name]) .. function:: password_reset_done(request[, template_name])
The page shown after a user has been emailed a link to reset their The page shown after a user has been emailed a link to reset their