1 # -*- coding: utf-8 -*-
 
   2 #############################################################################
 
   3 # from http://djangosnippets.org/snippets/243/
 
   5 from functools import wraps
 
   8 from django.http import HttpResponse
 
   9 from django.contrib.auth import authenticate, login
 
  12 def view_or_basicauth(view, request, test_func, realm="", *args, **kwargs):
 
  14     This is a helper function used by 'logged_in_or_basicauth' and
 
  15     'has_perm_or_basicauth' (deleted) that does the nitty of determining if they
 
  16     are already logged in or if they have provided proper http-authorization
 
  17     and returning the view if all goes well, otherwise responding with a 401.
 
  19     if test_func(request.user):
 
  20         # Already logged in, just return the view.
 
  22         return view(request, *args, **kwargs)
 
  24     # They are not logged in. See if they provided login credentials
 
  26     if 'HTTP_AUTHORIZATION' in request.META:
 
  27         auth = request.META['HTTP_AUTHORIZATION'].split()
 
  29             # NOTE: We are only support basic authentication for now.
 
  31             if auth[0].lower() == "basic":
 
  32                 uname, passwd = base64.b64decode(auth[1]).split(':')
 
  33                 user = authenticate(username=uname, password=passwd)
 
  38                         return view(request, *args, **kwargs)
 
  40     # Either they did not provide an authorization header or
 
  41     # something in the authorization attempt failed. Send a 401
 
  42     # back to them to ask them to authenticate.
 
  44     response = HttpResponse()
 
  45     response.status_code = 401
 
  46     response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
 
  51 def logged_in_or_basicauth(realm=""):
 
  53     A simple decorator that requires a user to be logged in. If they are not
 
  54     logged in the request is examined for a 'authorization' header.
 
  56     If the header is present it is tested for basic authentication and
 
  57     the user is logged in with the provided credentials.
 
  59     If the header is not present a http 401 is sent back to the
 
  60     requestor to provide credentials.
 
  62     The purpose of this is that in several django projects I have needed
 
  63     several specific views that need to support basic authentication, yet the
 
  64     web site as a whole used django's provided authentication.
 
  66     The uses for this are for urls that are access programmatically such as
 
  67     by rss feed readers, yet the view requires a user to be logged in. Many rss
 
  68     readers support supplying the authentication credentials via http basic
 
  69     auth (and they do NOT support a redirect to a form where they post a
 
  74     @logged_in_or_basicauth
 
  78     You can provide the name of the realm to ask for authentication within.
 
  80     def view_decorator(func):
 
  81         def wrapper(request, *args, **kwargs):
 
  82             return view_or_basicauth(func, request,
 
  83                                      lambda u: u.is_authenticated(),
 
  84                                      realm, *args, **kwargs)
 
  89 #############################################################################
 
  92 def factory_decorator(decorator):
 
  93     """ generates a decorator for a function factory class
 
  94     if A(*) == f, factory_decorator(D)(A)(*) == D(f)
 
  98         def wrapper(*args, **kwargs):
 
  99             return decorator(func(*args, **kwargs))