From 468b5e43e2582513c4ae862efa4511ea8313031e Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 1 Mar 2013 02:06:20 +0100 Subject: Add tests for OAuth2 authentication --- rest_framework/tests/authentication.py | 110 ++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) (limited to 'rest_framework/tests') diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index 2a2bfba9..3ceab808 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +from django.core.urlresolvers import reverse from django.contrib.auth.models import User from django.http import HttpResponse from django.test import Client, TestCase @@ -6,11 +7,15 @@ from rest_framework import HTTP_HEADER_ENCODING 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.compat import patterns +from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication, OAuth2Authentication +from rest_framework.compat import patterns, url, include +from rest_framework.compat import oauth2 +from rest_framework.compat import oauth2_provider from rest_framework.views import APIView import json import base64 +import datetime +import unittest class MockView(APIView): @@ -22,11 +27,16 @@ class MockView(APIView): def put(self, request): return HttpResponse({'a': 1, 'b': 2, 'c': 3}) + def get(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])), (r'^token/$', MockView.as_view(authentication_classes=[TokenAuthentication])), (r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'), + url(r'^oauth2/', include('provider.oauth2.urls', namespace = 'oauth2')), + url(r'^oauth2-test/$', MockView.as_view(authentication_classes=[OAuth2Authentication])), ) @@ -187,3 +197,99 @@ 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 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.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.models.AccessToken.objects.create( + token=self.ACCESS_TOKEN, + client=self.oauth2_client, + user=self.user, + ) + self.refresh_token = oauth2.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, '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, '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, '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, '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, '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, '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) -- cgit v1.2.3 From 9d5c3060386cc8deb4ee55eda022f0a134e897c0 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 1 Mar 2013 11:53:30 +0100 Subject: Improve the `django-oauth2-provider` import block to avoid naming collision with `oauth2` used for OAuth 1 --- rest_framework/tests/authentication.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'rest_framework/tests') diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index c2c23bcc..1212f0aa 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -16,7 +16,6 @@ from rest_framework.authentication import ( OAuth2Authentication ) from rest_framework.compat import patterns, url, include -from rest_framework.compat import oauth2 from rest_framework.compat import oauth2_provider from rest_framework.tests.utils import RequestFactory from rest_framework.views import APIView @@ -248,7 +247,7 @@ class OAuth2Tests(TestCase): self.ACCESS_TOKEN = "access_token" self.REFRESH_TOKEN = "refresh_token" - self.oauth2_client = oauth2.models.Client.objects.create( + self.oauth2_client = oauth2_provider.models.Client.objects.create( client_id=self.CLIENT_ID, client_secret=self.CLIENT_SECRET, redirect_uri='', @@ -257,12 +256,12 @@ class OAuth2Tests(TestCase): user=None, ) - self.access_token = oauth2.models.AccessToken.objects.create( + self.access_token = oauth2_provider.models.AccessToken.objects.create( token=self.ACCESS_TOKEN, client=self.oauth2_client, user=self.user, ) - self.refresh_token = oauth2.models.RefreshToken.objects.create( + self.refresh_token = oauth2_provider.models.RefreshToken.objects.create( user=self.user, access_token=self.access_token, client=self.oauth2_client @@ -274,7 +273,7 @@ class OAuth2Tests(TestCase): def _client_credentials_params(self): return {'client_id': self.CLIENT_ID, 'client_secret': self.CLIENT_SECRET} - @unittest.skipUnless(oauth2, 'django-oauth2-provider not installed') + @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() @@ -283,7 +282,7 @@ class OAuth2Tests(TestCase): response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 401) - @unittest.skipUnless(oauth2, 'django-oauth2-provider not installed') + @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() @@ -291,7 +290,7 @@ class OAuth2Tests(TestCase): response = self.csrf_client.get('/oauth2-test/', params, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) - @unittest.skipUnless(oauth2, 'django-oauth2-provider not installed') + @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() @@ -299,7 +298,7 @@ class OAuth2Tests(TestCase): response = self.csrf_client.post('/oauth2-test/', params, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) - @unittest.skipUnless(oauth2, 'django-oauth2-provider not installed') + @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() @@ -308,7 +307,7 @@ class OAuth2Tests(TestCase): 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, 'django-oauth2-provider not installed') + @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) @@ -316,7 +315,7 @@ class OAuth2Tests(TestCase): 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, 'django-oauth2-provider not installed') + @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 -- cgit v1.2.3 From 721dc519ecdb8435bdeed6aa67d99be6968c0972 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 1 Mar 2013 23:27:47 +0100 Subject: Use django.utils to import the unittest module for a cross python versions compatibility --- rest_framework/tests/authentication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework/tests') diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index 1212f0aa..0401ddd9 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -3,6 +3,7 @@ from django.core.urlresolvers import reverse from django.contrib.auth.models import User from django.http import HttpResponse from django.test import Client, TestCase +from django.utils import unittest from rest_framework import HTTP_HEADER_ENCODING from rest_framework import exceptions from rest_framework import permissions @@ -22,7 +23,6 @@ from rest_framework.views import APIView import json import base64 import datetime -import unittest factory = RequestFactory() -- cgit v1.2.3 From 8809c46ab5d2a09d5a956ccffcb2ae2db95c5c1b Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Sat, 2 Mar 2013 20:16:18 +0100 Subject: Add new OAuth2 tests --- rest_framework/tests/authentication.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'rest_framework/tests') diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index 0401ddd9..9d67a005 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -273,6 +273,36 @@ class OAuth2Tests(TestCase): 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""" -- cgit v1.2.3 From 8845c0be88bf68fa0e42d05c7196cd52d897623b Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Sun, 3 Mar 2013 01:09:39 +0100 Subject: Fix import errors --- rest_framework/tests/authentication.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'rest_framework/tests') diff --git a/rest_framework/tests/authentication.py b/rest_framework/tests/authentication.py index 9d67a005..a02aef55 100644 --- a/rest_framework/tests/authentication.py +++ b/rest_framework/tests/authentication.py @@ -46,10 +46,14 @@ urlpatterns = patterns('', (r'^basic/$', MockView.as_view(authentication_classes=[BasicAuthentication])), (r'^token/$', MockView.as_view(authentication_classes=[TokenAuthentication])), (r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'), - url(r'^oauth2/', include('provider.oauth2.urls', namespace = 'oauth2')), - url(r'^oauth2-test/$', MockView.as_view(authentication_classes=[OAuth2Authentication])), ) +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): """Basic authentication""" -- cgit v1.2.3