Skip to content

Commit d69f44a

Browse files
author
Ryan P Kilby
committed
Re-raise/wrap auth attribute errors
1 parent 22ec160 commit d69f44a

File tree

2 files changed

+29
-12
lines changed

2 files changed

+29
-12
lines changed

rest_framework/request.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from __future__ import unicode_literals
1212

1313
import sys
14+
from contextlib import contextmanager
1415

1516
from django.conf import settings
1617
from django.http import QueryDict
@@ -61,6 +62,24 @@ def __exit__(self, *args, **kwarg):
6162
self.view.action = self.action
6263

6364

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+
6483
class Empty(object):
6584
"""
6685
Placeholder for unset attributes.
@@ -193,7 +212,8 @@ def user(self):
193212
by the authentication classes provided to the request.
194213
"""
195214
if not hasattr(self, '_user'):
196-
self._authenticate()
215+
with wrap_attributeerrors():
216+
self._authenticate()
197217
return self._user
198218

199219
@user.setter
@@ -216,7 +236,8 @@ def auth(self):
216236
request, such as an authentication token.
217237
"""
218238
if not hasattr(self, '_auth'):
219-
self._authenticate()
239+
with wrap_attributeerrors():
240+
self._authenticate()
220241
return self._auth
221242

222243
@auth.setter
@@ -235,7 +256,8 @@ def successful_authenticator(self):
235256
to authenticate the request, or `None`.
236257
"""
237258
if not hasattr(self, '_authenticator'):
238-
self._authenticate()
259+
with wrap_attributeerrors():
260+
self._authenticate()
239261
return self._authenticator
240262

241263
def _load_data_and_files(self):
@@ -317,7 +339,7 @@ def _parse(self):
317339

318340
try:
319341
parsed = parser.parse(stream, media_type, self.parser_context)
320-
except:
342+
except Exception:
321343
# If we get an exception during parsing, fill in empty data and
322344
# re-raise. Ensures we don't simply repeat the error when
323345
# attempting to render the browsable renderer response, or when
@@ -380,11 +402,6 @@ def __getattribute__(self, attr):
380402
try:
381403
return super(Request, self).__getattribute__(attr)
382404
except AttributeError:
383-
# Do not proxy attribute access for `user` and `auth`, as this
384-
# potentially hides AttributeError's raised in `_authenticate`.
385-
if attr in ['user', 'auth']:
386-
raise
387-
388405
info = sys.exc_info()
389406
try:
390407
return getattr(self._request, attr)

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)