pyramid_cubicweb/session.py
author Christophe de Vienne <christophe@unlish.com>
Sat, 08 Nov 2014 23:07:20 +0100
changeset 11524 54c83bfda277
parent 11506 bfc1aa1dba30
child 11537 caf268942436
permissions -rw-r--r--
Don't rollback if exception is HTTPSuccessful or HTTPRedirection In the request finishing, the 'cleanup' callback set by _cw_cnx automatically commit the transaction except is an exception is set on the request. Problem is, redirections and successul http return code can raise exceptions. This patch detects such exceptions and avoid rolling back the transaction. Closes #4566482
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11506
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     1
import warnings
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     2
import logging
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     3
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     4
from pyramid.compat import pickle
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     5
from pyramid.session import SignedCookieSessionFactory
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     6
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     7
from cubicweb import Binary
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     8
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
     9
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    10
log = logging.getLogger(__name__)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    11
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    12
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    13
def logerrors(logger):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    14
    def wrap(fn):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    15
        def newfn(*args, **kw):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    16
            try:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    17
                return fn(*args, **kw)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    18
            except:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    19
                logger.exception("Error in %s" % fn.__name__)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    20
        return newfn
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    21
    return wrap
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    22
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    23
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    24
def CWSessionFactory(
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    25
        secret,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    26
        cookie_name='session',
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    27
        max_age=None,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    28
        path='/',
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    29
        domain=None,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    30
        secure=False,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    31
        httponly=False,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    32
        set_on_exception=True,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    33
        timeout=1200,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    34
        reissue_time=120,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    35
        hashalg='sha512',
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    36
        salt='pyramid.session.',
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    37
        serializer=None):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    38
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    39
    SignedCookieSession = SignedCookieSessionFactory(
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    40
        secret,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    41
        cookie_name=cookie_name,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    42
        max_age=max_age,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    43
        path=path,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    44
        domain=domain,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    45
        secure=secure,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    46
        httponly=httponly,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    47
        set_on_exception=set_on_exception,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    48
        timeout=timeout,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    49
        reissue_time=reissue_time,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    50
        hashalg=hashalg,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    51
        salt=salt,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    52
        serializer=serializer)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    53
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    54
    class CWSession(SignedCookieSession):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    55
        def __init__(self, request):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    56
            # _set_accessed will be called by the super __init__.
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    57
            # Setting _loaded to True inhibates it.
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    58
            self._loaded = True
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    59
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    60
            # the super __init__ will load a single value in the dictionnary,
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    61
            # the session id.
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    62
            super(CWSession, self).__init__(request)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    63
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    64
            # Remove the session id from the dict
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    65
            self.sessioneid = self.pop('sessioneid', None)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    66
            self.repo = request.registry['cubicweb.repository']
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    67
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    68
            # We need to lazy-load only for existing sessions
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    69
            self._loaded = self.sessioneid is None
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    70
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    71
        @logerrors(log)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    72
        def _set_accessed(self, value):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    73
            self._accessed = value
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    74
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    75
            if self._loaded:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    76
                return
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    77
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    78
            with self.repo.internal_cnx() as cnx:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    79
                session = cnx.find('CWSession', eid=self.sessioneid).one()
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    80
                value = session.cwsessiondata
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    81
                if value:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    82
                    # Use directly dict.update to avoir _set_accessed to be
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    83
                    # recursively called
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    84
                    dict.update(self, pickle.load(value))
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    85
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    86
            self._loaded = True
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    87
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    88
        def _get_accessed(self):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    89
            return self._accessed
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    90
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    91
        accessed = property(_get_accessed, _set_accessed)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    92
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    93
        @logerrors(log)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    94
        def _set_cookie(self, response):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    95
            # Save the value in the database
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    96
            data = Binary(pickle.dumps(dict(self)))
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    97
            sessioneid = self.sessioneid
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    98
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
    99
            with self.repo.internal_cnx() as cnx:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   100
                if not sessioneid:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   101
                    session = cnx.create_entity(
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   102
                        'CWSession', cwsessiondata=data)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   103
                    sessioneid = session.eid
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   104
                else:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   105
                    session = cnx.entity_from_eid(sessioneid)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   106
                    session.cw_set(cwsessiondata=data)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   107
                cnx.commit()
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   108
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   109
            # Only if needed actually set the cookie
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   110
            if self.new or self.accessed - self.renewed > self._reissue_time:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   111
                dict.clear(self)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   112
                dict.__setitem__(self, 'sessioneid', sessioneid)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   113
                return super(CWSession, self)._set_cookie(response)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   114
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   115
            return True
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   116
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   117
    return CWSession
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   118
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   119
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   120
def includeme(config):
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   121
    secret = config.registry['cubicweb.config']['pyramid-session-secret']
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   122
    if not secret:
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   123
        secret = 'notsosecret'
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   124
        warnings.warn('''
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   125
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   126
            !! WARNING !! !! WARNING !!
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   127
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   128
            The session cookies are signed with a static secret key.
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   129
            To put your own secret key, edit your all-in-one.conf file
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   130
            and set the 'pyramid-session-secret' key.
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   131
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   132
            YOU SHOULD STOP THIS INSTANCE unless your really know what you
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   133
            are doing !!
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   134
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   135
        ''')
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   136
    session_factory = CWSessionFactory(secret)
bfc1aa1dba30 DB-saved session data
Christophe de Vienne <christophe@unlish.com>
parents:
diff changeset
   137
    config.set_session_factory(session_factory)