diff options
| author | Chris McDonough <chrism@plope.com> | 2013-10-20 14:04:27 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2013-10-20 14:04:27 -0400 |
| commit | 0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7 (patch) | |
| tree | 41479dfa730206adebc19958b51e92f410e45dfe | |
| parent | 7d0b6ade3c77e4d512f193f86e074b94dc0ed8af (diff) | |
| parent | da295e4336fe04c11640ce8857adb214deaf2a93 (diff) | |
| download | pyramid-0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7.tar.gz pyramid-0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7.tar.bz2 pyramid-0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7.zip | |
Merge branch 'feature.bad-csrf-token-exception'
| -rw-r--r-- | CHANGES.txt | 5 | ||||
| -rw-r--r-- | docs/api/exceptions.rst | 2 | ||||
| -rw-r--r-- | docs/api/httpexceptions.rst | 6 | ||||
| -rw-r--r-- | pyramid/exceptions.py | 15 | ||||
| -rw-r--r-- | pyramid/httpexceptions.py | 8 | ||||
| -rw-r--r-- | pyramid/session.py | 6 | ||||
| -rw-r--r-- | pyramid/tests/test_exceptions.py | 6 | ||||
| -rw-r--r-- | pyramid/tests/test_session.py | 5 |
8 files changed, 45 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 6fdc08398..e215b21bc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,11 @@ Features python -3 -m pyramid.scripts.pserve development.ini +- Added a specific subclass of ``HTTPBadRequest`` named + ``pyramid.exceptions.BadCSRFToken`` which will now be raised in response + to failures in ``check_csrf_token``. + See https://github.com/Pylons/pyramid/pull/1149 + - Added a new ``SignedCookieSessionFactory`` which is very similar to the ``UnencryptedCookieSessionFactoryConfig`` but with a clearer focus on signing content. The custom serializer arguments to this function should diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst index ab158f18d..0c630571f 100644 --- a/docs/api/exceptions.rst +++ b/docs/api/exceptions.rst @@ -5,6 +5,8 @@ .. automodule:: pyramid.exceptions + .. autoclass:: BadCSRFToken + .. autoclass:: PredicateMismatch .. autoclass:: Forbidden diff --git a/docs/api/httpexceptions.rst b/docs/api/httpexceptions.rst index 6a08d1048..b50f10beb 100644 --- a/docs/api/httpexceptions.rst +++ b/docs/api/httpexceptions.rst @@ -7,9 +7,9 @@ .. attribute:: status_map - A mapping of integer status code to exception class (eg. the - integer "401" maps to - :class:`pyramid.httpexceptions.HTTPUnauthorized`). + A mapping of integer status code to HTTP exception class (eg. the integer + "401" maps to :class:`pyramid.httpexceptions.HTTPUnauthorized`). All + mapped exception classes are children of :class:`pyramid.httpexceptions`, .. autofunction:: exception_response diff --git a/pyramid/exceptions.py b/pyramid/exceptions.py index a8fca1d84..c59d109df 100644 --- a/pyramid/exceptions.py +++ b/pyramid/exceptions.py @@ -1,4 +1,5 @@ from pyramid.httpexceptions import ( + HTTPBadRequest, HTTPNotFound, HTTPForbidden, ) @@ -8,6 +9,20 @@ Forbidden = HTTPForbidden # bw compat CR = '\n' +class BadCSRFToken(HTTPBadRequest): + """ + This exception indicates the request has failed cross-site request + forgery token validation. + """ + title = 'Bad CSRF Token' + explanation = ( + 'Access is denied. This server can not verify that your cross-site ' + 'request forgery token belongs to your login session. Either you ' + 'supplied the wrong cross-site request forgery token or your session ' + 'no longer exists. This may be due to session timeout or because ' + 'browser is not supplying the credentials required, as can happen ' + 'when the browser has cookies turned off.') + class PredicateMismatch(HTTPNotFound): """ This exception is raised by multiviews when no view matches diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py index fff17b2df..ebee39ada 100644 --- a/pyramid/httpexceptions.py +++ b/pyramid/httpexceptions.py @@ -565,6 +565,14 @@ class HTTPClientError(HTTPError): 'it is either malformed or otherwise incorrect.') class HTTPBadRequest(HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This indicates that the body or headers failed validity checks, + preventing the server from being able to continue processing. + + code: 400, title: Bad Request + """ pass class HTTPUnauthorized(HTTPClientError): diff --git a/pyramid/session.py b/pyramid/session.py index 2471d94ad..9e0733661 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -16,7 +16,7 @@ from pyramid.compat import ( native_, ) -from pyramid.httpexceptions import HTTPBadRequest +from pyramid.exceptions import BadCSRFToken from pyramid.interfaces import ISession from pyramid.util import strings_differ @@ -102,7 +102,7 @@ def check_csrf_token(request, If the value supplied by param or by header doesn't match the value supplied by ``request.session.get_csrf_token()``, and ``raises`` is ``True``, this function will raise an - :exc:`pyramid.httpexceptions.HTTPBadRequest` exception. + :exc:`pyramid.exceptions.BadCSRFToken` 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. @@ -115,7 +115,7 @@ def check_csrf_token(request, supplied_token = request.params.get(token, request.headers.get(header)) if supplied_token != request.session.get_csrf_token(): if raises: - raise HTTPBadRequest('incorrect CSRF token') + raise BadCSRFToken('check_csrf_token(): Invalid token') return False return True diff --git a/pyramid/tests/test_exceptions.py b/pyramid/tests/test_exceptions.py index aa5ebb376..993209046 100644 --- a/pyramid/tests/test_exceptions.py +++ b/pyramid/tests/test_exceptions.py @@ -11,6 +11,12 @@ class TestBWCompat(unittest.TestCase): from pyramid.httpexceptions import HTTPForbidden as two self.assertTrue(one is two) +class TestBadCSRFToken(unittest.TestCase): + def test_response_equivalence(self): + from pyramid.exceptions import BadCSRFToken + from pyramid.httpexceptions import HTTPBadRequest + self.assertTrue(isinstance(BadCSRFToken(), HTTPBadRequest)) + class TestNotFound(unittest.TestCase): def _makeOne(self, message): from pyramid.exceptions import NotFound diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py index eba123ce5..c13d3ce5c 100644 --- a/pyramid/tests/test_session.py +++ b/pyramid/tests/test_session.py @@ -568,9 +568,10 @@ class Test_check_csrf_token(unittest.TestCase): self.assertEqual(self._callFUT(request), True) def test_failure_raises(self): - from pyramid.httpexceptions import HTTPBadRequest + from pyramid.exceptions import BadCSRFToken request = testing.DummyRequest() - self.assertRaises(HTTPBadRequest, self._callFUT, request, 'csrf_token') + self.assertRaises(BadCSRFToken, self._callFUT, request, + 'csrf_token') def test_failure_no_raises(self): request = testing.DummyRequest() |
