Skip to content
Browse files

Merge branch 'master' of github.com:Pylons/pyramid

  • Loading branch information...
2 parents 05a7625 + 411acbb commit 2f7aa7c6a7740f58744d51bdeaaf25a3e037d32c @mcdonc mcdonc committed Mar 19, 2013
View
10 CHANGES.txt
@@ -11,6 +11,16 @@ 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.
+
+- 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
---------
View
2 CONTRIBUTORS.txt
@@ -192,3 +192,5 @@ Contributors
- Robert Jackiewicz, 2012/11/12
- John Anderson, 2012/11/14
+
+- Bert JW Regeer, 2013/02/01
View
2 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.
View
7 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:
View
20 pyramid/authentication.py
@@ -450,6 +450,12 @@ 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. 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``
Default: ``None``. Maximum number of seconds which a newly
@@ -736,9 +742,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)
View
24 pyramid/config/views.py
@@ -1793,6 +1793,10 @@ def add_static_view(self, name, path, **kw):
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 @@ def add_static_view(self, name, path, **kw):
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 @@ def generate(self, path, request, **kw):
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 @@ def add(self, config, name, spec, **extra):
# 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
View
52 pyramid/tests/test_authentication.py
@@ -561,9 +561,13 @@ def _makeOne(self, *arg, **kw):
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 @@ def test_identify_good_cookie_include_ip(self):
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')
@@ -1098,6 +1119,20 @@ def test_cookie_value(self):
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
@@ -1141,6 +1176,19 @@ def test_correct_with_user_data_sha512(self):
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
View
7 pyramid/tests/test_config/test_views.py
@@ -3737,6 +3737,13 @@ def test_add_url_noendslash(self):
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
View
15 pyramid/tests/test_url.py
@@ -583,6 +583,21 @@ def test_static_url_abspath_integration_with_staticurlinfo(self):
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()

0 comments on commit 2f7aa7c

Please sign in to comment.
Something went wrong with that request. Please try again.