summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@digitalartefacts.com>2013-10-19 01:37:11 -0500
committerMichael Merickel <michael@digitalartefacts.com>2013-10-19 01:37:11 -0500
commit7fd86623a0d77d390980c5dc09f49dde2f781091 (patch)
tree71375e2fa9c74a5289ce21cf7f5442acc0da5e76
parent3acee31f86bcde8abbb4e63715afc5ca67976eaf (diff)
parent0905d2015e35e827c3fdb2135695710b80d549a5 (diff)
downloadpyramid-7fd86623a0d77d390980c5dc09f49dde2f781091.tar.gz
pyramid-7fd86623a0d77d390980c5dc09f49dde2f781091.tar.bz2
pyramid-7fd86623a0d77d390980c5dc09f49dde2f781091.zip
Merge branch 'check_csrf_token' of kpinc/pyramid into feature.bad-csrf-token-exception
-rw-r--r--docs/api/httpexceptions.rst19
-rw-r--r--pyramid/httpexceptions.py40
-rw-r--r--pyramid/session.py6
-rw-r--r--pyramid/tests/test_session.py5
4 files changed, 58 insertions, 12 deletions
diff --git a/docs/api/httpexceptions.rst b/docs/api/httpexceptions.rst
index 6a08d1048..0fdd0f0e9 100644
--- a/docs/api/httpexceptions.rst
+++ b/docs/api/httpexceptions.rst
@@ -7,9 +7,12 @@
.. 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`,
+ i.e. the :ref:`pyramid_specific_http_exceptions` such as
+ :class:`pyramid.httpexceptions.HTTPBadRequest.BadCSRFToken` are not
+ mapped.
.. autofunction:: exception_response
@@ -106,3 +109,13 @@
.. autoclass:: HTTPVersionNotSupported
.. autoclass:: HTTPInsufficientStorage
+
+
+.. _pyramid_specific_http_exceptions:
+
+Pyramid-specific HTTP Exceptions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each Pyramid-specific HTTP exception has the status code of it's parent.
+
+ .. autoclass:: HTTPBadCSRFToken
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index fff17b2df..21d862a6b 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -2,10 +2,13 @@
HTTP Exceptions
---------------
-This module contains Pyramid HTTP exception classes. Each class relates to a
-single HTTP status code. Each class is a subclass of the
-:class:`~HTTPException`. Each exception class is also a :term:`response`
-object.
+This module contains Pyramid HTTP exception classes. Each class is a subclass
+of the :class:`~HTTPException`. Each class relates to a single HTTP status
+code, although the reverse is not true. There are
+:ref:`pyramid_specific_http_exceptions` which are sub-classes of the
+:rfc:`2608` HTTP status codes. Each of these Pyramid-specific exceptions have
+the status code of it's parent. Each exception class is also a
+:term:`response` object.
Each exception class has a status code according to :rfc:`2068`:
codes with 100-300 are not really errors; 400s are client errors,
@@ -32,6 +35,9 @@ Exception
HTTPError
HTTPClientError
* 400 - HTTPBadRequest
+
+ * 400 - HTTPBadCSRFToken
+
* 401 - HTTPUnauthorized
* 402 - HTTPPaymentRequired
* 403 - HTTPForbidden
@@ -565,8 +571,34 @@ class HTTPClientError(HTTPError):
'it is either malformed or otherwise incorrect.')
class HTTPBadRequest(HTTPClientError):
+ """
+ subclass of :class:`~HTTPClientError`
+
+ base class for Pyramid-specific validity checks of the client's request
+
+ This class and it's sub-classes result in a '400 Bad Request' HTTP status,
+ although it's sub-classes specialize the 'Bad Request' text.
+ """
pass
+class HTTPBadCSRFToken(HTTPClientError):
+ """
+ subclass of :class:`~HTTPBadRequest`
+
+ This indicates the request has failed cross-site request forgery token
+ validation.
+
+ title: Bad CSRF Token
+ """
+ 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 HTTPUnauthorized(HTTPClientError):
"""
subclass of :class:`~HTTPClientError`
diff --git a/pyramid/session.py b/pyramid/session.py
index 3708ef879..72b69117c 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -15,7 +15,7 @@ from pyramid.compat import (
native_,
)
-from pyramid.httpexceptions import HTTPBadRequest
+from pyramid.httpexceptions import HTTPBadCSRFToken
from pyramid.interfaces import ISession
from pyramid.util import strings_differ
@@ -95,7 +95,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.httpexceptions.HTTPBadCSRFToken` 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.
@@ -108,7 +108,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 HTTPBadCSRFToken('check_csrf_token(): Invalid token')
return False
return True
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 35e2b5c27..a928af43e 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -381,9 +381,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.httpexceptions import HTTPBadCSRFToken
request = testing.DummyRequest()
- self.assertRaises(HTTPBadRequest, self._callFUT, request, 'csrf_token')
+ self.assertRaises(HTTPBadCSRFToken, self._callFUT, request,
+ 'csrf_token')
def test_failure_no_raises(self):
request = testing.DummyRequest()