1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/OpenSecurity/install/web.py-0.37/web/session.py Mon Dec 02 14:02:05 2013 +0100
1.3 @@ -0,0 +1,358 @@
1.4 +"""
1.5 +Session Management
1.6 +(from web.py)
1.7 +"""
1.8 +
1.9 +import os, time, datetime, random, base64
1.10 +import os.path
1.11 +from copy import deepcopy
1.12 +try:
1.13 + import cPickle as pickle
1.14 +except ImportError:
1.15 + import pickle
1.16 +try:
1.17 + import hashlib
1.18 + sha1 = hashlib.sha1
1.19 +except ImportError:
1.20 + import sha
1.21 + sha1 = sha.new
1.22 +
1.23 +import utils
1.24 +import webapi as web
1.25 +
1.26 +__all__ = [
1.27 + 'Session', 'SessionExpired',
1.28 + 'Store', 'DiskStore', 'DBStore',
1.29 +]
1.30 +
1.31 +web.config.session_parameters = utils.storage({
1.32 + 'cookie_name': 'webpy_session_id',
1.33 + 'cookie_domain': None,
1.34 + 'cookie_path' : None,
1.35 + 'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds
1.36 + 'ignore_expiry': True,
1.37 + 'ignore_change_ip': True,
1.38 + 'secret_key': 'fLjUfxqXtfNoIldA0A0J',
1.39 + 'expired_message': 'Session expired',
1.40 + 'httponly': True,
1.41 + 'secure': False
1.42 +})
1.43 +
1.44 +class SessionExpired(web.HTTPError):
1.45 + def __init__(self, message):
1.46 + web.HTTPError.__init__(self, '200 OK', {}, data=message)
1.47 +
1.48 +class Session(object):
1.49 + """Session management for web.py
1.50 + """
1.51 + __slots__ = [
1.52 + "store", "_initializer", "_last_cleanup_time", "_config", "_data",
1.53 + "__getitem__", "__setitem__", "__delitem__"
1.54 + ]
1.55 +
1.56 + def __init__(self, app, store, initializer=None):
1.57 + self.store = store
1.58 + self._initializer = initializer
1.59 + self._last_cleanup_time = 0
1.60 + self._config = utils.storage(web.config.session_parameters)
1.61 + self._data = utils.threadeddict()
1.62 +
1.63 + self.__getitem__ = self._data.__getitem__
1.64 + self.__setitem__ = self._data.__setitem__
1.65 + self.__delitem__ = self._data.__delitem__
1.66 +
1.67 + if app:
1.68 + app.add_processor(self._processor)
1.69 +
1.70 + def __contains__(self, name):
1.71 + return name in self._data
1.72 +
1.73 + def __getattr__(self, name):
1.74 + return getattr(self._data, name)
1.75 +
1.76 + def __setattr__(self, name, value):
1.77 + if name in self.__slots__:
1.78 + object.__setattr__(self, name, value)
1.79 + else:
1.80 + setattr(self._data, name, value)
1.81 +
1.82 + def __delattr__(self, name):
1.83 + delattr(self._data, name)
1.84 +
1.85 + def _processor(self, handler):
1.86 + """Application processor to setup session for every request"""
1.87 + self._cleanup()
1.88 + self._load()
1.89 +
1.90 + try:
1.91 + return handler()
1.92 + finally:
1.93 + self._save()
1.94 +
1.95 + def _load(self):
1.96 + """Load the session from the store, by the id from cookie"""
1.97 + cookie_name = self._config.cookie_name
1.98 + cookie_domain = self._config.cookie_domain
1.99 + cookie_path = self._config.cookie_path
1.100 + httponly = self._config.httponly
1.101 + self.session_id = web.cookies().get(cookie_name)
1.102 +
1.103 + # protection against session_id tampering
1.104 + if self.session_id and not self._valid_session_id(self.session_id):
1.105 + self.session_id = None
1.106 +
1.107 + self._check_expiry()
1.108 + if self.session_id:
1.109 + d = self.store[self.session_id]
1.110 + self.update(d)
1.111 + self._validate_ip()
1.112 +
1.113 + if not self.session_id:
1.114 + self.session_id = self._generate_session_id()
1.115 +
1.116 + if self._initializer:
1.117 + if isinstance(self._initializer, dict):
1.118 + self.update(deepcopy(self._initializer))
1.119 + elif hasattr(self._initializer, '__call__'):
1.120 + self._initializer()
1.121 +
1.122 + self.ip = web.ctx.ip
1.123 +
1.124 + def _check_expiry(self):
1.125 + # check for expiry
1.126 + if self.session_id and self.session_id not in self.store:
1.127 + if self._config.ignore_expiry:
1.128 + self.session_id = None
1.129 + else:
1.130 + return self.expired()
1.131 +
1.132 + def _validate_ip(self):
1.133 + # check for change of IP
1.134 + if self.session_id and self.get('ip', None) != web.ctx.ip:
1.135 + if not self._config.ignore_change_ip:
1.136 + return self.expired()
1.137 +
1.138 + def _save(self):
1.139 + if not self.get('_killed'):
1.140 + self._setcookie(self.session_id)
1.141 + self.store[self.session_id] = dict(self._data)
1.142 + else:
1.143 + self._setcookie(self.session_id, expires=-1)
1.144 +
1.145 + def _setcookie(self, session_id, expires='', **kw):
1.146 + cookie_name = self._config.cookie_name
1.147 + cookie_domain = self._config.cookie_domain
1.148 + cookie_path = self._config.cookie_path
1.149 + httponly = self._config.httponly
1.150 + secure = self._config.secure
1.151 + web.setcookie(cookie_name, session_id, expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, path=cookie_path)
1.152 +
1.153 + def _generate_session_id(self):
1.154 + """Generate a random id for session"""
1.155 +
1.156 + while True:
1.157 + rand = os.urandom(16)
1.158 + now = time.time()
1.159 + secret_key = self._config.secret_key
1.160 + session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key))
1.161 + session_id = session_id.hexdigest()
1.162 + if session_id not in self.store:
1.163 + break
1.164 + return session_id
1.165 +
1.166 + def _valid_session_id(self, session_id):
1.167 + rx = utils.re_compile('^[0-9a-fA-F]+$')
1.168 + return rx.match(session_id)
1.169 +
1.170 + def _cleanup(self):
1.171 + """Cleanup the stored sessions"""
1.172 + current_time = time.time()
1.173 + timeout = self._config.timeout
1.174 + if current_time - self._last_cleanup_time > timeout:
1.175 + self.store.cleanup(timeout)
1.176 + self._last_cleanup_time = current_time
1.177 +
1.178 + def expired(self):
1.179 + """Called when an expired session is atime"""
1.180 + self._killed = True
1.181 + self._save()
1.182 + raise SessionExpired(self._config.expired_message)
1.183 +
1.184 + def kill(self):
1.185 + """Kill the session, make it no longer available"""
1.186 + del self.store[self.session_id]
1.187 + self._killed = True
1.188 +
1.189 +class Store:
1.190 + """Base class for session stores"""
1.191 +
1.192 + def __contains__(self, key):
1.193 + raise NotImplementedError
1.194 +
1.195 + def __getitem__(self, key):
1.196 + raise NotImplementedError
1.197 +
1.198 + def __setitem__(self, key, value):
1.199 + raise NotImplementedError
1.200 +
1.201 + def cleanup(self, timeout):
1.202 + """removes all the expired sessions"""
1.203 + raise NotImplementedError
1.204 +
1.205 + def encode(self, session_dict):
1.206 + """encodes session dict as a string"""
1.207 + pickled = pickle.dumps(session_dict)
1.208 + return base64.encodestring(pickled)
1.209 +
1.210 + def decode(self, session_data):
1.211 + """decodes the data to get back the session dict """
1.212 + pickled = base64.decodestring(session_data)
1.213 + return pickle.loads(pickled)
1.214 +
1.215 +class DiskStore(Store):
1.216 + """
1.217 + Store for saving a session on disk.
1.218 +
1.219 + >>> import tempfile
1.220 + >>> root = tempfile.mkdtemp()
1.221 + >>> s = DiskStore(root)
1.222 + >>> s['a'] = 'foo'
1.223 + >>> s['a']
1.224 + 'foo'
1.225 + >>> time.sleep(0.01)
1.226 + >>> s.cleanup(0.01)
1.227 + >>> s['a']
1.228 + Traceback (most recent call last):
1.229 + ...
1.230 + KeyError: 'a'
1.231 + """
1.232 + def __init__(self, root):
1.233 + # if the storage root doesn't exists, create it.
1.234 + if not os.path.exists(root):
1.235 + os.makedirs(
1.236 + os.path.abspath(root)
1.237 + )
1.238 + self.root = root
1.239 +
1.240 + def _get_path(self, key):
1.241 + if os.path.sep in key:
1.242 + raise ValueError, "Bad key: %s" % repr(key)
1.243 + return os.path.join(self.root, key)
1.244 +
1.245 + def __contains__(self, key):
1.246 + path = self._get_path(key)
1.247 + return os.path.exists(path)
1.248 +
1.249 + def __getitem__(self, key):
1.250 + path = self._get_path(key)
1.251 + if os.path.exists(path):
1.252 + pickled = open(path).read()
1.253 + return self.decode(pickled)
1.254 + else:
1.255 + raise KeyError, key
1.256 +
1.257 + def __setitem__(self, key, value):
1.258 + path = self._get_path(key)
1.259 + pickled = self.encode(value)
1.260 + try:
1.261 + f = open(path, 'w')
1.262 + try:
1.263 + f.write(pickled)
1.264 + finally:
1.265 + f.close()
1.266 + except IOError:
1.267 + pass
1.268 +
1.269 + def __delitem__(self, key):
1.270 + path = self._get_path(key)
1.271 + if os.path.exists(path):
1.272 + os.remove(path)
1.273 +
1.274 + def cleanup(self, timeout):
1.275 + now = time.time()
1.276 + for f in os.listdir(self.root):
1.277 + path = self._get_path(f)
1.278 + atime = os.stat(path).st_atime
1.279 + if now - atime > timeout :
1.280 + os.remove(path)
1.281 +
1.282 +class DBStore(Store):
1.283 + """Store for saving a session in database
1.284 + Needs a table with the following columns:
1.285 +
1.286 + session_id CHAR(128) UNIQUE NOT NULL,
1.287 + atime DATETIME NOT NULL default current_timestamp,
1.288 + data TEXT
1.289 + """
1.290 + def __init__(self, db, table_name):
1.291 + self.db = db
1.292 + self.table = table_name
1.293 +
1.294 + def __contains__(self, key):
1.295 + data = self.db.select(self.table, where="session_id=$key", vars=locals())
1.296 + return bool(list(data))
1.297 +
1.298 + def __getitem__(self, key):
1.299 + now = datetime.datetime.now()
1.300 + try:
1.301 + s = self.db.select(self.table, where="session_id=$key", vars=locals())[0]
1.302 + self.db.update(self.table, where="session_id=$key", atime=now, vars=locals())
1.303 + except IndexError:
1.304 + raise KeyError
1.305 + else:
1.306 + return self.decode(s.data)
1.307 +
1.308 + def __setitem__(self, key, value):
1.309 + pickled = self.encode(value)
1.310 + now = datetime.datetime.now()
1.311 + if key in self:
1.312 + self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals())
1.313 + else:
1.314 + self.db.insert(self.table, False, session_id=key, data=pickled )
1.315 +
1.316 + def __delitem__(self, key):
1.317 + self.db.delete(self.table, where="session_id=$key", vars=locals())
1.318 +
1.319 + def cleanup(self, timeout):
1.320 + timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg
1.321 + last_allowed_time = datetime.datetime.now() - timeout
1.322 + self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals())
1.323 +
1.324 +class ShelfStore:
1.325 + """Store for saving session using `shelve` module.
1.326 +
1.327 + import shelve
1.328 + store = ShelfStore(shelve.open('session.shelf'))
1.329 +
1.330 + XXX: is shelve thread-safe?
1.331 + """
1.332 + def __init__(self, shelf):
1.333 + self.shelf = shelf
1.334 +
1.335 + def __contains__(self, key):
1.336 + return key in self.shelf
1.337 +
1.338 + def __getitem__(self, key):
1.339 + atime, v = self.shelf[key]
1.340 + self[key] = v # update atime
1.341 + return v
1.342 +
1.343 + def __setitem__(self, key, value):
1.344 + self.shelf[key] = time.time(), value
1.345 +
1.346 + def __delitem__(self, key):
1.347 + try:
1.348 + del self.shelf[key]
1.349 + except KeyError:
1.350 + pass
1.351 +
1.352 + def cleanup(self, timeout):
1.353 + now = time.time()
1.354 + for k in self.shelf.keys():
1.355 + atime, v = self.shelf[k]
1.356 + if now - atime > timeout :
1.357 + del self[k]
1.358 +
1.359 +if __name__ == '__main__' :
1.360 + import doctest
1.361 + doctest.testmod()