summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2013-03-19 16:20:11 -0700
committerMichael Merickel <michael@merickel.org>2013-03-19 16:20:11 -0700
commit411acbba3402aba9d09856a0c4cb88f572ba77da (patch)
treef13caa93c1c3b9bad3b0068a37c343735e324718
parent5491a8e4da58d08279d6023a33d76369296e94f7 (diff)
parent23a7c6d7835ca0745968d522a6d9b329d45bc635 (diff)
downloadpyramid-411acbba3402aba9d09856a0c4cb88f572ba77da.tar.gz
pyramid-411acbba3402aba9d09856a0c4cb88f572ba77da.tar.bz2
pyramid-411acbba3402aba9d09856a0c4cb88f572ba77da.zip
Merge branch 'fix.831'
-rw-r--r--CHANGES.txt6
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--pyramid/authentication.py20
-rw-r--r--pyramid/tests/test_authentication.py52
4 files changed, 75 insertions, 5 deletions
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
---------
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
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 4f6ed2c1d..bc0286ed3 100644
--- a/pyramid/authentication.py
+++ b/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)
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index 123e4f9f5..cfabf9a9d 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')
@@ -1098,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
@@ -1141,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