While I was setting up the RPC4Django authenticated demo site (user = pass = rpc4django, self signed certificate), I ran into an interesting problem. There is a well documented way to use the Django auth database for HTTP basic authentication with apache/mod_python, but an authentication handler for mod_wsgi was not built into Django. After some investigation, I found the part of the mod_wsgi documentation on Django authentication. However, I was curious why a mod_python authentication handler existed in the Django code line, but no such mod_wsgi handler existed despite the fact that mod_wsgi is now the preferred method of Django production deployment.
A mod_wsgi authentication handler
This investigation led me to Django ticket #10809 which contains a patch for a mod_wsgi authentication handler. I found this interesting and I tried to install it into the demo site. At the time, I hadn’t even implemented the .wsgi authentication script and I was using an Apache htpasswd file in addition to the Django auth database. However, when I attempted to install the mod_wsgi handler, I ran into a number of issues. Firstly, mod_wsgi does not seem to be able to import a python module from the python path. Therefore, the line: WSGIAuthUserScript django.contrib.auth.handlers.modwsgi yields:
1 |
Call to fopen() failed for 'django.contrib.auth.handlers.modwsgi'. |
This led me to believe that mod_wsgi literally opens and reads the WSGIAuthUserScript. The problem with this is that DJANGO_SETTINGS_MODULE is not set before that is called and mod_wsgi does not pass environment variables that are set to the auth script. As per the documentation:
Any configuration defined by SetEnv directives is not passed in the ‘environ’ dictionary because doing so would allow users to override the configuration specified in such a way from a ‘.htaccess’ file. Configuration should as a result be placed into the script file itself.
.
This means that any attempt at a generic mod_wsgi authentication handler is moot since it cannot be configured to connect to the correct project’s auth database.
The solution
The solution is simple and not very gratifying: write an auth script specific for your application. This is what the demo site does now.
httpd.conf:
1 2 3 4 5 6 7 |
<Location "/"> AuthType Basic AuthName "Login Required" Require valid-user AuthBasicProvider wsgi WSGIAuthUserScript /path/to/auth.wsgi </Location> |
auth.wsgi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import os import sys os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings' from django.contrib.auth.models import User from django import db def check_password(environ, user, password): """ Authenticates apache/mod_wsgi against Django's auth database. """ db.reset_queries() kwargs = {'username': user, 'is_active': True} try: # checks that the username is valid try: user = User.objects.get(**kwargs) except User.DoesNotExist: return None # verifies that the password is valid for the user if user.check_password(password): return True else: return False finally: db.connection.close() |
Update (October 9, 2009)
After some discussion on Django ticket #10809 with the author of mod_wsgi, I submitted a patch that includes a Django mod_wsgi auth handler. Hopefully it gets accepted soon.
Thank you very much for posting this work around. I am using auth.wsgi, as above, to authenticate against Django to provide WebDAV access. I was previously using mod_python just to handle the WebDAV authentication but am much happier using mod_wsgi for everything. Here is the Apache config snippet.
# mod_wsgi authenticator
dav on
AuthType Basic
AuthName “example.org”
Require valid-user
AuthBasicProvider wsgi
WSGIAuthUserScript /var/local/django/webapp/apache/auth.wsgi
Hi,
Is there way to define an authorization script for mod_wsgi like mod_python Authorization handler ?
@Vishwajeet
I’m not 100% sure what you mean. Instead of using mod_python’s PythonAuthenHandler and pointing to a Django built-in module like “PythonAuthenHandler django.contrib.auth.handlers.modpython”, you have to use mod_wsgi’s WSGIAuthUserScript. However, there is no Django built-in module to point to for authentication. However, since mod_wsgi is the recommended Apache module for deploying Django in production, I submitted that ticket to add the check_password helper function into Django. In the mean time, you’ll have to roll an .wsgi script similar to the one I have above for authentication. You can put check_password into your main application .wsgi script and simple point to it with WSGIAuthUserScript.
Hi,
the suggested method is not compatible with having both open and restricted methods, I solved the problem with a tiny middleware and with the following directive in the virtual server conf:
WSGIPassAuthorization On
The middleware :
Edit: Changes to formatting made by David.
Alessandro is correct. Without a middleware or something else, RPC4Django will not support some authenticated methods out of the box while others can be used by unauthenticated users. It’s currently all or nothing.
There is currently a blueprint regarding adding a sort of out of the box authentication which would solve this but I have not had time to work on it.
I also want to talk about security in the next release. HTTP basic authentication really should not be used without SSL/TLS for maximum security. The new out of the box authentication will not solve the security issue. Unless you don’t care about packet sniffing, passwords should be encrypted.