Exemple #1
0
 def _makeOne(self, wrapped):
     from pyramid.session import manage_accessed
     return manage_accessed(wrapped)
 def _makeOne(self, wrapped):
     from pyramid.session import manage_accessed
     return manage_accessed(wrapped)
    class SessionCookie(dict):

        _cookie_name = 'session'
        _reissue_time = 0

        # dirty flag
        _dirty = False

        def __init__(self, request):
            # log.debug('SessionCookie::__init__()')
            self._request = request

            # flags
            new = True  # assume that this is a new session

            # time base flags
            now = time.time()
            created = renewed = accessed = now

            cookie_value = self._request.cookies.get(self._cookie_name)

            state = {}
            value = None

            if cookie_value is not None:
                try:
                    jwt_key = self._request.registry.settings['jwt.secret']
                    value = jwt.decode(cookie_value,
                                       key=jwt_key,
                                       algorithms=['HS256'],
                                       verify=True)
                except ValueError as e:
                    log.error(e)
                    value = None

            if value is not None:
                try:
                    created = value['iat'] if 'iat' in value else now
                    renewed = now
                    accessed = value[
                        'updated_at'] if 'updated_at' in value else now

                    new = False
                    state = value
                except (TypeError, ValueError) as e:
                    log.error(e)
                    state = {}

            # check if session timed out
            if now - renewed > timeout:
                # session has timed out, expire the session
                log.debug('session expired')
                state = {}

            self.created = created
            self.accessed = accessed
            self.renewed = renewed
            self.new = new
            dict.__init__(self, state)

        # actually set the cookie
        def _set_cookie(self, response):
            # do not set cookie on exception
            if self._request.exception is not None:
                if not isinstance(self._request.exception,
                                  exception.HTTPFound):
                    return False

            jwt_key = self._request.registry.settings['jwt.secret']

            copy = dict(self)
            copy['iat'] = self.created
            copy['updated_at'] = self.renewed

            cookie_value = jwt.encode(copy, key=jwt_key, algorithm='HS256')

            if len(cookie_value) > 4064:
                raise ValueError('Cookie value is too long to store (%s bytes)'
                                 ) % len(cookie_value)

            response.set_cookie(
                self._cookie_name,
                value=cookie_value,
                max_age=max_age,
                path='/',
                # domain =
                secure=False,  # set to true when using https
                httponly=True,
                # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
                # samesite = 'Strict'
                samesite='Lax')
            return True

        # ISession methods
        def changed(self):
            if not self._dirty:
                self._dirty = True

                def set_cookie_callback(request, response):
                    self._set_cookie(response)
                    self._request = None  # explicitly break cycle for gc

                self._request.add_response_callback(set_cookie_callback)

        def invalidate(self):
            self.clear()

            def set_cookie_clear_callback(request, response):
                response.set_cookie(
                    self._cookie_name,
                    value='',
                    max_age=0,
                    path='/',
                    secure=False,  # set to true when using https
                    httponly=True,
                    # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
                    # samesite = 'Strict'
                    samesite='Lax')
                self._request = None
                return True

            self._request.add_response_callback(set_cookie_clear_callback)

        # non-modifying dictionary methods
        get = manage_accessed(dict.get)
        __getitem__ = manage_accessed(dict.__getitem__)
        items = manage_accessed(dict.items)
        values = manage_accessed(dict.values)
        keys = manage_accessed(dict.keys)
        __contains__ = manage_accessed(dict.__contains__)
        __len__ = manage_accessed(dict.__len__)
        __iter__ = manage_accessed(dict.__iter__)

        # modifying dictionary methods
        clear = manage_changed(dict.clear)
        update = manage_changed(dict.update)
        setdefault = manage_changed(dict.setdefault)
        pop = manage_changed(dict.pop)
        popitem = manage_changed(dict.popitem)
        __setitem__ = manage_changed(dict.__setitem__)
        __delitem__ = manage_changed(dict.__delitem__)
    class MongoSession(dict):
        """ Dictionary-like session object, based on CookieSession """

        # configuration parameters
        _collection = collection
        _to_pickle = to_pickle
        _cookie_name = cookie_name
        _cookie_max_age = max_age
        _cookie_path = path
        _cookie_domain = domain
        _cookie_secure = secure
        _cookie_httponly = httponly
        _cookie_on_exception = set_on_exception
        _timeout = timeout
        _reissue_time = reissue_time

        # dirty flag
        _dirty = False

        def __init__(self, request):
            self.request = request
            now = time.time()
            created = renewed = now
            new = True
            value = None
            state = {}
            cookieval = self._get_cookie()
            if cookieval:
                value = self._collection.find_one({'_id': cookieval})

            if value is not None:
                try:
                    renewed = float(value.get('accessed'))
                    created = float(value.get('created'))
                    sval = value.get('value')
                    pickled = value.get('pickled')
                    state = sval

                    if pickled:
                        if not PY3:
                            sval = sval.encode('utf-8')  # dammit
                        state = pickle.loads(sval)

                    new = False
                except (TypeError, ValueError, pickle.PickleError):
                    # value failed to unpack properly or renewed was not
                    # a numeric type so we'll fail deserialization here
                    state = {}

            if self._timeout is not None:
                if now - renewed > self._timeout:
                    # expire the session because it was not renewed
                    # before the timeout threshold
                    state = {}

            self.created = created
            self.accessed = renewed
            self.renewed = renewed
            self.new = new

            dict.__init__(self, state)

        # ISession methods
        def changed(self):
            if not self._dirty:
                self._dirty = True

                def set_cookie_callback(request, response):
                    self._set_cookie(response)
                    self.request = None  # explicitly break cycle for gc

                self.request.add_response_callback(set_cookie_callback)

        def invalidate(self):
            cookieval = self._get_cookie()
            self._collection.delete_one({'_id': cookieval})
            self.clear()  # XXX probably needs to unset cookie. But...

        # non-modifying dictionary methods
        get = manage_accessed(dict.get)
        __getitem__ = manage_accessed(dict.__getitem__)
        items = manage_accessed(dict.items)
        values = manage_accessed(dict.values)
        keys = manage_accessed(dict.keys)
        __contains__ = manage_accessed(dict.__contains__)
        __len__ = manage_accessed(dict.__len__)
        __iter__ = manage_accessed(dict.__iter__)

        if not PY3:
            iteritems = manage_accessed(dict.iteritems)
            itervalues = manage_accessed(dict.itervalues)
            iterkeys = manage_accessed(dict.iterkeys)
            has_key = manage_accessed(dict.has_key)

        # modifying dictionary methods
        clear = manage_changed(dict.clear)
        update = manage_changed(dict.update)
        setdefault = manage_changed(dict.setdefault)
        pop = manage_changed(dict.pop)
        popitem = manage_changed(dict.popitem)
        __setitem__ = manage_changed(dict.__setitem__)
        __delitem__ = manage_changed(dict.__delitem__)

        # flash API methods
        @manage_changed
        def flash(self, msg, queue='', allow_duplicate=True):
            storage = self.setdefault('_f_' + queue, [])
            if allow_duplicate or (msg not in storage):
                storage.append(msg)

        @manage_changed
        def pop_flash(self, queue=''):
            storage = self.pop('_f_' + queue, [])
            return storage

        @manage_accessed
        def peek_flash(self, queue=''):
            storage = self.get('_f_' + queue, [])
            return storage

        # CSRF API methods
        @manage_changed
        def new_csrf_token(self):
            token = get_random()
            self['_csrft_'] = token
            return token

        @manage_accessed
        def get_csrf_token(self):
            token = self.get('_csrft_', None)
            if token is None:
                token = self.new_csrf_token()
            return token

        # non-API methods
        def _get_cookie(self):  # cookie value, not value itself
            value = self.request.cookies.get(self._cookie_name, '')
            value = re.sub('[^a-f0-9]', '', value)
            return value

        def _set_cookie(self, response):
            if not self._cookie_on_exception:
                exception = getattr(self.request, 'exception', None)
                if exception is not None:  # dont set a cookie during exceptions
                    return False

            cookieval = self.new and get_random() or self._get_cookie()
            if not cookieval:
                return False

            value = self._to_pickle and pickle.dumps(dict(self)) or dict(self)
            data = dict(accessed=self.accessed,
                        created=self.created,
                        value=value,
                        pickled=self._to_pickle,
                        _id=cookieval)
            self._collection.replace_one({'_id': cookieval}, data, upsert=True)

            response.set_cookie(self._cookie_name,
                                value=cookieval,
                                max_age=self._cookie_max_age,
                                path=self._cookie_path,
                                domain=self._cookie_domain,
                                secure=self._cookie_secure,
                                httponly=self._cookie_httponly)
            return True
Exemple #5
0
    class PluggableSession(dict):
        """ Dictionary-like session object """

        # configuration parameters
        _cookie_on_exception = set_on_exception
        _timeout = timeout
        _reissue_time = reissue_time

        # dirty flag
        _dirty = False

        def __init__(self, request):
            self._cookie = CookieHelper(
                secret,
                salt,
                cookie_name,
                secure=secure,
                max_age=max_age,
                httponly=httponly,
                path=path,
                domains=domain,
                hashalg=hashalg,
            )
            self._session_id = None
            self.request = request

            reg = request.registry
            plug = reg.queryUtility(IPlugSession)

            if plug is None:
                raise RuntimeError(
                    'Unable to find any registered IPlugSession')

            now = time.time()
            created = renewed = now
            new = True
            value = None
            state = {}

            # Get the session_id
            self._session_id = self._cookie.bind(request).get_value()

            if self._session_id is not None:
                try:
                    sess_val = plug.loads(self, request)
                    value = serializer.loads(bytes_(sess_val))
                except ValueError:
                    value = None
                    # Cleanup the session, since it failed to deserialize
                    plug.clear(self, request)
                    self._session_id = None

            if value is not None:
                try:
                    rval, cval, sval = value
                    renewed = float(rval)
                    created = float(cval)
                    state = sval
                    new = False
                except (TypeError, ValueError):
                    # value failed to unpack properly or renewed was not
                    # a numeric type so we'll fail deserialization here
                    state = {}
                    # Clean up the session since it failed to unpack
                    plug.clear(self, request)
                    self._session_id = None

            if self._timeout is not None:
                if now - renewed > self._timeout:
                    # expire the session because it was not renewed
                    # before the timeout threshold
                    state = {}
                    # Session expired, cleanup this session
                    plug.clear(self, request)
                    self._session_id = None

            # Generate a new session id
            if self._session_id is None:
                self._generate_new_id()

            self.created = created
            self.accessed = renewed
            self.renewed = renewed
            self.new = new
            self._plug = plug
            dict.__init__(self, state)

        # ISession methods
        def changed(self):
            if not self._dirty:
                self._dirty = True

                def save_session_callback(request, response):
                    self._save_session(response)
                    self.request = None  # explicitly break cycle for gc

                self.request.add_response_callback(save_session_callback)

        def invalidate(self):
            self._plug.clear(self, self.request)
            self._generate_new_id()
            now = time.time()
            self.created = self.renewed = now
            self.new = True
            self.clear()

        # non-modifying dictionary methods
        get = manage_accessed(dict.get)
        __getitem__ = manage_accessed(dict.__getitem__)
        items = manage_accessed(dict.items)
        values = manage_accessed(dict.values)
        keys = manage_accessed(dict.keys)
        __contains__ = manage_accessed(dict.__contains__)
        __len__ = manage_accessed(dict.__len__)
        __iter__ = manage_accessed(dict.__iter__)

        if not PY3:
            iteritems = manage_accessed(dict.iteritems)
            itervalues = manage_accessed(dict.itervalues)
            iterkeys = manage_accessed(dict.iterkeys)
            has_key = manage_accessed(dict.has_key)

        # modifying dictionary methods
        clear = manage_changed(dict.clear)
        update = manage_changed(dict.update)
        setdefault = manage_changed(dict.setdefault)
        pop = manage_changed(dict.pop)
        popitem = manage_changed(dict.popitem)
        __setitem__ = manage_changed(dict.__setitem__)
        __delitem__ = manage_changed(dict.__delitem__)

        # flash API methods
        @manage_changed
        def flash(self, msg, queue='', allow_duplicate=True):
            storage = self.setdefault('_f_' + queue, [])
            if allow_duplicate or (msg not in storage):
                storage.append(msg)

        @manage_changed
        def pop_flash(self, queue=''):
            storage = self.pop('_f_' + queue, [])
            return storage

        @manage_accessed
        def peek_flash(self, queue=''):
            storage = self.get('_f_' + queue, [])
            return storage

        # CSRF API methods
        @manage_changed
        def new_csrf_token(self):
            token = text_(binascii.hexlify(os.urandom(20)))
            self['_csrft_'] = token
            return token

        @manage_accessed
        def get_csrf_token(self):
            token = self.get('_csrft_', None)
            if token is None:
                token = self.new_csrf_token()
            return token

        # non-API methods
        def _save_session(self, response):
            if not self._cookie_on_exception:
                exception = getattr(self.request, 'exception', None)
                if exception is not None:  # dont set a cookie during exceptions
                    return False

            sess_val = native_(
                serializer.dumps((self.accessed, self.created, dict(self))))

            self._plug.dumps(self, self.request, sess_val)
            self._cookie.set_cookies(response, self._session_id)

            return True

        def _generate_new_id(self):
            self._session_id = text_(binascii.hexlify(os.urandom(20)))