6.1.2.3 Authentication

The publisher handler provides simple ways to control access to modules and functions.

At every traversal step, the Publisher handler checks for presence of __auth__ and __access__ attributes (in this order), as well as __auth_realm__ attribute.

If __auth__ is found and it is callable, it will be called with three arguments: the Request object, a string containing the user name and a string containing the password. If the return value of __auth__ is false, then HTTP_UNAUTHORIZED is returned to the client (which will usually cause a password dialog box to appear).

If __auth__ is a dictionary, then the user name will be matched against the key and the password against the value associated with this key. If the key and password do not match, HTTP_UNAUTHORIZED is returned. Note that this requires storing passwords as clear text in source code, which is not very secure.

__auth__ can also be a constant. In this case, if it is false (i.e. None, 0, "", etc.), then HTTP_UNAUTHORIZED is returned.

If there exists an __auth_realm__ string, it will be sent to the client as Authorization Realm (this is the text that usually appears at the top of the password dialog box).

If __access__ is found and it is callable, it will be called with two arguments: the Request object and a string containing the user name. If the return value of __access__ is false, then HTTP_FORBIDDEN is returned to the client.

If __access__ is a list, then the user name will be matched against the list elements. If the user name is not in the list, HTTP_FORBIDDEN is returned.

Similarly to __auth__, __access__ can be a constant.

In the example below, only user "eggs" with password "spam" can access the hello function:

__auth_realm__ = "Members only"

def __auth__(req, user, passwd):

    if user == "eggs" and passwd == "spam" or \
       user == "joe" and passwd == "eoj":
        return 1
    else:
        return 0

def __access__(req, user):
    if user == "eggs":
        return 1
    else:
        return 0

def hello(req):
    return "hello"

Here is the same functionality, but using an alternative technique:

__auth_realm__ = "Members only"
__auth__ = {"eggs":"spam", "joe":"eoj"}
__access__ = ["eggs"]

def hello(req):
    return "hello"

Since functions cannot be assigned attributes, to protect a function, an __auth__ or __access__ function can be defined within the function, e.g.:

def sensitive(req):

    def __auth__(req, user, password):
        if user == 'spam' and password == 'eggs':
            # let them in
            return 1
        else:
            # no access
            return 0

    # something involving sensitive information
    return 'sensitive information`

Note that this technique will also work if __auth__ or __access__ is a constant, but will not work is they are a dictionary or a list.

The __auth__ and __access__ mechanisms exist independently of the standard PythonAuthenHandler. It is possible to use, for example, the handler to authenticate, then the __access__ list to verify that the authenticated user is allowed to a particular function.

NOTE: In order for mod_python to access __auth__, the module containing it must first be imported. Therefore, any module-level code will get executed during the import even if __auth__ is false. To truly protect a module from being accessed, use other authentication mechanisms, e.g. the Apache mod_auth or with a mod_python PythonAuthenHandler handler.