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 from __future__ import unicode_literals
import sys
from contextlib import contextmanager
from django.conf import settings from django.conf import settings
from django.http import QueryDict from django.http import QueryDict
from django.http.multipartparser import parse_header from django.http.multipartparser import parse_header
@ -59,6 +62,24 @@ class override_method(object):
self.view.action = self.action 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): class Empty(object):
""" """
Placeholder for unset attributes. Placeholder for unset attributes.
@ -191,7 +212,8 @@ class Request(object):
by the authentication classes provided to the request. by the authentication classes provided to the request.
""" """
if not hasattr(self, '_user'): if not hasattr(self, '_user'):
self._authenticate() with wrap_attributeerrors():
self._authenticate()
return self._user return self._user
@user.setter @user.setter
@ -214,7 +236,8 @@ class Request(object):
request, such as an authentication token. request, such as an authentication token.
""" """
if not hasattr(self, '_auth'): if not hasattr(self, '_auth'):
self._authenticate() with wrap_attributeerrors():
self._authenticate()
return self._auth return self._auth
@auth.setter @auth.setter
@ -233,7 +256,8 @@ class Request(object):
to authenticate the request, or `None`. to authenticate the request, or `None`.
""" """
if not hasattr(self, '_authenticator'): if not hasattr(self, '_authenticator'):
self._authenticate() with wrap_attributeerrors():
self._authenticate()
return self._authenticator return self._authenticator
def _load_data_and_files(self): def _load_data_and_files(self):
@ -316,7 +340,7 @@ class Request(object):
try: try:
parsed = parser.parse(stream, media_type, self.parser_context) parsed = parser.parse(stream, media_type, self.parser_context)
except: except Exception:
# If we get an exception during parsing, fill in empty data and # If we get an exception during parsing, fill in empty data and
# re-raise. Ensures we don't simply repeat the error when # re-raise. Ensures we don't simply repeat the error when
# attempting to render the browsable renderer response, or 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 import status
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from rest_framework.parsers import BaseParser, FormParser, MultiPartParser 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.response import Response
from rest_framework.test import APIClient, APIRequestFactory from rest_framework.test import APIClient, APIRequestFactory
from rest_framework.views import APIView 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 # The DRF request object does not have a user and should run authenticators
expected = r"no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'" expected = r"no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'"
with pytest.raises(AttributeError, match=expected): with pytest.raises(WrappedAttributeError, match=expected):
request.user request.user
with pytest.raises(AttributeError, match=expected): with pytest.raises(WrappedAttributeError, match=expected):
login(request, self.user) login(request, self.user)