1# (c) 2005 Clark C. Evans
2# This module is part of the Python Paste Project and is released under
3# the MIT License: http://www.opensource.org/licenses/mit-license.php
4# This code was written with funding by http://prometheusresearch.com
5"""
6Authentication via Multiple Methods
7
8In some environments, the choice of authentication method to be used
9depends upon the environment and is not "fixed".  This middleware allows
10N authentication methods to be registered along with a goodness function
11which determines which method should be used. The following example
12demonstrates how to use both form and digest authentication in a server
13stack; by default it uses form-based authentication unless
14``*authmeth=digest`` is specified as a query argument.
15
16>>> from paste.auth import form, cookie, digest, multi
17>>> from paste.wsgilib import dump_environ
18>>> from paste.httpserver import serve
19>>>
20>>> multi = multi.MultiHandler(dump_environ)
21>>> def authfunc(environ, realm, user):
22...     return digest.digest_password(realm, user, user)
23>>> multi.add_method('digest', digest.middleware, "Test Realm", authfunc)
24>>> multi.set_query_argument('digest')
25>>>
26>>> def authfunc(environ, username, password):
27...     return username == password
28>>> multi.add_method('form', form.middleware, authfunc)
29>>> multi.set_default('form')
30>>> serve(cookie.middleware(multi))
31serving on...
32
33"""
34
35class MultiHandler(object):
36    """
37    Multiple Authentication Handler
38
39    This middleware provides two othogonal facilities:
40
41      - a manner to register any number of authentication middlewares
42
43      - a mechanism to register predicates which cause one of the
44        registered middlewares to be used depending upon the request
45
46    If none of the predicates returns True, then the application is
47    invoked directly without middleware
48    """
49    def __init__(self, application):
50        self.application = application
51        self.default = application
52        self.binding = {}
53        self.predicate = []
54    def add_method(self, name, factory, *args, **kwargs):
55        self.binding[name] = factory(self.application, *args, **kwargs)
56    def add_predicate(self, name, checker):
57        self.predicate.append((checker, self.binding[name]))
58    def set_default(self, name):
59        """ set default authentication method """
60        self.default = self.binding[name]
61    def set_query_argument(self, name, key = '*authmeth', value = None):
62        """ choose authentication method based on a query argument """
63        lookfor = "%s=%s" % (key, value or name)
64        self.add_predicate(name,
65            lambda environ: lookfor in environ.get('QUERY_STRING',''))
66    def __call__(self, environ, start_response):
67        for (checker, binding) in self.predicate:
68            if checker(environ):
69                return binding(environ, start_response)
70        return self.default(environ, start_response)
71
72middleware = MultiHandler
73
74__all__ = ['MultiHandler']
75
76if "__main__" == __name__:
77    import doctest
78    doctest.testmod(optionflags=doctest.ELLIPSIS)
79
80