mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-06-06 14:43:06 +03:00
Merge pull request #767 from tomchristie/fix-oauth2-token-only
Fix OAuth 2 token only
This commit is contained in:
commit
a69262a1cd
|
@ -294,7 +294,7 @@ The only thing needed to make the `OAuth2Authentication` class work is to insert
|
||||||
|
|
||||||
The command line to test the authentication looks like:
|
The command line to test the authentication looks like:
|
||||||
|
|
||||||
curl -H "Authorization: Bearer <your-access-token>" http://localhost:8000/api/?client_id=YOUR_CLIENT_ID\&client_secret=YOUR_CLIENT_SECRET
|
curl -H "Authorization: Bearer <your-access-token>" http://localhost:8000/api/
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
Provides a set of pluggable authentication policies.
|
Provides a set of pluggable authentication policies.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import base64
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from rest_framework import exceptions, HTTP_HEADER_ENCODING
|
from rest_framework import exceptions, HTTP_HEADER_ENCODING
|
||||||
from rest_framework.compat import CsrfViewMiddleware
|
from rest_framework.compat import CsrfViewMiddleware
|
||||||
from rest_framework.compat import oauth, oauth_provider, oauth_provider_store
|
from rest_framework.compat import oauth, oauth_provider, oauth_provider_store
|
||||||
from rest_framework.compat import oauth2_provider, oauth2_provider_forms, oauth2_provider_backends
|
from rest_framework.compat import oauth2_provider, oauth2_provider_forms
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
import base64
|
|
||||||
|
|
||||||
|
|
||||||
def get_authorization_header(request):
|
def get_authorization_header(request):
|
||||||
|
@ -315,21 +317,15 @@ class OAuth2Authentication(BaseAuthentication):
|
||||||
Authenticate the request, given the access token.
|
Authenticate the request, given the access token.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Authenticate the client
|
try:
|
||||||
oauth2_client_form = oauth2_provider_forms.ClientAuthForm(request.REQUEST)
|
token = oauth2_provider.models.AccessToken.objects.select_related('user')
|
||||||
if not oauth2_client_form.is_valid():
|
# TODO: Change to timezone aware datetime when oauth2_provider add
|
||||||
raise exceptions.AuthenticationFailed('Client could not be validated')
|
# support to it.
|
||||||
client = oauth2_client_form.cleaned_data.get('client')
|
token = token.get(token=access_token, expires__gt=datetime.now())
|
||||||
|
except oauth2_provider.models.AccessToken.DoesNotExist:
|
||||||
# Retrieve the `OAuth2AccessToken` instance from the access_token
|
|
||||||
auth_backend = oauth2_provider_backends.AccessTokenBackend()
|
|
||||||
token = auth_backend.authenticate(access_token, client)
|
|
||||||
if token is None:
|
|
||||||
raise exceptions.AuthenticationFailed('Invalid token')
|
raise exceptions.AuthenticationFailed('Invalid token')
|
||||||
|
|
||||||
user = token.user
|
if not token.user.is_active:
|
||||||
|
|
||||||
if not user.is_active:
|
|
||||||
msg = 'User inactive or deleted: %s' % user.username
|
msg = 'User inactive or deleted: %s' % user.username
|
||||||
raise exceptions.AuthenticationFailed(msg)
|
raise exceptions.AuthenticationFailed(msg)
|
||||||
|
|
||||||
|
|
|
@ -476,14 +476,12 @@ except ImportError:
|
||||||
# OAuth 2 support is optional
|
# OAuth 2 support is optional
|
||||||
try:
|
try:
|
||||||
import provider.oauth2 as oauth2_provider
|
import provider.oauth2 as oauth2_provider
|
||||||
from provider.oauth2 import backends as oauth2_provider_backends
|
|
||||||
from provider.oauth2 import models as oauth2_provider_models
|
from provider.oauth2 import models as oauth2_provider_models
|
||||||
from provider.oauth2 import forms as oauth2_provider_forms
|
from provider.oauth2 import forms as oauth2_provider_forms
|
||||||
from provider import scope as oauth2_provider_scope
|
from provider import scope as oauth2_provider_scope
|
||||||
from provider import constants as oauth2_constants
|
from provider import constants as oauth2_constants
|
||||||
except ImportError:
|
except ImportError:
|
||||||
oauth2_provider = None
|
oauth2_provider = None
|
||||||
oauth2_provider_backends = None
|
|
||||||
oauth2_provider_models = None
|
oauth2_provider_models = None
|
||||||
oauth2_provider_forms = None
|
oauth2_provider_forms = None
|
||||||
oauth2_provider_scope = None
|
oauth2_provider_scope = None
|
||||||
|
|
|
@ -466,17 +466,13 @@ class OAuth2Tests(TestCase):
|
||||||
def _create_authorization_header(self, token=None):
|
def _create_authorization_header(self, token=None):
|
||||||
return "Bearer {0}".format(token or self.access_token.token)
|
return "Bearer {0}".format(token or self.access_token.token)
|
||||||
|
|
||||||
def _client_credentials_params(self):
|
|
||||||
return {'client_id': self.CLIENT_ID, 'client_secret': self.CLIENT_SECRET}
|
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
def test_get_form_with_wrong_authorization_header_token_type_failing(self):
|
def test_get_form_with_wrong_authorization_header_token_type_failing(self):
|
||||||
"""Ensure that a wrong token type lead to the correct HTTP error status code"""
|
"""Ensure that a wrong token type lead to the correct HTTP error status code"""
|
||||||
auth = "Wrong token-type-obsviously"
|
auth = "Wrong token-type-obsviously"
|
||||||
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
@ -485,8 +481,7 @@ class OAuth2Tests(TestCase):
|
||||||
auth = "Bearer wrong token format"
|
auth = "Bearer wrong token format"
|
||||||
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
@ -495,33 +490,21 @@ class OAuth2Tests(TestCase):
|
||||||
auth = "Bearer wrong-token"
|
auth = "Bearer wrong-token"
|
||||||
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 401)
|
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
|
||||||
def test_get_form_with_wrong_client_data_failing_auth(self):
|
|
||||||
"""Ensure GETing form over OAuth with incorrect client credentials fails"""
|
|
||||||
auth = self._create_authorization_header()
|
|
||||||
params = self._client_credentials_params()
|
|
||||||
params['client_id'] += 'a'
|
|
||||||
response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
def test_get_form_passing_auth(self):
|
def test_get_form_passing_auth(self):
|
||||||
"""Ensure GETing form over OAuth with correct client credentials succeed"""
|
"""Ensure GETing form over OAuth with correct client credentials succeed"""
|
||||||
auth = self._create_authorization_header()
|
auth = self._create_authorization_header()
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
def test_post_form_passing_auth(self):
|
def test_post_form_passing_auth(self):
|
||||||
"""Ensure POSTing form over OAuth with correct credentials passes and does not require CSRF"""
|
"""Ensure POSTing form over OAuth with correct credentials passes and does not require CSRF"""
|
||||||
auth = self._create_authorization_header()
|
auth = self._create_authorization_header()
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.post('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
@ -529,16 +512,14 @@ class OAuth2Tests(TestCase):
|
||||||
"""Ensure POSTing when there is no OAuth access token in db fails"""
|
"""Ensure POSTing when there is no OAuth access token in db fails"""
|
||||||
self.access_token.delete()
|
self.access_token.delete()
|
||||||
auth = self._create_authorization_header()
|
auth = self._create_authorization_header()
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.post('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
def test_post_form_with_refresh_token_failing_auth(self):
|
def test_post_form_with_refresh_token_failing_auth(self):
|
||||||
"""Ensure POSTing with refresh token instead of access token fails"""
|
"""Ensure POSTing with refresh token instead of access token fails"""
|
||||||
auth = self._create_authorization_header(token=self.refresh_token.token)
|
auth = self._create_authorization_header(token=self.refresh_token.token)
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.post('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
@ -547,8 +528,7 @@ class OAuth2Tests(TestCase):
|
||||||
self.access_token.expires = datetime.datetime.now() - datetime.timedelta(seconds=10) # 10 seconds late
|
self.access_token.expires = datetime.datetime.now() - datetime.timedelta(seconds=10) # 10 seconds late
|
||||||
self.access_token.save()
|
self.access_token.save()
|
||||||
auth = self._create_authorization_header()
|
auth = self._create_authorization_header()
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.post('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
||||||
self.assertIn('Invalid token', response.content)
|
self.assertIn('Invalid token', response.content)
|
||||||
|
|
||||||
|
@ -559,10 +539,9 @@ class OAuth2Tests(TestCase):
|
||||||
read_only_access_token.scope = oauth2_provider_scope.SCOPE_NAME_DICT['read']
|
read_only_access_token.scope = oauth2_provider_scope.SCOPE_NAME_DICT['read']
|
||||||
read_only_access_token.save()
|
read_only_access_token.save()
|
||||||
auth = self._create_authorization_header(token=read_only_access_token.token)
|
auth = self._create_authorization_header(token=read_only_access_token.token)
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.get('/oauth2-with-scope-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.get('/oauth2-with-scope-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
response = self.csrf_client.post('/oauth2-with-scope-test/', params, HTTP_AUTHORIZATION=auth)
|
response = self.csrf_client.post('/oauth2-with-scope-test/', HTTP_AUTHORIZATION=auth)
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
@ -572,6 +551,5 @@ class OAuth2Tests(TestCase):
|
||||||
read_write_access_token.scope = oauth2_provider_scope.SCOPE_NAME_DICT['write']
|
read_write_access_token.scope = oauth2_provider_scope.SCOPE_NAME_DICT['write']
|
||||||
read_write_access_token.save()
|
read_write_access_token.save()
|
||||||
auth = self._create_authorization_header(token=read_write_access_token.token)
|
auth = self._create_authorization_header(token=read_write_access_token.token)
|
||||||
params = self._client_credentials_params()
|
response = self.csrf_client.post('/oauth2-with-scope-test/', HTTP_AUTHORIZATION=auth)
|
||||||
response = self.csrf_client.post('/oauth2-with-scope-test/', params, HTTP_AUTHORIZATION=auth)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user