summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2013-10-20 14:04:27 -0400
committerChris McDonough <chrism@plope.com>2013-10-20 14:04:27 -0400
commit0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7 (patch)
tree41479dfa730206adebc19958b51e92f410e45dfe
parent7d0b6ade3c77e4d512f193f86e074b94dc0ed8af (diff)
parentda295e4336fe04c11640ce8857adb214deaf2a93 (diff)
downloadpyramid-0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7.tar.gz
pyramid-0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7.tar.bz2
pyramid-0264cc0c3d4ca091d4d5c8bd0c2fda38a3f8a0c7.zip
Merge branch 'feature.bad-csrf-token-exception'
-rw-r--r--CHANGES.txt5
-rw-r--r--docs/api/exceptions.rst2
-rw-r--r--docs/api/httpexceptions.rst6
-rw-r--r--pyramid/exceptions.py15
-rw-r--r--pyramid/httpexceptions.py8
-rw-r--r--pyramid/session.py6
-rw-r--r--pyramid/tests/test_exceptions.py6
-rw-r--r--pyramid/tests/test_session.py5
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()