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 Basic HTTP/1.0 Authentication 7 8 This module implements ``Basic`` authentication as described in 9 HTTP/1.0 specification [1]_ . Do not use this module unless you 10 are using SSL or need to work with very out-dated clients, instead 11 use ``digest`` authentication. 12 13 >>> from paste.wsgilib import dump_environ 14 >>> from paste.httpserver import serve 15 >>> # from paste.auth.basic import AuthBasicHandler 16 >>> realm = 'Test Realm' 17 >>> def authfunc(environ, username, password): 18 ... return username == password 19 >>> serve(AuthBasicHandler(dump_environ, realm, authfunc)) 20 serving on... 21 22 .. [1] http://www.w3.org/Protocols/HTTP/1.0/draft-ietf-http-spec.html#BasicAA 23 """ 24 from paste.httpexceptions import HTTPUnauthorized 25 from paste.httpheaders import * 26 27 class AuthBasicAuthenticator(object): 28 """ 29 implements ``Basic`` authentication details 30 """ 31 type = 'basic' 32 def __init__(self, realm, authfunc): 33 self.realm = realm 34 self.authfunc = authfunc 35 36 def build_authentication(self): 37 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) 38 return HTTPUnauthorized(headers=head) 39 40 def authenticate(self, environ): 41 authorization = AUTHORIZATION(environ) 42 if not authorization: 43 return self.build_authentication() 44 (authmeth, auth) = authorization.split(' ', 1) 45 if 'basic' != authmeth.lower(): 46 return self.build_authentication() 47 auth = auth.strip().decode('base64') 48 username, password = auth.split(':', 1) 49 if self.authfunc(environ, username, password): 50 return username 51 return self.build_authentication() 52 53 __call__ = authenticate 54 55 class AuthBasicHandler(object): 56 """ 57 HTTP/1.0 ``Basic`` authentication middleware 58 59 Parameters: 60 61 ``application`` 62 63 The application object is called only upon successful 64 authentication, and can assume ``environ['REMOTE_USER']`` 65 is set. If the ``REMOTE_USER`` is already set, this 66 middleware is simply pass-through. 67 68 ``realm`` 69 70 This is a identifier for the authority that is requesting 71 authorization. It is shown to the user and should be unique 72 within the domain it is being used. 73 74 ``authfunc`` 75 76 This is a mandatory user-defined function which takes a 77 ``environ``, ``username`` and ``password`` for its first 78 three arguments. It should return ``True`` if the user is 79 authenticated. 80 81 """ 82 def __init__(self, application, realm, authfunc): 83 self.application = application 84 self.authenticate = AuthBasicAuthenticator(realm, authfunc) 85 86 def __call__(self, environ, start_response): 87 username = REMOTE_USER(environ) 88 if not username: 89 result = self.authenticate(environ) 90 if isinstance(result, str): 91 AUTH_TYPE.update(environ, 'basic') 92 REMOTE_USER.update(environ, result) 93 else: 94 return result.wsgi_application(environ, start_response) 95 return self.application(environ, start_response) 96 97 middleware = AuthBasicHandler 98 99 __all__ = ['AuthBasicHandler'] 100 101 def make_basic(app, global_conf, realm, authfunc, **kw): 102 """ 103 Grant access via basic authentication 104 105 Config looks like this:: 106 107 [filter:grant] 108 use = egg:Paste#auth_basic 109 realm=myrealm 110 authfunc=somepackage.somemodule:somefunction 111 112 """ 113 from paste.util.import_string import eval_import 114 import types 115 authfunc = eval_import(authfunc) 116 assert isinstance(authfunc, types.FunctionType), "authfunc must resolve to a function" 117 return AuthBasicHandler(app, realm, authfunc) 118 119 120 if "__main__" == __name__: 121 import doctest 122 doctest.testmod(optionflags=doctest.ELLIPSIS) 123