Ticket #11025: 11025.diff
File 11025.diff, 14.9 KB (added by , 14 years ago) |
---|
-
django/conf/global_settings.py
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index cd85ce0..e4b0c92 100644
a b LOGOUT_URL = '/accounts/logout/' 473 473 474 474 LOGIN_REDIRECT_URL = '/accounts/profile/' 475 475 476 LOGIN_URL_NEXT_ARG = 'next' 477 476 478 # The number of days a password reset link is valid for 477 479 PASSWORD_RESET_TIMEOUT_DAYS = 3 478 480 -
django/contrib/auth/__init__.py
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index a184aea..861bad6 100644
a b 1 1 import datetime 2 2 from warnings import warn 3 from django.conf import settings 3 4 from django.core.exceptions import ImproperlyConfigured 4 5 from django.utils.importlib import import_module 5 6 6 7 SESSION_KEY = '_auth_user_id' 7 8 BACKEND_SESSION_KEY = '_auth_user_backend' 8 REDIRECT_FIELD_NAME = 'next'9 REDIRECT_FIELD_NAME = settings.LOGIN_URL_NEXT_ARG 9 10 10 11 def load_backend(path): 11 12 i = path.rfind('.') … … def load_backend(path): 32 33 return cls() 33 34 34 35 def get_backends(): 35 from django.conf import settings36 36 backends = [] 37 37 for backend_path in settings.AUTHENTICATION_BACKENDS: 38 38 backends.append(load_backend(backend_path)) -
django/contrib/auth/decorators.py
diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py index 7d7a0cd..64b77a5 100644
a b 1 import urlparse 1 2 try: 2 from functools import update_wrapper,wraps3 from functools import wraps 3 4 except ImportError: 4 from django.utils.functional import update_wrapper,wraps # Python 2.4 fallback.5 from django.utils.functional import wraps # Python 2.4 fallback. 5 6 7 from django.conf import settings 6 8 from django.contrib.auth import REDIRECT_FIELD_NAME 7 from django.http import HttpResponseRedirect8 9 from django.utils.decorators import available_attrs 9 from django.utils.http import urlquote10 10 11 11 12 12 def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): … … def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE 15 15 redirecting to the log-in page if necessary. The test should be a callable 16 16 that takes the user object and returns True if the user passes. 17 17 """ 18 if not login_url:19 from django.conf import settings20 login_url = settings.LOGIN_URL21 18 22 19 def decorator(view_func): 20 @wraps(view_func, assigned=available_attrs(view_func)) 23 21 def _wrapped_view(request, *args, **kwargs): 24 22 if test_func(request.user): 25 23 return view_func(request, *args, **kwargs) 26 path = urlquote(request.get_full_path()) 27 tup = login_url, redirect_field_name, path 28 return HttpResponseRedirect('%s?%s=%s' % tup) 29 return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view) 24 path = request.build_absolute_uri() 25 # If the login url is the same scheme and net location then just 26 # use the path as the "next" url. 27 login_scheme, login_netloc = urlparse.urlparse(login_url or 28 settings.LOGIN_URL)[:2] 29 current_scheme, current_netloc = urlparse.urlparse(path)[:2] 30 if ((not login_scheme or login_scheme == current_scheme) and 31 (not login_netloc or login_netloc == current_netloc)): 32 path = request.get_full_path() 33 from django.contrib.auth.views import redirect_to_login 34 return redirect_to_login(path, login_url, redirect_field_name) 35 return _wrapped_view 30 36 return decorator 31 37 32 38 -
django/contrib/auth/tests/__init__.py
diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py index a1d02b6..b969b52 100644
a b from django.contrib.auth.tests.remote_user \ 6 6 import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest 7 7 from django.contrib.auth.tests.models import ProfileTestCase 8 8 from django.contrib.auth.tests.tokens import TOKEN_GENERATOR_TESTS 9 from django.contrib.auth.tests.views \10 import PasswordResetTest, ChangePasswordTest, LoginTest, LogoutTest9 from django.contrib.auth.tests.views import PasswordResetTest, \ 10 ChangePasswordTest, LoginTest, LogoutTest, LoginURLSettings 11 11 12 12 # The password for the fixture data users is 'password' 13 13 -
django/contrib/auth/tests/decorators.py
diff --git a/django/contrib/auth/tests/decorators.py b/django/contrib/auth/tests/decorators.py index 0240a76..e58bbfa 100644
a b class LoginRequiredTestCase(AuthViewsTestCase): 42 42 login_required decorator with a login_url set. 43 43 """ 44 44 self.testLoginRequired(view_url='/login_required_login_url/', 45 login_url='/somewhere/') 46 No newline at end of file 45 login_url='/somewhere/') -
django/contrib/auth/tests/views.py
diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py index 42f7f12..681d2cc 100644
a b import urllib 5 5 from django.conf import settings 6 6 from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME 7 7 from django.contrib.auth.forms import AuthenticationForm 8 from django.contrib.sites.models import Site , RequestSite8 from django.contrib.sites.models import Site 9 9 from django.contrib.auth.models import User 10 10 from django.test import TestCase 11 11 from django.core import mail 12 12 from django.core.urlresolvers import reverse 13 from django.http import QueryDict 13 14 14 15 class AuthViewsTestCase(TestCase): 15 16 """ … … class AuthViewsTestCase(TestCase): 25 26 settings.LANGUAGE_CODE = 'en' 26 27 self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS 27 28 settings.TEMPLATE_DIRS = ( 28 os.path.join( 29 os.path.dirname(__file__), 30 'templates' 31 ) 32 ,) 29 os.path.join(os.path.dirname(__file__), 'templates'), 30 ) 31 self.old_LOGIN_URL_NEXT_ARG = settings.LOGIN_URL_NEXT_ARG 32 settings.LOGIN_URL_NEXT_ARG = 'next' 33 33 34 34 def tearDown(self): 35 35 settings.LANGUAGES = self.old_LANGUAGES 36 36 settings.LANGUAGE_CODE = self.old_LANGUAGE_CODE 37 37 settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS 38 settings.LOGIN_URL_NEXT_ARG = self.old_LOGIN_URL_NEXT_ARG 38 39 39 40 def login(self, password='password'): 40 41 response = self.client.post('/login/', { … … class LoginTest(AuthViewsTestCase): 214 215 } 215 216 ) 216 217 self.assertEquals(response.status_code, 302) 217 self.assertFalse(bad_url in response['Location'], "%s should be blocked" % bad_url) 218 219 # Now, these URLs have an other URL as a GET parameter and therefore 220 # should be allowed 221 for url_ in ('http://example.com', 'https://example.com', 222 'ftp://exampel.com', '//example.com'): 223 safe_url = '%(url)s?%(next)s=/view/?param=%(safe_param)s' % { 218 self.assertFalse(bad_url in response['Location'], 219 "%s should be blocked" % bad_url) 220 221 # These URLs *should* still pass the security check 222 for good_url in ('/view/?param=http://example.com', 223 '/view/?param=https://example.com', 224 '/view?param=ftp://exampel.com', 225 'view/?param=//example.com', 226 'https:///', 227 '//testserver/'): 228 safe_url = '%(url)s?%(next)s=%(good_url)s' % { 224 229 'url': login_url, 225 230 'next': REDIRECT_FIELD_NAME, 226 ' safe_param': urllib.quote(url_)231 'good_url': urllib.quote(good_url) 227 232 } 228 233 response = self.client.post(safe_url, { 229 234 'username': 'testclient', … … class LoginTest(AuthViewsTestCase): 231 236 } 232 237 ) 233 238 self.assertEquals(response.status_code, 302) 234 self.assertTrue('/view/?param=%s' % url_ in response['Location'], "/view/?param=%s should be allowed" % url_) 239 self.assertTrue(good_url in response['Location'], 240 "%s should be allowed" % good_url) 235 241 242 class LoginURLSettings(AuthViewsTestCase): 243 urls = 'django.contrib.auth.tests.urls' 244 245 def setUp(self): 246 super(LoginURLSettings, self).setUp() 247 self.old_LOGIN_URL = settings.LOGIN_URL 248 249 def tearDown(self): 250 super(LoginURLSettings, self).tearDown() 251 settings.LOGIN_URL = self.old_LOGIN_URL 252 253 def get_login_required_url(self, login_url): 254 settings.LOGIN_URL = login_url 255 response = self.client.get('/login_required/') 256 self.assertEquals(response.status_code, 302) 257 return response['Location'] 258 259 def test_standard_login_url(self): 260 login_url = '/login/' 261 login_required_url = self.get_login_required_url(login_url) 262 querystring = QueryDict('', mutable=True) 263 querystring['next'] = '/login_required/' 264 self.assertEqual(login_required_url, 265 'http://testserver%s?%s' % (login_url, querystring.urlencode())) 266 267 def test_remote_login_url(self): 268 login_url = 'http://remote.example.com/login' 269 login_required_url = self.get_login_required_url(login_url) 270 querystring = QueryDict('', mutable=True) 271 querystring['next'] = 'http://testserver/login_required/' 272 self.assertEqual(login_required_url, 273 '%s?%s' % (login_url, querystring.urlencode())) 274 275 def test_https_login_url(self): 276 login_url = 'https:///login/' 277 login_required_url = self.get_login_required_url(login_url) 278 querystring = QueryDict('', mutable=True) 279 querystring['next'] = 'http://testserver/login_required/' 280 self.assertEqual(login_required_url, 281 '%s?%s' % (login_url, querystring.urlencode())) 282 283 def test_login_url_with_querystring(self): 284 login_url = '/login/?pretty=1' 285 login_required_url = self.get_login_required_url(login_url) 286 querystring = QueryDict('pretty=1', mutable=True) 287 querystring['next'] = '/login_required/' 288 self.assertEqual(login_required_url, 'http://testserver/login/?%s' % 289 querystring.urlencode()) 290 291 def test_remote_login_url_with_next_querystring(self): 292 login_url = 'http://remote.example.com/login/' 293 login_required_url = self.get_login_required_url('%s?next=/default/' % 294 login_url) 295 querystring = QueryDict('', mutable=True) 296 querystring['next'] = 'http://testserver/login_required/' 297 self.assertEqual(login_required_url, '%s?%s' % (login_url, 298 querystring.urlencode())) 236 299 237 300 class LogoutTest(AuthViewsTestCase): 238 301 urls = 'django.contrib.auth.tests.urls' -
django/contrib/auth/views.py
diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index a55c866..f55ca79 100644
a b 1 1 import re 2 import urlparse 2 3 from django.conf import settings 3 4 from django.contrib.auth import REDIRECT_FIELD_NAME 4 5 # Avoid shadowing the login() view below. … … from django.views.decorators.csrf import csrf_protect 11 12 from django.core.urlresolvers import reverse 12 13 from django.shortcuts import render_to_response, get_object_or_404 13 14 from django.contrib.sites.models import get_current_site 14 from django.http import HttpResponseRedirect, Http404 15 from django.http import HttpResponseRedirect, Http404, QueryDict 15 16 from django.template import RequestContext 16 from django.utils.http import urlquote,base36_to_int17 from django.utils.http import base36_to_int 17 18 from django.utils.translation import ugettext as _ 18 19 from django.contrib.auth.models import User 19 20 from django.views.decorators.cache import never_cache … … def login(request, template_name='registration/login.html', 30 31 if request.method == "POST": 31 32 form = authentication_form(data=request.POST) 32 33 if form.is_valid(): 34 netloc = urlparse.urlparse(redirect_to)[1] 35 33 36 # Light security check -- make sure redirect_to isn't garbage. 34 37 if not redirect_to or ' ' in redirect_to: 35 38 redirect_to = settings.LOGIN_REDIRECT_URL 36 39 37 # Heavier security check -- redirects to http://example.com should 38 # not be allowed, but things like /view/?param=http://example.com 39 # should be allowed. This regex checks if there is a '//' *before* a 40 # question mark. 41 elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to): 42 redirect_to = settings.LOGIN_REDIRECT_URL 40 # Heavier security check -- don't allow redirection to a different 41 # host. 42 elif netloc and netloc != request.get_host(): 43 redirect_to = settings.LOGIN_REDIRECT_URL 43 44 44 45 # Okay, security checks complete. Log the user in. 45 46 auth_login(request, form.get_user()) … … def logout_then_login(request, login_url=None): 88 89 login_url = settings.LOGIN_URL 89 90 return logout(request, login_url) 90 91 91 def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): 92 def redirect_to_login(next, login_url=None, 93 redirect_field_name=REDIRECT_FIELD_NAME): 92 94 "Redirects the user to the login page, passing the given 'next' page" 93 95 if not login_url: 94 96 login_url = settings.LOGIN_URL 95 return HttpResponseRedirect('%s?%s=%s' % (login_url, urlquote(redirect_field_name), urlquote(next))) 97 98 login_url_parts = list(urlparse.urlparse(login_url)) 99 if redirect_field_name: 100 querystring = QueryDict(login_url_parts[4], mutable=True) 101 querystring[redirect_field_name] = next 102 login_url_parts[4] = querystring.urlencode() 103 104 return HttpResponseRedirect(urlparse.urlunparse(login_url_parts)) 96 105 97 106 # 4 views for password reset: 98 107 # - password_reset sends the mail -
docs/ref/settings.txt
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index c72b171..f4ebed2 100644
a b Default: ``'/accounts/login/'`` 1067 1067 The URL where requests are redirected for login, especially when using the 1068 1068 :func:`~django.contrib.auth.decorators.login_required` decorator. 1069 1069 1070 .. setting:: LOGIN_URL_NEXT_ARG 1071 1072 LOGIN_URL_NEXT_ARG 1073 ------------------ 1074 1075 .. versionadded:: 1.3 1076 1077 Default: ``'next'`` 1078 1079 The argument used when building the "next url" querystring for the login URL. 1080 For example, with the default settings an anonymous user accessing a URL of 1081 ``/secure_page/`` which maps to a view decorated with 1082 :func:`~django.contrib.auth.decorators.login_required` will be redirected to: 1083 ``/accounts/login/?next=/secure_page/``. 1084 1070 1085 .. setting:: LOGOUT_URL 1071 1086 1072 1087 LOGOUT_URL