mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-23 15:54:16 +03:00
Auth is no longer lazy. Closes #667.
More consistent auth failure behavior.
This commit is contained in:
parent
4e14b26fa9
commit
13b3af0d22
|
@ -10,7 +10,7 @@ Authentication is the mechanism of associating an incoming request with a set of
|
|||
|
||||
REST framework provides a number of authentication schemes out of the box, and also allows you to implement custom schemes.
|
||||
|
||||
Authentication will run the first time either the `request.user` or `request.auth` properties are accessed, and determines how those properties are initialized.
|
||||
Authentication is always run at the very start of the view, before the permission and throttling checks occur, and before any other code is allowed to proceed.
|
||||
|
||||
The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class.
|
||||
|
||||
|
@ -191,7 +191,7 @@ In some circumstances instead of returning `None`, you may want to raise an `Aut
|
|||
Typically the approach you should take is:
|
||||
|
||||
* If authentication is not attempted, return `None`. Any other authentication schemes also in use will still be checked.
|
||||
* If authentication is attempted but fails, raise a `AuthenticationFailed` exception. An error response will be returned immediately, without checking any other authentication schemes.
|
||||
* If authentication is attempted but fails, raise a `AuthenticationFailed` exception. An error response will be returned immediately, regardless of any permissions checks, and without checking any other authentication schemes.
|
||||
|
||||
You *may* also override the `.authenticate_header(self, request)` method. If implemented, it should return a string that will be used as the value of the `WWW-Authenticate` header in a `HTTP 401 Unauthorized` response.
|
||||
|
||||
|
|
|
@ -3,25 +3,39 @@ from django.contrib.auth.models import User
|
|||
from django.http import HttpResponse
|
||||
from django.test import Client, TestCase
|
||||
from rest_framework import HTTP_HEADER_ENCODING
|
||||
from rest_framework import exceptions
|
||||
from rest_framework import permissions
|
||||
from rest_framework import status
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication
|
||||
from rest_framework.authentication import (
|
||||
BaseAuthentication,
|
||||
TokenAuthentication,
|
||||
BasicAuthentication,
|
||||
SessionAuthentication
|
||||
)
|
||||
from rest_framework.compat import patterns
|
||||
from rest_framework.tests.utils import RequestFactory
|
||||
from rest_framework.views import APIView
|
||||
import json
|
||||
import base64
|
||||
|
||||
|
||||
factory = RequestFactory()
|
||||
|
||||
|
||||
class MockView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get(self, request):
|
||||
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
||||
|
||||
def post(self, request):
|
||||
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
||||
|
||||
def put(self, request):
|
||||
return HttpResponse({'a': 1, 'b': 2, 'c': 3})
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^session/$', MockView.as_view(authentication_classes=[SessionAuthentication])),
|
||||
(r'^basic/$', MockView.as_view(authentication_classes=[BasicAuthentication])),
|
||||
|
@ -187,3 +201,24 @@ class TokenAuthTests(TestCase):
|
|||
{'username': self.username, 'password': self.password})
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(json.loads(response.content.decode('ascii'))['token'], self.key)
|
||||
|
||||
|
||||
class IncorrectCredentialsTests(TestCase):
|
||||
def test_incorrect_credentials(self):
|
||||
"""
|
||||
If a request contains bad authentication credentials, then
|
||||
authentication should run and error, even if no permissions
|
||||
are set on the view.
|
||||
"""
|
||||
class IncorrectCredentialsAuth(BaseAuthentication):
|
||||
def authenticate(self, request):
|
||||
raise exceptions.AuthenticationFailed('Bad credentials')
|
||||
|
||||
request = factory.get('/')
|
||||
view = MockView.as_view(
|
||||
authentication_classes=(IncorrectCredentialsAuth,),
|
||||
permission_classes=()
|
||||
)
|
||||
response = view(request)
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertEqual(response.data, {'detail': 'Bad credentials'})
|
||||
|
|
|
@ -257,6 +257,16 @@ class APIView(View):
|
|||
return (renderers[0], renderers[0].media_type)
|
||||
raise
|
||||
|
||||
def perform_authentication(self, request):
|
||||
"""
|
||||
Perform authentication on the incoming request.
|
||||
|
||||
Note that if you override this and simply 'pass', then authentication
|
||||
will instead be performed lazily, the first time either
|
||||
`request.user` or `request.auth` is accessed.
|
||||
"""
|
||||
request.user
|
||||
|
||||
def check_permissions(self, request):
|
||||
"""
|
||||
Check if the request should be permitted.
|
||||
|
@ -305,6 +315,7 @@ class APIView(View):
|
|||
self.format_kwarg = self.get_format_suffix(**kwargs)
|
||||
|
||||
# Ensure that the incoming request is permitted
|
||||
self.perform_authentication(request)
|
||||
self.check_permissions(request)
|
||||
self.check_throttles(request)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user