Skip to content

Commit fba2a8a

Browse files
author
Ryan P Kilby
committed
Re-raise/wrap auth attribute errors
1 parent 0d5d11b commit fba2a8a

2 files changed

Lines changed: 31 additions & 7 deletions

File tree

rest_framework/request.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
"""
1111
from __future__ import unicode_literals
1212

13+
import sys
14+
from contextlib import contextmanager
15+
1316
from django.conf import settings
1417
from django.http import QueryDict
1518
from django.http.multipartparser import parse_header
@@ -59,6 +62,24 @@ def __exit__(self, *args, **kwarg):
5962
self.view.action = self.action
6063

6164

65+
class WrappedAttributeError(Exception):
66+
pass
67+
68+
69+
@contextmanager
70+
def wrap_attributeerrors():
71+
"""
72+
Used to re-raise AttributeErrors caught during authentication, preventing
73+
these errors from otherwise being handled by the attribute access protocol.
74+
"""
75+
try:
76+
yield
77+
except AttributeError:
78+
info = sys.exc_info()
79+
exc = WrappedAttributeError(str(info[1]))
80+
six.reraise(type(exc), exc, info[2])
81+
82+
6283
class Empty(object):
6384
"""
6485
Placeholder for unset attributes.
@@ -191,7 +212,8 @@ def user(self):
191212
by the authentication classes provided to the request.
192213
"""
193214
if not hasattr(self, '_user'):
194-
self._authenticate()
215+
with wrap_attributeerrors():
216+
self._authenticate()
195217
return self._user
196218

197219
@user.setter
@@ -214,7 +236,8 @@ def auth(self):
214236
request, such as an authentication token.
215237
"""
216238
if not hasattr(self, '_auth'):
217-
self._authenticate()
239+
with wrap_attributeerrors():
240+
self._authenticate()
218241
return self._auth
219242

220243
@auth.setter
@@ -233,7 +256,8 @@ def successful_authenticator(self):
233256
to authenticate the request, or `None`.
234257
"""
235258
if not hasattr(self, '_authenticator'):
236-
self._authenticate()
259+
with wrap_attributeerrors():
260+
self._authenticate()
237261
return self._authenticator
238262

239263
def _load_data_and_files(self):
@@ -316,7 +340,7 @@ def _parse(self):
316340

317341
try:
318342
parsed = parser.parse(stream, media_type, self.parser_context)
319-
except:
343+
except Exception:
320344
# If we get an exception during parsing, fill in empty data and
321345
# re-raise. Ensures we don't simply repeat the error when
322346
# attempting to render the browsable renderer response, or when

tests/test_request.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from rest_framework import status
2020
from rest_framework.authentication import SessionAuthentication
2121
from rest_framework.parsers import BaseParser, FormParser, MultiPartParser
22-
from rest_framework.request import Request
22+
from rest_framework.request import Request, WrappedAttributeError
2323
from rest_framework.response import Response
2424
from rest_framework.test import APIClient, APIRequestFactory
2525
from rest_framework.views import APIView
@@ -227,10 +227,10 @@ def authenticate(self, request):
227227

228228
# The DRF request object does not have a user and should run authenticators
229229
expected = r"no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'"
230-
with pytest.raises(AttributeError, match=expected):
230+
with pytest.raises(WrappedAttributeError, match=expected):
231231
request.user
232232

233-
with pytest.raises(AttributeError, match=expected):
233+
with pytest.raises(WrappedAttributeError, match=expected):
234234
login(request, self.user)
235235

236236

0 commit comments

Comments
 (0)