mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-04 12:30:11 +03:00
Merge cf2cf5c3b5
into 48b66ec2a2
This commit is contained in:
commit
3ae1b9225c
|
@ -83,7 +83,7 @@ When an unauthenticated request is denied permission there are two different err
|
||||||
|
|
||||||
HTTP 401 responses must always include a `WWW-Authenticate` header, that instructs the client how to authenticate. HTTP 403 responses do not include the `WWW-Authenticate` header.
|
HTTP 401 responses must always include a `WWW-Authenticate` header, that instructs the client how to authenticate. HTTP 403 responses do not include the `WWW-Authenticate` header.
|
||||||
|
|
||||||
The kind of response that will be used depends on the authentication scheme. Although multiple authentication schemes may be in use, only one scheme may be used to determine the type of response. **The first authentication class set on the view is used when determining the type of response**.
|
The kind of response that will be used depends on the authentication scheme. Although multiple authentication schemes may be in use, only one scheme may be used to determine the type of response. The first authentication class set on the view that can provide a valid `WWW-Authenticate` header is used when determining the type of response.
|
||||||
|
|
||||||
Note that when a request may successfully authenticate, but still be denied permission to perform the request, in which case a `403 Permission Denied` response will always be used, regardless of the authentication scheme.
|
Note that when a request may successfully authenticate, but still be denied permission to perform the request, in which case a `403 Permission Denied` response will always be used, regardless of the authentication scheme.
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,9 @@ urlpatterns = patterns('',
|
||||||
(r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
|
(r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
|
||||||
(r'^oauth/$', MockView.as_view(authentication_classes=[OAuthAuthentication])),
|
(r'^oauth/$', MockView.as_view(authentication_classes=[OAuthAuthentication])),
|
||||||
(r'^oauth-with-scope/$', MockView.as_view(authentication_classes=[OAuthAuthentication],
|
(r'^oauth-with-scope/$', MockView.as_view(authentication_classes=[OAuthAuthentication],
|
||||||
permission_classes=[permissions.TokenHasReadWriteScope]))
|
permission_classes=[permissions.TokenHasReadWriteScope])),
|
||||||
|
(r'^session-and-basic/$', MockView.as_view(
|
||||||
|
authentication_classes=[SessionAuthentication, BasicAuthentication])),
|
||||||
)
|
)
|
||||||
|
|
||||||
class OAuth2AuthenticationDebug(OAuth2Authentication):
|
class OAuth2AuthenticationDebug(OAuth2Authentication):
|
||||||
|
@ -641,6 +643,37 @@ class OAuth2Tests(TestCase):
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class SessionAndBasicAuthTests(TestCase):
|
||||||
|
"""Session + basic authentication"""
|
||||||
|
urls = 'rest_framework.tests.test_authentication'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.csrf_client = APIClient(enforce_csrf_checks=True)
|
||||||
|
self.non_csrf_client = APIClient(enforce_csrf_checks=False)
|
||||||
|
self.username = 'john'
|
||||||
|
self.email = 'lennon@thebeatles.com'
|
||||||
|
self.password = 'password'
|
||||||
|
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||||
|
|
||||||
|
def test_post_form_basic_auth_passing(self):
|
||||||
|
credentials = ('%s:%s' % (self.username, self.password))
|
||||||
|
base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING)
|
||||||
|
auth = 'Basic %s' % base64_credentials
|
||||||
|
response = self.csrf_client.post('/session-and-basic/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_post_form_session_auth_passing(self):
|
||||||
|
self.non_csrf_client.login(username=self.username, password=self.password)
|
||||||
|
response = self.non_csrf_client.post('/session-and-basic/', {'example': 'example'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_post_form_no_auth_failing(self):
|
||||||
|
response = self.non_csrf_client.post('/session-and-basic/', {'example': 'example'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||||
|
response_headers = dict(response.items())
|
||||||
|
self.assertEqual(response_headers.get('WWW-Authenticate'), 'Basic realm="api"')
|
||||||
|
|
||||||
|
|
||||||
class FailingAuthAccessedInRenderer(TestCase):
|
class FailingAuthAccessedInRenderer(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class AuthAccessingRenderer(renderers.BaseRenderer):
|
class AuthAccessingRenderer(renderers.BaseRenderer):
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.authentication import BasicAuthentication
|
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
||||||
from rest_framework.parsers import JSONParser
|
from rest_framework.parsers import JSONParser
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAdminUser
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
from rest_framework.test import APIRequestFactory
|
from rest_framework.test import APIRequestFactory
|
||||||
|
@ -132,8 +132,13 @@ class DecoratorTestCase(TestCase):
|
||||||
def test_permission_classes(self):
|
def test_permission_classes(self):
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
@permission_classes([IsAuthenticated])
|
@authentication_classes([SessionAuthentication])
|
||||||
|
@permission_classes([IsAdminUser])
|
||||||
def view(request):
|
def view(request):
|
||||||
|
self.assertEqual(len(request.permission_classes), 1)
|
||||||
|
self.assertTrue(isinstance(request.permission_classes[0],
|
||||||
|
IsAdminUser))
|
||||||
|
|
||||||
return Response({})
|
return Response({})
|
||||||
|
|
||||||
request = self.factory.get('/')
|
request = self.factory.get('/')
|
||||||
|
|
|
@ -147,8 +147,10 @@ class APIView(View):
|
||||||
header to use for 401 responses, if any.
|
header to use for 401 responses, if any.
|
||||||
"""
|
"""
|
||||||
authenticators = self.get_authenticators()
|
authenticators = self.get_authenticators()
|
||||||
if authenticators:
|
for authenticator in authenticators:
|
||||||
return authenticators[0].authenticate_header(request)
|
header = authenticator.authenticate_header(request)
|
||||||
|
if header:
|
||||||
|
return header
|
||||||
|
|
||||||
def get_parser_context(self, http_request):
|
def get_parser_context(self, http_request):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user