Skip to content

Commit

Permalink
fix docs, upgrade tutorials, add change note, deprecate using zope.de…
Browse files Browse the repository at this point in the history
…precation instead of a warning, make hashalg arg a kwarg in certain cases in case someone (maybe me) is using nonapi function imports from authentication
  • Loading branch information
mcdonc committed Nov 4, 2012
1 parent eb1807f commit 0487545
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 48 deletions.
20 changes: 19 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,32 @@ Bug Fixes
attribute of the request. It no longer fails in this case. See
https://github.com/Pylons/pyramid/issues/700

Deprecations
------------

- The ``pyramid.authentication.AuthTktAuthenticationPolicy`` authentication
policy is deprecated in Pyramid 1.4 due to its use of the MD5 hashing
algorithm, which has known hash collision vulnerabilities. The risk of an
exploit is low. However, for improved authentication security, use the
``pyramid.authentication.SHA512AuthTktAuthenticationPolicy`` instead.
Cookies generated by the AuthTktAuthenticationPolicy are not compatible with
cookies generated by the SHA512AuthTktAuthenticationPolicy, however, so
switching to the latter will imply that all existing users with a valid
cookie will be required to re-login. The SHA-512 version is not compatible
with Apache's mod_auth_tkt either, so if you are relying on that
compatibility, you'll want to stick with the MD5 version.

A deprecation warning is now emitted when the AuthTktAuthenticationPolicy is
imported.

Internals
---------

- Move ``TopologicalSorter`` from ``pyramid.config.util`` to ``pyramid.util``,
move ``CyclicDependencyError`` from ``pyramid.config.util`` to
``pyramid.exceptions``, rename ``Singleton`` to ``Sentinel`` and move from
``pyramid.config.util`` to ``pyramid.config.util``; this is in an effort to
move that stuff that may be an API one day out of ``pyramid.config.util,
move that stuff that may be an API one day out of ``pyramid.config.util``,
because that package should never be imported from non-Pyramid code.
TopologicalSorter is still not an API, but may become one.

Expand Down
2 changes: 2 additions & 0 deletions docs/api/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Authentication Policies
.. automodule:: pyramid.authentication

.. autoclass:: SHA512AuthTktAuthenticationPolicy
:members:
:inherited-members:

.. autoclass:: AuthTktAuthenticationPolicy
:members:
Expand Down
4 changes: 2 additions & 2 deletions docs/narr/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ For example:
:linenos:
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
authentication_policy = AuthTktAuthenticationPolicy('seekrit')
authentication_policy = SHA512AuthTktAuthenticationPolicy('seekrit')
authorization_policy = ACLAuthorizationPolicy()
config = Configurator()
config.set_authentication_policy(authentication_policy)
Expand Down
18 changes: 9 additions & 9 deletions docs/tutorials/wiki/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ Now add those policies to the configuration:

(Only the highlighted lines need to be added.)

We are enabling an ``AuthTktAuthenticationPolicy``, it is based in an auth
ticket that may be included in the request, and an ``ACLAuthorizationPolicy``
that uses an ACL to determine the allow or deny outcome for a view.

Note that the
:class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor
accepts two arguments: ``secret`` and ``callback``. ``secret`` is a string
representing an encryption key used by the "authentication ticket" machinery
represented by this policy: it is required. The ``callback`` is the
We are enabling an ``SHA512AuthTktAuthenticationPolicy``, it is based in an
auth ticket that may be included in the request, and an
``ACLAuthorizationPolicy`` that uses an ACL to determine the allow or deny
outcome for a view.

Note that the :class:`pyramid.authentication.SHA512AuthTktAuthenticationPolicy`
constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is
a string representing an encryption key used by the "authentication ticket"
machinery represented by this policy: it is required. The ``callback`` is the
``groupfinder()`` function that we created before.

Add permission declarations
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorials/wiki/src/authorization/tutorial/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pyramid.config import Configurator
from pyramid_zodbconn import get_connection

from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy

from .models import appmaker
Expand All @@ -14,8 +14,8 @@ def root_factory(request):
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
authn_policy = AuthTktAuthenticationPolicy(secret='sosecret',
callback=groupfinder)
authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret',
callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=root_factory, settings=settings)
config.set_authentication_policy(authn_policy)
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorials/wiki/src/tests/tutorial/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pyramid.config import Configurator
from pyramid_zodbconn import get_connection

from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy

from .models import appmaker
Expand All @@ -14,8 +14,8 @@ def root_factory(request):
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
authn_policy = AuthTktAuthenticationPolicy(secret='sosecret',
callback=groupfinder)
authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret',
callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=root_factory, settings=settings)
config.set_authentication_policy(authn_policy)
Expand Down
18 changes: 9 additions & 9 deletions docs/tutorials/wiki2/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ Now add those policies to the configuration:

(Only the highlighted lines need to be added.)

We are enabling an ``AuthTktAuthenticationPolicy``, it is based in an auth
ticket that may be included in the request, and an ``ACLAuthorizationPolicy``
that uses an ACL to determine the allow or deny outcome for a view.

Note that the
:class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor
accepts two arguments: ``secret`` and ``callback``. ``secret`` is a string
representing an encryption key used by the "authentication ticket" machinery
represented by this policy: it is required. The ``callback`` is the
We are enabling an ``SHA512AuthTktAuthenticationPolicy``, it is based in an
auth ticket that may be included in the request, and an
``ACLAuthorizationPolicy`` that uses an ACL to determine the allow or deny
outcome for a view.

Note that the :class:`pyramid.authentication.SHA512AuthTktAuthenticationPolicy`
constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is
a string representing an encryption key used by the "authentication ticket"
machinery represented by this policy: it is required. The ``callback`` is the
``groupfinder()`` function that we created before.

Add permission declarations
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy

from sqlalchemy import engine_from_config
Expand All @@ -17,7 +17,7 @@ def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
authn_policy = AuthTktAuthenticationPolicy(
authn_policy = SHA512AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/wiki2/src/tests/tutorial/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy

from sqlalchemy import engine_from_config
Expand All @@ -17,7 +17,7 @@ def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
authn_policy = AuthTktAuthenticationPolicy(
authn_policy = SHA512AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
Expand Down
40 changes: 25 additions & 15 deletions pyramid/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import datetime
import re
import time as time_mod
import warnings

from zope.deprecation import deprecated
from zope.interface import implementer

from pyramid.compat import (
Expand Down Expand Up @@ -407,7 +407,7 @@ def forget(self, request):
return []

class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
"""A :app:`Pyramid` :term:`authentication policy` which
obtains data from a Pyramid "auth ticket" cookie.
Constructor Arguments
Expand Down Expand Up @@ -562,8 +562,8 @@ def forget(self, request):

@implementer(IAuthenticationPolicy)
class SHA512AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy):
__doc__ = """
.. versionadded:: 1.4
__doc__ = """.. versionadded:: 1.4
""" + BaseAuthTktAuthenticationPolicy.__doc__
hashalg = 'sha512'

Expand All @@ -578,13 +578,20 @@ class AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy):
""" + BaseAuthTktAuthenticationPolicy.__doc__
hashalg = 'md5'

def __init__(self, *a, **kw):
warnings.warn('Deprecated due to the usage of md5, '
'hash function known to have collisions. '
'Use SHA512AuthTktAuthenticationPolicy instead.',
DeprecationWarning,
stacklevel=2)
super(AuthTktAuthenticationPolicy, self).__init__(*a, **kw)
deprecated(
'AuthTktAuthenticationPolicy',
'The AuthTktAuthenticationPolicy is deprecated in Pyramid 1.4 '
'due to its use of the MD5 hashing algorithm, which has known '
'hash collision vulnerabilities. The risk of an exploit is low. '
'However, for improved authentication security, use the '
'pyramid.authentication.SHA512AuthTktAuthenticationPolicy instead. '
'Cookies generated by the AuthTktAuthenticationPolicy are *not* '
'compatible with cookies generated by the '
'SHA512AuthTktAuthenticationPolicy, however, so switching to the '
'latter will imply that all existing users with a valid cookie '
'will be required to re-login. The SHA-512 version is not compatible '
'with Apache\'s mod_auth_tkt either.'
)

def b64encode(v):
return base64.b64encode(bytes_(v)).strip().replace(b'\n', b'')
Expand Down Expand Up @@ -654,7 +661,7 @@ def __init__(self, msg, expected=None):
Exception.__init__(self, msg)

# this function licensed under the MIT license (stolen from Paste)
def parse_ticket(secret, ticket, ip, hashalg):
def parse_ticket(secret, ticket, ip, hashalg='md5'):
"""
Parse the ticket, returning (timestamp, userid, tokens, user_data).
Expand Down Expand Up @@ -694,7 +701,8 @@ def parse_ticket(secret, ticket, ip, hashalg):
return (timestamp, userid, tokens, user_data)

# this function licensed under the MIT license (stolen from Paste)
def calculate_digest(ip, timestamp, secret, userid, tokens, user_data, hashalg):
def calculate_digest(ip, timestamp, secret, userid, tokens, user_data,
hashalg='md5'):
secret = bytes_(secret, 'utf-8')
userid = bytes_(userid, 'utf-8')
tokens = bytes_(tokens, 'utf-8')
Expand Down Expand Up @@ -749,7 +757,8 @@ class AuthTktCookieHelper(object):

def __init__(self, secret, cookie_name='auth_tkt', secure=False,
include_ip=False, timeout=None, reissue_time=None,
max_age=None, http_only=False, path="/", wild_domain=True, hashalg='md5'):
max_age=None, http_only=False, path="/", wild_domain=True,
hashalg='md5'):
self.secret = secret
self.cookie_name = cookie_name
self.include_ip = include_ip
Expand Down Expand Up @@ -945,7 +954,8 @@ def remember(self, request, userid, max_age=None, tokens=()):
user_data=user_data,
cookie_name=self.cookie_name,
secure=self.secure,
hashalg=self.hashalg)
hashalg=self.hashalg
)

cookie_value = ticket.cookie_value()
return self._get_cookies(environ, cookie_value, max_age)
Expand Down
12 changes: 10 additions & 2 deletions pyramid/tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,15 @@ def test_forget(self):
result = policy.forget(request)
self.assertEqual(result, [])

class TestAutkTktAuthenticationPolicy(unittest.TestCase):
class TestAuthTktAuthenticationPolicy(unittest.TestCase):
def setUp(self):
from zope.deprecation import __show__
__show__.off()

def tearDown(self):
from zope.deprecation import __show__
__show__.on()

def _getTargetClass(self):
from pyramid.authentication import AuthTktAuthenticationPolicy
return AuthTktAuthenticationPolicy
Expand Down Expand Up @@ -459,7 +467,7 @@ def test_instance_implements_IAuthenticationPolicy(self):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne(None, None))

class TestSHA512AutkTktAuthenticationPolicy(unittest.TestCase):
class TestSHA512AuthTktAuthenticationPolicy(unittest.TestCase):
def _getTargetClass(self):
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
return SHA512AuthTktAuthenticationPolicy
Expand Down

0 comments on commit 0487545

Please sign in to comment.