Skip to content

Instantly share code, notes, and snippets.

@tomds
Created July 2, 2012 10:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save tomds/3032515 to your computer and use it in GitHub Desktop.
Save tomds/3032515 to your computer and use it in GitHub Desktop.
Django site-wide login required middleware including AJAX support
# Based on http://djangosnippets.org/snippets/1158/
import json
import re
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect
class EnforceLoginMiddleware(object):
"""
Middlware class which requires the user to be authenticated for all urls except
those defined in PUBLIC_URLS in settings.py. PUBLIC_URLS should be a tuple of regular
expresssions for the urls you want anonymous users to have access to. If PUBLIC_URLS
is not defined, it falls back to LOGIN_URL or failing that '/accounts/login/'.
Requests for urls not matching PUBLIC_URLS get redirected to LOGIN_URL with next set
to original path of the unauthenticted request.
"""
def __init__(self):
self.login_url = getattr(settings, 'LOGIN_URL', '/accounts/login/' )
if hasattr(settings,'PUBLIC_URLS'):
public_urls = [re.compile(url) for url in settings.PUBLIC_URLS]
else:
public_urls = [(re.compile("^%s$" % ( self.login_url[1:] )))]
self.public_urls = tuple(public_urls)
def process_request(self, request):
"""
Redirect anonymous users to login_url from non public urls
"""
redirect_to_login = False
try:
if request.user.is_anonymous():
for url in self.public_urls:
if url.match(request.path):
return None
redirect_to_login = True
except AttributeError:
redirect_to_login = True
if redirect_to_login:
# Return a 401 for AJAX requests so it's easy to tell from JS that login is required
if request.is_ajax() or \
'application/json' in request.META.get('HTTP_ACCEPT', '') or \
request.POST.get('httpAccept') == 'json':
return HttpResponse(json.dumps({'loginRedirect': True}), status=401)
return HttpResponseRedirect("%s?next=%s" % (self.login_url, request.path))
PUBLIC_URLS = (
r'^/login/',
r'^/logout/',
r'^/register/',
r'^/forgotten/',
r'^/reset/',
r'^%s' % STATIC_URL,
r'^/jsi18n/'
)
@tomds
Copy link
Author

tomds commented Jul 2, 2012

If you want the django core tests to still pass, you'll need a separate settings file that removes the middleware and then use the --settings parameter when running tests.

@samdobson
Copy link

Thanks for sharing, this is very useful.

One suggestion - we should really return 404 if the page doesn't exist:

from django.core.urlresolvers import resolve, Resolver404

              ...
              try:
                  resolve(request.path)
                  redirect_to_login = True
              except Resolver404:
                  'not found'
              ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment