Package turbogears :: Package identity :: Module base

Source Code for Module turbogears.identity.base

  1  """The TurboGears identity management package.""" 
  2   
  3  # declare what should be exported 
  4  __all__ = ['_encrypt_password', 'create_default_provider', 
  5      'current', 'current_provider', 
  6      'encrypt_password', 'encrypt_pw_with_algorithm', 
  7      'get_identity_errors', 'get_failure_url', 
  8      'set_current_identity', 'set_current_provider', 
  9      'set_identity_errors', 'set_login_attempted', 
 10      'was_login_attempted', 'verify_identity_status'] 
 11   
 12   
 13  import logging 
 14  try: 
 15      from hashlib import md5, sha1 
 16  except ImportError: # Python < 2.5 
 17      from sha import new as sha1 
 18      from md5 import new as md5 
 19   
 20  import cherrypy 
 21  import pkg_resources 
 22  import turbogears 
 23   
 24  from turbogears.util import deprecated, request_available, load_class 
 25  from turbogears.identity.exceptions import (IdentityConfigurationException, 
 26      RequestRequiredException, IdentityManagementNotEnabledException) 
 27   
 28   
 29  log = logging.getLogger('turbogears.identity') 
 30   
 31   
32 -def create_default_provider():
33 """Create default identity provider. 34 35 Creates an identity provider according to what is found in 36 the configuration file for the current TurboGears application 37 38 Returns an identity provider instance or 39 raises an IdentityConfigurationException. 40 41 """ 42 provider_plugin = turbogears.config.get('identity.provider', 'sqlalchemy') 43 plugins = pkg_resources.iter_entry_points( 44 'turbogears.identity.provider', provider_plugin) 45 46 log.debug("Loading provider from plugin: %s", provider_plugin) 47 48 for entrypoint in plugins: 49 try: 50 provider_class = entrypoint.load() 51 except Exception, load_error: 52 raise IdentityConfigurationException( 53 "IdentityProvider plugin can't be loaded: %s\n%s" 54 % (provider_plugin, load_error)) 55 break 56 else: 57 provider_class = load_class(provider_plugin) 58 59 if not provider_class: 60 raise IdentityConfigurationException( 61 "IdentityProvider plugin missing: %s" % provider_plugin) 62 63 return provider_class()
64 65
66 -def was_login_attempted():
67 try: 68 return cherrypy.request.identity_login_attempted 69 except AttributeError: 70 return False
71 72
73 -def set_login_attempted(flag):
74 cherrypy.request.identity_login_attempted = flag
75 76
77 -def set_current_identity(identity):
78 cherrypy.request.identity = identity 79 try: 80 user_name = identity.user_name 81 except AttributeError: 82 user_name = None 83 cherrypy.request.login = cherrypy.request.user_name = user_name
84 85
86 -def set_current_provider(provider):
87 cherrypy.request.identityProvider = provider
88 89
90 -def encrypt_pw_with_algorithm(algorithm, password):
91 """Hash the given password with the specified algorithm. 92 93 Valid values for algorithm are 'md5' and 'sha1' or 'custom'. If the 94 algorithm is 'custom', the config setting 'identity.custom_encryption' 95 needs to be set to a dotted-notation path to a callable that takes 96 an unencrypted password and gives back the password hash. 97 98 All other algorithms values will be essentially a no-op. 99 100 """ 101 hashed_password = password 102 # The algorithms don't work with unicode objects, so decode first. 103 if isinstance(password, unicode): 104 password_8bit = password.encode('utf-8') 105 else: 106 password_8bit = password 107 if algorithm == 'md5': 108 hashed_password = md5(password_8bit).hexdigest() 109 elif algorithm == 'sha1': 110 hashed_password = sha1(password_8bit).hexdigest() 111 elif algorithm == 'custom': 112 custom_encryption_path = turbogears.config.get( 113 'identity.custom_encryption', None) 114 if custom_encryption_path: 115 custom_encryption = turbogears.util.load_class( 116 custom_encryption_path) 117 if custom_encryption: 118 hashed_password = custom_encryption(password_8bit) 119 # Make sure the hashed password is a unicode object at the end of the 120 # process, because SQLAlchemy _wants_ that for unicode columns. 121 if not isinstance(hashed_password, unicode): 122 hashed_password = hashed_password.decode('utf-8') 123 return hashed_password
124 125 _encrypt_password = deprecated( 126 "Use identity.encrypt_pw_with_algorithm instead." 127 )(encrypt_pw_with_algorithm) 128 129
130 -def encrypt_password(cleartext):
131 return current_provider.encrypt_password(cleartext)
132 133
134 -class IdentityWrapper(object):
135 """A wrapper class for the thread local data. 136 137 This allows developers to access the current user information via 138 turbogears.identity.current and get the identity for the current request. 139 140 """ 141
142 - def identity(self):
143 try: 144 identity = cherrypy.request.identity 145 except AttributeError: 146 identity = None 147 148 if not identity: 149 if not request_available(): 150 raise RequestRequiredException() 151 152 raise IdentityManagementNotEnabledException() 153 154 return identity
155
156 - def __getattr__(self, name):
157 """Return the named attribute of the global state.""" 158 identity = self.identity() 159 if name == '__str__': 160 return identity.__str__ 161 162 elif name == '__repr__': 163 return identity.__repr__ 164 165 else: 166 return getattr(identity, name)
167
168 - def __setattr__(self, name, value):
169 """Stash a value in the global state.""" 170 identity = self.identity() 171 setattr(identity, name, value)
172 173
174 -class ProviderWrapper(object):
175
176 - def __getattr__(self, name):
177 try: 178 provider = cherrypy.request.identityProvider 179 except AttributeError: 180 try: 181 provider = create_default_provider() 182 except Exception, exc: 183 log.warning("Could not create default identity provider: %s", exc) 184 provider = None 185 186 if provider is None: 187 if not request_available(): 188 raise RequestRequiredException() 189 190 raise IdentityManagementNotEnabledException() 191 192 return getattr(provider, name)
193 194 195 current = IdentityWrapper() 196 current_provider = ProviderWrapper() 197 198
199 -def verify_identity_status():
200 """A tool that sets response status based on identity's success or failure. 201 202 This is necessary since the status will be overridden by the result of 203 forwarding the user to the login page. 204 205 Does not override status if the login controller errors out. 206 207 """ 208 status = cherrypy.response.status 209 if not status or str(status) < '400': 210 new_status = cherrypy.request.wsgi_environ.get('identity.status') 211 if new_status: 212 cherrypy.response.status = new_status 213 auth_realm = cherrypy.request.wsgi_environ.get('identity.auth_realm') 214 if auth_realm: 215 cherrypy.response.headers['WWW-Authenticate'] = auth_realm 216 # During the finalize stage, CP stops looking at response.headers, 217 # so populate header_list as well. 218 if not cherrypy.response.header_list: 219 cherrypy.response.header_list = [] 220 cherrypy.response.header_list.append(('WWW-Authenticate', auth_realm))
221