86113a1f2b1a4d0a3091927c3e7167d9b60618ad
[cascardo/ipsilon.git] / ipsilon / util / sessions.py
1 # Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
2
3 import base64
4 from cherrypy.lib.sessions import Session
5 from ipsilon.util.data import Store, SqlQuery
6 import threading
7 try:
8     import cPickle as pickle
9 except ImportError:
10     import pickle
11
12
13 SESSION_TABLE = {'columns': ['id', 'data', 'expiration_time'],
14                  'primary_key': ('id', ),
15                  'indexes': [('expiration_time',)]
16                  }
17
18
19 class SessionStore(Store):
20     def _initialize_schema(self):
21         q = self._query(self._db, 'sessions', SESSION_TABLE,
22                         trans=False)
23         q.create()
24         q._con.close()  # pylint: disable=protected-access
25
26     def _upgrade_schema(self, old_version):
27         if old_version == 1:
28             # In schema version 2, we added indexes and primary keys
29             # pylint: disable=protected-access
30             table = self._query(self._db, 'sessions', SESSION_TABLE,
31                                 trans=False)._table
32             self._db.add_constraint(table.primary_key)
33             for index in table.indexes:
34                 self._db.add_index(index)
35             return 2
36         else:
37             raise NotImplementedError()
38
39
40 class SqlSession(Session):
41
42     dburi = None
43     _db = None
44     _store = None
45     _proto = 2
46     locks = {}
47
48     @classmethod
49     def setup(cls, **kwargs):
50         """Initialization from cherrypy"""
51
52         for k, v in kwargs.items():
53             if k == 'storage_dburi':
54                 cls.dburi = v
55
56         cls._store = SessionStore(database_url=cls.dburi)
57         # pylint: disable=protected-access
58         cls._db = cls._store._db
59
60     def _exists(self):
61         q = SqlQuery(self._db, 'sessions', SESSION_TABLE)
62         result = q.select({'id': self.id})
63         return True if result.fetchone() else False
64
65     def _load(self):
66         q = SqlQuery(self._db, 'sessions', SESSION_TABLE)
67         result = q.select({'id': self.id})
68         r = result.fetchone()
69         if r:
70             data = str(base64.b64decode(r[1]))
71             return pickle.loads(data)
72
73     def _save(self, expiration_time):
74         q = None
75         try:
76             q = SqlQuery(self._db, 'sessions', SESSION_TABLE, trans=True)
77             q.delete({'id': self.id})
78             data = pickle.dumps((self._data, expiration_time), self._proto)
79             q.insert((self.id, base64.b64encode(data), expiration_time))
80             q.commit()
81         except Exception:  # pylint: disable=broad-except
82             if q:
83                 q.rollback()
84             raise
85
86     def _delete(self):
87         q = SqlQuery(self._db, 'sessions', SESSION_TABLE)
88         q.delete({'id': self.id})
89
90     # copy what RamSession does for now
91     def acquire_lock(self):
92         """Acquire an exclusive lock on the currently-loaded session data."""
93         self.locked = True
94         self.locks.setdefault(self.id, threading.RLock()).acquire()
95
96     def release_lock(self):
97         """Release the lock on the currently-loaded session data."""
98         self.locks[self.id].release()
99         self.locked = False