Code

Ticket #6083: auth-uses-newforms-2.diff

File auth-uses-newforms-2.diff, 20.6 KB (added by dstndstn@…, 6 years ago)

includes a one-liner update for delete_test_cookie() problem

Line 
1Index: django/contrib/comments/views/comments.py
2===================================================================
3--- django/contrib/comments/views/comments.py   (revision 7022)
4+++ django/contrib/comments/views/comments.py   (working copy)
5@@ -7,7 +7,6 @@
6 from django.template import RequestContext
7 from django.contrib.comments.models import Comment, FreeComment, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC
8 from django.contrib.contenttypes.models import ContentType
9-from django.contrib.auth.forms import AuthenticationForm
10 from django.http import HttpResponseRedirect
11 from django.utils.text import normalize_newlines
12 from django.conf import settings
13@@ -17,6 +16,47 @@
14 
15 COMMENTS_PER_PAGE = 20
16 
17+
18+class AuthenticationForm(oldforms.Manipulator):
19+    """
20+    Oldforms-based Base class for authenticating users, extended by contrib.comments
21+    """
22+    def __init__(self, request=None):
23+        """
24+        If request is passed in, the manipulator will validate that cookies are
25+        enabled. Note that the request (a HttpRequest object) must have set a
26+        cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
27+        running this validator.
28+        """
29+        self.request = request
30+        self.fields = [
31+            oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
32+                validator_list=[self.isValidUser, self.hasCookiesEnabled]),
33+            oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
34+        ]
35+        self.user_cache = None
36+
37+    def hasCookiesEnabled(self, field_data, all_data):
38+        if self.request and not self.request.session.test_cookie_worked():
39+            raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
40+
41+    def isValidUser(self, field_data, all_data):
42+        username = field_data
43+        password = all_data.get('password', None)
44+        self.user_cache = authenticate(username=username, password=password)
45+        if self.user_cache is None:
46+            raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
47+        elif not self.user_cache.is_active:
48+            raise validators.ValidationError, _("This account is inactive.")
49+
50+    def get_user_id(self):
51+        if self.user_cache:
52+            return self.user_cache.id
53+        return None
54+
55+    def get_user(self):
56+        return self.user_cache
57+
58 class PublicCommentManipulator(AuthenticationForm):
59     "Manipulator that handles public registered comments"
60     def __init__(self, user, ratings_required, ratings_range, num_rating_choices):
61Index: django/contrib/admin/views/auth.py
62===================================================================
63--- django/contrib/admin/views/auth.py  (revision 7022)
64+++ django/contrib/admin/views/auth.py  (working copy)
65@@ -11,12 +11,10 @@
66 def user_add_stage(request):
67     if not request.user.has_perm('auth.change_user'):
68         raise PermissionDenied
69-    manipulator = UserCreationForm()
70     if request.method == 'POST':
71-        new_data = request.POST.copy()
72-        errors = manipulator.get_validation_errors(new_data)
73-        if not errors:
74-            new_user = manipulator.save(new_data)
75+        form = UserCreationForm(request.POST)
76+        if form.is_valid():
77+            new_user = form.save()
78             msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user}
79             if "_addanother" in request.POST:
80                 request.user.message_set.create(message=msg)
81@@ -25,8 +23,7 @@
82                 request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
83                 return HttpResponseRedirect('../%s/' % new_user.id)
84     else:
85-        errors = new_data = {}
86-    form = oldforms.FormWrapper(manipulator, new_data, errors)
87+        form = UserCreationForm()
88     return render_to_response('admin/auth/user/add_form.html', {
89         'title': _('Add user'),
90         'form': form,
91@@ -49,18 +46,15 @@
92     if not request.user.has_perm('auth.change_user'):
93         raise PermissionDenied
94     user = get_object_or_404(User, pk=id)
95-    manipulator = AdminPasswordChangeForm(user)
96     if request.method == 'POST':
97-        new_data = request.POST.copy()
98-        errors = manipulator.get_validation_errors(new_data)
99-        if not errors:
100-            new_user = manipulator.save(new_data)
101+        form = AdminPasswordChangeForm(request_post=request.POST, user=user)
102+        if form.is_valid():
103+            new_user = form.save()
104             msg = _('Password changed successfully.')
105             request.user.message_set.create(message=msg)
106             return HttpResponseRedirect('..')
107     else:
108-        errors = new_data = {}
109-    form = oldforms.FormWrapper(manipulator, new_data, errors)
110+        form = AdminPasswordChangeForm(user=user)
111     return render_to_response('admin/auth/user/change_password.html', {
112         'title': _('Change password: %s') % escape(user.username),
113         'form': form,
114Index: django/contrib/auth/views.py
115===================================================================
116--- django/contrib/auth/views.py        (revision 7022)
117+++ django/contrib/auth/views.py        (working copy)
118@@ -1,6 +1,5 @@
119 from django.contrib.auth.forms import AuthenticationForm
120 from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm
121-from django import oldforms
122 from django.shortcuts import render_to_response
123 from django.template import RequestContext
124 from django.contrib.sites.models import Site, RequestSite
125@@ -11,21 +10,21 @@
126 
127 def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME):
128     "Displays the login form and handles the login action."
129-    manipulator = AuthenticationForm(request)
130     redirect_to = request.REQUEST.get(redirect_field_name, '')
131-    if request.POST:
132-        errors = manipulator.get_validation_errors(request.POST)
133-        if not errors:
134+    if request.method == 'POST':
135+        form = AuthenticationForm(request.POST)
136+        if form.is_valid():
137             # Light security check -- make sure redirect_to isn't garbage.
138             if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
139                 from django.conf import settings
140                 redirect_to = settings.LOGIN_REDIRECT_URL
141             from django.contrib.auth import login
142-            login(request, manipulator.get_user())
143-            request.session.delete_test_cookie()
144+            login(request, form.get_user())           
145+            if request.session.test_cookie_worked():
146+                request.session.delete_test_cookie()
147             return HttpResponseRedirect(redirect_to)
148     else:
149-        errors = {}
150+        form = AuthenticationForm(request=request)
151     request.session.set_test_cookie()
152 
153     if Site._meta.installed:
154@@ -34,7 +33,7 @@
155         current_site = RequestSite(request)
156 
157     return render_to_response(template_name, {
158-        'form': oldforms.FormWrapper(manipulator, request.POST, errors),
159+        'form': form,
160         redirect_field_name: redirect_to,
161         'site_name': current_site.name,
162     }, context_instance=RequestContext(request))
163@@ -65,18 +64,17 @@
164 
165 def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
166         email_template_name='registration/password_reset_email.html'):
167-    new_data, errors = {}, {}
168-    form = PasswordResetForm()
169     if request.POST:
170-        new_data = request.POST.copy()
171-        errors = form.get_validation_errors(new_data)
172-        if not errors:
173+        form = PasswordResetForm(request.POST)
174+        if form.is_valid():
175             if is_admin_site:
176                 form.save(domain_override=request.META['HTTP_HOST'])
177             else:
178                 form.save(email_template_name=email_template_name)
179             return HttpResponseRedirect('%sdone/' % request.path)
180-    return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
181+    else:
182+        form = PasswordResetForm()     
183+    return render_to_response(template_name, {'form': form},
184         context_instance=RequestContext(request))
185 
186 def password_reset_done(request, template_name='registration/password_reset_done.html'):
187@@ -84,14 +82,14 @@
188 
189 def password_change(request, template_name='registration/password_change_form.html'):
190     new_data, errors = {}, {}
191-    form = PasswordChangeForm(request.user)
192-    if request.POST:
193-        new_data = request.POST.copy()
194-        errors = form.get_validation_errors(new_data)
195-        if not errors:
196-            form.save(new_data)
197+    if request.method == 'POST':
198+        form = PasswordChangeForm(request.POST, request.user)
199+        if form.is_valid():
200+            form.save()
201             return HttpResponseRedirect('%sdone/' % request.path)
202-    return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
203+    else:
204+        form = PasswordChangeForm(user=request.user)
205+    return render_to_response(template_name, {'form': form},
206         context_instance=RequestContext(request))
207 password_change = login_required(password_change)
208 
209Index: django/contrib/auth/forms.py
210===================================================================
211--- django/contrib/auth/forms.py        (revision 7022)
212+++ django/contrib/auth/forms.py        (working copy)
213@@ -2,38 +2,48 @@
214 from django.contrib.auth import authenticate
215 from django.contrib.sites.models import Site
216 from django.template import Context, loader
217-from django.core import validators
218-from django import oldforms
219+from django import newforms as forms
220 from django.utils.translation import ugettext as _
221+import re
222 
223-class UserCreationForm(oldforms.Manipulator):
224+class UserCreationForm(forms.Form):
225     "A form that creates a user, with no privileges, from the given username and password."
226-    def __init__(self):
227-        self.fields = (
228-            oldforms.TextField(field_name='username', length=30, max_length=30, is_required=True,
229-                validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
230-            oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
231-            oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
232-                validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
233-        )
234+    username=forms.CharField(label=_("username"), max_length=30, required=True)
235+    password1 = forms.CharField(label=_("password"), max_length=30, required=True, widget=forms.PasswordInput)
236+    password2 = forms.CharField(label=_("password (again)"), max_length=30, required=True, widget=forms.PasswordInput)
237 
238-    def isValidUsername(self, field_data, all_data):
239+    #Following regex and error is borrowed from django.core.validators for backwards compatability for now (including i18n), but in anticipation of them being passed as parameters (or overridden).
240+    username_re = re.compile(r'^\w+$')
241+    username_re_validation_text = "This value must contain only letters, numbers and underscores."
242+
243+    def clean_password2(self):
244+        if self._errors: return
245+        if not self.cleaned_data['password1'] == self.cleaned_data['password2']:   
246+            raise forms.ValidationError, _("The two 'password' fields didn't match.")
247+        return self.cleaned_data['password2']
248+
249+    def clean_username(self):
250+        if not self.username_re.search(self.cleaned_data['username']):
251+            raise forms.ValidationError, _(self.username_re_validation_text)
252         try:
253-            User.objects.get(username=field_data)
254+            user = User.objects.get(username__exact=self.cleaned_data['username'])
255         except User.DoesNotExist:
256-            return
257-        raise validators.ValidationError, _('A user with that username already exists.')
258-
259-    def save(self, new_data):
260+                return self.cleaned_data['username']
261+        raise forms.ValidationError, _('A user with that username already exists.')
262+       
263+    def save(self):
264         "Creates the user."
265-        return User.objects.create_user(new_data['username'], '', new_data['password1'])
266+        return User.objects.create_user(self.cleaned_data['username'], '', self.cleaned_data['password1'])
267 
268-class AuthenticationForm(oldforms.Manipulator):
269+class AuthenticationForm(forms.Form):
270     """
271     Base class for authenticating users. Extend this to get a form that accepts
272     username/password logins.
273     """
274-    def __init__(self, request=None):
275+    username = forms.CharField(label=_("username"), max_length=30, required=True)
276+    password = forms.CharField(label=_("password"), max_length=30, required=True, widget=forms.PasswordInput)
277+   
278+    def __init__(self, request_post=None, request=None):
279         """
280         If request is passed in, the manipulator will validate that cookies are
281         enabled. Note that the request (a HttpRequest object) must have set a
282@@ -41,25 +51,24 @@
283         running this validator.
284         """
285         self.request = request
286-        self.fields = [
287-            oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
288-                validator_list=[self.isValidUser, self.hasCookiesEnabled]),
289-            oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
290-        ]
291         self.user_cache = None
292+        super(AuthenticationForm, self).__init__(request_post)
293 
294-    def hasCookiesEnabled(self, field_data, all_data):
295+    def clean(self):
296+        """
297+        Test that cookies are enabled and that self.username is a valid user with the right password.
298+        """
299+        if self._errors: return
300         if self.request and not self.request.session.test_cookie_worked():
301             raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
302-
303-    def isValidUser(self, field_data, all_data):
304-        username = field_data
305-        password = all_data.get('password', None)
306+        username = self.cleaned_data['username']
307+        password = self.cleaned_data['password']
308         self.user_cache = authenticate(username=username, password=password)
309         if self.user_cache is None:
310-            raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
311+            raise forms.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
312         elif not self.user_cache.is_active:
313-            raise validators.ValidationError, _("This account is inactive.")
314+            raise forms.ValidationError, _("This account is inactive.")
315+        return self.cleaned_data
316 
317     def get_user_id(self):
318         if self.user_cache:
319@@ -69,19 +78,16 @@
320     def get_user(self):
321         return self.user_cache
322 
323-class PasswordResetForm(oldforms.Manipulator):
324+class PasswordResetForm(forms.Form):
325     "A form that lets a user request a password reset"
326-    def __init__(self):
327-        self.fields = (
328-            oldforms.EmailField(field_name="email", length=40, is_required=True,
329-                validator_list=[self.isValidUserEmail]),
330-        )
331-
332-    def isValidUserEmail(self, new_data, all_data):
333+    email = forms.EmailField(label=_("email"), max_length=40, required=True)
334+   
335+    def clean_email(self):
336         "Validates that a user exists with the given e-mail address"
337-        self.users_cache = list(User.objects.filter(email__iexact=new_data))
338+        self.users_cache = list(User.objects.filter(email__iexact=self.cleaned_data['email']))
339         if len(self.users_cache) == 0:
340-            raise validators.ValidationError, _("That e-mail address doesn't have an associated user account. Are you sure you've registered?")
341+            raise forms.ValidationError, _("That e-mail address doesn't have an associated user account. Are you sure you've registered?")
342+        return self.cleaned_data['email']
343 
344     def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'):
345         "Calculates a new password randomly and sends it to the user"
346@@ -106,39 +112,49 @@
347                 }
348             send_mail(_('Password reset on %s') % site_name, t.render(Context(c)), None, [user.email])
349 
350-class PasswordChangeForm(oldforms.Manipulator):
351-    "A form that lets a user change his password."
352-    def __init__(self, user):
353+class PasswordChangeForm(forms.Form):
354+    "A form that lets a user change his or her password."
355+    old_password = forms.CharField(label=_("old password"), max_length=30, required=True, widget=forms.PasswordInput)
356+    new_password1 = forms.CharField(label=_("new password"), max_length=30, required=True, widget=forms.PasswordInput)
357+    new_password2 = forms.CharField(label=_("new password again"), max_length=30, required=True, widget=forms.PasswordInput)
358+
359+    def __init__(self, request_post=None, user=None):
360         self.user = user
361-        self.fields = (
362-            oldforms.PasswordField(field_name="old_password", length=30, max_length=30, is_required=True,
363-                validator_list=[self.isValidOldPassword]),
364-            oldforms.PasswordField(field_name="new_password1", length=30, max_length=30, is_required=True,
365-                validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
366-            oldforms.PasswordField(field_name="new_password2", length=30, max_length=30, is_required=True),
367-        )
368+        super(PasswordChangeForm,self).__init__(request_post)
369+       
370+    def clean_new_password2(self):
371+        if self._errors: return
372+        if not self.cleaned_data['new_password1'] == self.cleaned_data['new_password2']:   
373+            raise forms.ValidationError, _("The two 'new password' fields didn't match.")
374+        return self.cleaned_data['new_password2']
375 
376-    def isValidOldPassword(self, new_data, all_data):
377+    def clean_old_password(self):
378         "Validates that the old_password field is correct."
379-        if not self.user.check_password(new_data):
380-            raise validators.ValidationError, _("Your old password was entered incorrectly. Please enter it again.")
381+        if not self.user.check_password(self.cleaned_data['old_password']):
382+            raise forms.ValidationError, _("Your old password was entered incorrectly. Please enter it again.")
383+        return self.cleaned_data['old_password']
384 
385-    def save(self, new_data):
386+    def save(self):
387         "Saves the new password."
388-        self.user.set_password(new_data['new_password1'])
389+        self.user.set_password(self.cleaned_data['new_password1'])
390         self.user.save()
391 
392-class AdminPasswordChangeForm(oldforms.Manipulator):
393-    "A form used to change the password of a user in the admin interface."
394-    def __init__(self, user):
395+class AdminPasswordChangeForm(forms.Form):
396+    "A form used to change the password of a user in the admin interface - it is not necessary to know the old password."
397+    new_password1 = forms.CharField(label=_("new password"), max_length=30, required=True, widget=forms.PasswordInput)
398+    new_password2 = forms.CharField(label=_("new password again"), max_length=30, required=True, widget=forms.PasswordInput)
399+
400+    def __init__(self, request_post=None, user=None):
401         self.user = user
402-        self.fields = (
403-            oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
404-            oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
405-                validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
406-        )
407+        super(AdminPasswordChangeForm,self).__init__(request_post)
408 
409-    def save(self, new_data):
410+    def clean_new_password2(self):
411+        if self._errors: return
412+        if not self.cleaned_data['new_password1'] == self.cleaned_data['new_password2']:   
413+            raise forms.ValidationError, _("The two 'new password' fields didn't match.")
414+        return self.cleaned_data['new_password2']
415+
416+    def save(self):
417         "Saves the new password."
418-        self.user.set_password(new_data['password1'])
419+        self.user.set_password(self.cleaned_data['new_password1'])
420         self.user.save()
421Index: AUTHORS
422===================================================================
423--- AUTHORS     (revision 7022)
424+++ AUTHORS     (working copy)
425@@ -329,6 +329,7 @@
426     tstromberg@google.com
427     Makoto Tsuyuki <mtsuyuki@gmail.com>
428     tt@gurgle.no
429+    Greg Turner <http://gregturner.org>
430     Amit Upadhyay
431     Geert Vanderkelen
432     I.S. van Oostveen <v.oostveen@idca.nl>