Package turbogears :: Package identity :: Module visitor

Source Code for Module turbogears.identity.visitor

  1  """The visit and identity management *plugins* are defined here.""" 
  2   
  3  __all__ = ['IdentityVisitPlugin', 'create_extension_model', 
  4      'shutdown_extension', 'start_extension'] 
  5   
  6  import base64 
  7  import logging 
  8   
  9  from cherrypy import request 
 10  from formencode.variabledecode import variable_decode, variable_encode 
 11   
 12  from turbogears import config, visit 
 13  from turbogears.identity import (create_default_provider, set_current_identity, 
 14      set_current_provider, set_login_attempted) 
 15   
 16  from turbogears.identity.exceptions import (IdentityConfigurationException, 
 17      IdentityException, IdentityFailure) 
 18   
 19   
 20  log = logging.getLogger('turbogears.identity') 
 21   
 22   
 23  # Global Visit plugin 
 24  _plugin = None 
 25   
 26   
 27  # Interface for the TurboGears extension 
 28   
29 -def start_extension():
30 """Register the IdentityVisitPlugin with the visit plugin framework. 31 32 Also sets up the configured Identity provider. 33 34 """ 35 global _plugin 36 37 # Bail out if the application hasn't enabled this extension 38 if not config.get('identity.on', False): 39 return 40 41 # Identity requires that Visit tracking be enabled 42 if not config.get('visit.on', False): 43 raise IdentityConfigurationException( 44 "Visit tracking must be enabled (via visit.on).") 45 46 # Bail out if Visit tracking plugin is already registered 47 if _plugin: 48 log.info("Identity already started") 49 return 50 51 log.info("Identity starting") 52 # Temporary until tg-admin can call create_extension_model 53 create_extension_model() 54 # Create and register the plugin for the Visit Tracking framework 55 _plugin = IdentityVisitPlugin() 56 visit.enable_visit_plugin(_plugin)
57 58
59 -def shutdown_extension():
60 """Stops the IdentityVisitPlugin.""" 61 global _plugin 62 63 # Bail out if the application hasn't enabled this extension 64 if not config.get('identity.on', False): 65 return 66 67 # Bail out if the Visit tracking plugin is already unregistered 68 if not _plugin: 69 log.info("Identity already shut down") 70 return 71 72 # Unregister the plugin for the Visit Tracking framework 73 visit.disable_visit_plugin(_plugin) 74 _plugin = None 75 log.info("Identity has been shut down.")
76 77
78 -def create_extension_model():
79 """Create the identity provider object.""" 80 provider = create_default_provider() 81 provider.create_provider_model()
82 83
84 -class IdentityVisitPlugin(object):
85 """Visit plugin tying the Identity framework to the visit management.""" 86
87 - def __init__(self):
88 log.info("Identity visit plugin initialized") 89 get = config.get 90 91 self.provider = create_default_provider() 92 93 # When retrieving identity information from the form, use the following 94 # form field names. These fields will be removed from the post data to 95 # prevent the controller from receiving unexpected fields. 96 self.user_name_field = get('identity.form.user_name', 'user_name') 97 self.password_field = get('identity.form.password', 'password') 98 self.submit_button_name = get('identity.form.submit', 'login') 99 100 # Sources for identity information and the order in which they should be 101 # checked. These terms are mapped to methods by prepending 102 # "identity_from_". 103 sources = filter(None, map(str.strip, 104 get('identity.source', 'form,http_auth,visit').split(','))) 105 if not sources: 106 raise IdentityConfigurationException( 107 "You must set some identity source (via identity.source).") 108 if 'http_auth' in sources and not get('identity.http_basic_auth'): 109 sources.remove('http_auth') 110 if 'visit' in sources and not get('visit.on'): 111 sources.remove('visit') 112 if not sources: 113 raise IdentityConfigurationException( 114 "You must activate at least one of the identity sources.") 115 self.identity_sources = list() 116 for s in sources: 117 if s: 118 try: 119 source_method = getattr(self, 'identity_from_' + s) 120 except AttributeError: 121 raise IdentityConfigurationException("Invalid " 122 "identity source: %s (check identity.source)" % s) 123 self.identity_sources.append(source_method)
124
125 - def identity_from_request(self, visit_key):
126 """Retrieve identity information from the HTTP request. 127 128 Checks first for form fields defining the identity then for a cookie. 129 If no identity is found, returns an anonymous identity. 130 131 """ 132 identity = None 133 log.debug("Retrieving identity for visit: %s", visit_key) 134 for source in self.identity_sources: 135 identity = source(visit_key) 136 if identity: 137 return identity 138 139 log.debug("No identity found") 140 # No source reported an identity 141 identity = self.provider.anonymous_identity() 142 return identity
143
144 - def decode_basic_credentials(self, credentials):
145 """Decode base64 user_name:password credentials used in Basic Auth. 146 147 Returns a list with username in element 0 and password in element 1. 148 149 """ 150 credentials = base64.decodestring(credentials.strip()) 151 try: 152 credentials = credentials.decode('utf-8') 153 except UnicodeError: 154 try: 155 credentials = credentials.decode('latin-1') 156 except UnicodeError: 157 credentials = '' 158 credentials = credentials.split(':', 1) 159 if len(credentials) < 2: 160 credentials.append('') 161 return credentials
162
163 - def identity_from_http_auth(self, visit_key):
164 """Try to get authentication data from Authorization request header. 165 166 Only HTTP basic auth is handled at the moment. 167 168 """ 169 try: 170 authorisation = request.headers['Authorization'] 171 except KeyError: 172 return None 173 174 authScheme, schemeData = authorisation.split(' ', 1) 175 # Only basic is handled at the moment 176 if authScheme.lower() != 'basic': 177 log.error("HTTP Auth is not basic") 178 return None 179 180 # decode credentials 181 user_name, password = self.decode_basic_credentials(schemeData) 182 set_login_attempted(True) 183 return self.provider.validate_identity(user_name, password, visit_key)
184
185 - def identity_from_visit(self, visit_key):
186 """Load identity from Identity provider.""" 187 return self.provider.load_identity(visit_key)
188
189 - def identity_from_form(self, visit_key):
190 """Inspect the request params to pull out identity information. 191 192 Must have fields for user name, password, and a login submit button. 193 194 Returns an identity object whose class depends on the current identity 195 provider or None if the form contained no identity information or the 196 information was incorrect. 197 198 """ 199 # only try to process credentials for login forms 200 params = request.params 201 if params.pop(self.submit_button_name, None) is None: 202 return None 203 # form data contains login credentials 204 params.pop(self.submit_button_name + '.x', None) 205 params.pop(self.submit_button_name + '.y', None) 206 user_name = params.pop(self.user_name_field, None) 207 password = params.pop(self.password_field, None) 208 if user_name is None: 209 log.error("Missing user name in login form") 210 return None 211 elif isinstance(user_name, list): 212 log.error("Multiple user names in login form") 213 return None 214 if password is None: 215 log.error("Missing password in login form") 216 return None 217 elif isinstance(password, list): 218 log.error("Multiple passwords in login form") 219 return None 220 set_login_attempted(True) 221 identity = self.provider.validate_identity( 222 user_name, password, visit_key) 223 if identity is None: 224 log.warning("The credentials specified weren't valid") 225 return None 226 return identity
227
228 - def record_request(self, visit):
229 """Authenticate request and try to associate the visit with an identity.""" 230 # This method is called by the visit plugin mechanism on each request with a visit key. 231 # default to keeping the identity filter off 232 if not config.get('identity.on', False): 233 log.debug("Identity is not enabled. Setting current identity to None") 234 set_current_identity(None) 235 return 236 if 'identity.path_info' in request.wsgi_environ: 237 # restore path_info and params after internal redirect 238 request.path_info = request.wsgi_environ.pop('identity.path_info') 239 request.params = request.wsgi_environ.pop('identity.params', {}) 240 try: 241 identity = self.identity_from_request(visit.key) 242 except IdentityException, e: 243 log.exception("Caught exception while getting identity from request") 244 errors = [str(e)] 245 raise IdentityFailure(errors) 246 247 log.debug("Identity is available...") 248 # stash the user in the thread data for this request 249 set_current_identity(identity) 250 set_current_provider(self.provider)
251