mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	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:
		| @@ -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, | ||||||
|   | |||||||
| @@ -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 "" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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."]) | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user