From 918c9d9dd632d346909d2429647758352d753a42 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 1 Feb 2013 17:38:11 -0700 Subject: Update documentation for include_ip Add a note that using the include_ip functionality with IPv6 is not a good idea due to the users network expiring IPv6 addresses quickly. See preferred lifetime/valid lifetime for routers doing SLAAC in IPv6 for more information. --- pyramid/authentication.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyramid/authentication.py b/pyramid/authentication.py index 4f6ed2c1d..190298f98 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -450,6 +450,10 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): Default: ``False``. Make the requesting IP address part of the authentication data in the cookie. Optional. + For IPv6 this option is not recommended. It ties the authentication + ticket to that individual's IPv6 address. Depending on the network they + are on, the IPv6 address that a user is using may expire quickly. + ``timeout`` Default: ``None``. Maximum number of seconds which a newly -- cgit v1.2.3 From fbd5b4a8023f3ff3c42ab66b1a660d587e9c331b Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 1 Feb 2013 18:00:22 -0700 Subject: Add test for AuthTktCookieHelper for IPv6 compat Add an extra test that sets environ['REMOTE_ADDR'] to an IPv6 address "::1". --- pyramid/tests/test_authentication.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py index 123e4f9f5..37e088934 100644 --- a/pyramid/tests/test_authentication.py +++ b/pyramid/tests/test_authentication.py @@ -561,9 +561,13 @@ class TestAuthTktCookieHelper(unittest.TestCase): helper.BadTicket = auth_tkt.BadTicket return helper - def _makeRequest(self, cookie=None): + def _makeRequest(self, cookie=None, ipv6=False): environ = {'wsgi.version': (1,0)} - environ['REMOTE_ADDR'] = '1.1.1.1' + + if ipv6 is False: + environ['REMOTE_ADDR'] = '1.1.1.1' + else: + environ['REMOTE_ADDR'] = '::1' environ['SERVER_NAME'] = 'localhost' return DummyRequest(environ, cookie=cookie) @@ -612,6 +616,23 @@ class TestAuthTktCookieHelper(unittest.TestCase): self.assertEqual(environ['REMOTE_USER_DATA'],'') self.assertEqual(environ['AUTH_TYPE'],'cookie') + def test_identify_good_cookie_include_ipv6(self): + helper = self._makeOne('secret', include_ip=True) + request = self._makeRequest('ticket', ipv6=True) + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], 'userid') + self.assertEqual(result['userdata'], '') + self.assertEqual(result['timestamp'], 0) + self.assertEqual(helper.auth_tkt.value, 'ticket') + self.assertEqual(helper.auth_tkt.remote_addr, '::1') + self.assertEqual(helper.auth_tkt.secret, 'secret') + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'],'') + self.assertEqual(environ['AUTH_TYPE'],'cookie') + def test_identify_good_cookie_dont_include_ip(self): helper = self._makeOne('secret', include_ip=False) request = self._makeRequest('ticket') -- cgit v1.2.3 From 54d31379c1c10879ba89b6616b033152d9bb69bf Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 1 Feb 2013 18:30:42 -0700 Subject: Add tests for AuthTicket/parse_ticket for IPv6 compat Add some simple tests that test IPv6 compatibility with the AuthTicket class and the parse_ticket function. --- pyramid/tests/test_authentication.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py index 37e088934..cfabf9a9d 100644 --- a/pyramid/tests/test_authentication.py +++ b/pyramid/tests/test_authentication.py @@ -1119,6 +1119,20 @@ class TestAuthTicket(unittest.TestCase): self.assertEqual(result, '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!') + def test_ipv4(self): + ticket = self._makeOne('secret', 'userid', '198.51.100.1', + time=10, hashalg='sha256') + result = ticket.cookie_value() + self.assertEqual(result, 'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b'\ + '798400ecdade8d76c530000000auserid!') + + def test_ipv6(self): + ticket = self._makeOne('secret', 'userid', '2001:db8::1', + time=10, hashalg='sha256') + result = ticket.cookie_value() + self.assertEqual(result, 'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c8'\ + '5becf8760cd7a2fa4910000000auserid!') + class TestBadTicket(unittest.TestCase): def _makeOne(self, msg, expected=None): from pyramid.authentication import BadTicket @@ -1162,6 +1176,19 @@ class Test_parse_ticket(unittest.TestCase): result = self._callFUT('secret', ticket, '0.0.0.0', 'sha512') self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) + def test_ipv4(self): + ticket = 'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecdade8d7'\ + '6c530000000auserid!' + result = self._callFUT('secret', ticket, '198.51.100.1', 'sha256') + self.assertEqual(result, (10, 'userid', [''], '')) + + def test_ipv6(self): + ticket = 'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760cd7a2f'\ + 'a4910000000auserid!' + result = self._callFUT('secret', ticket, '2001:db8::1', 'sha256') + self.assertEqual(result, (10, 'userid', [''], '')) + pass + class TestSessionAuthenticationPolicy(unittest.TestCase): def _getTargetClass(self): from pyramid.authentication import SessionAuthenticationPolicy -- cgit v1.2.3 From c0151a32b4cf30fe58dbe63f2c58f1f862c3f82e Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 1 Feb 2013 18:33:23 -0700 Subject: Fix IPv6 compatibility in calculate_digest() This is a quick fix for adding IPv6 compatibility so that if the AuthTktAuthenticationPolicy is used with include_ip enabled then it won't throw an error. --- pyramid/authentication.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pyramid/authentication.py b/pyramid/authentication.py index 190298f98..43353f3e2 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -740,9 +740,17 @@ def calculate_digest(ip, timestamp, secret, userid, tokens, user_data, tokens = bytes_(tokens, 'utf-8') user_data = bytes_(user_data, 'utf-8') hash_obj = hashlib.new(hashalg) - hash_obj.update( - encode_ip_timestamp(ip, timestamp) + secret + userid + b'\0' - + tokens + b'\0' + user_data) + + # Check to see if this is an IPv6 address + if ':' in ip: + ip_timestamp = ip + str(int(timestamp)) + ip_timestamp = bytes_(ip_timestamp) + else: + # encode_ip_timestamp not required, left in for backwards compatibility + ip_timestamp = encode_ip_timestamp(ip, timestamp) + + hash_obj.update(ip_timestamp + secret + userid + b'\0' + + tokens + b'\0' + user_data) digest = hash_obj.hexdigest() hash_obj2 = hashlib.new(hashalg) hash_obj2.update(bytes_(digest) + secret) -- cgit v1.2.3 From 4b711020a33bcbcc931132c2333d9c819dbac344 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 1 Feb 2013 18:38:00 -0700 Subject: Sign CONTRIBUTORS.txt --- CONTRIBUTORS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 971c172f8..02fb81528 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -192,3 +192,5 @@ Contributors - Robert Jackiewicz, 2012/11/12 - John Anderson, 2012/11/14 + +- Bert JW Regeer, 2013/02/01 -- cgit v1.2.3 From ff41f867aef84c06833f19de18d4fbc2957a983a Mon Sep 17 00:00:00 2001 From: Wolfgang Schnerring Date: Tue, 19 Mar 2013 10:43:17 -0700 Subject: Fixes #798: Allow a protocol-relative URL to be passed to add_static_view, generate URLs using the current protocol. --- CHANGES.txt | 5 +++++ pyramid/config/views.py | 24 ++++++++++++++---------- pyramid/tests/test_config/test_views.py | 7 +++++++ pyramid/tests/test_url.py | 15 +++++++++++++++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ae2cafba4..53a09078a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,11 @@ Features a dynamic property. It is recommended to define a dynamic ACL as a callable to avoid this ambiguity. See https://github.com/Pylons/pyramid/issues/735. +- Allow a protocol-relative URL (e.g. ``//example.com/images``) to be passed to + ``pyramid.config.Configurator.add_static_view``. This allows + externally-hosted static URLs to be generated based on the current protocol. + + 1.4 (2012-12-18) ================ diff --git a/pyramid/config/views.py b/pyramid/config/views.py index c53b2b091..1c7620e67 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -1793,6 +1793,10 @@ class ViewsConfiguratorMixin(object): qualified URL (e.g. starts with ``http://`` or similar). In this mode, the ``name`` is used as the prefix of the full URL when generating a URL using :meth:`pyramid.request.Request.static_url`. + Furthermore, if a protocol-relative URL (e.g. ``//example.com/images``) + is used as the ``name`` argument, the generated URL will use the + protocol of the request (http or https, respectively). + For example, if ``add_static_view`` is called like so: .. code-block:: python @@ -1801,20 +1805,14 @@ class ViewsConfiguratorMixin(object): Subsequently, the URLs generated by :meth:`pyramid.request.Request.static_url` for that static view will - be prefixed with ``http://example.com/images``: + be prefixed with ``http://example.com/images`` (the external webserver + listening on ``example.com`` must be itself configured to respond + properly to such a request.): .. code-block:: python static_url('mypackage:images/logo.png', request) - When ``add_static_view`` is called with a ``name`` argument that is - the URL ``http://example.com/images``, subsequent calls to - :meth:`pyramid.request.Request.static_url` with paths that start with - the ``path`` argument passed to ``add_static_view`` will generate a - URL something like ``http://example.com/logo.png``. The external - webserver listening on ``example.com`` must be itself configured to - respond properly to such a request. - See :ref:`static_assets_section` for more information. """ spec = self._make_spec(path) @@ -1858,6 +1856,12 @@ class StaticURLInfo(object): kw['subpath'] = subpath return request.route_url(route_name, **kw) else: + parsed = url_parse(url) + if not parsed.scheme: + # parsed.scheme is readonly, so we have to parse again + # to change the scheme, sigh. + url = urlparse.urlunparse(url_parse( + url, scheme=request.environ['wsgi.url_scheme'])) subpath = url_quote(subpath) return urljoin(url, subpath) @@ -1886,7 +1890,7 @@ class StaticURLInfo(object): # make sure it ends with a slash name = name + '/' - if url_parse(name)[0]: + if url_parse(name).netloc: # it's a URL # url, spec, route_name url = name diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index 4cebdce8a..5388001f6 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -3737,6 +3737,13 @@ class TestStaticURLInfo(unittest.TestCase): expected = [('http://example.com/', 'anotherpackage:path/', None)] self._assertRegistrations(config, expected) + def test_add_url_noscheme(self): + inst = self._makeOne() + config = self._makeConfig() + inst.add(config, '//example.com', 'anotherpackage:path') + expected = [('//example.com/', 'anotherpackage:path/', None)] + self._assertRegistrations(config, expected) + def test_add_viewname(self): from pyramid.security import NO_PERMISSION_REQUIRED from pyramid.static import static_view diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index a7a565356..e33eeebfd 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -583,6 +583,21 @@ class TestURLMethodsMixin(unittest.TestCase): self.assertEqual(result, 'http://example.com:5432/absstatic/test_url.py') + def test_static_url_noscheme_uses_scheme_from_request(self): + import os + from pyramid.interfaces import IStaticURLInfo + from pyramid.config.views import StaticURLInfo + info = StaticURLInfo() + here = os.path.abspath(os.path.dirname(__file__)) + info.add(self.config, '//subdomain.example.com/static', here) + request = self._makeOne({'wsgi.url_scheme': 'https'}) + registry = request.registry + registry.registerUtility(info, IStaticURLInfo) + abspath = os.path.join(here, 'test_url.py') + result = request.static_url(abspath) + self.assertEqual(result, + 'https://subdomain.example.com/static/test_url.py') + def test_static_path_abspath(self): from pyramid.interfaces import IStaticURLInfo request = self._makeOne() -- cgit v1.2.3 From 6842561d909ca041cb02d4152f4df3f5c6e57eab Mon Sep 17 00:00:00 2001 From: Steven Citron-Pousty Date: Tue, 19 Mar 2013 12:43:53 -0700 Subject: More explanation around pcreate Not sure how much to explain there but it needed some more explanation. Perhaps we could link to a doc about pcreate? --- docs/tutorials/wiki2/installation.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index 255a60ec2..64e069e6f 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -66,6 +66,13 @@ On Windows: startup problems, try putting both the virtualenv and the project into directories that do not contain spaces in their paths. +Pcreate is a script that comes with Pyramid that helps by creating and organizing files +needed as part of a Pyramid project. By passing in `alchemy` was are asking the script to +create the files needed to use SQLAlchemy. By passing in our app name `tutorial` it goes through and +places that application name in all the different files required. For example, the ``initialize_tutorial_db`` +that is in the ``pyramidtut/bin`` directory that we use later in this tutorial was created by `pcreate` + + .. _installing_project_in_dev_mode: -- cgit v1.2.3 From ed214d879cab4f00f5151011279c69ea07da6540 Mon Sep 17 00:00:00 2001 From: Steven Citron-Pousty Date: Tue, 19 Mar 2013 13:00:29 -0700 Subject: fixed the syntax for exporting the VENV --- docs/narr/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 04a060ac3..9bc62dc62 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -269,7 +269,7 @@ you can then create a virtual environment. To do so, invoke the following: .. code-block:: text - $ export $VENV=~/env + $ export VENV=~/env $ virtualenv --no-site-packages $VENV New python executable in /home/foo/env/bin/python Installing setuptools.............done. -- cgit v1.2.3 From 50e1a3b596b3c33c2254163e52eacfb1a519450c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 19 Mar 2013 16:03:24 -0700 Subject: enhance the docstring on include_ip in auth tkt to explain compatability --- pyramid/authentication.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyramid/authentication.py b/pyramid/authentication.py index 43353f3e2..bc0286ed3 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -450,9 +450,11 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): Default: ``False``. Make the requesting IP address part of the authentication data in the cookie. Optional. - For IPv6 this option is not recommended. It ties the authentication - ticket to that individual's IPv6 address. Depending on the network they - are on, the IPv6 address that a user is using may expire quickly. + For IPv6 this option is not recommended. The ``mod_auth_tkt`` + specification does not specify how to handle IPv6 addresses, so using + this option in combination with IPv6 addresses may cause an + incompatible cookie. It ties the authentication ticket to that + individual's IPv6 address. ``timeout`` -- cgit v1.2.3 From 23a7c6d7835ca0745968d522a6d9b329d45bc635 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 19 Mar 2013 16:11:42 -0700 Subject: update CHANGES.txt --- CHANGES.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index d087bf43b..86257cc22 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,12 @@ Features ``pyramid.config.Configurator.add_static_view``. This allows externally-hosted static URLs to be generated based on the current protocol. +- The ``AuthTktAuthenticationPolicy`` now supports IPv6 addresses when using + the ``include_ip=True`` option. This is possibly incompatible with + alternative ``auth_tkt`` implementations, as the specification does not + define how to properly handle IPv6. See + https://github.com/Pylons/pyramid/issues/831. + Bug Fixes --------- -- cgit v1.2.3