Created
July 29, 2014 12:49
-
-
Save aaugustin/c5f72ff86b1a0c7fdbc8 to your computer and use it in GitHub Desktop.
WSGI middleware that authenticates against a Django user database.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
WSGI middleware that authenticates against a Django user database. | |
DJANGO_SETTINGS_MODULE should point to a valid Django settings module. | |
In addition, the following settings are available: | |
- BASIC_AUTH_LOGIN_URL: DjangoAuth will trigger basic authentication on this | |
URL. Since browsers only propagate auth to resources on the same level or | |
below, this URL will usually be '/<something>' without a trailing slash. | |
Defaults to '/login'. | |
- BASIC_AUTH_MESSAGE: Content of the 401 error page. Defaults to | |
"Authorization required.". | |
- BASIC_AUTH_MESSAGE_TYPE: Content type of the message. Defaults to | |
'text/plain'. | |
- BASIC_AUTH_REALM: Authentication realm. Defaults to "Authenticate". | |
- BASIC_AUTH_REDIRECT_URL: DjangoAuth will redirect to this URL after login if | |
it isn't empty and to the HTTP Referer otherwise. If provided, it should be | |
an absolute URL including the domain name. Defaults to ''. | |
If the user authenticates successfully, the REMOTE_USER variable is set in the | |
WSGI environment. | |
See http://tools.ietf.org/html/rfc2617#section-2 for details on basic auth. | |
""" | |
from base64 import b64decode | |
import django | |
from django.conf import settings | |
from django.contrib.auth import authenticate | |
from django.db import close_old_connections | |
if django.VERSION[:2] >= (1, 7): | |
from django.core.handlers.wsgi import get_path_info | |
else: | |
def get_path_info(environ): | |
path_info = environ.get('PATH_INFO', '/') | |
if six.PY3: | |
path_info = path_info.encode('iso-8859-1') | |
return path_info.decode('utf-8') | |
from django.utils import six | |
if django.VERSION[:2] >= (1, 7): | |
django.setup() | |
class DjangoAuth: | |
login_url = getattr(settings, 'BASIC_AUTH_LOGIN_URL', '/login') | |
message = getattr(settings, 'BASIC_AUTH_MESSAGE', "Authorization required.") | |
message_type = getattr(settings, 'BASIC_AUTH_MESSAGE_TYPE', 'text/plain') | |
realm = getattr(settings, 'BASIC_AUTH_REALM', "Authenticate") | |
redirect_url = getattr(settings, 'BASIC_AUTH_REDIRECT_URL', '') | |
def __init__(self, application): | |
self.application = application | |
def __call__(self, environ, start_response): | |
try: | |
username = self.process_authorization(environ) | |
print(username) | |
print(get_path_info(environ)) | |
print(self.login_url) | |
if get_path_info(environ) == self.login_url: | |
if username is None: | |
start_response('401 Unauthorized', [ | |
('Content-Type', self.message_type), | |
('WWW-Authenticate', 'Basic realm="%s"' % self.realm), | |
]) | |
return [self.message] | |
else: | |
if self.redirect_url: | |
location = self.redirect_url | |
else: | |
location = environ.get('HTTP_REFERER', '/') | |
start_response('302 Found', [ | |
('Location', location) | |
]) | |
return [] | |
finally: | |
close_old_connections() | |
return self.application(environ, start_response) | |
@staticmethod | |
def process_authorization(environ): | |
# Don't override authentication information set by another component. | |
remote_user = environ.get('REMOTE_USER') | |
if remote_user is not None: | |
return | |
authorization = environ.get('HTTP_AUTHORIZATION') | |
if authorization is None: | |
return | |
if six.PY3: # because fuck you PEP 3333. | |
authorization = authorization.encode('iso-8859-1').decode('utf-8') | |
method, _, credentials = authorization.partition(' ') | |
if not method.lower() == 'basic': | |
return | |
try: | |
credentials = b64decode(credentials.strip()) | |
username, _, password = credentials.partition(':') | |
except Exception: | |
return | |
if authenticate(username=username, password=password) is None: | |
return | |
remote_user = username | |
if six.PY3: # because fuck you PEP 3333. | |
remote_user = remote_user.encode('utf-8').decode('iso-8859-1') | |
environ['REMOTE_USER'] = remote_user | |
return username |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment