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,
30 'ignore_expiry': True,
31 'ignore_change_ip': True,
32 'secret_key': 'fLjUfxqXtfNoIldA0A0J',
33 'expired_message': 'Session expired',
34 })
35
39
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
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
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
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
91
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
99
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
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
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
127 rx = utils.re_compile('^[0-9a-fA-F]+$')
128 return rx.match(session_id)
129
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
139 """Called when an expired session is atime"""
140 self._killed = True
141 self._save()
142 raise SessionExpired(self._config.expired_message)
143
145 """Kill the session, make it no longer available"""
146 del self.store[self.session_id]
147 self._killed = True
148
150 """Base class for session stores"""
151
153 raise NotImplementedError
154
156 raise NotImplementedError
157
159 raise NotImplementedError
160
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
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 """
193
194 if not os.path.exists(root):
195 os.mkdir(root)
196 self.root = root
197
199 if os.path.sep in key:
200 raise ValueError, "Bad key: %s" % repr(key)
201 return os.path.join(self.root, key)
202
204 path = self._get_path(key)
205 return os.path.exists(path)
206
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
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
228 path = self._get_path(key)
229 if os.path.exists(path):
230 os.remove(path)
231
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
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 """
249 self.db = db
250 self.table = table_name
251
253 data = self.db.select(self.table, where="session_id=$key", vars=locals())
254 return bool(list(data))
255
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
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
275 self.db.delete(self.table, where="session_id=$key", vars=locals())
276
278 timeout = datetime.timedelta(timeout/(24.0*60*60))
279 last_allowed_time = datetime.datetime.now() - timeout
280 self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals())
281
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 """
292
294 return key in self.shelf
295
297 atime, v = self.shelf[key]
298 self[key] = v
299 return v
300
302 self.shelf[key] = time.time(), value
303
305 try:
306 del self.shelf[key]
307 except KeyError:
308 pass
309
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