Package web :: Package web :: Module session
[hide private]
[frames] | no frames]

Source Code for Module web.web.session

  1  """ 
  2  Session Management 
  3  (from web.py) 
  4  """ 
  5   
  6  import os, time, datetime, random, base64 
  7  try: 
  8      import cPickle as pickle 
  9  except ImportError: 
 10      import pickle 
 11  try: 
 12      import hashlib 
 13      sha1 = hashlib.sha1 
 14  except ImportError: 
 15      import sha 
 16      sha1 = sha.new 
 17   
 18  import utils 
 19  import webapi as web 
 20   
 21  __all__ = [ 
 22      'Session', 'SessionExpired', 
 23      'Store', 'DiskStore', 'DBStore', 
 24  ] 
 25   
 26  web.config.session_parameters = utils.storage({ 
 27      'cookie_name': 'webpy_session_id', 
 28      'cookie_domain': None, 
 29      'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds 
 30      'ignore_expiry': True, 
 31      'ignore_change_ip': True, 
 32      'secret_key': 'fLjUfxqXtfNoIldA0A0J', 
 33      'expired_message': 'Session expired', 
 34  }) 
 35   
36 -class SessionExpired(web.HTTPError):
37 - def __init__(self, message):
38 web.HTTPError.__init__(self, '200 OK', {}, data=message)
39
40 -class Session(utils.ThreadedDict):
41 """Session management for web.py 42 """ 43
44 - def __init__(self, app, store, initializer=None):
45 self.__dict__['store'] = store 46 self.__dict__['_initializer'] = initializer 47 self.__dict__['_last_cleanup_time'] = 0 48 self.__dict__['_config'] = utils.storage(web.config.session_parameters) 49 50 if app: 51 app.add_processor(self._processor)
52
53 - def _processor(self, handler):
54 """Application processor to setup session for every request""" 55 self._cleanup() 56 self._load() 57 58 try: 59 return handler() 60 finally: 61 self._save()
62
63 - def _load(self):
64 """Load the session from the store, by the id from cookie""" 65 cookie_name = self._config.cookie_name 66 cookie_domain = self._config.cookie_domain 67 self.session_id = web.cookies().get(cookie_name) 68 69 # protection against session_id tampering 70 if self.session_id and not self._valid_session_id(self.session_id): 71 self.session_id = None 72 73 self._check_expiry() 74 if self.session_id: 75 d = self.store[self.session_id] 76 self.update(d) 77 self._validate_ip() 78 79 if not self.session_id: 80 self.session_id = self._generate_session_id() 81 82 if self._initializer: 83 if isinstance(self._initializer, dict): 84 self.update(self._initializer) 85 elif hasattr(self._initializer, '__call__'): 86 self._initializer() 87 88 self.ip = web.ctx.ip
89
90 - def _check_expiry(self):
91 # check for expiry 92 if self.session_id and self.session_id not in self.store: 93 if self._config.ignore_expiry: 94 self.session_id = None 95 else: 96 return self.expired()
97
98 - def _validate_ip(self):
99 # check for change of IP 100 if self.session_id and self.get('ip', None) != web.ctx.ip: 101 if not self._config.ignore_change_ip: 102 return self.expired()
103
104 - def _save(self):
105 cookie_name = self._config.cookie_name 106 cookie_domain = self._config.cookie_domain 107 if not self.get('_killed'): 108 web.setcookie(cookie_name, self.session_id, domain=cookie_domain) 109 self.store[self.session_id] = dict(self) 110 else: 111 web.setcookie(cookie_name, self.session_id, expires=-1, domain=cookie_domain)
112
113 - def _generate_session_id(self):
114 """Generate a random id for session""" 115 116 while True: 117 rand = os.urandom(16) 118 now = time.time() 119 secret_key = self._config.secret_key 120 session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key)) 121 session_id = session_id.hexdigest() 122 if session_id not in self.store: 123 break 124 return session_id
125
126 - def _valid_session_id(self, session_id):
127 rx = utils.re_compile('^[0-9a-fA-F]+$') 128 return rx.match(session_id)
129
130 - def _cleanup(self):
131 """Cleanup the stored sessions""" 132 current_time = time.time() 133 timeout = self._config.timeout 134 if current_time - self._last_cleanup_time > timeout: 135 self.store.cleanup(timeout) 136 self.__dict__['_last_cleanup_time'] = current_time
137
138 - def expired(self):
139 """Called when an expired session is atime""" 140 self._killed = True 141 self._save() 142 raise SessionExpired(self._config.expired_message)
143
144 - def kill(self):
145 """Kill the session, make it no longer available""" 146 del self.store[self.session_id] 147 self._killed = True
148
149 -class Store:
150 """Base class for session stores""" 151
152 - def __contains__(self, key):
153 raise NotImplementedError
154
155 - def __getitem__(self, key):
156 raise NotImplementedError
157
158 - def __setitem__(self, key, value):
159 raise NotImplementedError
160
161 - def cleanup(self, timeout):
162 """removes all the expired sessions""" 163 raise NotImplementedError
164
165 - def encode(self, session_dict):
166 """encodes session dict as a string""" 167 pickled = pickle.dumps(session_dict) 168 return base64.encodestring(pickled)
169
170 - def decode(self, session_data):
171 """decodes the data to get back the session dict """ 172 pickled = base64.decodestring(session_data) 173 return pickle.loads(pickled)
174
175 -class DiskStore(Store):
176 """ 177 Store for saving a session on disk. 178 179 >>> import tempfile 180 >>> root = tempfile.mkdtemp() 181 >>> s = DiskStore(root) 182 >>> s['a'] = 'foo' 183 >>> s['a'] 184 'foo' 185 >>> time.sleep(0.01) 186 >>> s.cleanup(0.01) 187 >>> s['a'] 188 Traceback (most recent call last): 189 ... 190 KeyError: 'a' 191 """
192 - def __init__(self, root):
193 # if the storage root doesn't exists, create it. 194 if not os.path.exists(root): 195 os.mkdir(root) 196 self.root = root
197
198 - def _get_path(self, key):
199 if os.path.sep in key: 200 raise ValueError, "Bad key: %s" % repr(key) 201 return os.path.join(self.root, key)
202
203 - def __contains__(self, key):
204 path = self._get_path(key) 205 return os.path.exists(path)
206
207 - def __getitem__(self, key):
208 path = self._get_path(key) 209 if os.path.exists(path): 210 pickled = open(path).read() 211 return self.decode(pickled) 212 else: 213 raise KeyError, key
214
215 - def __setitem__(self, key, value):
216 path = self._get_path(key) 217 pickled = self.encode(value) 218 try: 219 f = open(path, 'w') 220 try: 221 f.write(pickled) 222 finally: 223 f.close() 224 except IOError: 225 pass
226
227 - def __delitem__(self, key):
228 path = self._get_path(key) 229 if os.path.exists(path): 230 os.remove(path)
231
232 - def cleanup(self, timeout):
233 now = time.time() 234 for f in os.listdir(self.root): 235 path = self._get_path(f) 236 atime = os.stat(path).st_atime 237 if now - atime > timeout : 238 os.remove(path)
239
240 -class DBStore(Store):
241 """Store for saving a session in database 242 Needs a table with the following columns: 243 244 session_id CHAR(128) UNIQUE NOT NULL, 245 atime DATETIME NOT NULL default current_timestamp, 246 data TEXT 247 """
248 - def __init__(self, db, table_name):
249 self.db = db 250 self.table = table_name
251
252 - def __contains__(self, key):
253 data = self.db.select(self.table, where="session_id=$key", vars=locals()) 254 return bool(list(data))
255
256 - def __getitem__(self, key):
257 now = datetime.datetime.now() 258 try: 259 s = self.db.select(self.table, where="session_id=$key", vars=locals())[0] 260 self.db.update(self.table, where="session_id=$key", atime=now, vars=locals()) 261 except IndexError: 262 raise KeyError 263 else: 264 return self.decode(s.data)
265
266 - def __setitem__(self, key, value):
267 pickled = self.encode(value) 268 now = datetime.datetime.now() 269 if key in self: 270 self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals()) 271 else: 272 self.db.insert(self.table, False, session_id=key, data=pickled )
273
274 - def __delitem__(self, key):
275 self.db.delete(self.table, where="session_id=$key", vars=locals())
276
277 - def cleanup(self, timeout):
278 timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg 279 last_allowed_time = datetime.datetime.now() - timeout 280 self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals())
281
282 -class ShelfStore:
283 """Store for saving session using `shelve` module. 284 285 import shelve 286 store = ShelfStore(shelve.open('session.shelf')) 287 288 XXX: is shelve thread-safe? 289 """
290 - def __init__(self, shelf):
291 self.shelf = shelf
292
293 - def __contains__(self, key):
294 return key in self.shelf
295
296 - def __getitem__(self, key):
297 atime, v = self.shelf[key] 298 self[key] = v # update atime 299 return v
300
301 - def __setitem__(self, key, value):
302 self.shelf[key] = time.time(), value
303
304 - def __delitem__(self, key):
305 try: 306 del self.shelf[key] 307 except KeyError: 308 pass
309
310 - def cleanup(self, timeout):
311 now = time.time() 312 for k in self.shelf.keys(): 313 atime, v = self.shelf[k] 314 if now - atime > timeout : 315 del self[k]
316 317 if __name__ == '__main__' : 318 import doctest 319 doctest.testmod() 320