# HG changeset patch # User Marcin Kuzminski # Date 2019-06-14 10:43:09 # Node ID 9f6c56d4e81a108218090ed146f6a02a5df44d02 # Parent b066f13df067469ce1135d3eb5862ef7a221fdfb session: moved pyramid_beaker internally to apply some patches required for better session handling diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -1395,21 +1395,6 @@ self: super: { license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ]; }; }; - "pyramid-beaker" = super.buildPythonPackage { - name = "pyramid-beaker-0.8"; - doCheck = false; - propagatedBuildInputs = [ - self."pyramid" - self."beaker" - ]; - src = fetchurl { - url = "https://files.pythonhosted.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz"; - sha256 = "0hflx3qkcdml1mwpq53sz46s7jickpfn0zy0ns2c7j445j66bp3p"; - }; - meta = { - license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ]; - }; - }; "pyramid-debugtoolbar" = super.buildPythonPackage { name = "pyramid-debugtoolbar-4.5"; doCheck = false; @@ -1793,7 +1778,6 @@ self: super: { self."pycrypto" self."pygments" self."pyparsing" - self."pyramid-beaker" self."pyramid-debugtoolbar" self."pyramid-mako" self."pyramid" diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -44,7 +44,6 @@ pycurl==7.43.0.2 pycrypto==2.6.1 pygments==2.4.2 pyparsing==2.3.0 -pyramid-beaker==0.8 pyramid-debugtoolbar==4.5.0 pyramid-mako==1.0.2 pyramid==1.10.4 diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -250,7 +250,7 @@ def includeme(config): # Includes which are required. The application would fail without them. config.include('pyramid_mako') - config.include('pyramid_beaker') + config.include('rhodecode.lib.rc_beaker') config.include('rhodecode.lib.rc_cache') config.include('rhodecode.apps._base.navigation') diff --git a/rhodecode/lib/base.py b/rhodecode/lib/base.py --- a/rhodecode/lib/base.py +++ b/rhodecode/lib/base.py @@ -536,7 +536,7 @@ def bootstrap_config(request): # allow pyramid lookup in testing config.include('pyramid_mako') - config.include('pyramid_beaker') + config.include('rhodecode.lib.rc_beaker') config.include('rhodecode.lib.rc_cache') add_events_routes(config) diff --git a/rhodecode/lib/rc_beaker.py b/rhodecode/lib/rc_beaker.py new file mode 100644 --- /dev/null +++ b/rhodecode/lib/rc_beaker.py @@ -0,0 +1,200 @@ +# Copyright (c) 2010 Agendaless Consulting and Contributors. +# (http://www.agendaless.com), All Rights Reserved +# License: BSD-derived (http://www.repoze.org/LICENSE.txt) +# With Patches from RhodeCode GmBH + + +import os + +from beaker import cache +from beaker.session import SessionObject +from beaker.util import coerce_cache_params +from beaker.util import coerce_session_params + +from pyramid.interfaces import ISession +from pyramid.settings import asbool +from zope.interface import implementer + +from binascii import hexlify + + +def BeakerSessionFactoryConfig(**options): + """ Return a Pyramid session factory using Beaker session settings + supplied directly as ``**options``""" + + class PyramidBeakerSessionObject(SessionObject): + _options = options + _cookie_on_exception = _options.pop('cookie_on_exception', True) + _constant_csrf_token = _options.pop('constant_csrf_token', False) + + def __init__(self, request): + SessionObject.__init__(self, request.environ, **self._options) + + def session_callback(request, response): + exception = getattr(request, 'exception', None) + if (exception is None or self._cookie_on_exception) and self.accessed(): + self.persist() + headers = self.__dict__['_headers'] + if headers['set_cookie'] and headers['cookie_out']: + response.headerlist.append(('Set-Cookie', headers['cookie_out'])) + request.add_response_callback(session_callback) + + # ISession API + + @property + def id(self): + # this is as inspected in SessionObject.__init__ + if self.__dict__['_params'].get('type') != 'cookie': + return self._session().id + return None + + @property + def new(self): + return self.last_accessed is None + + changed = SessionObject.save + + # modifying dictionary methods + + @call_save + def clear(self): + return self._session().clear() + + @call_save + def update(self, d, **kw): + return self._session().update(d, **kw) + + @call_save + def setdefault(self, k, d=None): + return self._session().setdefault(k, d) + + @call_save + def pop(self, k, d=None): + return self._session().pop(k, d) + + @call_save + def popitem(self): + return self._session().popitem() + + __setitem__ = call_save(SessionObject.__setitem__) + __delitem__ = call_save(SessionObject.__delitem__) + + # Flash API methods + def flash(self, msg, queue='', allow_duplicate=True): + storage = self.setdefault('_f_' + queue, []) + if allow_duplicate or (msg not in storage): + storage.append(msg) + + def pop_flash(self, queue=''): + storage = self.pop('_f_' + queue, []) + return storage + + def peek_flash(self, queue=''): + storage = self.get('_f_' + queue, []) + return storage + + # CSRF API methods + def new_csrf_token(self): + token = (self._constant_csrf_token + or hexlify(os.urandom(20)).decode('ascii')) + self['_csrft_'] = token + return token + + def get_csrf_token(self): + token = self.get('_csrft_', None) + if token is None: + token = self.new_csrf_token() + return token + + return implementer(ISession)(PyramidBeakerSessionObject) + + +def call_save(wrapped): + """ By default, in non-auto-mode beaker badly wants people to + call save even though it should know something has changed when + a mutating method is called. This hack should be removed if + Beaker ever starts to do this by default. """ + def save(session, *arg, **kw): + value = wrapped(session, *arg, **kw) + session.save() + return value + save.__doc__ = wrapped.__doc__ + return save + + +def session_factory_from_settings(settings): + """ Return a Pyramid session factory using Beaker session settings + supplied from a Paste configuration file""" + prefixes = ('session.', 'beaker.session.') + options = {} + + # Pull out any config args meant for beaker session. if there are any + for k, v in settings.items(): + for prefix in prefixes: + if k.startswith(prefix): + option_name = k[len(prefix):] + if option_name == 'cookie_on_exception': + v = asbool(v) + options[option_name] = v + + options = coerce_session_params(options) + return BeakerSessionFactoryConfig(**options) + + +def set_cache_regions_from_settings(settings): + """ Add cache support to the Pylons application. + + The ``settings`` passed to the configurator are used to setup + the cache options. Cache options in the settings should start + with either 'beaker.cache.' or 'cache.'. + + """ + cache_settings = {'regions': []} + for key in settings.keys(): + for prefix in ['beaker.cache.', 'cache.']: + if key.startswith(prefix): + name = key.split(prefix)[1].strip() + cache_settings[name] = settings[key].strip() + + if ('expire' in cache_settings + and isinstance(cache_settings['expire'], basestring) + and cache_settings['expire'].lower() in ['none', 'no']): + cache_settings['expire'] = None + + coerce_cache_params(cache_settings) + + if 'enabled' not in cache_settings: + cache_settings['enabled'] = True + + regions = cache_settings['regions'] + if regions: + for region in regions: + if not region: + continue + + region_settings = { + 'data_dir': cache_settings.get('data_dir'), + 'lock_dir': cache_settings.get('lock_dir'), + 'expire': cache_settings.get('expire', 60), + 'enabled': cache_settings['enabled'], + 'key_length': cache_settings.get('key_length', 250), + 'type': cache_settings.get('type'), + 'url': cache_settings.get('url'), + } + region_prefix = '%s.' % region + region_len = len(region_prefix) + for key in list(cache_settings.keys()): + if key.startswith(region_prefix): + region_settings[key[region_len:]] = cache_settings.pop(key) + + if (isinstance(region_settings['expire'], basestring) + and region_settings['expire'].lower() in ['none', 'no']): + region_settings['expire'] = None + coerce_cache_params(region_settings) + cache.cache_regions[region] = region_settings + + +def includeme(config): + session_factory = session_factory_from_settings(config.registry.settings) + config.set_session_factory(session_factory) + set_cache_regions_from_settings(config.registry.settings)