diff -r eca6387f5b87 -r bfc1aa1dba30 pyramid_cubicweb/session.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyramid_cubicweb/session.py Fri Sep 19 14:26:55 2014 +0200 @@ -0,0 +1,137 @@ +import warnings +import logging + +from pyramid.compat import pickle +from pyramid.session import SignedCookieSessionFactory + +from cubicweb import Binary + + +log = logging.getLogger(__name__) + + +def logerrors(logger): + def wrap(fn): + def newfn(*args, **kw): + try: + return fn(*args, **kw) + except: + logger.exception("Error in %s" % fn.__name__) + return newfn + return wrap + + +def CWSessionFactory( + secret, + cookie_name='session', + max_age=None, + path='/', + domain=None, + secure=False, + httponly=False, + set_on_exception=True, + timeout=1200, + reissue_time=120, + hashalg='sha512', + salt='pyramid.session.', + serializer=None): + + SignedCookieSession = SignedCookieSessionFactory( + secret, + cookie_name=cookie_name, + max_age=max_age, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + set_on_exception=set_on_exception, + timeout=timeout, + reissue_time=reissue_time, + hashalg=hashalg, + salt=salt, + serializer=serializer) + + class CWSession(SignedCookieSession): + def __init__(self, request): + # _set_accessed will be called by the super __init__. + # Setting _loaded to True inhibates it. + self._loaded = True + + # the super __init__ will load a single value in the dictionnary, + # the session id. + super(CWSession, self).__init__(request) + + # Remove the session id from the dict + self.sessioneid = self.pop('sessioneid', None) + self.repo = request.registry['cubicweb.repository'] + + # We need to lazy-load only for existing sessions + self._loaded = self.sessioneid is None + + @logerrors(log) + def _set_accessed(self, value): + self._accessed = value + + if self._loaded: + return + + with self.repo.internal_cnx() as cnx: + session = cnx.find('CWSession', eid=self.sessioneid).one() + value = session.cwsessiondata + if value: + # Use directly dict.update to avoir _set_accessed to be + # recursively called + dict.update(self, pickle.load(value)) + + self._loaded = True + + def _get_accessed(self): + return self._accessed + + accessed = property(_get_accessed, _set_accessed) + + @logerrors(log) + def _set_cookie(self, response): + # Save the value in the database + data = Binary(pickle.dumps(dict(self))) + sessioneid = self.sessioneid + + with self.repo.internal_cnx() as cnx: + if not sessioneid: + session = cnx.create_entity( + 'CWSession', cwsessiondata=data) + sessioneid = session.eid + else: + session = cnx.entity_from_eid(sessioneid) + session.cw_set(cwsessiondata=data) + cnx.commit() + + # Only if needed actually set the cookie + if self.new or self.accessed - self.renewed > self._reissue_time: + dict.clear(self) + dict.__setitem__(self, 'sessioneid', sessioneid) + return super(CWSession, self)._set_cookie(response) + + return True + + return CWSession + + +def includeme(config): + secret = config.registry['cubicweb.config']['pyramid-session-secret'] + if not secret: + secret = 'notsosecret' + warnings.warn(''' + + !! WARNING !! !! WARNING !! + + The session cookies are signed with a static secret key. + To put your own secret key, edit your all-in-one.conf file + and set the 'pyramid-session-secret' key. + + YOU SHOULD STOP THIS INSTANCE unless your really know what you + are doing !! + + ''') + session_factory = CWSessionFactory(secret) + config.set_session_factory(session_factory)