web/lib/django/contrib/auth/views.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- a/web/lib/django/contrib/auth/views.py	Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/contrib/auth/views.py	Tue May 25 02:43:45 2010 +0200
@@ -1,9 +1,13 @@
+import re
 from django.conf import settings
 from django.contrib.auth import REDIRECT_FIELD_NAME
+# Avoid shadowing the login() view below.
+from django.contrib.auth import login as auth_login
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm, PasswordChangeForm
 from django.contrib.auth.tokens import default_token_generator
+from django.views.decorators.csrf import csrf_protect
 from django.core.urlresolvers import reverse
 from django.shortcuts import render_to_response, get_object_or_404
 from django.contrib.sites.models import Site, RequestSite
@@ -14,34 +18,53 @@
 from django.contrib.auth.models import User
 from django.views.decorators.cache import never_cache
 
-def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME):
-    "Displays the login form and handles the login action."
+@csrf_protect
+@never_cache
+def login(request, template_name='registration/login.html',
+          redirect_field_name=REDIRECT_FIELD_NAME,
+          authentication_form=AuthenticationForm):
+    """Displays the login form and handles the login action."""
+
     redirect_to = request.REQUEST.get(redirect_field_name, '')
+    
     if request.method == "POST":
-        form = AuthenticationForm(data=request.POST)
+        form = authentication_form(data=request.POST)
         if form.is_valid():
             # Light security check -- make sure redirect_to isn't garbage.
-            if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
+            if not redirect_to or ' ' in redirect_to:
                 redirect_to = settings.LOGIN_REDIRECT_URL
-            from django.contrib.auth import login
-            login(request, form.get_user())
+            
+            # Heavier security check -- redirects to http://example.com should 
+            # not be allowed, but things like /view/?param=http://example.com 
+            # should be allowed. This regex checks if there is a '//' *before* a
+            # question mark.
+            elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to):
+                    redirect_to = settings.LOGIN_REDIRECT_URL
+            
+            # Okay, security checks complete. Log the user in.
+            auth_login(request, form.get_user())
+
             if request.session.test_cookie_worked():
                 request.session.delete_test_cookie()
+
             return HttpResponseRedirect(redirect_to)
+
     else:
-        form = AuthenticationForm(request)
+        form = authentication_form(request)
+    
     request.session.set_test_cookie()
+    
     if Site._meta.installed:
         current_site = Site.objects.get_current()
     else:
         current_site = RequestSite(request)
+    
     return render_to_response(template_name, {
         'form': form,
         redirect_field_name: redirect_to,
         'site': current_site,
         'site_name': current_site.name,
     }, context_instance=RequestContext(request))
-login = never_cache(login)
 
 def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME):
     "Logs out the user and displays 'You are logged out' message."
@@ -78,6 +101,7 @@
 #   prompts for a new password
 # - password_reset_complete shows a success message for the above
 
+@csrf_protect
 def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
         email_template_name='registration/password_reset_email.html',
         password_reset_form=PasswordResetForm, token_generator=default_token_generator,
@@ -107,6 +131,7 @@
 def password_reset_done(request, template_name='registration/password_reset_done.html'):
     return render_to_response(template_name, context_instance=RequestContext(request))
 
+# Doesn't need csrf_protect since no-one can guess the URL
 def password_reset_confirm(request, uidb36=None, token=None, template_name='registration/password_reset_confirm.html',
                            token_generator=default_token_generator, set_password_form=SetPasswordForm,
                            post_reset_redirect=None):
@@ -137,28 +162,29 @@
     else:
         context_instance['validlink'] = False
         form = None
-    context_instance['form'] = form    
+    context_instance['form'] = form
     return render_to_response(template_name, context_instance=context_instance)
 
 def password_reset_complete(request, template_name='registration/password_reset_complete.html'):
     return render_to_response(template_name, context_instance=RequestContext(request,
                                                                              {'login_url': settings.LOGIN_URL}))
 
+@csrf_protect
+@login_required
 def password_change(request, template_name='registration/password_change_form.html',
-                    post_change_redirect=None):
+                    post_change_redirect=None, password_change_form=PasswordChangeForm):
     if post_change_redirect is None:
         post_change_redirect = reverse('django.contrib.auth.views.password_change_done')
     if request.method == "POST":
-        form = PasswordChangeForm(request.user, request.POST)
+        form = password_change_form(user=request.user, data=request.POST)
         if form.is_valid():
             form.save()
             return HttpResponseRedirect(post_change_redirect)
     else:
-        form = PasswordChangeForm(request.user)
+        form = password_change_form(user=request.user)
     return render_to_response(template_name, {
         'form': form,
     }, context_instance=RequestContext(request))
-password_change = login_required(password_change)
 
 def password_change_done(request, template_name='registration/password_change_done.html'):
     return render_to_response(template_name, context_instance=RequestContext(request))