OpenSecurity/install/web.py-0.37/web/session.py
changeset 3 65432e6c6042
     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()