[mod_python] how to make fallback authentication

Graham Dumpleton grahamd at dscpl.com.au
Fri Feb 17 00:14:43 EST 2006


Deron Meranda wrote ..
> Implementing HTTP BASIC is actually pretty straight forward.
> Read http://www.faqs.org/rfcs/rfc2617.html -- you can ignore
> all the Proxy stuff if you're not trying to write a proxy server.
> The general strategy is to look for the "Authorization"
> input header.  If it's there, decode it (check that the scheme
> is "BASIC" (case-insensitive) then base64-decode the
> username:password).  Then validate the username and password
> by whatever means you want.
> 
> If there is no Authorization header, or it is not valid, then you
> need to return an HTTP 401; and also insure you also output
> an WWW-Authenticate response header.

Code for basic authentication included down below as drawn from my own
library of play stuff. Haven't had a chance to put something together to
properly show how to use it in original posters example, but can be
adapted by simply creating instance of BasicAuthentication class with
appropriate parameters and then using instance as callable object.

  realm = "Disney Land"
  userdb = { "mickey": "mouse" }

  authenticator = BasicAuthentication(realm,userdb)

  authenticator(req)

  if req.user:
    # was able to autheticate okay
    ....

Code for BasicAuthentication is then as follows. I hope it still runs, I have
not used/tested this in a while.

class BasicAuthentication:

    def __init__(self,realm,auth,access=None):
        self.__realm = realm
        self.__auth = auth
        self.__access = access

    def __call__(self,req):

        user = None
        passwd = None

        if req.headers_in.has_key('Authorization'):
            try:
                header = req.headers_in['Authorization']
                scheme,credentials = header.split(None,1)
                credentials = credentials.strip()

                scheme = scheme.lower()
                if scheme == 'basic':
                    credentials = base64.decodestring(credentials)
                    user,passwd = string.split(credentials,':',1)
                else:
                    raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
            except:
                raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST

        if self.__realm is None:
            raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR

        if not user:
            s = 'Basic realm="%s"' % self.__realm
            req.err_headers_out['WWW-Authenticate'] = s
            raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED

        if callable(self.__auth):
            result = self.__auth(req,user,passwd)
        else:
            if type(self.__auth) is types.DictionaryType:
                result = self.__auth.has_key(user) and \
                        self.__auth[user] == passwd
            else:
                result = self.__auth

        if not result:
            s = 'Basic realm="%s"' % self.__realm
            req.err_headers_out['WWW-Authenticate'] = s
            raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED

        if self.__access is not None:

            if callable(self.__access):
                result = self.__access(req,user)
            else:
                if type(self.__access) in (types.ListType,types.TupleType):
                    result = user in self.__access
                else:
                    result = self.__access

	# If access check failed, and authentication was
	# performed with actual user details being
	# checked, as opposed to any user being allowed,
	# must provide ability to reauthenticate with a
	# different user and not just return forbidden.

        if not result:
            if self.__auth is not None and (callable(self.__auth) or \
                    type(self.__auth) == types.DictionaryType):
                s = 'Basic realm="%s"' % self.__realm
                req.err_headers_out['WWW-Authenticate'] = s
                raise apache.SERVER_RETURN, apache.HTTP_UNAUTHORIZED
            else:
                raise apache.SERVER_RETURN, apache.HTTP_FORBIDDEN

        req.user = user




More information about the Mod_python mailing list