class HybridAuthenticationPolicy(): """ HybridAuthenticationPolicy. Called in the same way as other auth policies, but wraps Basic and AuthTkt. This policy also caches password lookups by remembering them in the request object. """ def __init__(self, secret, realm='Realm', hardcoded=()): """ We need to initialise variables here for both forms of auth which we're planning on using. :param secret: A hashing secret for AuthTkt, which should be generated outside the Pyhton process. :param realm: The Basic Auth realm which is probably set to eos_db. :param hardcoded: Triplets of user:password:group that should not be looked up in the database. """ self.hardcoded = {x[0]: (x[1], x[2]) for x in hardcoded} #DELETE ME #self.check = check # Password check routine passed to the constructor. #self.realm = realm # Basic Auth realm. # Now initialise both Auth Policies. AuthTkt has sha256 specified in # place of the default MD5 in order to suppress warnings about # security. self.bap = BasicAuthAuthenticationPolicy(check=self.passwordcheck, realm=realm) self.tap = AuthTktAuthenticationPolicy(secret=secret, callback=self.groupfinder, cookie_name='auth_tkt', hashalg='sha256') #Utility functions to interact with eos_db.server def groupfinder(self, username, request): """ Return the user group (just one) associated with the user. This uses a server function to check which group a user has been associated with. This provides the standard callback wanted by AuthTktAuthenticationPolicy. An alternative would be to encode the groups in the Tkt. The mapping of groups to actual capabilities is stored in views.PermissionsMap """ group = server.get_user_group(username) if group: return ["group:" + str(group)] def passwordcheck(self, login, password, request): """Password checking callback. """ hc = self.hardcoded if login in hc and hc[login][0] == password: return ['group:' + hc[login][1]] elif server.check_password(login, password): user_group = server.get_user_group(login) log.debug("Found user group %s" % user_group) return ['group:' + user_group] else: log.debug("Password chack failed for user %s" % login) return None def unauthenticated_userid(self, request): """ Return the userid parsed from the auth ticket cookie. If this does not exist, then check the basic auth header, and return that, if it exists. """ #Allow forcing the auth_tkt cookie. Helpful for JS calls. #Maybe move this to a callback so it only ever happens once? if request.headers.get('auth_tkt'): request.cookies['auth_tkt'] = request.headers['auth_tkt'] #Or, surely: return (self.tap.unauthenticated_userid(request) or self.bap.unauthenticated_userid(request)) def authenticated_userid(self, request): """ Return the Auth Ticket user ID if that exists. If not, then check for a user ID in Basic Auth. """ try: return request.cached_authenticated_userid except: #Proceed to look-up then pass #Allow forcing the auth_tkt cookie. if request.headers.get('auth_tkt'): request.cookies['auth_tkt'] = request.headers['auth_tkt'] request.cached_authenticated_userid = ( self.tap.unauthenticated_userid(request) or self.bap.unauthenticated_userid(request)) return request.cached_authenticated_userid def effective_principals(self, request): """ Returns the list of effective principles from the auth policy under which the user is currently authenticated. Auth ticket takes precedence. """ try: return request.cached_effective_principals except: #Proceed to look-up then pass #Allow forcing the auth_tkt cookie. if request.headers.get('auth_tkt'): request.cookies['auth_tkt'] = request.headers['auth_tkt'] userid = self.tap.authenticated_userid(request) if userid: request.cached_effective_principals = self.tap.effective_principals( request) else: request.cached_effective_principals = self.bap.effective_principals( request) return request.cached_effective_principals def remember(self, request, principal, **kw): """Causes the session info to be remembered by passing the appropriate AuthTkt into the response. """ # We always rememeber by creating an AuthTkt, but only if there is something to remember # and if the user was not in the hard-coded list. if principal and principal not in self.hardcoded: return self.tap.remember(request, principal, **kw) else: return () def forget(self, request): """ Forget both sessions. """ return self.bap.forget(request) + self.tap.forget(request) def get_forbidden_view(self, request): """ Fire a 401 when authentication needed. """ # FIXME - this doesn't distinguish between unauthenticated and # unauthorized. Should it? if request.headers.get('auth_tkt'): return HTTPRequestTimeout() #print ("Access Forbidden") response = HTTPUnauthorized() response.headers.extend(self.bap.forget(request)) return response
class HybridAuthenticationPolicy(): """ HybridAuthenticationPolicy. Called in the same way as other auth policies, but wraps Basic and AuthTkt. This policy also caches password lookups by remembering them in the request object. """ def __init__(self, secret, realm='Realm', hardcoded=()): """ We need to initialise variables here for both forms of auth which we're planning on using. :param secret: A hashing secret for AuthTkt, which should be generated outside the Pyhton process. :param realm: The Basic Auth realm which is probably set to eos_db. :param hardcoded: Triplets of user:password:group that should not be looked up in the database. """ self.hardcoded = { x[0]: (x[1],x[2]) for x in hardcoded } #DELETE ME #self.check = check # Password check routine passed to the constructor. #self.realm = realm # Basic Auth realm. # Now initialise both Auth Policies. AuthTkt has sha256 specified in # place of the default MD5 in order to suppress warnings about # security. self.bap = BasicAuthAuthenticationPolicy(check=self.passwordcheck, realm=realm) self.tap = AuthTktAuthenticationPolicy(secret=secret, callback=self.groupfinder, cookie_name='auth_tkt', hashalg='sha256') #Utility functions to interact with eos_db.server def groupfinder(self, username, request): """ Return the user group (just one) associated with the user. This uses a server function to check which group a user has been associated with. This provides the standard callback wanted by AuthTktAuthenticationPolicy. An alternative would be to encode the groups in the Tkt. The mapping of groups to actual capabilities is stored in views.PermissionsMap """ group = server.get_user_group(username) if group: return ["group:" + str(group)] def passwordcheck(self, login, password, request): """Password checking callback. """ hc = self.hardcoded if login in hc and hc[login][0] == password: return ['group:' + hc[login][1]] elif server.check_password(login, password): user_group = server.get_user_group(login) log.debug("Found user group %s" % user_group) return ['group:' + user_group] else: log.debug("Password chack failed for user %s" % login) return None def unauthenticated_userid(self, request): """ Return the userid parsed from the auth ticket cookie. If this does not exist, then check the basic auth header, and return that, if it exists. """ #Allow forcing the auth_tkt cookie. Helpful for JS calls. #Maybe move this to a callback so it only ever happens once? if request.headers.get('auth_tkt'): request.cookies['auth_tkt'] = request.headers['auth_tkt'] #Or, surely: return ( self.tap.unauthenticated_userid(request) or self.bap.unauthenticated_userid(request) ) def authenticated_userid(self, request): """ Return the Auth Ticket user ID if that exists. If not, then check for a user ID in Basic Auth. """ try: return request.cached_authenticated_userid except: #Proceed to look-up then pass #Allow forcing the auth_tkt cookie. if request.headers.get('auth_tkt'): request.cookies['auth_tkt'] = request.headers['auth_tkt'] request.cached_authenticated_userid = ( self.tap.unauthenticated_userid(request) or self.bap.unauthenticated_userid(request) ) return request.cached_authenticated_userid def effective_principals(self, request): """ Returns the list of effective principles from the auth policy under which the user is currently authenticated. Auth ticket takes precedence. """ try: return request.cached_effective_principals except: #Proceed to look-up then pass #Allow forcing the auth_tkt cookie. if request.headers.get('auth_tkt'): request.cookies['auth_tkt'] = request.headers['auth_tkt'] userid = self.tap.authenticated_userid(request) if userid: request.cached_effective_principals = self.tap.effective_principals(request) else: request.cached_effective_principals = self.bap.effective_principals(request) return request.cached_effective_principals def remember(self, request, principal, **kw): """Causes the session info to be remembered by passing the appropriate AuthTkt into the response. """ # We always rememeber by creating an AuthTkt, but only if there is something to remember # and if the user was not in the hard-coded list. if principal and principal not in self.hardcoded: return self.tap.remember(request, principal, **kw) else: return () def forget(self, request): """ Forget both sessions. """ return self.bap.forget(request) + self.tap.forget(request) def get_forbidden_view(self, request): """ Fire a 401 when authentication needed. """ # FIXME - this doesn't distinguish between unauthenticated and # unauthorized. Should it? if request.headers.get('auth_tkt'): return HTTPRequestTimeout() #print ("Access Forbidden") response = HTTPUnauthorized() response.headers.extend(self.bap.forget(request)) return response