mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-29 17:39:48 +03:00
Merge 5a56f92abf
into 5fffaf89e2
This commit is contained in:
commit
bea6882112
|
@ -14,6 +14,7 @@ env:
|
||||||
install:
|
install:
|
||||||
- pip install $DJANGO
|
- pip install $DJANGO
|
||||||
- pip install defusedxml==0.3
|
- pip install defusedxml==0.3
|
||||||
|
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.3 --use-mirrors; fi"
|
||||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi"
|
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi"
|
||||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi"
|
- "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi"
|
||||||
- export PYTHONPATH=.
|
- export PYTHONPATH=.
|
||||||
|
|
|
@ -207,6 +207,80 @@ Unauthenticated responses that are denied permission will result in an `HTTP 403
|
||||||
|
|
||||||
If you're using an AJAX style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as `PUT`, `PATCH`, `POST` or `DELETE` requests. See the [Django CSRF documentation][csrf-ajax] for more details.
|
If you're using an AJAX style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as `PUT`, `PATCH`, `POST` or `DELETE` requests. See the [Django CSRF documentation][csrf-ajax] for more details.
|
||||||
|
|
||||||
|
## OAuth2Authentication
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
** Note:** This isn't available for Python 3, because the module [`django-oauth2-provider`][django-oauth2-provider] is not Python 3 ready.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This authentication uses [OAuth 2.0][rfc6749] authentication scheme. It depends on the optional [`django-oauth2-provider`][django-oauth2-provider] project. In order to make it work you must install this package and add `provider` and `provider.oauth2` to your `INSTALLED_APPS` :
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
#(...)
|
||||||
|
'provider',
|
||||||
|
'provider.oauth2',
|
||||||
|
)
|
||||||
|
|
||||||
|
And include the urls needed in your root `urls.py` file to be able to begin the *oauth 2 dance* :
|
||||||
|
|
||||||
|
url(r'^oauth2/', include('provider.oauth2.urls', namespace = 'oauth2')),
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
** Note:** The *namespace* argument is required !
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Finally, sync your database with those two new django apps.
|
||||||
|
|
||||||
|
$ python manage.py syncdb
|
||||||
|
$ python manage.py migrate
|
||||||
|
|
||||||
|
`OAuth2Authentication` class provides only token verification for requests. The *oauth 2 dance* is taken care by the [`django-oaut2-provider`][django-oauth2-provider] dependency. The official [documentation][django-oauth2-provider--doc] is being [rewritten][django-oauth2-provider--rewritten-doc].
|
||||||
|
|
||||||
|
The Good news is, here is a minimal "How to start" because **OAuth 2** is dramatically simpler than **OAuth 1**, so no more headache with signature, cryptography on client side, and other complex things.
|
||||||
|
|
||||||
|
### How to start with *django-oauth2-provider* ?
|
||||||
|
|
||||||
|
#### Create a client in the django-admin panel
|
||||||
|
|
||||||
|
Go to the admin panel and create a new `Provider.Client` entry. It will create the `client_id` and `client_secret` properties for you.
|
||||||
|
|
||||||
|
#### Request an access token
|
||||||
|
|
||||||
|
Your client interface – I mean by that your iOS code, HTML code, or whatever else language – just have to submit a `POST` request at the url `/oauth2/access_token` with the following fields :
|
||||||
|
|
||||||
|
* `client_id` the client id you've just configured at the previous step.
|
||||||
|
* `client_secret` again configured at the previous step.
|
||||||
|
* `username` the username with which you want to log in.
|
||||||
|
* `password` well, that speaks for itself.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Note:** Remember that you should use HTTPS in production.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
You can use the command line to test that your local configuration is working :
|
||||||
|
|
||||||
|
$ curl -X POST -d "client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=password&username=YOUR_USERNAME&password=YOUR_PASSWORD" http://localhost:8000/oauth2/access_token/
|
||||||
|
|
||||||
|
Here is the response you should get :
|
||||||
|
|
||||||
|
{"access_token": "<your-access-token>", "scope": "read", "expires_in": 86399, "refresh_token": "<your-refresh-token>"}
|
||||||
|
|
||||||
|
#### Access the api
|
||||||
|
|
||||||
|
The only thing needed to make the `OAuth2Authentication` class work is to insert the `access_token` you've received in the `Authorization` api request header.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
And it will work like a charm.
|
||||||
|
|
||||||
# Custom authentication
|
# Custom authentication
|
||||||
|
|
||||||
To implement a custom authentication scheme, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
|
To implement a custom authentication scheme, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
|
||||||
|
@ -262,3 +336,7 @@ HTTP digest authentication is a widely implemented scheme that was intended to r
|
||||||
[south-dependencies]: http://south.readthedocs.org/en/latest/dependencies.html
|
[south-dependencies]: http://south.readthedocs.org/en/latest/dependencies.html
|
||||||
[juanriaza]: https://github.com/juanriaza
|
[juanriaza]: https://github.com/juanriaza
|
||||||
[djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth
|
[djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth
|
||||||
|
[django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider
|
||||||
|
[django-oauth2-provider--doc]: https://django-oauth2-provider.readthedocs.org/en/latest/
|
||||||
|
[django-oauth2-provider--rewritten-doc]: http://django-oauth2-provider-dulaccc.readthedocs.org/en/latest/
|
||||||
|
[rfc6749]: http://tools.ietf.org/html/rfc6749
|
||||||
|
|
|
@ -2,3 +2,4 @@ markdown>=2.1.0
|
||||||
PyYAML>=3.10
|
PyYAML>=3.10
|
||||||
defusedxml>=0.3
|
defusedxml>=0.3
|
||||||
django-filter>=0.5.4
|
django-filter>=0.5.4
|
||||||
|
django-oauth2-provider>=0.2.3
|
|
@ -6,6 +6,7 @@ from django.contrib.auth import authenticate
|
||||||
from django.utils.encoding import DjangoUnicodeDecodeError
|
from django.utils.encoding import DjangoUnicodeDecodeError
|
||||||
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 oauth2_provider
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
@ -155,4 +156,78 @@ class TokenAuthentication(BaseAuthentication):
|
||||||
return 'Token'
|
return 'Token'
|
||||||
|
|
||||||
|
|
||||||
# TODO: OAuthAuthentication
|
class OAuth2Authentication(BaseAuthentication):
|
||||||
|
"""
|
||||||
|
OAuth 2 authentication backend using `django-oauth2-provider`
|
||||||
|
"""
|
||||||
|
require_active = True
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(OAuth2Authentication, self).__init__(**kwargs)
|
||||||
|
if oauth2_provider is None:
|
||||||
|
raise ImproperlyConfigured("The 'django-oauth2-provider' package could not be imported. It is required for use with the 'OAuth2Authentication' class.")
|
||||||
|
|
||||||
|
def authenticate(self, request):
|
||||||
|
"""
|
||||||
|
The Bearer type is the only finalized type
|
||||||
|
|
||||||
|
Read the spec for more details
|
||||||
|
http://tools.ietf.org/html/rfc6749#section-7.1
|
||||||
|
"""
|
||||||
|
auth = request.META.get('HTTP_AUTHORIZATION', '').split()
|
||||||
|
if not auth or auth[0].lower() != "bearer":
|
||||||
|
raise exceptions.AuthenticationFailed('Invalid Authorization token type')
|
||||||
|
|
||||||
|
if len(auth) != 2:
|
||||||
|
raise exceptions.AuthenticationFailed('Invalid token header')
|
||||||
|
|
||||||
|
return self.authenticate_credentials(request, auth[1])
|
||||||
|
|
||||||
|
def authenticate_credentials(self, request, access_token):
|
||||||
|
"""
|
||||||
|
:returns: two-tuple of (user, auth) if authentication succeeds, or None otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# authenticate the client
|
||||||
|
oauth2_client_form = oauth2_provider.forms.ClientAuthForm(request.REQUEST)
|
||||||
|
if not oauth2_client_form.is_valid():
|
||||||
|
raise exceptions.AuthenticationFailed("Client could not be validated")
|
||||||
|
client = oauth2_client_form.cleaned_data.get('client')
|
||||||
|
|
||||||
|
# retrieve the `oauth2_provider.models.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") # does not exist or is expired
|
||||||
|
|
||||||
|
# TODO check scope
|
||||||
|
|
||||||
|
if not self.check_active(token.user):
|
||||||
|
raise exceptions.AuthenticationFailed('User not active: %s' % token.user.username)
|
||||||
|
|
||||||
|
if client and token:
|
||||||
|
request.user = token.user
|
||||||
|
return (request.user, None)
|
||||||
|
|
||||||
|
raise exceptions.AuthenticationFailed(
|
||||||
|
'You are not allowed to access this resource.')
|
||||||
|
|
||||||
|
def authenticate_header(self, request):
|
||||||
|
"""
|
||||||
|
Bearer is the only finalized type currently
|
||||||
|
|
||||||
|
Check details on the `OAuth2Authentication.authenticate` method
|
||||||
|
"""
|
||||||
|
return 'Bearer'
|
||||||
|
|
||||||
|
def check_active(self, user):
|
||||||
|
"""
|
||||||
|
Ensures the user has an active account.
|
||||||
|
|
||||||
|
Optimized for the ``django.contrib.auth.models.User`` case.
|
||||||
|
"""
|
||||||
|
if not self.require_active:
|
||||||
|
# Ignore & move on.
|
||||||
|
return True
|
||||||
|
|
||||||
|
return user.is_active
|
||||||
|
|
|
@ -426,3 +426,17 @@ try:
|
||||||
import defusedxml.ElementTree as etree
|
import defusedxml.ElementTree as etree
|
||||||
except ImportError:
|
except ImportError:
|
||||||
etree = None
|
etree = None
|
||||||
|
|
||||||
|
|
||||||
|
# OAuth 2 support is optional
|
||||||
|
try:
|
||||||
|
import provider.oauth2 as oauth2_provider
|
||||||
|
|
||||||
|
# Hack to fix submodule import issues
|
||||||
|
submodules = ['backends', 'forms','managers','models','urls','views']
|
||||||
|
for s in submodules:
|
||||||
|
mod = __import__('provider.oauth2.%s.*' % s)
|
||||||
|
setattr(oauth2_provider, s, mod)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
oauth2_provider = None
|
||||||
|
|
|
@ -97,9 +97,19 @@ INSTALLED_APPS = (
|
||||||
# 'django.contrib.admindocs',
|
# 'django.contrib.admindocs',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'rest_framework.tests'
|
'rest_framework.tests',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import provider
|
||||||
|
INSTALLED_APPS += (
|
||||||
|
'provider',
|
||||||
|
'provider.oauth2',
|
||||||
|
)
|
||||||
|
except ImportError:
|
||||||
|
import logging
|
||||||
|
logging.warning("django-oauth2-provider is not install, some tests will be skipped")
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
|
|
||||||
PASSWORD_HASHERS = (
|
PASSWORD_HASHERS = (
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
|
from django.utils import unittest
|
||||||
from rest_framework import HTTP_HEADER_ENCODING
|
from rest_framework import HTTP_HEADER_ENCODING
|
||||||
from rest_framework import exceptions
|
from rest_framework import exceptions
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
|
@ -11,13 +13,16 @@ from rest_framework.authentication import (
|
||||||
BaseAuthentication,
|
BaseAuthentication,
|
||||||
TokenAuthentication,
|
TokenAuthentication,
|
||||||
BasicAuthentication,
|
BasicAuthentication,
|
||||||
SessionAuthentication
|
SessionAuthentication,
|
||||||
|
OAuth2Authentication
|
||||||
)
|
)
|
||||||
from rest_framework.compat import patterns
|
from rest_framework.compat import patterns, url, include
|
||||||
|
from rest_framework.compat import oauth2_provider
|
||||||
from rest_framework.tests.utils import RequestFactory
|
from rest_framework.tests.utils import RequestFactory
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
factory = RequestFactory()
|
factory = RequestFactory()
|
||||||
|
@ -43,6 +48,12 @@ urlpatterns = patterns('',
|
||||||
(r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
|
(r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if oauth2_provider is not None:
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
url(r'^oauth2/', include('provider.oauth2.urls', namespace = 'oauth2')),
|
||||||
|
url(r'^oauth2-test/$', MockView.as_view(authentication_classes=[OAuth2Authentication])),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BasicAuthTests(TestCase):
|
class BasicAuthTests(TestCase):
|
||||||
"""Basic authentication"""
|
"""Basic authentication"""
|
||||||
|
@ -222,3 +233,129 @@ class IncorrectCredentialsTests(TestCase):
|
||||||
response = view(request)
|
response = view(request)
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertEqual(response.data, {'detail': 'Bad credentials'})
|
self.assertEqual(response.data, {'detail': 'Bad credentials'})
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth2Tests(TestCase):
|
||||||
|
"""OAuth 2.0 authentication"""
|
||||||
|
urls = 'rest_framework.tests.authentication'
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.csrf_client = Client(enforce_csrf_checks=True)
|
||||||
|
self.username = 'john'
|
||||||
|
self.email = 'lennon@thebeatles.com'
|
||||||
|
self.password = 'password'
|
||||||
|
self.user = User.objects.create_user(self.username, self.email, self.password)
|
||||||
|
|
||||||
|
self.CLIENT_ID = 'client_key'
|
||||||
|
self.CLIENT_SECRET = 'client_secret'
|
||||||
|
self.ACCESS_TOKEN = "access_token"
|
||||||
|
self.REFRESH_TOKEN = "refresh_token"
|
||||||
|
|
||||||
|
self.oauth2_client = oauth2_provider.models.Client.objects.create(
|
||||||
|
client_id=self.CLIENT_ID,
|
||||||
|
client_secret=self.CLIENT_SECRET,
|
||||||
|
redirect_uri='',
|
||||||
|
client_type=0,
|
||||||
|
name='example',
|
||||||
|
user=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.access_token = oauth2_provider.models.AccessToken.objects.create(
|
||||||
|
token=self.ACCESS_TOKEN,
|
||||||
|
client=self.oauth2_client,
|
||||||
|
user=self.user,
|
||||||
|
)
|
||||||
|
self.refresh_token = oauth2_provider.models.RefreshToken.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
access_token=self.access_token,
|
||||||
|
client=self.oauth2_client
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_authorization_header(self, token=None):
|
||||||
|
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')
|
||||||
|
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"""
|
||||||
|
auth = "Wrong token-type-obsviously"
|
||||||
|
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
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_authorization_header_token_format_failing(self):
|
||||||
|
"""Ensure that a wrong token format lead to the correct HTTP error status code"""
|
||||||
|
auth = "Bearer wrong token format"
|
||||||
|
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
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_authorization_header_token_failing(self):
|
||||||
|
"""Ensure that a wrong token lead to the correct HTTP error status code"""
|
||||||
|
auth = "Bearer wrong-token"
|
||||||
|
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
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)
|
||||||
|
|
||||||
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
def test_get_form_passing_auth(self):
|
||||||
|
"""Ensure GETing form over OAuth with correct client credentials succeed"""
|
||||||
|
auth = self._create_authorization_header()
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
def test_post_form_passing_auth(self):
|
||||||
|
"""Ensure POSTing form over OAuth with correct credentials passes and does not require CSRF"""
|
||||||
|
auth = self._create_authorization_header()
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
def test_post_form_token_removed_failing_auth(self):
|
||||||
|
"""Ensure POSTing when there is no OAuth access token in db fails"""
|
||||||
|
self.access_token.delete()
|
||||||
|
auth = self._create_authorization_header()
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
||||||
|
|
||||||
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
def test_post_form_with_refresh_token_failing_auth(self):
|
||||||
|
"""Ensure POSTing with refresh token instead of access token fails"""
|
||||||
|
auth = self._create_authorization_header(token=self.refresh_token.token)
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth)
|
||||||
|
self.assertIn(response.status_code, (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN))
|
||||||
|
|
||||||
|
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||||
|
def test_post_form_with_expired_access_token_failing_auth(self):
|
||||||
|
"""Ensure POSTing with expired access token fails with an 'Invalid token' error"""
|
||||||
|
self.access_token.expires = datetime.datetime.now() - datetime.timedelta(seconds=10) # 10 seconds late
|
||||||
|
self.access_token.save()
|
||||||
|
auth = self._create_authorization_header()
|
||||||
|
params = self._client_credentials_params()
|
||||||
|
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('Invalid token', response.content)
|
||||||
|
|
10
tox.ini
10
tox.ini
|
@ -8,46 +8,52 @@ commands = {envpython} rest_framework/runtests/runtests.py
|
||||||
[testenv:py3.3-django1.5]
|
[testenv:py3.3-django1.5]
|
||||||
basepython = python3.3
|
basepython = python3.3
|
||||||
deps = django==1.5
|
deps = django==1.5
|
||||||
https://github.com/alex/django-filter/archive/master.tar.gz
|
-egit+git://github.com/alex/django-filter.git#egg=django_filter
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
|
||||||
[testenv:py3.2-django1.5]
|
[testenv:py3.2-django1.5]
|
||||||
basepython = python3.2
|
basepython = python3.2
|
||||||
deps = django==1.5
|
deps = django==1.5
|
||||||
https://github.com/alex/django-filter/archive/master.tar.gz
|
-egit+git://github.com/alex/django-filter.git#egg=django_filter
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
|
||||||
[testenv:py2.7-django1.5]
|
[testenv:py2.7-django1.5]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = django==1.5
|
deps = django==1.5
|
||||||
django-filter==0.5.4
|
django-filter==0.5.4
|
||||||
|
django-oauth2-provider==0.2.3
|
||||||
|
|
||||||
[testenv:py2.6-django1.5]
|
[testenv:py2.6-django1.5]
|
||||||
basepython = python2.6
|
basepython = python2.6
|
||||||
deps = django==1.5
|
deps = django==1.5
|
||||||
django-filter==0.5.4
|
django-filter==0.5.4
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
django-oauth2-provider==0.2.3
|
||||||
|
|
||||||
[testenv:py2.7-django1.4]
|
[testenv:py2.7-django1.4]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = django==1.4.3
|
deps = django==1.4.3
|
||||||
django-filter==0.5.4
|
django-filter==0.5.4
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
django-oauth2-provider==0.2.3
|
||||||
|
|
||||||
[testenv:py2.6-django1.4]
|
[testenv:py2.6-django1.4]
|
||||||
basepython = python2.6
|
basepython = python2.6
|
||||||
deps = django==1.4.3
|
deps = django==1.4.3
|
||||||
django-filter==0.5.4
|
django-filter==0.5.4
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
django-oauth2-provider==0.2.3
|
||||||
|
|
||||||
[testenv:py2.7-django1.3]
|
[testenv:py2.7-django1.3]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = django==1.3.5
|
deps = django==1.3.5
|
||||||
django-filter==0.5.4
|
django-filter==0.5.4
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
django-oauth2-provider==0.2.3
|
||||||
|
|
||||||
[testenv:py2.6-django1.3]
|
[testenv:py2.6-django1.3]
|
||||||
basepython = python2.6
|
basepython = python2.6
|
||||||
deps = django==1.3.5
|
deps = django==1.3.5
|
||||||
django-filter==0.5.4
|
django-filter==0.5.4
|
||||||
defusedxml==0.3
|
defusedxml==0.3
|
||||||
|
django-oauth2-provider==0.2.3
|
||||||
|
|
Loading…
Reference in New Issue
Block a user