Refs #32329 -- Allowed specifying request class in csrf_tests test hooks.

This commit is contained in:
Virtosu Bogdan 2021-07-23 12:13:31 +02:00 committed by Mariusz Felisiak
parent 6b513f0137
commit 852fa7617e
1 changed files with 39 additions and 43 deletions

View File

@ -99,6 +99,23 @@ class TestingHttpRequest(HttpRequest):
return getattr(self, '_is_secure_override', False) return getattr(self, '_is_secure_override', False)
class PostErrorRequest(TestingHttpRequest):
"""
TestingHttpRequest that can raise errors when accessing POST data.
"""
post_error = None
def _get_post(self):
if self.post_error is not None:
raise self.post_error
return self._post
def _set_post(self, post):
self._post = post
POST = property(_get_post, _set_post)
class CsrfViewMiddlewareTestMixin: class CsrfViewMiddlewareTestMixin:
""" """
Shared methods and tests for session-based and cookie-based tokens. Shared methods and tests for session-based and cookie-based tokens.
@ -131,10 +148,12 @@ class CsrfViewMiddlewareTestMixin:
secrets_set = [_unmask_cipher_token(cookie) for cookie in cookies_set] secrets_set = [_unmask_cipher_token(cookie) for cookie in cookies_set]
self.assertEqual(secrets_set, expected_secrets) self.assertEqual(secrets_set, expected_secrets)
def _get_request(self, method=None, cookie=None): def _get_request(self, method=None, cookie=None, request_class=None):
if method is None: if method is None:
method = 'GET' method = 'GET'
req = TestingHttpRequest() if request_class is None:
request_class = TestingHttpRequest
req = request_class()
req.method = method req.method = method
if cookie is not None: if cookie is not None:
self._set_csrf_cookie(req, cookie) self._set_csrf_cookie(req, cookie)
@ -142,7 +161,7 @@ class CsrfViewMiddlewareTestMixin:
def _get_csrf_cookie_request( def _get_csrf_cookie_request(
self, method=None, cookie=None, post_token=None, meta_token=None, self, method=None, cookie=None, post_token=None, meta_token=None,
token_header=None, token_header=None, request_class=None,
): ):
""" """
The method argument defaults to "GET". The cookie argument defaults to The method argument defaults to "GET". The cookie argument defaults to
@ -156,7 +175,11 @@ class CsrfViewMiddlewareTestMixin:
cookie = self._csrf_id_cookie cookie = self._csrf_id_cookie
if token_header is None: if token_header is None:
token_header = 'HTTP_X_CSRFTOKEN' token_header = 'HTTP_X_CSRFTOKEN'
req = self._get_request(method=method, cookie=cookie) req = self._get_request(
method=method,
cookie=cookie,
request_class=request_class,
)
if post_token is not None: if post_token is not None:
req.POST['csrfmiddlewaretoken'] = post_token req.POST['csrfmiddlewaretoken'] = post_token
if meta_token is not None: if meta_token is not None:
@ -165,15 +188,21 @@ class CsrfViewMiddlewareTestMixin:
def _get_POST_csrf_cookie_request( def _get_POST_csrf_cookie_request(
self, cookie=None, post_token=None, meta_token=None, token_header=None, self, cookie=None, post_token=None, meta_token=None, token_header=None,
request_class=None,
): ):
return self._get_csrf_cookie_request( return self._get_csrf_cookie_request(
method='POST', cookie=cookie, post_token=post_token, method='POST', cookie=cookie, post_token=post_token,
meta_token=meta_token, token_header=token_header, meta_token=meta_token, token_header=token_header,
request_class=request_class,
) )
def _get_POST_request_with_token(self, cookie=None): def _get_POST_request_with_token(self, cookie=None, request_class=None):
"""The cookie argument defaults to this class's default test cookie.""" """The cookie argument defaults to this class's default test cookie."""
return self._get_POST_csrf_cookie_request(cookie=cookie, post_token=self._csrf_id_token) return self._get_POST_csrf_cookie_request(
cookie=cookie,
post_token=self._csrf_id_token,
request_class=request_class,
)
def _check_token_present(self, response, csrf_id=None): def _check_token_present(self, response, csrf_id=None):
text = str(response.content, response.charset) text = str(response.content, response.charset)
@ -702,49 +731,16 @@ class CsrfViewMiddlewareTestMixin:
def test_post_data_read_failure(self): def test_post_data_read_failure(self):
""" """
OSErrors during POST data reading are caught and treated as if the OSErrors during POST data reading are caught and treated as if the
POST data wasn't there (#20128). POST data wasn't there.
""" """
class CsrfPostRequest(HttpRequest): req = self._get_POST_request_with_token()
"""
HttpRequest that can raise an OSError when accessing POST data
"""
def __init__(self, token, raise_error):
super().__init__()
self.method = 'POST'
self.raise_error = False
self.COOKIES[settings.CSRF_COOKIE_NAME] = token
# Handle both cases here to prevent duplicate code in the
# session tests.
self.session = {}
self.session[CSRF_SESSION_KEY] = token
self.POST['csrfmiddlewaretoken'] = token
self.raise_error = raise_error
def _load_post_and_files(self):
raise OSError('error reading input data')
def _get_post(self):
if self.raise_error:
self._load_post_and_files()
return self._post
def _set_post(self, post):
self._post = post
POST = property(_get_post, _set_post)
token = ('ABC' + self._csrf_id_token)[:CSRF_TOKEN_LENGTH]
req = CsrfPostRequest(token, raise_error=False)
mw = CsrfViewMiddleware(post_form_view) mw = CsrfViewMiddleware(post_form_view)
mw.process_request(req) mw.process_request(req)
resp = mw.process_view(req, post_form_view, (), {}) resp = mw.process_view(req, post_form_view, (), {})
self.assertIsNone(resp) self.assertIsNone(resp)
req = CsrfPostRequest(token, raise_error=True) req = self._get_POST_request_with_token(request_class=PostErrorRequest)
req.post_error = OSError('error reading input data')
mw.process_request(req) mw.process_request(req)
with self.assertLogs('django.security.csrf', 'WARNING') as cm: with self.assertLogs('django.security.csrf', 'WARNING') as cm:
resp = mw.process_view(req, post_form_view, (), {}) resp = mw.process_view(req, post_form_view, (), {})