| 1 |
''' |
|---|
| 2 |
Django middleware for HTTP authentication. |
|---|
| 3 |
|
|---|
| 4 |
Copyright (c) 2007, Accense Technology, Inc. |
|---|
| 5 |
|
|---|
| 6 |
All rights reserved. |
|---|
| 7 |
|
|---|
| 8 |
Redistribution and use in source and binary forms, with or without |
|---|
| 9 |
modification, are permitted provided that the following conditions are met: |
|---|
| 10 |
|
|---|
| 11 |
* Redistributions of source code must retain the above copyright notice, |
|---|
| 12 |
this list of conditions and the following disclaimer. |
|---|
| 13 |
* Redistributions in binary form must reproduce the above copyright notice, |
|---|
| 14 |
this list of conditions and the following disclaimer in the documentation |
|---|
| 15 |
and/or other materials provided with the distribution. |
|---|
| 16 |
* Neither the name of the Accense Technology nor the names of its |
|---|
| 17 |
contributors may be used to endorse or promote products derived from |
|---|
| 18 |
this software without specific prior written permission. |
|---|
| 19 |
|
|---|
| 20 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 21 |
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 22 |
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|---|
| 23 |
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|---|
| 24 |
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|---|
| 25 |
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|---|
| 26 |
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|---|
| 27 |
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|---|
| 28 |
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|---|
| 29 |
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|---|
| 30 |
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 31 |
''' |
|---|
| 32 |
|
|---|
| 33 |
from django.contrib.auth.models import User, AnonymousUser |
|---|
| 34 |
from django.http import HttpResponse |
|---|
| 35 |
import base64 |
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
class ByHttpServerMiddleware(object): |
|---|
| 44 |
''' |
|---|
| 45 |
Reflect the authentication result by HTTP server which hosts Django. |
|---|
| 46 |
|
|---|
| 47 |
This middleware must be placed in the 'settings.py' MIDDLEWARE_CLASSES |
|---|
| 48 |
definition before the above Django builtin 'AuthenticationMiddleware'. |
|---|
| 49 |
You can use the ordinaly '@login_required' decorator to restrict views |
|---|
| 50 |
to authenticated users. Set the 'settings.py' LOGIN_URL definition |
|---|
| 51 |
appropriately if required. |
|---|
| 52 |
''' |
|---|
| 53 |
|
|---|
| 54 |
def process_request(self, request): |
|---|
| 55 |
if hasattr(request, '_cached_user'): |
|---|
| 56 |
return None |
|---|
| 57 |
try: |
|---|
| 58 |
username = request.META['REMOTE_USER'] |
|---|
| 59 |
user = User.objects.get(username=username) |
|---|
| 60 |
except (KeyError, User.DoesNotExist): |
|---|
| 61 |
|
|---|
| 62 |
return None |
|---|
| 63 |
request._cached_user = user |
|---|
| 64 |
return None |
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
class Middleware(object): |
|---|
| 68 |
''' |
|---|
| 69 |
Django implementation of the HTTP basic authentication. |
|---|
| 70 |
|
|---|
| 71 |
This middleware must be placed in the 'settings.py' MIDDLEWARE_CLASSES |
|---|
| 72 |
definition before the above Django builtin 'AuthenticationMiddleware'. |
|---|
| 73 |
Set the 'settings.py' LOGIN_URL definition appropriately if required. |
|---|
| 74 |
|
|---|
| 75 |
To show the browser generated login dialog to user, you have to use the |
|---|
| 76 |
following '@http_login_required(realm=realm)' decorator instead of the |
|---|
| 77 |
ordinaly '@login_required' decorator. |
|---|
| 78 |
''' |
|---|
| 79 |
def process_request(self, request): |
|---|
| 80 |
if hasattr(request, '_cached_user'): |
|---|
| 81 |
return None |
|---|
| 82 |
try: |
|---|
| 83 |
(method, encoded) = request.META['HTTP_AUTHORIZATION'].split() |
|---|
| 84 |
if method.lower() != 'basic': |
|---|
| 85 |
return None |
|---|
| 86 |
(username, password) = base64.b64decode(encoded).split(':') |
|---|
| 87 |
user = User.objects.get(username=username, is_active=True) |
|---|
| 88 |
if not user.check_password(password): |
|---|
| 89 |
user = AnonymousUser() |
|---|
| 90 |
except (KeyError, TypeError, User.DoesNotExist): |
|---|
| 91 |
|
|---|
| 92 |
return None |
|---|
| 93 |
request._cached_user = user |
|---|
| 94 |
return None |
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
def http_login_required(realm=None): |
|---|
| 98 |
''' |
|---|
| 99 |
Decorator factory to restrict views to authenticated user and show the |
|---|
| 100 |
browser generated login dialog if the user is not authenticated. |
|---|
| 101 |
|
|---|
| 102 |
This is the function that returns a decorator. To use the decorator, |
|---|
| 103 |
use '@http_login_required()' or '@http_login_required(realm='...')'. |
|---|
| 104 |
''' |
|---|
| 105 |
def decorator(func): |
|---|
| 106 |
def handler(request, *args, **kw): |
|---|
| 107 |
if request.user.is_authenticated(): |
|---|
| 108 |
return func(request, *args, **kw) |
|---|
| 109 |
response = HttpResponse(status=401) |
|---|
| 110 |
response['WWW-Authenticate'] = 'Basic realm="%s"' % (realm or 'Django') |
|---|
| 111 |
return response |
|---|
| 112 |
return handler |
|---|
| 113 |
return decorator |
|---|