Code

Ticket #14674: ticket14674.diff

File ticket14674.diff, 4.5 KB (added by lrekucki, 3 years ago)
Line 
1diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
2index aa33640..908b4e5 100644
3--- a/django/contrib/auth/forms.py
4+++ b/django/contrib/auth/forms.py
5@@ -1,4 +1,4 @@
6-from django.contrib.auth.models import User
7+from django.contrib.auth.models import User, UNUSABLE_PASSWORD
8 from django.contrib.auth import authenticate
9 from django.contrib.auth.tokens import default_token_generator
10 from django.contrib.sites.models import get_current_site
11@@ -6,6 +6,7 @@ from django.template import Context, loader
12 from django import forms
13 from django.utils.translation import ugettext_lazy as _
14 from django.utils.http import int_to_base36
15+from django.utils.itercompat import any
16 
17 class UserCreationForm(forms.ModelForm):
18     """
19@@ -112,8 +113,10 @@ class PasswordResetForm(forms.Form):
20         """
21         email = self.cleaned_data["email"]
22         self.users_cache = User.objects.filter(email__iexact=email)
23-        if len(self.users_cache) == 0:
24+        if not len(self.users_cache):
25             raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))
26+        if any((user.password == UNUSABLE_PASSWORD) for user in self.users_cache):
27+            raise forms.ValidationError(_("The user account associated with this email address cannot reset it's password."))
28         return email
29 
30     def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
31diff --git a/django/contrib/auth/tests/forms.py b/django/contrib/auth/tests/forms.py
32index 5aa49e0..7fd4846 100644
33--- a/django/contrib/auth/tests/forms.py
34+++ b/django/contrib/auth/tests/forms.py
35@@ -250,3 +250,15 @@ class PasswordResetFormTest(TestCase):
36         self.assertEqual(user.email, 'tesT@example.com')
37         user = User.objects.create_user('forms_test3', 'tesT', 'test')
38         self.assertEqual(user.email, 'tesT')
39+
40+    def test_unusable_password(self):
41+        user = User.objects.create_user('testuser', 'test@example.com', 'test')
42+        data = {"email": "test@example.com"}
43+        form = PasswordResetForm(data)
44+        self.assertTrue(form.is_valid())
45+        user.set_unusable_password()
46+        user.save()
47+        form = PasswordResetForm(data)
48+        self.assertFalse(form.is_valid())
49+        self.assertEqual(form["email"].errors,
50+                         [u"The user account associated with this email address cannot reset it's password."])
51diff --git a/django/utils/itercompat.py b/django/utils/itercompat.py
52index ab27c3e..4e2a4d8 100644
53--- a/django/utils/itercompat.py
54+++ b/django/utils/itercompat.py
55@@ -6,8 +6,15 @@ these implementations if necessary.
56 
57 import itertools
58 
59+__all__ = [
60+    'all',
61+    'any',
62+    'is_iterable',
63+    'product',
64+]
65+
66 # Fallback for Python 2.4, Python 2.5
67-def product(*args, **kwds):
68+def _product(*args, **kwds):
69     """
70     Taken from http://docs.python.org/library/itertools.html#itertools.product
71     """
72@@ -19,9 +26,7 @@ def product(*args, **kwds):
73         result = [x+[y] for x in result for y in pool]
74     for prod in result:
75         yield tuple(prod)
76-
77-if hasattr(itertools, 'product'):
78-    product = itertools.product
79+product = getattr(itertools, 'product', _product)
80 
81 def is_iterable(x):
82     "A implementation independent way of checking for iterables"
83@@ -32,8 +37,22 @@ def is_iterable(x):
84     else:
85         return True
86 
87-def all(iterable):
88+def _all(iterable):
89+    """
90+    Taken from http://docs.python.org/library/functions.html#all
91+    """
92     for item in iterable:
93         if not item:
94             return False
95     return True
96+all = getattr(__builtins__, "all", _all)
97+
98+def _any(iterable):
99+    """
100+    Taken from http://docs.python.org/library/functions.html#any
101+    """
102+    for element in iterable:
103+        if element:
104+            return True
105+    return False
106+any = getattr(__builtins__, "any", _any)
107diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
108index f45c61a..365778f 100644
109--- a/docs/topics/auth.txt
110+++ b/docs/topics/auth.txt
111@@ -945,6 +945,14 @@ includes a few other useful built-in views located in
112 
113         * ``form``: The form for resetting the user's password.
114 
115+    .. note::
116+
117+       Users flagged with an unusable password (see
118+       :meth:`~django.contrib.auth.models.User.set_unusable_password()`
119+       will not be able to request a password reset. This is done to
120+       prevent a password reset when using an external authentication
121+       source like LDAP.
122+
123 .. function:: views.password_reset_done(request[, template_name])
124 
125     The page shown after a user has reset their password.