|
import time |
|
|
|
from django.conf import settings |
|
from django.http import HttpResponseRedirect |
|
from django.utils.http import cookie_date |
|
|
|
AUTH_COOKIE_NAME = getattr(settings, 'AUTH_COOKIE_NAME', 'authenticated_user') |
|
|
|
|
|
def http_redirect(request): |
|
"""Returns an HttpResponseRedirect to the URL of the given request with |
|
the protocol set to http:// |
|
""" |
|
absolute_uri = request.build_absolute_uri() |
|
if absolute_uri.startswith('https'): |
|
absolute_uri = 'http%s' % absolute_uri[5:] |
|
return HttpResponseRedirect(absolute_uri) |
|
|
|
|
|
def https_redirect(request): |
|
"""Returns an HttpResponseRedirect to the URL of the given request with |
|
the protocol set to https:// |
|
""" |
|
absolute_uri = request.build_absolute_uri() |
|
if not absolute_uri.startswith('https'): |
|
absolute_uri = 'https%s' % absolute_uri[4:] |
|
return HttpResponseRedirect(absolute_uri) |
|
|
|
|
|
def require_https(func): |
|
"""A view decorator that enforces HTTPS requests for the given view. |
|
If the view is requested via HTTP it returns an HttpResponseRedirect for |
|
HTTPS. |
|
""" |
|
def inner(request, *args, **kwargs): |
|
if not request.is_secure(): |
|
return https_redirect(request) |
|
return func(request, *args, **kwargs) |
|
inner.require_https = True |
|
return inner |
|
|
|
|
|
class HTTPSMiddleware(object): |
|
"""This middleware ensures that logged in users always connect via HTTPS. |
|
Authenticated users connecting via HTTP are redirected to the same URL via |
|
HTTPS. |
|
Unauthenticated users connecting via HTTPS are redirected to the same |
|
URL via HTTP. This can be overridden with the require_https view decorator, |
|
which enforces HTTPS connections. |
|
|
|
Using this middleware with SESSION_COOKIE_SECURE = True in settings.py is |
|
*highly* reccomended. |
|
This middleware needs one setting in settings.py: |
|
AUTH_COOKIE_NAME: name of the cookie used to indicate that a user is |
|
logged in. (When SESSION_COOKIE_SECURE is True the user is "invisible" |
|
when requesting via HTTP, so another non-secure cookie is needed). |
|
""" |
|
|
|
def _get_auth_cookie(self, request): |
|
"""Returns the auth cookie if it is present in the given request, and |
|
False otherwise. |
|
""" |
|
return request.COOKIES.get(AUTH_COOKIE_NAME, False) |
|
|
|
def process_request(self, request): |
|
"""Ensures that an authenticated user connects via HTTPS.""" |
|
auth_cookie = self._get_auth_cookie(request) |
|
if not request.is_secure() and auth_cookie: |
|
return https_redirect(request) |
|
|
|
def process_view(self, request, view_func, view_args, view_kwargs): |
|
"""Ensures that only views decorated with the require_https decorator |
|
can be requested via HTTPS by unauthenticated users. |
|
""" |
|
require_https = getattr(view_func, 'require_https', False) |
|
if request.is_secure() and not request.user.is_authenticated() and not require_https: |
|
redirect = http_redirect(request) |
|
redirect.delete_cookie(AUTH_COOKIE_NAME, |
|
path=settings.SESSION_COOKIE_PATH, |
|
domain=settings.SESSION_COOKIE_DOMAIN) |
|
return redirect |
|
|
|
def process_response(self, request, response): |
|
"""Ensures that authenticated users can be detected in HTTP requests by |
|
setting the auth cookie. |
|
""" |
|
auth_cookie = self._get_auth_cookie(request) |
|
try: |
|
user = request.user |
|
except AttributeError: |
|
return response |
|
|
|
if not auth_cookie and user.is_authenticated(): |
|
if request.session.get_expire_at_browser_close(): |
|
max_age = None |
|
expires = None |
|
else: |
|
max_age = request.session.get_expiry_age() |
|
expires_time = time.time() + max_age |
|
expires = cookie_date(expires_time) |
|
|
|
response.set_cookie(AUTH_COOKIE_NAME, 'true', |
|
max_age=max_age, expires=expires, |
|
domain=settings.SESSION_COOKIE_DOMAIN, |
|
path=settings.SESSION_COOKIE_PATH) |
|
|
|
return response |