Changeset 67
- Timestamp:
- 04/18/07 15:46:07
- Files:
-
- AuthKit/branches/0.4/README.txt (modified) (1 diff)
- AuthKit/branches/0.4/authkit/authenticate/__init__.py (modified) (4 diffs)
- AuthKit/branches/0.4/authkit/authenticate/auth_tkt.py (modified) (1 diff)
- AuthKit/branches/0.4/authkit/authenticate/basic.py (modified) (4 diffs)
- AuthKit/branches/0.4/authkit/authenticate/digest.py (modified) (4 diffs)
- AuthKit/branches/0.4/authkit/authenticate/form.py (modified) (2 diffs)
- AuthKit/branches/0.4/authkit/authenticate/multi.py (modified) (5 diffs)
- AuthKit/branches/0.4/authkit/authenticate/passurl.py (modified) (2 diffs)
- AuthKit/branches/0.4/authkit/permissions.py (modified) (6 diffs)
- AuthKit/branches/0.4/authkit/users (added)
- AuthKit/branches/0.4/authkit/users.py (deleted)
- AuthKit/branches/0.4/authkit/users/__init__.py (added)
- AuthKit/branches/0.4/examples/user (added)
- AuthKit/branches/0.4/examples/user/database (added)
- AuthKit/branches/0.4/examples/user/database/README.txt (added)
- AuthKit/branches/0.4/examples/user/database/app.py (added)
- AuthKit/branches/0.4/examples/user/database/create.py (added)
- AuthKit/branches/0.4/examples/user/database/model.py (added)
- AuthKit/branches/0.4/test/test.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
AuthKit/branches/0.4/README.txt
r59 r67 3 3 4 4 WARNING: THIS CODE IS NOT PRODUCTION READY 5 6 Need Testing 7 ============ 8 9 * Intercept code with redirect, passurl and forward 5 10 6 11 Changes AuthKit/branches/0.4/authkit/authenticate/__init__.py
r66 r67 229 229 how to create your own ``Users`` objects. 230 230 """ 231 232 231 if not environ.has_key('authkit.users'): 233 232 raise no_authkit_users_in_environ 234 233 users = environ['authkit.users'] 235 if users.passwords.has_key(username.lower()) and users.passwords[username.lower()] == password: 234 if not users.user_exists(username): 235 return False 236 elif users.user_has_password(username.lower(), password): 236 237 return True 237 238 return False … … 255 256 Only required if you intend to use HTTP digest authentication. 256 257 """ 257 #import authkit.authenticate.digest258 258 if not environ.has_key('authkit.users'): 259 259 raise no_authkit_users_in_environ 260 260 users = environ['authkit.users'] 261 if users. passwords.has_key(username.lower()):262 password = users. passwords[username.lower()]261 if users.user_exists(username): 262 password = users.user(username)['password'] 263 263 return digest.digest_password(realm, username, password) 264 264 # After speaking to Clark Evans who wrote the origianl code, this is the correct thing: 265 265 return None 266 266 267 #def load_cookie_middleware(app, final, prefix):268 # """269 # Various AuthKit authenticate mathods such as ``form`` and ``passurl`` use a270 # cookie. Their cookie support is based on the271 # ``authkit.authenticate.auth_tkt`` middleware. This utility function reads272 # the cookie config options and returns the correctly configured cookie273 # middleware automatically, reducing duplication in the code.274 # """275 # from paste.deploy.converters import asbool276 # from authkit.authenticate.auth_tkt import AuthKitCookieMiddleware277 # params = {278 # 'secret':_get_value(final, 'cookie_secret', prefix),279 # }280 # for param in [281 # ['cookie_name','cookie_name'],282 # ['cookie_includeip', 'include_ip'],283 # ['cookie_signout', 'logout_path'],284 # ['cookie_enforce', 'cookie_enforce'],285 # ]:286 # if param[0] in final:287 # params[param[1]] = final[param[0]]288 # cookie_params = {}289 # if final.has_key('cookie_params'):290 # data = _get_value(final, 'cookie_params', prefix)291 # if isinstance(data, str):292 # lines = data.replace('\r\n','\n').replace('\r','\n').split('\n')293 # for line in lines:294 # if line.strip():295 # parts = line.strip().split(':')296 # name = parts[0].strip().lower()297 # if name in [298 # 'expires',299 # 'path',300 # 'comment',301 # 'domain',302 # 'max-age',303 # 'secure',304 # 'version',305 # ]:306 # cookie_params[name] = ':'.join(parts[1:]).strip()307 # else:308 # raise AuthKitConfigError('Invalid cookie parameter %r'%name)309 # elif isinstance(data, dict):310 # cookie_params = data.copy()311 # else:312 # raise AuthKitConfigError("Expected a string or dictionary for authkit.cookie_params")313 # # raise Exception(params)314 # if params.has_key('include_ip'):315 # params['include_ip'] = asbool(params['include_ip'])316 # app = AuthKitCookieMiddleware(app, cookie_params=cookie_params, **params)317 # return app318 319 267 def get_authenticate_function(app, authenticate_conf, format, prefix): 320 268 """ … … 324 272 """ 325 273 function = None 274 users = None 326 275 if len(authenticate_conf) < 1: 327 276 raise AuthKitConfigError('Expected at least one authenticate key, not %r'%authenticate_conf) … … 349 298 else: 350 299 raise Exception('Invalid format for authenticate function %r'%format) 351 return app, function 300 return app, function, users 352 301 353 302 def get_template(template_conf, prefix): AuthKit/branches/0.4/authkit/authenticate/auth_tkt.py
r61 r67 322 322 return [msg] 323 323 try: 324 log.error("Parsing ticket secret %r, cookie value %r, remote address %s", self.secret, cookie_value, remote_addr) 325 324 log.debug("Parsing ticket secret %r, cookie value %r, remote address %s", self.secret, cookie_value, remote_addr) 326 325 timestamp, userid, tokens, user_data = parse_ticket( 327 326 self.secret, cookie_value, remote_addr) AuthKit/branches/0.4/authkit/authenticate/basic.py
r59 r67 4 4 This implementation is identical to the `paste.auth.basic 5 5 <http://pythonpaste.org/module-paste.auth.basic.html>`_ implemenation. 6 7 8 Note:: If users are prompted to sign in this also seems to have the effect of 9 signing them out. 10 6 11 """ 7 12 #from paste.auth.basic import middleware … … 93 98 94 99 def __call__(self, environ, start_response): 95 result = self.authenticate(environ) 96 return result.wsgi_application(environ, start_response) 100 if environ.has_key('authkit.multi'): 101 # Shouldn't ever allow a response if this is called via the multi handler 102 authenitcation = self.authenticate.build_authentication() 103 return authenitcation.wsgi_application(environ, start_response) 104 else: 105 result = self.authenticate(environ) 106 return result.wsgi_application(environ, start_response) 97 107 98 108 middleware = AuthBasicHandler 99 109 100 110 class TryToAddUsername: 101 def __init__(self, application, realm, authfunc ):111 def __init__(self, application, realm, authfunc, users): 102 112 self.application = application 113 self.users = users 103 114 self.authenticate = AuthBasicAuthenticator(realm, authfunc) 104 115 105 116 def __call__(self, environ, start_response): 117 environ['authkit.users'] = self.users 106 118 result = self.authenticate(environ) 107 119 if isinstance(result, str): … … 121 133 ): 122 134 authenticate_conf = strip_base(auth_conf, 'authenticate.') 123 app, authfunc = get_authenticate_function(135 app, authfunc, users = get_authenticate_function( 124 136 app, 125 137 authenticate_conf, … … 132 144 app.add_method('basic', middleware, auth_conf['realm'], authfunc) 133 145 app.add_checker('basic', status_checker) 134 app = TryToAddUsername(app, auth_conf['realm'], authfunc )146 app = TryToAddUsername(app, auth_conf['realm'], authfunc, users) 135 147 return app 136 148 AuthKit/branches/0.4/authkit/authenticate/digest.py
r59 r67 4 4 This implementation is identical to the `paste.auth.digest 5 5 <http://pythonpaste.org/module-paste.auth.digest.html>`_ implemenation. 6 7 Note:: If users are prompted to sign in this also seems to have the effect of 8 signing them out. 6 9 """ 7 10 … … 178 181 179 182 def __call__(self, environ, start_response): 180 method = REQUEST_METHOD(environ) 181 fullpath = SCRIPT_NAME(environ) + PATH_INFO(environ) 182 authorization = AUTHORIZATION(environ) 183 result = self.authenticate(environ, authorization, fullpath, method) 184 return result.wsgi_application(environ, start_response) 183 if environ.has_key('authkit.multi'): 184 # Shouldn't ever allow a response if this is called via the multi handler 185 authenitcation = self.authenticate.build_authentication() 186 return authenitcation.wsgi_application(environ, start_response) 187 else: 188 method = REQUEST_METHOD(environ) 189 fullpath = SCRIPT_NAME(environ) + PATH_INFO(environ) 190 authorization = AUTHORIZATION(environ) 191 result = self.authenticate(environ, authorization, fullpath, method) 192 return result.wsgi_application(environ, start_response) 185 193 186 194 class TryToAddUsername: 187 def __init__(self, application, realm, authfunc ):195 def __init__(self, application, realm, authfunc, users): 188 196 self.application = application 197 self.users = users 189 198 self.authenticate = AuthDigestAuthenticator(realm, authfunc) 190 199 191 200 def __call__(self, environ, start_response): 201 environ['authkit.users'] = self.users 192 202 method = REQUEST_METHOD(environ) 193 203 fullpath = SCRIPT_NAME(environ) + PATH_INFO(environ) … … 213 223 ): 214 224 authenticate_conf = strip_base(auth_conf, 'authenticate.') 215 app, authfunc = get_authenticate_function(225 app, authfunc, users = get_authenticate_function( 216 226 app, 217 227 authenticate_conf, … … 224 234 app.add_method('digest', middleware, auth_conf['realm'], authfunc) 225 235 app.add_checker('digest', status_checker) 226 app = TryToAddUsername(app, auth_conf['realm'], authfunc )236 app = TryToAddUsername(app, auth_conf['realm'], authfunc, users) 227 237 return app 228 238 AuthKit/branches/0.4/authkit/authenticate/form.py
r61 r67 43 43 44 44 def __call__(self, environ, start_response): 45 # Shouldn't ever allow a response if this is called via the multi handler 45 46 username = environ.get('REMOTE_USER','') 46 if username:47 return self.application(environ, start_response)48 49 47 if 'POST' == environ['REQUEST_METHOD']: 50 48 formvars = parse_formvars(environ, include_get_vars=False) … … 85 83 template_ = template 86 84 authenticate_conf = strip_base(auth_conf, 'authenticate.') 87 app, authfunc = get_authenticate_function(85 app, authfunc, users = get_authenticate_function( 88 86 app, 89 87 authenticate_conf, AuthKit/branches/0.4/authkit/authenticate/multi.py
r59 r67 5 5 6 6 from paste.auth import multi 7 import logging 8 9 log = logging.getLogger('authkit.authenticate.multi') 7 10 8 11 class NoBindingFoundError(Exception): … … 30 33 status_.append(status) 31 34 headers_.append(headers) 32 exc_info_.append(exc_info) 35 exc_info_.append(exc_info) 36 log.debug("Status: %r, Headers: %r", status, headers) 33 37 return self.default(environ, find) 34 38 39 def logging_start_response(status, headers, exc_info=None): 40 log.debug("Matched binding returns status: %r, headers: %r, exc_info: %r", status, headers, exc_info) 41 return start_response(status, headers, exc_info) 42 35 43 def check(): 36 44 for (checker,binding) in self.predicate: 37 45 if checker(environ): 38 return binding(environ, start_response) 46 log.debug("MultMiddleware self.predicate check() returning %r", binding) 47 environ['authkit.multi'] = True 48 return binding(environ, logging_start_response) 39 49 for (checker,binding) in self.checker: 40 50 if not len(status_): … … 43 53 raise Exception('No headers were returned by the application') 44 54 if checker(environ, status_[0], headers_ and headers_[0] or []): 45 return binding(environ, start_response) 55 log.debug("MultMiddleware self.checker check() returning %r", binding) 56 environ['authkit.multi'] = True 57 return binding(environ, logging_start_response) 46 58 raise NoBindingFoundError('Check failed') 47 59 48 60 checked = False 49 61 f = [] … … 58 70 result = check() 59 71 except NoBindingFoundError, e: 72 log.debug("MutliMiddleware: No binding was found for the check") 60 73 start_response(status_[0], headers_ and headers_[0] or [], exc_info_[0]) 61 74 else: 75 # Commented out because it could create huge logs very quickly! 76 log.debug("Binding matched, returning result %r", result) 62 77 return result 63 78 f.append(data) … … 65 80 app_iter.close() 66 81 return f 67 #68 #class ChangeTo401:69 # def __init__(self, app, catch=[], exclude=[]):70 # self.app = app71 # ex = []72 # for e in exclude:73 # e_ = str(e).strip()74 # if str(e_) in ['401','*']:75 # raise AuthKitConfigError('You cannot exclude %s since this would disable the authkit middleware'%e_)76 # ex.append(e_)77 # self.catch = []78 # for c in catch:79 # ch = str(c).strip()80 # if ch not in ex:81 # self.catch.append(ch)82 #83 # def __call__(self, environ, start_response):84 # def authkit_start_response(status, headers, exc_info=None):85 # if '*' in self.catch:86 # status = '401 Please sign in'87 # else:88 # for code in self.catch:89 # if str(code) == status[:3]:90 # status = '401 Please sign in'91 # return start_response(status, headers, exc_info)92 # return self.app(environ, authkit_start_response)93 82 94 83 def status_checker(environ, status, headers): 95 if status[:3] in environ['authkit.intercept']: 84 log.debug("Status checker recieved status %r, headers %r, intecept %r", status, headers, environ['authkit.intercept']) 85 if str(status[:3]) in environ['authkit.intercept']: 86 log.debug("Status checker returns True") 96 87 return True 88 log.debug("Status checker returns False") 97 89 return False AuthKit/branches/0.4/authkit/authenticate/passurl.py
r59 r67 121 121 raise Exception("Invalid store type %r"%store) 122 122 return conn, cstore 123 124 123 125 124 class AuthOpenIDHandler: … … 176 175 177 176 def verify(self, environ, start_response): 178 # XXX This method should accept sreg options and continue with them177 # XXX This method should accept sreg options and continue with them 179 178 baseurl = self.baseurl or construct_url(environ, with_query_string=False, with_path_info=False) 180 179 params = dict(paste.request.parse_formvars(environ)) AuthKit/branches/0.4/authkit/permissions.py
r61 r67 52 52 """ 53 53 54 def check( app, environ, start_response):54 def check(self, app, environ, start_response): 55 55 return app(environ, start_response) 56 56 … … 189 189 In this implementation role names are case insensitive. 190 190 """ 191 if not environ.has_key('authkit.users'): 192 raise no_authkit_users_in_environ 191 193 if not environ.has_key('REMOTE_USER'): 192 194 if self.error: 193 195 raise self.error 194 196 raise NotAuthenticatedError('Not authenticated') 195 196 if not environ.has_key('authkit.users'):197 raise no_authkit_users_in_environ198 197 users = environ['authkit.users'] 199 200 if not users.passwords.has_key(environ['REMOTE_USER']): 198 if not users.user_exists(environ['REMOTE_USER']): 201 199 raise NotAuthorizedError('No such user') 202 203 if not users.roles.has_key(environ['REMOTE_USER']): 204 if self.roles == None: 205 return app(environ, start_response) 206 else: 207 raise NotAuthorizedError("User has no roles specified") 208 209 if not self.all: 200 # Check the groups specified when setup actually exist 201 for role in self.roles: 202 if not users.role_exists(role): 203 raise Exception("No such role %r exists"%role) 204 if self.all: 210 205 for role in self.roles: 211 if role.lower() in users.roles[environ['REMOTE_USER']]: 206 if not users.user_has_role(environ['REMOTE_USER'], role): 207 if self.error: 208 raise self.error 209 else: 210 raise NotAuthorizedError("User doesn't have the role %s"%role.lower()) 211 return app(environ, start_response) 212 else: 213 for role in self.roles: 214 if users.user_has_role(environ['REMOTE_USER'], role): 212 215 return app(environ, start_response) 213 216 if self.error: … … 215 218 else: 216 219 raise NotAuthorizedError("User doesn't have any of the specified roles") 217 else:218 for role in self.roles:219 if role.lower() not in users.roles[environ['REMOTE_USER']]:220 if self.error:221 raise self.error222 else:223 raise NotAuthorizedError("User doesn't have the role %s"%role.lower())224 return app(environ, start_response)225 226 220 227 221 class HasAuthKitGroup(RequestPermission): … … 246 240 In this implementation group names are case insensitive. 247 241 """ 242 if not environ.has_key('authkit.users'): 243 raise no_authkit_users_in_environ 248 244 if not environ.has_key('REMOTE_USER'): 249 245 if self.error: 250 246 raise self.error 251 247 raise NotAuthenticatedError('Not authenticated') 252 253 if not environ.has_key('authkit.users'):254 raise no_authkit_users_in_environ255 248 users = environ['authkit.users'] 256 if not users.passwords.has_key(environ['REMOTE_USER']): 249 # Check the groups specified when setup actually exist 250 for group in self.groups: 251 if group is not None: 252 if not users.group_exists(group): 253 raise Exception("No such group %r exists"%group) 254 255 if not users.user_exists(environ['REMOTE_USER']): 257 256 raise NotAuthorizedError('No such user') 258 if not users.groups.has_key(environ['REMOTE_USER']) and self.groups == None:259 return app(environ, start_response)260 elif self.groups == None:261 if self.error:262 raise self.error263 else:264 raise NotAuthorizedError("User is not a member of any group")265 257 for group in self.groups: 266 if users. groups[environ['REMOTE_USER']] == group.lower():258 if users.user_has_group(environ['REMOTE_USER'], group): 267 259 return app(environ, start_response) 268 260 if self.error: … … 270 262 else: 271 263 raise NotAuthorizedError("User is not a member of the specified group(s) %r"%self.groups) 272 264 273 265 class ValidAuthKitUser(UserIn): 274 266 """ … … 279 271 pass 280 272 281 def check( environ, start_response):273 def check(self, app, environ, start_response): 282 274 if not environ.has_key('authkit.users'): 283 275 raise no_authkit_users_in_environ 284 self.users = environ['authkit.users'].usernames 285 return UserIn.check(self, environ, start_response) 286 276 if not environ.has_key('REMOTE_USER'): 277 raise NotAuthenticatedError('Not Authenticated') 278 if not environ['authkit.users'].user_exists(environ['REMOTE_USER']): 279 raise NotAuthorizedError('You are not one of the users allowed to access this resource.') 280 return app(environ, start_response) AuthKit/branches/0.4/test/test.py
r66 r67 52 52 assert 'You Have Access To This Page.' in res 53 53 54 def test_intercept(): 55 # XXX Note, these tests don't test when the inclusion of a username and only test form 56 # should also test all the other methods too for correct behaviour 57 def sample_app(environ, start_response): 58 if environ.get('PATH_INFO') == '/403': 59 start_response('403 Forbidden', [('Content-type', 'text/plain')]) 60 return ['Access denied'] 61 elif environ.get('PATH_INFO') == '/401': 62 start_response('401 Unauth', [('Content-type', 'text/plain')]) 63 return ['Not Authed'] 64 elif environ.get('PATH_INFO') == '/702': 65 start_response('702 Doesnt exist', [('Content-type', 'text/plain')]) 66 return ['Access denied'] 67 elif environ.get('PATH_INFO') == '/500': 68 start_response('500 Error', [('Content-type', 'text/plain')]) 69 return ['Error'] 70 71 app = middleware( 72 sample_app, 73 setup_method='form,cookie', 74 cookie_secret='secret encryption string', 75 form_authenticate_user_data = """ 76 Username1:password1 77 username2:password2 78 """, 79 cookie_signoutpath = '/signout', 80 setup_intercept = "403, 702", 81 ) 82 res = TestApp(app).get('/403', status=401) 83 assertEqual(res.header('content-type'), 'text/html') 84 # XXX Should this keep the original status code or not? 85 assertEqual(res.full_status, '401 Unauthorized') 86 assert 'Please Sign In' in res 87 88 res = TestApp(app).get('/702', status=401) 89 assertEqual(res.header('content-type'), 'text/html') 90 # XXX Should this keep the original status code or not? 91 assertEqual(res.full_status, '401 Unauthorized') 92 assert 'Please Sign In' in res 93 94 res = TestApp(app).get('/500', status=500) 95 assertEqual(res.header('content-type'), 'text/plain') 96 assertEqual(res.full_status, '500 Error') 97 assert 'Error' in res 98 99 res = TestApp(app).get('/401', status=401) 100 assertEqual(res.header('content-type'), 'text/plain') 101 assertEqual(res.full_status, '401 Unauth') 102 assert 'Not Authed' in res 103 54 104 def test_fail(): 55 105 for app in [basic_app, digest_app]: … … 90 140 os.remove("test/mydb.db") 91 141 import test_model 92 from authkit.users import UsersFromDatabase142 from authkit.users.database import UsersFromDatabase 93 143 94 144
