Hello community, here is the log from the commit of package python-Beaker for openSUSE:Factory checked in at 2019-09-25 08:26:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Beaker (Old) and /work/SRC/openSUSE:Factory/.python-Beaker.new.7948 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Beaker" Wed Sep 25 08:26:32 2019 rev:24 rq:732776 version:1.11.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Beaker/python-Beaker.changes 2019-04-19 18:39:11.719246611 +0200 +++ /work/SRC/openSUSE:Factory/.python-Beaker.new.7948/python-Beaker.changes 2019-09-25 08:26:33.490399551 +0200 @@ -1,0 +2,9 @@ +Mon Sep 23 14:43:25 UTC 2019 - [email protected] + +- version update to 1.11.0 + * Fixed cookie path option not being properly set (`self._path` + was removed, only `self.path` exists) + * Documented `SameSite` option + * Fixed cookie expiration being localised when it shouldn't. + +------------------------------------------------------------------- Old: ---- 1.10.1.tar.gz New: ---- 1.11.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Beaker.spec ++++++ --- /var/tmp/diff_new_pack.hHn6Ef/_old 2019-09-25 08:26:33.910399493 +0200 +++ /var/tmp/diff_new_pack.hHn6Ef/_new 2019-09-25 08:26:33.910399493 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define oldpython python Name: python-Beaker -Version: 1.10.1 +Version: 1.11.0 Release: 0 Summary: A Session and Caching library with WSGI Middleware License: BSD-3-Clause @@ -44,6 +44,7 @@ BuildRequires: python3-dbm Requires: python-pylibmc Requires: python-python-memcached +Requires: python-setuptools Recommends: python-SQLAlchemy Recommends: python-cryptography Recommends: python-pycrypto ++++++ 1.10.1.tar.gz -> 1.11.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/.travis.yml new/beaker-1.11.0/.travis.yml --- old/beaker-1.10.1/.travis.yml 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/.travis.yml 2019-08-26 23:52:26.000000000 +0200 @@ -2,6 +2,11 @@ os: - linux dist: xenial +addons: + apt: + packages: + - language-pack-it + - locales services: - mongodb - memcached diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/CHANGELOG new/beaker-1.11.0/CHANGELOG --- old/beaker-1.10.1/CHANGELOG 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/CHANGELOG 2019-08-26 23:52:26.000000000 +0200 @@ -1,4 +1,11 @@ +Release 1.11.0 (2019-08-26) +=========================== + +* Fixed cookie path option not being properly set (`self._path` was removed, only `self.path` exists) +* Documented `SameSite` option +* Fixed cookie expiration being localised when it shouldn't. + Release 1.10.1 (2019-02-21) =========================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/beaker/__init__.py new/beaker-1.11.0/beaker/__init__.py --- old/beaker-1.10.1/beaker/__init__.py 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/beaker/__init__.py 2019-08-26 23:52:26.000000000 +0200 @@ -1 +1 @@ -__version__ = '1.10.1' +__version__ = '1.11.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/beaker/docs/configuration.rst new/beaker-1.11.0/beaker/docs/configuration.rst --- old/beaker-1.10.1/beaker/docs/configuration.rst 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/beaker/docs/configuration.rst 2019-08-26 23:52:26.000000000 +0200 @@ -111,9 +111,10 @@ the environ for use with WebTest. The name provided here is where the session object will be attached to the WebTest TestApp return value. -url (**optional**, string) URL is specific to use of either ``ext:memcached``, - ``ext:database``, ``ext:mongodb``, or ``ext:redis``. When using one of those - types, this option is **required**. +url (**optional**, string) + URL is specific to use of either ``ext:memcached``, ``ext:database``, + ``ext:mongodb``, or ``ext:redis``. When using one of those types, this + option is **required**. When used with ``ext:memcached``, this should be either a single, or semi-colon separated list of memcached servers:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/beaker/docs/sessions.rst new/beaker-1.11.0/beaker/docs/sessions.rst --- old/beaker-1.10.1/beaker/docs/sessions.rst 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/beaker/docs/sessions.rst 2019-08-26 23:52:26.000000000 +0200 @@ -224,18 +224,21 @@ ====================== Beaker uses the defaults of setting cookie attributes `httponly` and `secure` -to False. You may want to set those to True in production, and the reasons for +to False. You may want to set those to True in production. `samesite` also setting +with default value `Lax`, you can choice `Strict` for more protection. And the reasons for using these cookie attributes are explained in these Owasp guides - `HttpOnly`_ -, `SecureFlag`_. +, `SecureFlag`_, `SameSite`_. Example:: # Best practice cookie flags for security session.httponly = True session.secure = True + session.samesite = 'Lax' # or 'Strict' .. _SecureFlag: https://www.owasp.org/index.php/SecureFlag .. _HttpOnly: https://www.owasp.org/index.php/HttpOnly#Mitigating_the_Most_Common_XSS_attack_using_HttpOnly +.. _SameSite: https://www.owasp.org/index.php/SameSite Cookie-Based ============ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/beaker/session.py new/beaker-1.11.0/beaker/session.py --- old/beaker-1.10.1/beaker/session.py 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/beaker/session.py 2019-08-26 23:52:26.000000000 +0200 @@ -9,6 +9,12 @@ from beaker.exceptions import BeakerException, InvalidCryptoBackendError from beaker.cookie import SimpleCookie + +months = (None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") +weekdays = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") + + __all__ = ['SignedCookie', 'Session', 'InvalidSignature'] @@ -86,7 +92,27 @@ return str(val), ("%s%s" % (sig, val)) -class Session(dict): +class _ConfigurableSession(dict): + """Provides support for configurable Session objects. + + Provides a way to ensure some properties of sessions + are always available with pre-configured values + when they are not available in the session cookie itself. + """ + + def __init__(self, cookie_domain=None, cookie_path='/'): + self._config = { + '_domain': cookie_domain, + '_path': cookie_path + } + + def clear(self): + """Clears Session data. Preserves session configuration.""" + super(_ConfigurableSession, self).clear() + self.update(self._config) + + +class Session(_ConfigurableSession): """Session object that uses container package for storage. :param invalidate_corrupt: How to handle corrupt data when loading. When @@ -139,6 +165,13 @@ encrypt_key=None, validate_key=None, encrypt_nonce_bits=DEFAULT_NONCE_BITS, crypto_type='default', samesite='Lax', **namespace_args): + _ConfigurableSession.__init__( + self, + cookie_domain=cookie_domain, + cookie_path=cookie_path + ) + self.clear() + if not type: if data_dir: self.type = 'file' @@ -174,8 +207,6 @@ self._set_serializer(data_serializer) # Default cookie domain/path - self._domain = cookie_domain - self._path = cookie_path self.was_invalidated = False self.secret = secret self.secure = secure @@ -245,17 +276,24 @@ def _set_cookie_values(self, expires=None): self.cookie[self.key] = self.id - if self._domain: - self.cookie[self.key]['domain'] = self._domain + if self.domain: + self.cookie[self.key]['domain'] = self.domain if self.secure: self.cookie[self.key]['secure'] = True if self.samesite: self.cookie[self.key]['samesite'] = self.samesite self._set_cookie_http_only() - self.cookie[self.key]['path'] = self._path + self.cookie[self.key]['path'] = self.path self._set_cookie_expires(expires) + @staticmethod + def serialize_cookie_date(v): + v = v.timetuple() + r = time.strftime("%%s, %d-%%s-%Y %H:%M:%S GMT", v) + return r % (weekdays[v[6]], months[v[1]]) + + def _set_cookie_expires(self, expires): if expires is None: expires = self.cookie_expires @@ -275,12 +313,15 @@ self.cookie[self.key]['expires'] = '' return True self.cookie[self.key]['expires'] = \ - expires_date.strftime("%a, %d-%b-%Y %H:%M:%S GMT") + self.serialize_cookie_date(expires_date) return expires_date def _update_cookie_out(self, set_cookie=True): self._set_cookie_values() - self.request['cookie_out'] = self.cookie[self.key].output(header='') + cookie_out = self.cookie[self.key].output(header='') + if not isinstance(cookie_out, str): + cookie_out = cookie_out.encode('latin1') + self.request['cookie_out'] = cookie_out self.request['set_cookie'] = set_cookie def _set_cookie_http_only(self): @@ -307,20 +348,20 @@ return self['_creation_time'] def _set_domain(self, domain): - self['_domain'] = self._domain = domain + self['_domain'] = domain self._update_cookie_out() def _get_domain(self): - return self._domain + return self['_domain'] domain = property(_get_domain, _set_domain) def _set_path(self, path): - self['_path'] = self._path = path + self['_path'] = path self._update_cookie_out() def _get_path(self): - return self._path + return self.get('_path', '/') path = property(_get_path, _set_path) @@ -434,9 +475,6 @@ # Update the current _accessed_time session_data['_accessed_time'] = now - # Set the path if applicable - if '_path' in session_data: - self._path = session_data['_path'] self.update(session_data) self.accessed_dict = session_data.copy() finally: @@ -571,6 +609,12 @@ encrypt_nonce_bits=DEFAULT_NONCE_BITS, invalidate_corrupt=False, crypto_type='default', samesite='Lax', **kwargs): + _ConfigurableSession.__init__( + self, + cookie_domain=cookie_domain, + cookie_path=cookie_path + ) + self.clear() self.crypto_module = get_crypto_module(crypto_type) @@ -590,8 +634,6 @@ self.secure = secure self.httponly = httponly self.samesite = samesite - self._domain = cookie_domain - self._path = cookie_path self.invalidate_corrupt = invalidate_corrupt self._set_serializer(data_serializer) @@ -628,7 +670,6 @@ if cookie_data is InvalidSignature: raise BeakerException("Invalid signature") self.update(self._decrypt_data(cookie_data)) - self._path = self.get('_path', '/') except Exception as e: if self.invalidate_corrupt: util.warn( @@ -658,18 +699,17 @@ def _set_domain(self, domain): self['_domain'] = domain - self._domain = domain def _get_domain(self): - return self._domain + return self['_domain'] domain = property(_get_domain, _set_domain) def _set_path(self, path): - self['_path'] = self._path = path + self['_path'] = path def _get_path(self): - return self._path + return self['_path'] path = property(_get_path, _set_path) @@ -708,10 +748,8 @@ if expires is not None: self['_expires'] = expires - if '_domain' in self: - self.cookie[self.key]['domain'] = self['_domain'] - elif self._domain: - self.cookie[self.key]['domain'] = self._domain + if self.domain: + self.cookie[self.key]['domain'] = self.domain if self.secure: self.cookie[self.key]['secure'] = True if self.samesite: @@ -720,7 +758,10 @@ self.cookie[self.key]['path'] = self.get('_path', '/') - self.request['cookie_out'] = self.cookie[self.key].output(header='') + cookie_out = self.cookie[self.key].output(header='') + if not isinstance(cookie_out, str): + cookie_out = cookie_out.encode('latin1') + self.request['cookie_out'] = cookie_out self.request['set_cookie'] = True def delete(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/tests/test_cookie_expires.py new/beaker-1.11.0/tests/test_cookie_expires.py --- old/beaker-1.10.1/tests/test_cookie_expires.py 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/tests/test_cookie_expires.py 2019-08-26 23:52:26.000000000 +0200 @@ -56,6 +56,17 @@ assert no_expires is False, no_expires +def test_cookie_expires_different_locale(): + from locale import setlocale, LC_TIME + expires_date = datetime.datetime(2019, 5, 22) + setlocale(LC_TIME, 'it_IT.UTF-8') + # if you get locale.Error: unsupported locale setting. you have to enable that locale in your OS. + assert expires_date.strftime("%a, %d-%b-%Y %H:%M:%S GMT").startswith('mer,') + session = Session({}, cookie_expires=True, validate_key='validate_key') + assert session._set_cookie_expires(expires_date) + expires = cookie_expiration(session) + assert expires == 'Wed, 22-May-2019 00:00:00 GMT', expires + setlocale(LC_TIME, '') # restore default locale for further tests def test_set_cookie_expires(): """Exhibit Set-Cookie: values.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/beaker-1.10.1/tests/test_cookie_only.py new/beaker-1.11.0/tests/test_cookie_only.py --- old/beaker-1.10.1/tests/test_cookie_only.py 2019-02-21 20:42:27.000000000 +0100 +++ new/beaker-1.11.0/tests/test_cookie_only.py 2019-08-26 23:52:26.000000000 +0200 @@ -315,6 +315,61 @@ assert 'httponly' in cookie.lower() assert 'samesite=strict' in cookie.lower() + +def test_cookie_path_properly_set_after_init(): + COOKIE_PATH = '/app' + + options = { + 'session.validate_key': 'hoobermas', + 'session.type': 'cookie', + 'session.cookie_path': COOKIE_PATH, + } + app = TestApp(SessionMiddleware(simple_app, **options)) + res = app.get('/app') + cookie = res.headers['Set-Cookie'] + + assert ('path=%s' % COOKIE_PATH) in cookie.lower() + + +def test_cookie_path_properly_set_after_load(): + COOKIE_PATH = '/app' + + options = { + 'session.validate_key': 'hoobermas', + 'session.type': 'cookie', + 'session.cookie_path': COOKIE_PATH, + } + app = TestApp(SessionMiddleware(simple_app, **options)) + # Perform one request to set the cookie + res = app.get('/app') + # Perform another request to load the previous session from the cookie + res = app.get('/app') + cookie = res.headers['Set-Cookie'] + + assert ('path=%s' % COOKIE_PATH) in cookie.lower() + + +def test_cookie_path_properly_set_after_delete(): + COOKIE_PATH = '/app' + + def delete_session_app(environ, start_response): + session = environ['beaker.session'] + session.delete() + start_response('200 OK', [('Content-type', 'text/plain')]) + return [('Cookie is %s' % session).encode('UTF-8')] + + options = { + 'session.validate_key': 'hoobermas', + 'session.type': 'cookie', + 'session.cookie_path': COOKIE_PATH, + } + app = TestApp(SessionMiddleware(delete_session_app, **options)) + res = app.get('/app') + cookie = res.headers['Set-Cookie'] + + assert ('path=%s' % COOKIE_PATH) in cookie.lower() + + if __name__ == '__main__': from paste import httpserver wsgi_app = SessionMiddleware(simple_app, {})
