Chris McDonough
2012-09-19 68c60204490ba104c592d4de322ce21bdf6df0d3
Merge branch 'master' into 1.4-branch
10 files modified
177 ■■■■■ changed files
docs/glossary.rst 9 ●●●●● patch | view | raw | blame | history
docs/narr/introspector.rst 4 ●●●● patch | view | raw | blame | history
docs/narr/viewconfig.rst 31 ●●●●● patch | view | raw | blame | history
docs/whatsnew-1.4.rst 6 ●●●●● patch | view | raw | blame | history
pyramid/config/predicates.py 23 ●●●●● patch | view | raw | blame | history
pyramid/config/views.py 31 ●●●●● patch | view | raw | blame | history
pyramid/session.py 17 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_predicates.py 43 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_session.py 11 ●●●● patch | view | raw | blame | history
pyramid/view.py 2 ●●● patch | view | raw | blame | history
docs/glossary.rst
@@ -818,9 +818,12 @@
      application.
   session factory
      A callable, which, when called with a single argument named
      ``request`` (a :term:`request` object), returns a
      :term:`session` object.
      A callable, which, when called with a single argument named ``request``
      (a :term:`request` object), returns a :term:`session` object.  See
      :ref:`using_the_default_session_factory`,
      :ref:`using_alternate_session_factories` and
      :meth:`pyramid.config.Configurator.set_session_factory` for more
      information.
   Mako
     `Mako <http://www.makotemplates.org/>`_ is a template language language
docs/narr/introspector.rst
@@ -393,6 +393,10 @@
    The ``match_param`` argument passed to ``add_view``.
  ``csrf_token``
    The ``csrf_token`` argument passed to ``add_view``.
  ``callable``
    The (resolved) ``view`` argument passed to ``add_view``.  Represents the
docs/narr/viewconfig.rst
@@ -394,6 +394,28 @@
  consideration when deciding whether or not to invoke the associated view
  callable.
``check_csrf``
  If specified, this value should be one of ``None``, ``True``, ``False``, or
  a string representing the 'check name'.  If the value is ``True`` or a
  string, CSRF checking will be performed.  If the value is ``False`` or
  ``None``, CSRF checking will not be performed.
  If the value provided is a string, that string will be used as the 'check
  name'.  If the value provided is ``True``, ``csrf_token`` will be used as
  the check name.
  If CSRF checking is performed, the checked value will be the value of
  ``request.params[check_name]``.  This value will be compared against the
  value of ``request.session.get_csrf_token()``, and the check will pass if
  these two values are the same.  If the check passes, the associated view
  will be permitted to execute.  If the check fails, the associated view
  will not be permitted to execute.
  Note that using this feature requires a :term:`session factory` to have
  been configured.
  .. versionadded:: 1.4a2
``custom_predicates``
  If ``custom_predicates`` is specified, it must be a sequence of references
  to custom predicate callables.  Use custom predicates when no set of
@@ -407,6 +429,15 @@
  If ``custom_predicates`` is not specified, no custom predicates are
  used.
``predicates``
  Pass a key/value pair here to use a third-party predicate registered via
  :meth:`pyramid.config.Configurator.add_view_predicate`.  More than one
  key/value pair can be used at the same time.  See
  :ref:`view_and_route_predicates` for more information about third-party
  predicates.
  .. versionadded:: 1.4a1
.. index::
   single: view_config decorator
docs/whatsnew-1.4.rst
@@ -156,6 +156,12 @@
- A new :func:`pyramid.session.check_csrf_token` convenience API function was
  added.
- A ``check_csrf`` view predicate was added.  For example, you can now do
  ``config.add_view(someview, check_csrf=True)``.  When the predicate is
  checked, if the ``csrf_token`` value in ``request.params`` matches the csrf
  token in the request's session, the view will be permitted to execute.
  Otherwise, it will not be permitted to execute.
Backwards Incompatibilities
---------------------------
pyramid/config/predicates.py
@@ -13,6 +13,8 @@
from pyramid.util import object_description
from pyramid.session import check_csrf_token
from .util import as_sorted_tuple
class XHRPredicate(object):
@@ -226,3 +228,24 @@
        # injects ``traverse`` into the matchdict.  As a result, we just
        # return True.
        return True
class CheckCSRFTokenPredicate(object):
    check_csrf_token = staticmethod(check_csrf_token) # testing
    def __init__(self, val, config):
        self.val = val
    def text(self):
        return 'check_csrf = %s' % (self.val,)
    phash = text
    def __call__(self, context, request):
        val = self.val
        if val:
            if val is True:
                val = 'csrf_token'
            return self.check_csrf_token(request, val, raises=False)
        return True
pyramid/config/views.py
@@ -662,6 +662,7 @@
        mapper=None,
        http_cache=None,
        match_param=None,
        check_csrf=None,
        **predicates):
        """ Add a :term:`view configuration` to the current
        configuration state.  Arguments to ``add_view`` are broken
@@ -989,6 +990,29 @@
          variable.  If the regex matches, this predicate will be
          ``True``.
        check_csrf
          If specified, this value should be one of ``None``, ``True``,
          ``False``, or a string representing the 'check name'.  If the value
          is ``True`` or a string, CSRF checking will be performed.  If the
          value is ``False`` or ``None``, CSRF checking will not be performed.
          If the value provided is a string, that string will be used as the
          'check name'.  If the value provided is ``True``, ``csrf_token`` will
          be used as the check name.
          If CSRF checking is performed, the checked value will be the value
          of ``request.params[check_name]``.  This value will be compared
          against the value of ``request.session.get_csrf_token()``, and the
          check will pass if these two values are the same.  If the check
          passes, the associated view will be permitted to execute.  If the
          check fails, the associated view will not be permitted to execute.
          Note that using this feature requires a :term:`session factory` to
          have been configured.
          .. versionadded:: 1.4a2
        custom_predicates
          This value should be a sequence of references to custom
@@ -1007,7 +1031,9 @@
          :meth:`pyramid.config.Configurator.add_view_predicate`.  More than
          one key/value pair can be used at the same time.  See
          :ref:`view_and_route_predicates` for more information about
          third-party predicates.  This argument is new as of Pyramid 1.4.
          third-party predicates.
          .. versionadded: 1.4a1
        """
        view = self.maybe_dotted(view)
@@ -1061,6 +1087,7 @@
                containment=containment,
                request_type=request_type,
                match_param=match_param,
                check_csrf=check_csrf,
                custom=predvalseq(custom_predicates),
                )
            )
@@ -1098,6 +1125,7 @@
                 header=header,
                 path_info=path_info,
                 match_param=match_param,
                 check_csrf=check_csrf,
                 callable=view,
                 mapper=mapper,
                 decorator=decorator,
@@ -1340,6 +1368,7 @@
            ('containment', p.ContainmentPredicate),
            ('request_type', p.RequestTypePredicate),
            ('match_param', p.MatchParamPredicate),
            ('check_csrf', p.CheckCSRFTokenPredicate),
            ('custom', p.CustomPredicate),
            ):
            self.add_view_predicate(name, factory)
pyramid/session.py
@@ -81,19 +81,26 @@
    return pickle.loads(pickled)
def check_csrf_token(request, token='csrf_token'):
def check_csrf_token(request, token='csrf_token', raises=True):
    """ Check the CSRF token in the request's session against the value in
    ``request.params.get(token)``.  If ``token`` is not supplied, the string
    value ``csrf_token`` will be used as the token value.  If the value in
    ``request.params.get(token)`` doesn't match the value supplied by
    ``request.session.get_csrf_token()``, this function will raise an
    :exc:`pyramid.httpexceptions.HTTPBadRequest` exception.  If the CSRF
    check is successful, this function will return ``True``.
    ``request.session.get_csrf_token()``, and ``raises`` is ``True``, this
    function will raise an :exc:`pyramid.httpexceptions.HTTPBadRequest`
    exception.  If the check does succeed and ``raises`` is ``False``, this
    function will return ``False``.  If the CSRF check is successful, this
    function will return ``True`` unconditionally.
    Note that using this function requires that a :term:`session factory` is
    configured.
    .. versionadded:: 1.4a2
    """
    if request.params.get(token) != request.session.get_csrf_token():
        raise HTTPBadRequest('incorrect CSRF token')
        if raises:
            raise HTTPBadRequest('incorrect CSRF token')
        return False
    return True
def UnencryptedCookieSessionFactoryConfig(
pyramid/tests/test_config/test_predicates.py
@@ -256,6 +256,49 @@
        inst = self._makeOne('/abc')
        self.assertEqual(inst.phash(), '')
class Test_CheckCSRFTokenPredicate(unittest.TestCase):
    def _makeOne(self, val, config):
        from pyramid.config.predicates import CheckCSRFTokenPredicate
        return CheckCSRFTokenPredicate(val, config)
    def test_text(self):
        inst = self._makeOne(True, None)
        self.assertEqual(inst.text(), 'check_csrf = True')
    def test_phash(self):
        inst = self._makeOne(True, None)
        self.assertEqual(inst.phash(), 'check_csrf = True')
    def test_it_call_val_True(self):
        inst = self._makeOne(True, None)
        request = Dummy()
        def check_csrf_token(req, val, raises=True):
            self.assertEqual(req, request)
            self.assertEqual(val, 'csrf_token')
            self.assertEqual(raises, False)
            return True
        inst.check_csrf_token  = check_csrf_token
        result = inst(None, request)
        self.assertEqual(result, True)
    def test_it_call_val_str(self):
        inst = self._makeOne('abc', None)
        request = Dummy()
        def check_csrf_token(req, val, raises=True):
            self.assertEqual(req, request)
            self.assertEqual(val, 'abc')
            self.assertEqual(raises, False)
            return True
        inst.check_csrf_token  = check_csrf_token
        result = inst(None, request)
        self.assertEqual(result, True)
    def test_it_call_val_False(self):
        inst = self._makeOne(False, None)
        request = Dummy()
        result = inst(None, request)
        self.assertEqual(result, True)
class predicate(object):
    def __repr__(self):
        return 'predicate'
pyramid/tests/test_session.py
@@ -356,9 +356,9 @@
        self.assertRaises(ValueError, self._callFUT, serialized, 'secret')
        
class Test_check_csrf_token(unittest.TestCase):
    def _callFUT(self, request, token):
    def _callFUT(self, request, token, raises=True):
        from ..session import check_csrf_token
        return check_csrf_token(request, token)
        return check_csrf_token(request, token, raises=raises)
    def test_success(self):
        request = testing.DummyRequest()
@@ -371,11 +371,16 @@
        request.params['csrf_token'] = request.session.get_csrf_token()
        self.assertEqual(check_csrf_token(request), True)
    def test_failure(self):
    def test_failure_raises(self):
        from pyramid.httpexceptions import HTTPBadRequest
        request = testing.DummyRequest()
        self.assertRaises(HTTPBadRequest, self._callFUT, request, 'csrf_token')
    def test_failure_no_raises(self):
        request = testing.DummyRequest()
        result = self._callFUT(request, 'csrf_token', raises=False)
        self.assertEqual(result, False)
class DummySessionFactory(dict):
    _dirty = False
    _cookie_name = 'session'
pyramid/view.py
@@ -170,7 +170,7 @@
    ``request_type``, ``route_name``, ``request_method``, ``request_param``,
    ``containment``, ``xhr``, ``accept``, ``header``, ``path_info``,
    ``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``,
    ``match_param``, and ``predicates``.
    ``match_param``, ``csrf_token``, and ``predicates``.
    The meanings of these arguments are the same as the arguments passed to
    :meth:`pyramid.config.Configurator.add_view`.  If any argument is left