From f06f3b043136490d8de0e02027b24307630318ce Mon Sep 17 00:00:00 2001 From: Steven Day Date: Wed, 17 Jul 2013 10:27:32 +0100 Subject: [PATCH 1/5] PEP8 Fixes --- pylib/djangomiddleware/redirect_middleware.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylib/djangomiddleware/redirect_middleware.py b/pylib/djangomiddleware/redirect_middleware.py index 6292ca65..f67ad82f 100644 --- a/pylib/djangomiddleware/redirect_middleware.py +++ b/pylib/djangomiddleware/redirect_middleware.py @@ -6,6 +6,7 @@ from django.http import HttpResponseRedirect from django.contrib.sites.models import Site + class FullyQualifiedRedirectMiddleware(object): def process_response(self, request, response): """Makes all redirects include a scheme and a domain. @@ -28,8 +29,7 @@ def process_response(self, request, response): new_location[0] = 'http' new_location[1] = Site.objects.get_current().domain new_location[2] = urlparse.urljoin(request.META['PATH_INFO'], parsed_location.path) - + response['Location'] = urlparse.urlunparse(new_location) return response - From 5a2b148aa1448994a95aa7871cf8f945b8df33dd Mon Sep 17 00:00:00 2001 From: Steven Day Date: Wed, 17 Jul 2013 10:34:01 +0100 Subject: [PATCH 2/5] Add a method to sniff the ssl status of the request and use that scheme in our response --- pylib/djangomiddleware/redirect_middleware.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pylib/djangomiddleware/redirect_middleware.py b/pylib/djangomiddleware/redirect_middleware.py index f67ad82f..5b7db1a1 100644 --- a/pylib/djangomiddleware/redirect_middleware.py +++ b/pylib/djangomiddleware/redirect_middleware.py @@ -26,10 +26,17 @@ def process_response(self, request, response): new_location = list(parsed_location) # FIXME - we can do better than hardcoding this - new_location[0] = 'http' + new_location[0] = 'https' if self.request_is_secure(request) else 'http' new_location[1] = Site.objects.get_current().domain new_location[2] = urlparse.urljoin(request.META['PATH_INFO'], parsed_location.path) response['Location'] = urlparse.urlunparse(new_location) return response + + def request_is_secure(request): + """Check if a request is secure""" + + # It might not be secure, but it might be forwarded for a secure request + forwarded_https = request.META.get('HTTP_X_FORWARDED_PROTO', '') == 'https' + return forwarded_https or request.is_secure() From ab03ea1643f415c3b3d777302f38c6fc928f8108 Mon Sep 17 00:00:00 2001 From: stevenday Date: Wed, 17 Jul 2013 11:59:10 +0000 Subject: [PATCH 3/5] Be smarter about sniffing the scheme, using the settings based approach Django itself uses to know which headers to trust --- pylib/djangomiddleware/redirect_middleware.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/pylib/djangomiddleware/redirect_middleware.py b/pylib/djangomiddleware/redirect_middleware.py index 5b7db1a1..e6ffc2a5 100644 --- a/pylib/djangomiddleware/redirect_middleware.py +++ b/pylib/djangomiddleware/redirect_middleware.py @@ -5,6 +5,8 @@ from django.http import HttpResponseRedirect from django.contrib.sites.models import Site +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured class FullyQualifiedRedirectMiddleware(object): @@ -25,7 +27,6 @@ def process_response(self, request, response): if not (parsed_location.scheme and parsed_location.netloc): new_location = list(parsed_location) - # FIXME - we can do better than hardcoding this new_location[0] = 'https' if self.request_is_secure(request) else 'http' new_location[1] = Site.objects.get_current().domain new_location[2] = urlparse.urljoin(request.META['PATH_INFO'], parsed_location.path) @@ -36,7 +37,22 @@ def process_response(self, request, response): def request_is_secure(request): """Check if a request is secure""" - - # It might not be secure, but it might be forwarded for a secure request - forwarded_https = request.META.get('HTTP_X_FORWARDED_PROTO', '') == 'https' - return forwarded_https or request.is_secure() + # From Django 1.4 onwards request.is_secure() takes care of identifying + # secure requests forwarded via a proxy, by checking the header given + # in the SECURE_PROXY_SSL_HEADER setting. + # See: https://docs.djangoproject.com/en/1.4/ref/settings/#secure-proxy-ssl-header + # Older versions would always return false if the request was being + # forwarded by a proxy, since they would see only http. + # Therefore, we duplicate the functionality from: + # https://github.com/django/django/blob/master/django/http/request.py + # here, so that older versions can use the setting too. + # Remember to set it correctly! + if settings.SECURE_PROXY_SSL_HEADER: + try: + header, value = settings.SECURE_PROXY_SSL_HEADER + except ValueError: + raise ImproperlyConfigured('The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.') + if request.META.get(header, None) == value: + return True + else: + return request.is_secure() From b684e456a84bad7d4ed8a1b23b75a5569d4efef8 Mon Sep 17 00:00:00 2001 From: Steven Day Date: Wed, 17 Jul 2013 14:38:51 +0100 Subject: [PATCH 4/5] Fix missing self parameter in request_is_secure method definition --- pylib/djangomiddleware/redirect_middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylib/djangomiddleware/redirect_middleware.py b/pylib/djangomiddleware/redirect_middleware.py index e6ffc2a5..b615bf7d 100644 --- a/pylib/djangomiddleware/redirect_middleware.py +++ b/pylib/djangomiddleware/redirect_middleware.py @@ -35,7 +35,7 @@ def process_response(self, request, response): return response - def request_is_secure(request): + def request_is_secure(self, request): """Check if a request is secure""" # From Django 1.4 onwards request.is_secure() takes care of identifying # secure requests forwarded via a proxy, by checking the header given From 941912cd257bd2f728702e974e50307d2c596f3b Mon Sep 17 00:00:00 2001 From: Steven Day Date: Tue, 30 Jul 2013 12:44:36 +0100 Subject: [PATCH 5/5] Switch to using the parsed host from the request, rather than the current site object, to work behind proxies which don't rewrite redirects --- pylib/djangomiddleware/redirect_middleware.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pylib/djangomiddleware/redirect_middleware.py b/pylib/djangomiddleware/redirect_middleware.py index b615bf7d..2544eba0 100644 --- a/pylib/djangomiddleware/redirect_middleware.py +++ b/pylib/djangomiddleware/redirect_middleware.py @@ -4,7 +4,6 @@ import logging from django.http import HttpResponseRedirect -from django.contrib.sites.models import Site from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -28,7 +27,7 @@ def process_response(self, request, response): new_location = list(parsed_location) new_location[0] = 'https' if self.request_is_secure(request) else 'http' - new_location[1] = Site.objects.get_current().domain + new_location[1] = request.get_host() new_location[2] = urlparse.urljoin(request.META['PATH_INFO'], parsed_location.path) response['Location'] = urlparse.urlunparse(new_location)