Index: django/contrib/auth/handlers/modpython.py
===================================================================
--- django/contrib/auth/handlers/modpython.py (revision 5377)
+++ django/contrib/auth/handlers/modpython.py (working copy)
@@ -1,52 +1,108 @@
from mod_python import apache
import os
+from django.core import signals
+from django.dispatch import dispatcher
+from django.core.handlers.base import BaseHandler
+from django.core.handlers.modpython import ModPythonRequest
+from django.contrib.auth import authenticate
+_str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes')
+
+class ModPythonAuthOptions:
+ def __init__(self, req):
+ options = req.get_options()
+ self.permission_name = options.get('DjangoPermissionName', None)
+ self.staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on"))
+ self.superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off"))
+ self.settings_module = options.get('DJANGO_SETTINGS_MODULE', None)
+
+def setup_environment(req, options):
+ """
+ mod_python fakes the environ, and thus doesn't process SetEnv. This ensures
+ any future imports relying on settings will work.
+ """
+ os.environ.update(req.subprocess_env)
+ if options.settings_module:
+ os.environ['DJANGO_SETTINGS_MODULE'] = options.settings_module
+
+def validate_user(user, options):
+ if not user:
+ return False
+ # Don't require an is_authenticated property, but if it's there then check it
+ if hasattr(user, 'is_authenticated') and not user.is_authenticated():
+ return False
+ # Don't require an is_active property, but if it's there then check it
+ if hasattr(user, 'is_active') and not user.is_active:
+ return False
+ if options.staff_only and not getattr(user, 'is_staff', None):
+ return False
+ if options.superuser_only and not getattr(user, 'is_superuser', None):
+ return False
+ # If a permission is required then user must have a has_perm function to validate
+ if options.permission_name and (not hasattr(user, 'has_perm') or not user.has_perm(self.permission_name)):
+ return False
+ return True
+
def authenhandler(req, **kwargs):
"""
- Authentication handler that checks against Django's auth database.
+ mod_python authentication handler that checks against Django's auth
+ database.
"""
+ options = ModPythonAuthOptions(req)
+ setup_environment(req, options)
- # mod_python fakes the environ, and thus doesn't process SetEnv. This fixes
- # that so that the following import works
- os.environ.update(req.subprocess_env)
+ dispatcher.send(signal=signals.request_started)
+ try:
+ # This populates req.user too, so it's important to do first
+ password = req.get_basic_auth_pw()
- # check for PythonOptions
- _str_to_bool = lambda s: s.lower() in ('1', 'true', 'on', 'yes')
+ # Get the user from any of the installed backends
+ user = authenticate(username=req.user, password=password)
- options = req.get_options()
- permission_name = options.get('DjangoPermissionName', None)
- staff_only = _str_to_bool(options.get('DjangoRequireStaffStatus', "on"))
- superuser_only = _str_to_bool(options.get('DjangoRequireSuperuserStatus', "off"))
- settings_module = options.get('DJANGO_SETTINGS_MODULE', None)
- if settings_module:
- os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
+ # Raise unauthorized if the user wasn't authenticated to bring up
+ # a password dialog box to allow the user to authenticate.
+ if not user:
+ return apache.HTTP_UNAUTHORIZED
- from django.contrib.auth.models import User
- from django import db
- db.reset_queries()
+ # Validate the user
+ if validate_user(user, options):
+ return apache.OK
+ else:
+ # mod_python docs say that HTTP_FORBIDDEN should be raised if the
+ # user authenticates but doesn't validate.
+ return apache.HTTP_FORBIDDEN
+ finally:
+ dispatcher.send(signal=signals.request_finished)
- # check that the username is valid
- kwargs = {'username': req.user, 'is_active': True}
- if staff_only:
- kwargs['is_staff'] = True
- if superuser_only:
- kwargs['is_superuser'] = True
+def accesshandler(req):
+ """
+ mod_python access handler that uses the contrib.auth framework (with
+ sessions and therefore requiring a session cookie).
+ """
+ options = ModPythonAuthOptions(req)
+ setup_environment(req, options)
+
+ # Set up middleware, now that settings works we can do it now.
+ base_handler = BaseHandler()
+ base_handler.load_middleware()
+
+ dispatcher.send(signal=signals.request_started)
try:
- try:
- user = User.objects.get(**kwargs)
- except User.DoesNotExist:
- return apache.HTTP_UNAUTHORIZED
-
- # check the password and any permission given
- if user.check_password(req.get_basic_auth_pw()):
- if permission_name:
- if user.has_perm(permission_name):
- return apache.OK
- else:
- return apache.HTTP_UNAUTHORIZED
- else:
- return apache.OK
+ request = ModPythonRequest(req)
+
+ # Apply request middleware
+ for middleware_method in base_handler._request_middleware:
+ response = middleware_method(request)
+ if response:
+ # If we get a response, we should probably stop processing any
+ # remaining request middleware.
+ break
+
+ # Validate the user
+ user = getattr(request, 'user', None)
+ if validate_user(user, options):
+ return apache.OK
else:
- return apache.HTTP_UNAUTHORIZED
+ return apache.HTTP_FORBIDDEN
finally:
- db.connection.close()
+ dispatcher.send(signal=signals.request_finished)
Index: docs/apache_auth.txt
===================================================================
--- docs/apache_auth.txt (revision 5377)
+++ docs/apache_auth.txt (working copy)
@@ -17,7 +17,7 @@
==================
To check against Django's authorization database from a Apache configuration
-file, you'll need to use mod_python's ``PythonAuthenHandler`` directive along
+file, you can either use mod_python's ``PythonAuthenHandler`` directive along
with the standard ``Auth*`` and ``Require`` directives::
@@ -29,6 +29,22 @@
PythonAuthenHandler django.contrib.auth.handlers.modpython
+... or use mod_python's ``PythonAccessHandler`` directive::
+
+
+ SetEnv DJANGO_SETTINGS_MODULE mysite.settings
+ PythonAccessHandler django.contrib.auth.handlers.modpython
+
+
+The difference between these two methods is that ``PythonAuthenHandler`` uses
+basic HTTP authentication where as ``PythonAccessHandler`` uses the built-in
+``contrib.auth`` authentication (which uses a session cookie).
+
+``PythonAuthenHandler`` will prompt the user to enter their user name and
+password (usually via a basic authentication dialog box), while
+``PythonAccessHandler`` will simply raise an Apache "Forbidden" error if the
+user is not logged in or does not have the correct authorization.
+
By default, the authentication handler will limit access to the ``/example/``
location to users marked as staff members. You can use a set of
``PythonOption`` directives to modify this behavior: