| 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 | """ |
|---|
| 6 | Authentication via Multiple Methods |
|---|
| 7 | |
|---|
| 8 | In some environments, the choice of authentication method to be used |
|---|
| 9 | depends upon the environment and is not "fixed". This middleware allows |
|---|
| 10 | N authentication methods to be registered along with a goodness function |
|---|
| 11 | which determines which method should be used. The following example |
|---|
| 12 | demonstrates how to use both form and digest authentication in a server |
|---|
| 13 | stack; 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)) |
|---|
| 31 | serving on... |
|---|
| 32 | |
|---|
| 33 | """ |
|---|
| 34 | |
|---|
| 35 | class 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 | |
|---|
| 72 | middleware = MultiHandler |
|---|
| 73 | |
|---|
| 74 | __all__ = ['MultiHandler'] |
|---|
| 75 | |
|---|
| 76 | if "__main__" == __name__: |
|---|
| 77 | import doctest |
|---|
| 78 | doctest.testmod(optionflags=doctest.ELLIPSIS) |
|---|
| 79 | |
|---|