From e1eef863bc35514aa6d352583baa5b2a967b47f8 Mon Sep 17 00:00:00 2001 From: Benoit Bryon Date: Wed, 7 Nov 2012 09:43:55 +0100 Subject: [PATCH 1/4] Refs #17209 - Added methods in LoginView: allow fine-grained overrides. --- django/contrib/auth/views.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 7c87ba84fbc7..6a5c3950ad13 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -72,7 +72,7 @@ class LoginView(CurrentAppMixin, CurrentSiteMixin, generic.FormView): @method_decorator(csrf_protect) @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs): - request.session.set_test_cookie() + self.set_test_cookie(request) return super(LoginView, self).dispatch(request, *args, **kwargs) def get_form_kwargs(self): @@ -85,16 +85,26 @@ def get_context_data(self, **kwargs): context[self.redirect_field_name] = self.get_success_url() return context - def form_valid(self, form): - """Log the user in and redirect.""" - auth_login(self.request, form.get_user()) + def login(self, user): + """Log user in.""" + auth_login(self.request, user) + + def set_test_cookie(self, request): + """Set test cookie to request.""" + request.session.set_test_cookie() + def unset_test_cookie(self): + """Cleanup test cookie if necessary.""" if self.request.session.test_cookie_worked(): self.request.session.delete_test_cookie() + def form_valid(self, form): + """Log the user in and redirect.""" + self.login(form.get_user()) + self.unset_test_cookie() # Redirect return super(LoginView, self).form_valid(form) - + def get_success_url(self): """ Look for a redirect URL in the request parameters. From d7e50fd06c93b1c26e2584286bbce640dbdceb98 Mon Sep 17 00:00:00 2001 From: Benoit Bryon Date: Wed, 7 Nov 2012 10:08:34 +0100 Subject: [PATCH 2/4] Refs #17209 - Used a validator to check redirect URL in LoginView and LogoutView. --- django/contrib/auth/views.py | 45 ++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 6a5c3950ad13..e3cf1e4cf93d 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -6,6 +6,7 @@ from urlparse import urlparse, urlunparse from django.conf import settings +from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse_lazy from django.http import HttpResponseRedirect, QueryDict from django.utils.decorators import method_decorator @@ -53,12 +54,25 @@ def get_context_data(self, **kwargs): return context -def is_valid_redirect(url, request, allow_empty=False): # XXX: Name? - """"Validate that the given URL is on the same host as the given request.""" +def validate_redirect_url(url, request, allow_empty=False): + """"Validate that ``url`` is a valid URL for use in redirections. + + Raise ValidationError if: + + * ``allow_empty`` is False and ``url`` is empty string. + * URL is not on the same host as the given request. + + """ if not url: - return allow_empty - netloc = urlparse(url)[1] - return not netloc or netloc == request.get_host() + if not allow_empty: + raise ValidationError("URL can't be empty.") + else: + netloc = urlparse(url)[1] + request_host = request.get_host() + if netloc and netloc != request_host: + raise ValidationError("URL must belong to '%s' host." + % request_host) + return url class LoginView(CurrentAppMixin, CurrentSiteMixin, generic.FormView): @@ -112,7 +126,11 @@ def get_success_url(self): """ redir = self.request.REQUEST.get(self.redirect_field_name) - if not is_valid_redirect(redir, self.request, allow_empty=False): + try: + redir = validate_redirect_url(redir, self.request, + allow_empty=False) + except ValidationError: + # Silently fallback to settings. redir = settings.LOGIN_REDIRECT_URL return redir @@ -151,12 +169,15 @@ def get_success_url(self): """ redir = self.request.REQUEST.get(self.redirect_field_name) - if is_valid_redirect(redir, self.request, allow_empty=False): - return redir - elif self.success_url is not None: - return self.success_url or self.request.path - else: - return None + try: + return validate_redirect_url(redir, self.request, + allow_empty=False) + except ValidationError: + # Silently fallback to view's ``success_url`` or request.path. + if self.success_url is not None: + return self.success_url or self.request.path + else: + return None class LogoutThenLoginView(LogoutView): From ef35395abd9663cd875765b1b48eeaea4cb2c3f8 Mon Sep 17 00:00:00 2001 From: Benoit Bryon Date: Wed, 7 Nov 2012 10:19:42 +0100 Subject: [PATCH 3/4] Refs #17209 - Rely on parent's implementation of get_success_url() in LogoutView. --- django/contrib/auth/views.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index e3cf1e4cf93d..64e118920c1b 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -6,7 +6,7 @@ from urlparse import urlparse, urlunparse from django.conf import settings -from django.core.exceptions import ValidationError +from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.urlresolvers import reverse_lazy from django.http import HttpResponseRedirect, QueryDict from django.utils.decorators import method_decorator @@ -173,11 +173,17 @@ def get_success_url(self): return validate_redirect_url(redir, self.request, allow_empty=False) except ValidationError: - # Silently fallback to view's ``success_url`` or request.path. - if self.success_url is not None: - return self.success_url or self.request.path - else: - return None + # Silently fallback to view's ``success_url``. + try: + return super(LogoutView, self).get_success_url() + except ImproperlyConfigured: + # Silently fallback to current request.path if success_url has + # been set to empty value (except None). + if self.success_url is not None: + return self.request.path + else: + # Silently fallback to None. + return None class LogoutThenLoginView(LogoutView): From 0edbf3542bf0f0ff22e0061707c1e11747d39149 Mon Sep 17 00:00:00 2001 From: Benoit Bryon Date: Wed, 7 Nov 2012 10:50:01 +0100 Subject: [PATCH 4/4] Refs #17209 - Used view's success_url in LoginView. --- django/contrib/auth/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 64e118920c1b..1d0826a5d7ac 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -130,8 +130,12 @@ def get_success_url(self): redir = validate_redirect_url(redir, self.request, allow_empty=False) except ValidationError: - # Silently fallback to settings. - redir = settings.LOGIN_REDIRECT_URL + # Silently fallback to view's ``success_url``. + try: + redir = super(LoginView, self).get_success_url() + except ImproperlyConfigured: + # Silently fallback to settings. + redir = settings.LOGIN_REDIRECT_URL return redir