Re-raise/wrap auth attribute errors

This commit is contained in:
Ryan P Kilby 2017-11-15 19:55:29 -05:00
parent 0d5d11b277
commit fba2a8ab0f
2 changed files with 31 additions and 7 deletions

View File

@ -10,6 +10,9 @@ The wrapped request then offers a richer API, in particular :
"""
from __future__ import unicode_literals
import sys
from contextlib import contextmanager
from django.conf import settings
from django.http import QueryDict
from django.http.multipartparser import parse_header
@ -59,6 +62,24 @@ class override_method(object):
self.view.action = self.action
class WrappedAttributeError(Exception):
pass
@contextmanager
def wrap_attributeerrors():
"""
Used to re-raise AttributeErrors caught during authentication, preventing
these errors from otherwise being handled by the attribute access protocol.
"""
try:
yield
except AttributeError:
info = sys.exc_info()
exc = WrappedAttributeError(str(info[1]))
six.reraise(type(exc), exc, info[2])
class Empty(object):
"""
Placeholder for unset attributes.
@ -191,7 +212,8 @@ class Request(object):
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
self._authenticate()
with wrap_attributeerrors():
self._authenticate()
return self._user
@user.setter
@ -214,7 +236,8 @@ class Request(object):
request, such as an authentication token.
"""
if not hasattr(self, '_auth'):
self._authenticate()
with wrap_attributeerrors():
self._authenticate()
return self._auth
@auth.setter
@ -233,7 +256,8 @@ class Request(object):
to authenticate the request, or `None`.
"""
if not hasattr(self, '_authenticator'):
self._authenticate()
with wrap_attributeerrors():
self._authenticate()
return self._authenticator
def _load_data_and_files(self):
@ -316,7 +340,7 @@ class Request(object):
try:
parsed = parser.parse(stream, media_type, self.parser_context)
except:
except Exception:
# If we get an exception during parsing, fill in empty data and
# re-raise. Ensures we don't simply repeat the error when
# attempting to render the browsable renderer response, or when

View File

@ -19,7 +19,7 @@ from django.utils import six
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.parsers import BaseParser, FormParser, MultiPartParser
from rest_framework.request import Request
from rest_framework.request import Request, WrappedAttributeError
from rest_framework.response import Response
from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.views import APIView
@ -227,10 +227,10 @@ class TestUserSetter(TestCase):
# The DRF request object does not have a user and should run authenticators
expected = r"no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'"
with pytest.raises(AttributeError, match=expected):
with pytest.raises(WrappedAttributeError, match=expected):
request.user
with pytest.raises(AttributeError, match=expected):
with pytest.raises(WrappedAttributeError, match=expected):
login(request, self.user)