diff -Naur mod_python-3.3.1/lib/python/mod_python/apache.py mod_python-3.3.1-new/lib/python/mod_python/apache.py
--- mod_python-3.3.1/lib/python/mod_python/apache.py	2006-10-26 19:54:12.000000000 -0500
+++ mod_python-3.3.1-new/lib/python/mod_python/apache.py	2007-04-10 08:48:31.000000000 -0500
@@ -294,6 +294,240 @@
 
         return OK
 
+    def AuthBasicDispatch(self, req, user, password):
+        """
+        This is the basic authentication dispatcher
+        """
+
+        result = AUTH_GENERAL_ERROR
+
+        # config
+        config = req.get_config()
+        debug = int(config.get("PythonDebug", 0))
+
+        try:
+            hlist = req.hlist
+
+            while hlist.handler is not None:
+
+                # split module::handler
+                l = hlist.handler.split('::', 1)
+
+                module_name = l[0]
+                if len(l) == 1:
+                    # no oject, provide default
+                    object_str = "auth_basic_provider"
+                else:
+                    object_str = l[1]
+
+                # add the directory to pythonpath if
+                # not there yet, or pythonpath specified
+                if config.has_key("PythonPath"):
+                    # we want to do as little evaling as possible,
+                    # so we remember the path in un-evaled form and
+                    # compare it
+                    global _path
+                    pathstring = config["PythonPath"]
+                    if pathstring != _path:
+                        _path = pathstring
+                        newpath = eval(pathstring)
+                        if sys.path != newpath:
+                            sys.path[:] = newpath
+                else:
+                    dir = hlist.directory
+                    if dir and (dir not in sys.path):
+                        sys.path[:0] = [dir]
+
+                # import module
+                module = import_module(module_name,
+                                       autoreload=int(config.get("PythonAutoReload", 1)),
+                                       log=debug)
+
+                # find the object
+                object = resolve_object(module, object_str,
+                                        arg=req, silent=hlist.silent)
+
+                if object:
+
+                    # call the object
+                    if config.has_key("PythonEnablePdb"):
+                        result = pdb.runcall(object, req, user, password)
+                    else:
+                        result = object(req, user, password)
+
+                    assert (type(result) == type(int())), \
+                           "Handler '%s' returned invalid return code." % hlist.handler
+
+                    # stop cycling through handlers
+                    if result != OK:
+                        break
+
+                elif hlist.silent:
+                    # A faulty handler marked as silent will only 
+                    # propagate DECLINED if it is the first and only handler.
+                    if result != OK:
+                        result = DECLINED
+
+                hlist.next()
+
+        except SERVER_RETURN, value:
+            # SERVER_RETURN indicates an abort from below
+            # with value as (result, status) or (result, None) or result
+            try:
+                if len(value.args) == 2:
+                    (result, status) = value.args
+                    if status:
+                        req.status = status
+                else:
+                    result = value.args[0]
+
+                if type(result) != type(7):
+                    s = "Value raised with SERVER_RETURN is invalid. It is a "
+                    s = s + "%s, but it must be a tuple or an int." % type(result)
+                    _apache.log_error(s, APLOG_NOERRNO|APLOG_ERR, req.server)
+                    return HTTP_INTERNAL_SERVER_ERROR
+
+            except:
+                pass
+
+        except PROG_TRACEBACK, traceblock:
+            # Program run-time error
+            try:
+                (etype, value, traceback) = traceblock
+                result = self.ReportError(etype, value, traceback, req=req,
+                                          phase=req.phase, hname=hlist.handler,
+                                          debug=debug)
+            finally:
+                traceback = None
+
+        except:
+            # Any other rerror (usually parsing)
+            try:
+                exc_type, exc_value, exc_traceback = sys.exc_info()
+                result = self.ReportError(exc_type, exc_value, exc_traceback, req=req,
+                                          phase=req.phase, hname=hlist.handler, debug=debug)
+            finally:
+                exc_traceback = None
+
+        return result
+
+    def AuthDigestDispatch(self, req, user, realm):
+        """
+        This is the digest authentication dispatcher
+        """
+
+        result = AUTH_GENERAL_ERROR
+
+        # config
+        config = req.get_config()
+        debug = int(config.get("PythonDebug", 0))
+
+        try:
+            hlist = req.hlist
+
+            while hlist.handler is not None:
+
+                # split module::handler
+                l = hlist.handler.split('::', 1)
+
+                module_name = l[0]
+                if len(l) == 1:
+                    # no oject, provide default
+                    object_str = "auth_digest_provider"
+                else:
+                    object_str = l[1]
+
+                # add the directory to pythonpath if
+                # not there yet, or pythonpath specified
+                if config.has_key("PythonPath"):
+                    # we want to do as little evaling as possible,
+                    # so we remember the path in un-evaled form and
+                    # compare it
+                    global _path
+                    pathstring = config["PythonPath"]
+                    if pathstring != _path:
+                        _path = pathstring
+                        newpath = eval(pathstring)
+                        if sys.path != newpath:
+                            sys.path[:] = newpath
+                else:
+                    dir = hlist.directory
+                    if dir and (dir not in sys.path):
+                        sys.path[:0] = [dir]
+
+                # import module
+                module = import_module(module_name,
+                                       autoreload=int(config.get("PythonAutoReload", 1)),
+                                       log=debug)
+
+                # find the object
+                object = resolve_object(module, object_str,
+                                        arg=req, silent=hlist.silent)
+
+                if object:
+
+                    # call the object
+                    if config.has_key("PythonEnablePdb"):
+                        result = pdb.runcall(object, req, user, realm)
+                    else:
+                        result = object(req, user, realm)
+
+                    assert ((None == result) or (type(result) == type(str()))), \
+                           "Handler '%s' returned invalid return code type." % hlist.handler
+
+                    # stop cycling through handlers
+                    if result != OK:
+                        break
+
+                elif hlist.silent:
+                    # A faulty handler marked as silent will only 
+                    # propagate DECLINED if it is the first and only handler.
+                    if result != OK:
+                        result = DECLINED
+
+                hlist.next()
+
+        except SERVER_RETURN, value:
+            # SERVER_RETURN indicates an abort from below
+            # with value as (result, status) or (result, None) or result
+            try:
+                if len(value.args) == 2:
+                    (result, status) = value.args
+                    if status:
+                        req.status = status
+                else:
+                    result = value.args[0]
+
+                if type(result) != type(7):
+                    s = "Value raised with SERVER_RETURN is invalid. It is a "
+                    s = s + "%s, but it must be a tuple or an int." % type(result)
+                    _apache.log_error(s, APLOG_NOERRNO|APLOG_ERR, req.server)
+                    return HTTP_INTERNAL_SERVER_ERROR
+
+            except:
+                pass
+
+        except PROG_TRACEBACK, traceblock:
+            # Program run-time error
+            try:
+                (etype, value, traceback) = traceblock
+                result = self.ReportError(etype, value, traceback, req=req,
+                                          phase=req.phase, hname=hlist.handler,
+                                          debug=debug)
+            finally:
+                traceback = None
+
+        except:
+            # Any other rerror (usually parsing)
+            try:
+                exc_type, exc_value, exc_traceback = sys.exc_info()
+                result = self.ReportError(exc_type, exc_value, exc_traceback, req=req,
+                                          phase=req.phase, hname=hlist.handler, debug=debug)
+            finally:
+                exc_traceback = None
+
+        return result
+
     def HandlerDispatch(self, req):
         """
         This is the handler dispatcher.
@@ -1000,6 +1234,13 @@
 HTTP_INSUFFICIENT_STORAGE         = 507
 HTTP_NOT_EXTENDED                 = 510
 
+# More constants, these for authentication modules
+AUTH_DENIED         = 0
+AUTH_GRANTED        = 1
+AUTH_USER_FOUND     = 2
+AUTH_USER_NOT_FOUND = 3
+AUTH_GENERAL_ERROR  = 4
+
 # The APLOG constants in Apache are derived from syslog.h
 # constants, so we do same here.
 
diff -Naur mod_python-3.3.1/src/include/mod_python.h mod_python-3.3.1-new/src/include/mod_python.h
--- mod_python-3.3.1/src/include/mod_python.h	2006-10-23 22:41:54.000000000 -0500
+++ mod_python-3.3.1-new/src/include/mod_python.h	2007-04-11 22:19:55.000000000 -0500
@@ -21,7 +21,7 @@
  *
  * mod_python.h 
  *
- * $Id: mod_python.h 467228 2006-10-24 03:41:54Z grahamd $
+ * $Id: mod_python.h 231054 2005-08-09 15:37:04Z jgallacher $
  *
  * See accompanying documentation and source code comments 
  * for details.
@@ -58,6 +58,7 @@
 #include "ap_mpm.h"
 #include "ap_mmn.h"
 #include "mod_include.h"
+#include "mod_auth.h"
 #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
 #include "unixd.h"
 #endif
@@ -77,6 +78,9 @@
 #if defined(_POSIX_THREADS)
 #undef _POSIX_THREADS
 #endif
+#if defined(_POSIX_C_SOURCE)
+#undef _POSIX_C_SOURCE
+#endif
 #include "Python.h"
 #include "structmember.h"
 
@@ -160,6 +164,8 @@
     apr_hash_t   *in_filters;
     apr_hash_t   *out_filters;
     apr_table_t  *imports;  /* for PythonImport */
+    hl_entry	 *password_handler;
+    hl_entry     *digest_handler;
 } py_config;
 
 /* register_cleanup info */
diff -Naur mod_python-3.3.1/src/include/mod_python.h.in mod_python-3.3.1-new/src/include/mod_python.h.in
--- mod_python-3.3.1/src/include/mod_python.h.in	2006-10-23 22:41:54.000000000 -0500
+++ mod_python-3.3.1-new/src/include/mod_python.h.in	2007-04-10 14:35:01.000000000 -0500
@@ -58,6 +58,7 @@
 #include "ap_mpm.h"
 #include "ap_mmn.h"
 #include "mod_include.h"
+#include "mod_auth.h"
 #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
 #include "unixd.h"
 #endif
@@ -77,6 +78,9 @@
 #if defined(_POSIX_THREADS)
 #undef _POSIX_THREADS
 #endif
+#if defined(_POSIX_C_SOURCE)
+#undef _POSIX_C_SOURCE
+#endif
 #include "Python.h"
 #include "structmember.h"
 
@@ -160,6 +164,8 @@
     apr_hash_t   *in_filters;
     apr_hash_t   *out_filters;
     apr_table_t  *imports;  /* for PythonImport */
+    hl_entry	 *password_handler;
+    hl_entry     *digest_handler;
 } py_config;
 
 /* register_cleanup info */
diff -Naur mod_python-3.3.1/src/mod_python.c mod_python-3.3.1-new/src/mod_python.c
--- mod_python-3.3.1/src/mod_python.c	2006-11-09 00:21:23.000000000 -0600
+++ mod_python-3.3.1-new/src/mod_python.c	2007-04-10 22:24:17.000000000 -0500
@@ -961,6 +961,9 @@
         apr_hash_set(merged_conf->out_filters, key, klen, (void *)fh);
     }
 
+    merged_conf->password_handler = cc->password_handler;
+    merged_conf->digest_handler = cc->digest_handler;
+
     /** copy new **/
 
     if (nc->authoritative != merged_conf->authoritative)
@@ -983,6 +986,9 @@
         apr_hash_set(merged_conf->out_filters, key, klen, (void *)fh);
     }
 
+    merged_conf->password_handler = nc->password_handler;
+    merged_conf->digest_handler = nc->digest_handler;
+
     return (void *) merged_conf;
 }
 
@@ -2696,6 +2702,32 @@
     return NULL;
 }
 
+static const char *directive_PythonAuthBasicProvider(cmd_parms *cmd, void *mconfig,
+						     const char *val) {
+/*
+ * At this point, I've got the name of the module or module::method in val
+ * I need to build an hl_entry value from this and put it in the configuration
+ */
+    py_config *conf;
+    conf = (py_config *) mconfig;
+    conf->password_handler = hlist_new(cmd->pool, val, 0, conf->config_dir, 0, 0, 0, 0, 0, NOTSILENT, 0);
+
+    return NULL;
+}
+
+static const char *directive_PythonAuthDigestProvider(cmd_parms *cmd, void *mconfig, 
+						     const char *val) {
+/*
+ * At this point, I've got the name of the modules or module::method in val
+ * I need to build an hl_entry value from this and put it in the configuration
+ */
+    py_config *conf;
+    conf = mconfig;
+    conf->digest_handler = hlist_new(cmd->pool, val, 0, conf->config_dir, 0, 0, 0, 0, 0, NOTSILENT, 0);
+
+    return NULL;
+}
+
 /**
  ** python_finalize
  **
@@ -2909,6 +2941,176 @@
     return python_handler(req, "PythonTypeHandler");
 }
 
+
+static authn_status python_handle_authn(request_rec *req, const char *phase,
+					const char *user, const char *password,
+					const char *realm, char **rethash)
+{
+    PyObject *resultobject = NULL;
+    interpreterdata *idata;
+    requestobject *request_obj;
+    py_config * conf;
+    int result = AUTH_GENERAL_ERROR;
+    const char *interp_name = NULL;
+    char *ext = NULL;
+    hl_entry *hle = NULL;
+
+    conf = (py_config *) ap_get_module_config(req->per_dir_config, 
+                                                  &python_module);
+    /* get file extension */
+    if (req->filename) {        /* filename is null until after transhandler */
+        /* get rid of preceeding path */
+        if ((ext = (char *)ap_strrchr_c(req->filename, '/')) == NULL)
+            ext = req->filename;
+        else 
+            ++ext;
+        /* get extension */
+        ap_getword(req->pool, (const char **)&ext, '.');
+        if (*ext != '\0')
+            ext = apr_pstrcat(req->pool, ".", ext, NULL);
+    }
+
+    if (NULL == realm) {
+	hle = conf->password_handler;
+    }
+    else {
+	hle = conf->digest_handler;
+    }
+    if (hle) {
+	/* determine interpreter to use */
+	interp_name = select_interp_name(req, NULL, conf, hle, NULL);
+
+	/* get/create interpreter */
+	idata = get_interpreter(interp_name);
+
+	if (!idata) {
+	    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req,
+			  "python_handle_authn: Can't get/create interpreter.");
+	    return AUTH_GENERAL_ERROR;
+	}
+
+	/* create/acquire request object */
+	request_obj = python_get_request_object(req, phase);
+
+	if (!request_obj) {
+	    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req,
+			  "python_handle_authn: request_obj is NULL.");
+	    return AUTH_DENIED;
+	}
+	
+	/* remember the extension if any. used by publisher */
+	if (ext) 
+	    request_obj->extension = apr_pstrdup(req->pool, ext);
+
+        /* create a handler list object */
+        request_obj->hlo = (hlistobject *)MpHList_FromHLEntry(hle);
+
+	/* 
+	 * Here is where we call into Python!
+	 * This is the C equivalent of
+	 * >>> resultobject = obCallBack.Dispatch(request_object, phase)
+	 */
+	if (NULL == realm) {
+	    resultobject = PyObject_CallMethod(idata->obcallback, "AuthBasicDispatch", "Oss", 
+					       request_obj, user, password);
+     
+	    /* release the lock and destroy tstate*/
+	    release_interpreter();
+
+	    if (! resultobject) {
+		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req, 
+			      "python_handle_authn: Dispatch() returned nothing.");
+		return AUTH_GENERAL_ERROR;
+	    }
+	    else {
+		/* Attempt to analyze the result as a string indicating which
+		   result to return */
+		if (! PyInt_Check(resultobject)) {
+		    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req, 
+		      "python_handle_authn: Dispatch() returned non-integer value.");
+		    return AUTH_GENERAL_ERROR;
+		}
+		else {
+		    result = PyInt_AsLong(resultobject);
+		}
+	    }
+	}
+	else {
+	    resultobject = PyObject_CallMethod(idata->obcallback, "AuthDigestDispatch", "Oss", 
+					       request_obj, user, realm);
+     
+	    /* release the lock and destroy tstate*/
+	    release_interpreter();
+
+	    if (! resultobject) {
+		ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req, 
+			      "python_check_realm_hash: Dispatch() returned nothing.");
+		return AUTH_GENERAL_ERROR;
+	    }
+	    else {
+		/* Here's where this function differs from the other dispatch functions */
+		/* The return value should be a string, not an integer, which should hold the */
+		/* password hash if the user is found (and this function should, therefore, return */
+		/* apache.AUTH_USER_FOUND) or None if the user is not found (and this function */
+		/* therefore, return apache.AUTH_USER_NOT_FOUND */
+		if (Py_None == resultobject) {
+		    result = AUTH_USER_NOT_FOUND;
+		}
+		else {
+		    if (! PyInt_Check(resultobject)) {
+		        if (! PyString_Check(resultobject)) {
+			    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req, 
+				          "python_handle_authn: Dispatch() returned non-string value.");
+			    return AUTH_GENERAL_ERROR;
+		        }
+		        else {
+			    *rethash = apr_pstrdup(req->pool, PyString_AsString(resultobject));
+			    result = AUTH_USER_FOUND;
+		        }
+		    }
+		    else {
+		        result = PyInt_AsLong(resultobject);
+			if (AUTH_USER_NOT_FOUND != result) {
+			    /*
+		             * It should only get here if there's an exception and the only valid value
+			     * at this point is AUTH_USER_NOT_FOUND, so if the value coming back is not
+			     * that, then there's an error 
+			     */
+			    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req, 
+			 	          "python_check_realm_hash: Dispatch() returned invalid value.");
+	  		    result = AUTH_GENERAL_ERROR;
+			}
+		    }
+		}
+	    }
+	}
+    }
+    else {
+	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, req, 
+				  "python_handle_authn: hle is NULL");
+    }
+    return result;
+}
+
+static authn_status python_check_password(request_rec *req, const char *user, const char *password)
+{
+    return python_handle_authn(req, "PythonAuthBasicProvider", user, password, NULL, NULL);
+}
+
+
+static authn_status python_check_realm_hash(request_rec *req, const char *user,
+					    const char *realm, char **rethash)
+{
+    return python_handle_authn(req, "PythonAuthDigestProvider", user, NULL, realm, rethash);
+}
+
+
+static const authn_provider authn_mod_python_provider =
+{
+    &python_check_password,
+    &python_check_realm_hash,
+};
+
 static void python_register_hooks(apr_pool_t *p)
 {
 
@@ -2973,6 +3175,9 @@
     ap_hook_child_init(PythonChildInitHandler,
                        NULL, NULL, APR_HOOK_MIDDLE);
 
+    /* authentication provider */
+    ap_register_provider(p, AUTHN_PROVIDER_GROUP, "mod_python", "0",
+			 &authn_mod_python_provider);
 }
 
 /* command table */
@@ -3057,6 +3262,12 @@
     AP_INIT_TAKE12(
         "PythonOutputFilter", directive_PythonOutputFilter, NULL, RSRC_CONF|ACCESS_CONF,
         "Python output filter."),
+    AP_INIT_TAKE1(
+        "PythonAuthBasicProvider", directive_PythonAuthBasicProvider, NULL, OR_ALL,
+        "A Python module containing a basic authentication provider."),
+    AP_INIT_TAKE1(
+        "PythonAuthDigestProvider", directive_PythonAuthDigestProvider, NULL, OR_ALL,
+        "A Python module containing a digest authentication provider."),
     {NULL}
 };
 
