| | |
| | | Features |
| | | -------- |
| | | |
| | | - ``pyramid.authentication.AuthTktAuthenticationPolicy`` has been updated to |
| | | support newer hashing algorithms such as ``sha512``. Existing applications |
| | | should consider updating if possible. |
| | | |
| | | - Added an ``effective_principals`` route and view predicate. |
| | | |
| | | - Do not allow the userid returned from the ``authenticated_userid`` or the |
| | |
| | | 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. |
| | | - ``pyramid.authentication.AuthTktAuthenticationPolicy`` will emit a warning |
| | | if an application is using the policy without explicitly setting the |
| | | ``hashalg``. This is because the default is "md5" which is considered |
| | | insecure. If you really want "md5" then you must specify it explicitly to |
| | | get rid of the warning. |
| | | |
| | | Internals |
| | | --------- |
| | |
| | | |
| | | - 1.6: Remove IContextURL and TraversalContextURL. |
| | | |
| | | - 1.7: Change ``pyramid.authentication.AuthTktAuthenticationPolicy`` default |
| | | ``hashalg`` to ``sha512``. |
| | | |
| | | Probably Bad Ideas |
| | | ------------------ |
| | | |
| | |
| | | |
| | | .. automodule:: pyramid.authentication |
| | | |
| | | .. autoclass:: SHA512AuthTktAuthenticationPolicy |
| | | :members: |
| | | :inherited-members: |
| | | |
| | | .. autoclass:: AuthTktAuthenticationPolicy |
| | | :members: |
| | | :inherited-members: |
| | |
| | | :linenos: |
| | | |
| | | from pyramid.config import Configurator |
| | | from pyramid.authentication import SHA512AuthTktAuthenticationPolicy |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | authentication_policy = SHA512AuthTktAuthenticationPolicy('seekrit') |
| | | authorization_policy = ACLAuthorizationPolicy() |
| | | authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config = Configurator() |
| | | config.set_authentication_policy(authentication_policy) |
| | | config.set_authorization_policy(authorization_policy) |
| | | config.set_authentication_policy(authn_policy) |
| | | config.set_authorization_policy(authz_policy) |
| | | |
| | | .. note:: the ``authentication_policy`` and ``authorization_policy`` |
| | | arguments may also be passed to their respective methods mentioned above |
| | |
| | | |
| | | (Only the highlighted lines need to be added.) |
| | | |
| | | We are enabling an ``SHA512AuthTktAuthenticationPolicy``, it is based in an |
| | | 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.SHA512AuthTktAuthenticationPolicy` |
| | | 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 |
| | |
| | | from pyramid.config import Configurator |
| | | from pyramid_zodbconn import get_connection |
| | | |
| | | from pyramid.authentication import SHA512AuthTktAuthenticationPolicy |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | |
| | | from .models import appmaker |
| | |
| | | def main(global_config, **settings): |
| | | """ This function returns a WSGI application. |
| | | """ |
| | | authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret', |
| | | callback=groupfinder) |
| | | authn_policy = AuthTktAuthenticationPolicy( |
| | | 'sosecret', callback=groupfinder, hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config = Configurator(root_factory=root_factory, settings=settings) |
| | | config.set_authentication_policy(authn_policy) |
| | |
| | | from pyramid.config import Configurator |
| | | from pyramid_zodbconn import get_connection |
| | | |
| | | from pyramid.authentication import SHA512AuthTktAuthenticationPolicy |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | |
| | | from .models import appmaker |
| | |
| | | def main(global_config, **settings): |
| | | """ This function returns a WSGI application. |
| | | """ |
| | | authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret', |
| | | callback=groupfinder) |
| | | authn_policy = AuthTktAuthenticationPolicy( |
| | | 'sosecret', callback=groupfinder, hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config = Configurator(root_factory=root_factory, settings=settings) |
| | | config.set_authentication_policy(authn_policy) |
| | |
| | | |
| | | (Only the highlighted lines need to be added.) |
| | | |
| | | We are enabling an ``SHA512AuthTktAuthenticationPolicy``, it is based in an |
| | | 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.SHA512AuthTktAuthenticationPolicy` |
| | | 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 |
| | |
| | | from pyramid.config import Configurator |
| | | from pyramid.authentication import SHA512AuthTktAuthenticationPolicy |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | |
| | | from sqlalchemy import engine_from_config |
| | |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.bind = engine |
| | | authn_policy = SHA512AuthTktAuthenticationPolicy( |
| | | 'sosecret', callback=groupfinder) |
| | | authn_policy = AuthTktAuthenticationPolicy( |
| | | 'sosecret', callback=groupfinder, hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config = Configurator(settings=settings, |
| | | root_factory='tutorial.models.RootFactory') |
| | |
| | | from pyramid.config import Configurator |
| | | from pyramid.authentication import SHA512AuthTktAuthenticationPolicy |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | |
| | | from sqlalchemy import engine_from_config |
| | |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.bind = engine |
| | | authn_policy = SHA512AuthTktAuthenticationPolicy( |
| | | 'sosecret', callback=groupfinder) |
| | | authn_policy = AuthTktAuthenticationPolicy( |
| | | 'sosecret', callback=groupfinder, hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config = Configurator(settings=settings, |
| | | root_factory='tutorial.models.RootFactory') |
| | |
| | | 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 ( |
| | |
| | | be done somewhere else or in a subclass.""" |
| | | return [] |
| | | |
| | | class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): |
| | | _marker = object() |
| | | |
| | | @implementer(IAuthenticationPolicy) |
| | | class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): |
| | | """A :app:`Pyramid` :term:`authentication policy` which |
| | | obtains data from a Pyramid "auth ticket" cookie. |
| | | |
| | | .. warning:: |
| | | |
| | | The default hash algorithm used in this policy is MD5 and has known |
| | | hash collision vulnerabilities. The risk of an exploit is low. |
| | | However, for improved authentication security, use |
| | | ``hashalg='sha512'``. |
| | | |
| | | Constructor Arguments |
| | | |
| | |
| | | wildcard domain. |
| | | Optional. |
| | | |
| | | ``hashalg`` |
| | | |
| | | Default: ``md5`` (the literal string). |
| | | |
| | | Any hash algorithm supported by Python's ``hashlib.new()`` function |
| | | can be used as the ``hashalg``. |
| | | |
| | | Cookies generated by different instances of AuthTktAuthenticationPolicy |
| | | using different ``hashalg`` options are not compatible. Switching the |
| | | ``hashalg`` will imply that all existing users with a valid cookie will |
| | | be required to re-login. |
| | | |
| | | A warning is emitted at startup if an explicit ``hashalg`` is not |
| | | passed. This is for backwards compatibility reasons. |
| | | |
| | | This option is available as of :app:`Pyramid` 1.4. |
| | | |
| | | Optional. |
| | | |
| | | .. note:: |
| | | |
| | | ``md5`` is the default for backwards compatibility reasons. However, |
| | | if you don't specify ``md5`` as the hashalg explicitly, a warning is |
| | | issued at application startup time. An explicit value of ``sha512`` |
| | | is recommended for improved security, and ``sha512`` will become the |
| | | default in a future Pyramid version. |
| | | |
| | | ``debug`` |
| | | |
| | | Default: ``False``. If ``debug`` is ``True``, log messages to the |
| | |
| | | Objects of this class implement the interface described by |
| | | :class:`pyramid.interfaces.IAuthenticationPolicy`. |
| | | """ |
| | | hashalg = '' |
| | | |
| | | def __init__(self, |
| | | secret, |
| | |
| | | http_only=False, |
| | | wild_domain=True, |
| | | debug=False, |
| | | hashalg=_marker |
| | | ): |
| | | if hashalg is _marker: |
| | | hashalg = 'md5' |
| | | warnings.warn( |
| | | 'The MD5 hash function used by default by the ' |
| | | 'AuthTktAuthenticationPolicy is known to be ' |
| | | 'susceptible to collision attacks. It is the current default ' |
| | | 'for backwards compatibility reasons, but we recommend that ' |
| | | 'you use the SHA512 algorithm instead for improved security. ' |
| | | 'Pass ``hashalg=\'sha512\'`` to the ' |
| | | 'AuthTktAuthenticationPolicy constructor to do so.\n\nNote ' |
| | | 'that a change to the hash algorithms will invalidate existing ' |
| | | 'auth tkt cookies set by your application. If backwards ' |
| | | 'compatibility of existing auth tkt cookies is of greater ' |
| | | 'concern than the risk posed by the potential for a hash ' |
| | | 'collision, you\'ll want to continue using MD5 explicitly. ' |
| | | 'To do so, pass ``hashalg=\'md5\'`` in your application to ' |
| | | 'the AuthTktAuthenticationPolicy constructor. When you do so ' |
| | | 'this warning will not be emitted again. The default ' |
| | | 'algorithm used in this policy will change in the future, so ' |
| | | 'setting an explicit hashalg will futureproof your ' |
| | | 'application.', |
| | | DeprecationWarning, |
| | | stacklevel=2 |
| | | ) |
| | | self.cookie = AuthTktCookieHelper( |
| | | secret, |
| | | cookie_name=cookie_name, |
| | |
| | | http_only=http_only, |
| | | path=path, |
| | | wild_domain=wild_domain, |
| | | hashalg=self.hashalg, |
| | | hashalg=hashalg, |
| | | ) |
| | | self.callback = callback |
| | | self.debug = debug |
| | |
| | | def forget(self, request): |
| | | """ A list of headers which will delete appropriate cookies.""" |
| | | return self.cookie.forget(request) |
| | | |
| | | @implementer(IAuthenticationPolicy) |
| | | class SHA512AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy): |
| | | __doc__ = """.. versionadded:: 1.4 |
| | | |
| | | """ + BaseAuthTktAuthenticationPolicy.__doc__ |
| | | hashalg = 'sha512' |
| | | |
| | | @implementer(IAuthenticationPolicy) |
| | | class AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy): |
| | | __doc__ = """ |
| | | .. warning:: |
| | | |
| | | Deprecated in 1.4 due to security concerns, |
| | | use :class:`SHA512AuthTktAuthenticationPolicy` instead. |
| | | |
| | | """ + BaseAuthTktAuthenticationPolicy.__doc__ |
| | | hashalg = 'md5' |
| | | |
| | | 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'') |
| | |
| | | config.add_view(protectedview, name='protected', permission='view') |
| | | config.add_view(routeview, route_name='aroute') |
| | | config.add_route('aroute', '/route') |
| | | config.set_authentication_policy(AuthTktAuthenticationPolicy('seekri1t')) |
| | | config.set_authentication_policy(AuthTktAuthenticationPolicy( |
| | | 'seekri1t', hashalg='sha512')) |
| | | config.set_authorization_policy(ACLAuthorizationPolicy()) |
| | | config.include('pyramid.tests.pkgs.conflictapp.included') |
| | |
| | | def includeme(config): |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | authn_policy = AuthTktAuthenticationPolicy('seekt1t') |
| | | authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config.scan('pyramid.tests.pkgs.defpermbugapp') |
| | | config._set_authentication_policy(authn_policy) |
| | |
| | | def includeme(config): |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | authn_policy = AuthTktAuthenticationPolicy('seekr1t') |
| | | authn_policy = AuthTktAuthenticationPolicy('seekr1t', hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config._set_authentication_policy(authn_policy) |
| | | config._set_authorization_policy(authz_policy) |
| | |
| | | return Response('OK bar') |
| | | |
| | | def includeme(config): |
| | | authn_policy = AuthTktAuthenticationPolicy('seekri1') |
| | | authn_policy = AuthTktAuthenticationPolicy('seekri1', hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config.set_authentication_policy(authn_policy) |
| | | config.set_authorization_policy(authz_policy) |
| | |
| | | def includeme(config): |
| | | from pyramid.authentication import AuthTktAuthenticationPolicy |
| | | from pyramid.authorization import ACLAuthorizationPolicy |
| | | authn_policy = AuthTktAuthenticationPolicy('seekt1t') |
| | | authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') |
| | | authz_policy = ACLAuthorizationPolicy() |
| | | config.set_authentication_policy(authn_policy) |
| | | config.set_authorization_policy(authz_policy) |
| | |
| | | import unittest |
| | | import warnings |
| | | from pyramid import testing |
| | | from pyramid.compat import ( |
| | | text_, |
| | |
| | | self.assertEqual(result, []) |
| | | |
| | | 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 |
| | |
| | | inst.cookie = DummyCookieHelper(cookieidentity) |
| | | return inst |
| | | |
| | | def test_is_subclass(self): |
| | | from pyramid.authentication import BaseAuthTktAuthenticationPolicy |
| | | inst = self._makeOne(None, None) |
| | | self.assertTrue(isinstance(inst, BaseAuthTktAuthenticationPolicy)) |
| | | def setUp(self): |
| | | self.warnings = warnings.catch_warnings() |
| | | self.warnings.__enter__() |
| | | warnings.simplefilter('ignore', DeprecationWarning) |
| | | |
| | | def test_md5(self): |
| | | inst = self._makeOne(None, None) |
| | | self.assertEqual(inst.hashalg, 'md5') |
| | | |
| | | def test_class_implements_IAuthenticationPolicy(self): |
| | | from zope.interface.verify import verifyClass |
| | | from pyramid.interfaces import IAuthenticationPolicy |
| | | verifyClass(IAuthenticationPolicy, self._getTargetClass()) |
| | | |
| | | def test_instance_implements_IAuthenticationPolicy(self): |
| | | from zope.interface.verify import verifyObject |
| | | from pyramid.interfaces import IAuthenticationPolicy |
| | | verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) |
| | | |
| | | class TestSHA512AuthTktAuthenticationPolicy(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.authentication import SHA512AuthTktAuthenticationPolicy |
| | | return SHA512AuthTktAuthenticationPolicy |
| | | |
| | | def _makeOne(self, callback, cookieidentity, **kw): |
| | | inst = self._getTargetClass()('secret', callback, **kw) |
| | | inst.cookie = DummyCookieHelper(cookieidentity) |
| | | return inst |
| | | |
| | | def test_is_subclass(self): |
| | | from pyramid.authentication import BaseAuthTktAuthenticationPolicy |
| | | inst = self._makeOne(None, None) |
| | | self.assertTrue(isinstance(inst, BaseAuthTktAuthenticationPolicy)) |
| | | |
| | | def test_sha512(self): |
| | | inst = self._makeOne(None, None) |
| | | self.assertEqual(inst.hashalg, 'sha512') |
| | | |
| | | def test_class_implements_IAuthenticationPolicy(self): |
| | | from zope.interface.verify import verifyClass |
| | | from pyramid.interfaces import IAuthenticationPolicy |
| | | verifyClass(IAuthenticationPolicy, self._getTargetClass()) |
| | | |
| | | def test_instance_implements_IAuthenticationPolicy(self): |
| | | from zope.interface.verify import verifyObject |
| | | from pyramid.interfaces import IAuthenticationPolicy |
| | | verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) |
| | | |
| | | class TestBaseAutkTktAuthenticationPolicy(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.authentication import BaseAuthTktAuthenticationPolicy |
| | | return BaseAuthTktAuthenticationPolicy |
| | | |
| | | def _makeOne(self, callback, cookieidentity, **kw): |
| | | inst = self._getTargetClass()('secret', callback, **kw) |
| | | inst.cookie = DummyCookieHelper(cookieidentity) |
| | | return inst |
| | | def tearDown(self): |
| | | self.warnings.__exit__(None, None, None) |
| | | |
| | | def test_allargs(self): |
| | | # pass all known args |
| | | inst = self._getTargetClass()( |
| | | 'secret', callback=None, cookie_name=None, secure=False, |
| | | include_ip=False, timeout=None, reissue_time=None, |
| | | hashalg='sha512', |
| | | ) |
| | | self.assertEqual(inst.callback, None) |
| | | |
| | | def test_hashalg_override(self): |
| | | # important to ensure hashalg is passed to cookie helper |
| | | inst = self._getTargetClass()('secret', hashalg='sha512') |
| | | self.assertEqual(inst.cookie.hashalg, 'sha512') |
| | | |
| | | def test_unauthenticated_userid_returns_None(self): |
| | | request = DummyRequest({}) |
| | |
| | | result = policy.forget(request) |
| | | self.assertEqual(result, []) |
| | | |
| | | def test_class_implements_IAuthenticationPolicy(self): |
| | | from zope.interface.verify import verifyClass |
| | | from pyramid.interfaces import IAuthenticationPolicy |
| | | verifyClass(IAuthenticationPolicy, self._getTargetClass()) |
| | | |
| | | def test_instance_implements_IAuthenticationPolicy(self): |
| | | from zope.interface.verify import verifyObject |
| | | from pyramid.interfaces import IAuthenticationPolicy |
| | | verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) |
| | | |
| | | class TestAuthTktCookieHelper(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.authentication import AuthTktCookieHelper |