Commit 1270df15 by J. Cliff Dyer

Enable dispatch of Authorization endpoint.

* Created URL route
* Expanded test code to confirm expected behavior

MA-2124
parent 928e4b46
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
Constants for testing purposes Constants for testing purposes
""" """
DUMMY_REDIRECT_URL = u'https://example.edx/redirect' DUMMY_REDIRECT_URL = u'https://example.com/edx/redirect'
...@@ -8,6 +8,7 @@ import ddt ...@@ -8,6 +8,7 @@ import ddt
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import httpretty import httpretty
from provider import constants
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from third_party_auth.tests.utils import ThirdPartyOAuthTestMixin, ThirdPartyOAuthTestMixinGoogle from third_party_auth.tests.utils import ThirdPartyOAuthTestMixin, ThirdPartyOAuthTestMixinGoogle
...@@ -37,7 +38,7 @@ class _DispatchingViewTestCase(TestCase): ...@@ -37,7 +38,7 @@ class _DispatchingViewTestCase(TestCase):
redirect_uri=DUMMY_REDIRECT_URL, redirect_uri=DUMMY_REDIRECT_URL,
client_id='dot-app-client-id', client_id='dot-app-client-id',
) )
self.dop_client = self.dop_adapter.create_public_client( self.dop_app = self.dop_adapter.create_public_client(
name='test dop client', name='test dop client',
user=self.user, user=self.user,
redirect_uri=DUMMY_REDIRECT_URL, redirect_uri=DUMMY_REDIRECT_URL,
...@@ -83,7 +84,7 @@ class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase): ...@@ -83,7 +84,7 @@ class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase):
return body return body
@ddt.data('dop_client', 'dot_app') @ddt.data('dop_app', 'dot_app')
def test_access_token_fields(self, client_attr): def test_access_token_fields(self, client_attr):
client = getattr(self, client_attr) client = getattr(self, client_attr)
response = self._post_request(self.user, client) response = self._post_request(self.user, client)
...@@ -94,7 +95,7 @@ class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase): ...@@ -94,7 +95,7 @@ class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase):
self.assertIn('scope', data) self.assertIn('scope', data)
self.assertIn('token_type', data) self.assertIn('token_type', data)
@ddt.data('dop_client', 'dot_app') @ddt.data('dop_app', 'dot_app')
def test_jwt_access_token(self, client_attr): def test_jwt_access_token(self, client_attr):
client = getattr(self, client_attr) client = getattr(self, client_attr)
response = self._post_request(self.user, client, token_type='jwt') response = self._post_request(self.user, client, token_type='jwt')
...@@ -111,7 +112,7 @@ class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase): ...@@ -111,7 +112,7 @@ class TestAccessTokenView(mixins.AccessTokenMixin, _DispatchingViewTestCase):
self.assertIn('refresh_token', data) self.assertIn('refresh_token', data)
def test_dop_public_client_access_token(self): def test_dop_public_client_access_token(self):
response = self._post_request(self.user, self.dop_client) response = self._post_request(self.user, self.dop_app)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
data = json.loads(response.content) data = json.loads(response.content)
self.assertNotIn('refresh_token', data) self.assertNotIn('refresh_token', data)
...@@ -133,7 +134,7 @@ class TestAccessTokenExchangeView(ThirdPartyOAuthTestMixinGoogle, ThirdPartyOAut ...@@ -133,7 +134,7 @@ class TestAccessTokenExchangeView(ThirdPartyOAuthTestMixinGoogle, ThirdPartyOAut
'access_token': self.access_token, 'access_token': self.access_token,
} }
@ddt.data('dop_client', 'dot_app') @ddt.data('dop_app', 'dot_app')
def test_access_token_exchange_calls_dispatched_view(self, client_attr): def test_access_token_exchange_calls_dispatched_view(self, client_attr):
client = getattr(self, client_attr) client = getattr(self, client_attr)
self.oauth_client = client self.oauth_client = client
...@@ -143,7 +144,7 @@ class TestAccessTokenExchangeView(ThirdPartyOAuthTestMixinGoogle, ThirdPartyOAut ...@@ -143,7 +144,7 @@ class TestAccessTokenExchangeView(ThirdPartyOAuthTestMixinGoogle, ThirdPartyOAut
@ddt.ddt @ddt.ddt
class TestAuthorizationView(TestCase): class TestAuthorizationView(_DispatchingViewTestCase):
""" """
Test class for AuthorizationView Test class for AuthorizationView
""" """
...@@ -153,43 +154,81 @@ class TestAuthorizationView(TestCase): ...@@ -153,43 +154,81 @@ class TestAuthorizationView(TestCase):
def setUp(self): def setUp(self):
super(TestAuthorizationView, self).setUp() super(TestAuthorizationView, self).setUp()
self.user = UserFactory() self.user = UserFactory()
self.dop_client = self._create_confidential_client(user=self.user, client_id='dop-app-client-id') self.dot_app = self.dot_adapter.create_confidential_client(
name='test dot application',
def _create_confidential_client(self, user, client_id): user=self.user,
""" redirect_uri=DUMMY_REDIRECT_URL,
Create a confidential client suitable for testing purposes. client_id='confidential-dot-app-client-id',
""" )
return self.dop_adapter.create_confidential_client( self.dop_app = self.dop_adapter.create_confidential_client(
name='test_app', name='test dop client',
user=user, user=self.user,
client_id=client_id, redirect_uri=DUMMY_REDIRECT_URL,
redirect_uri=DUMMY_REDIRECT_URL client_id='confidential-dop-app-client-id',
) )
def test_authorization_view(self): @ddt.data(
('dop', 'authorize'),
('dot', 'allow')
)
@ddt.unpack
def test_post_authorization_view(self, client_type, allow_field):
oauth_application = getattr(self, '{}_app'.format(client_type))
self.client.login(username=self.user.username, password='test') self.client.login(username=self.user.username, password='test')
response = self.client.post( response = self.client.post(
'/oauth2/authorize/', '/oauth2/authorize/',
{ {
'client_id': self.dop_client.client_id, # TODO: DOT is not yet supported (MA-2124) 'client_id': oauth_application.client_id,
'response_type': 'code', 'response_type': 'code',
'state': 'random_state_string', 'state': 'random_state_string',
'redirect_uri': DUMMY_REDIRECT_URL, 'redirect_uri': DUMMY_REDIRECT_URL,
'scope': 'profile email',
allow_field: True,
}, },
follow=True, follow=True,
) )
self.assertEqual(response.status_code, 200) check_response = getattr(self, '_check_{}_response'.format(client_type))
check_response(response)
def _check_dot_response(self, response):
"""
Check that django-oauth-toolkit gives an appropriate authorization response.
"""
# django-oauth-toolkit tries to redirect to the user's redirect URL
self.assertEqual(response.status_code, 404) # We used a non-existent redirect url.
expected_redirect_prefix = u'{}?'.format(DUMMY_REDIRECT_URL)
self._assert_startswith(self._redirect_destination(response), expected_redirect_prefix)
def _check_dop_response(self, response):
"""
Check that django-oauth2-provider gives an appropriate authorization response.
"""
# django-oauth-provider redirects to a confirmation page
self.assertRedirects(response, u'http://testserver/oauth2/authorize/confirm', target_status_code=200)
# check form is in context and form params are valid context = response.context_data
context = response.context_data # pylint: disable=no-member form = context['form']
self.assertIn('form', context) self.assertIsNone(form['authorize'].value())
self.assertIsNone(context['form']['authorize'].value())
self.assertIn('oauth_data', context)
oauth_data = context['oauth_data'] oauth_data = context['oauth_data']
self.assertEqual(oauth_data['redirect_uri'], DUMMY_REDIRECT_URL) self.assertEqual(oauth_data['redirect_uri'], DUMMY_REDIRECT_URL)
self.assertEqual(oauth_data['state'], 'random_state_string') self.assertEqual(oauth_data['state'], 'random_state_string')
# TODO: figure out why it chooses this scope.
self.assertEqual(oauth_data['scope'], constants.READ_WRITE)
def _assert_startswith(self, string, prefix):
"""
Assert that the string starts with the specified prefix.
"""
self.assertTrue(string.startswith(prefix), u'{} does not start with {}'.format(string, prefix))
@staticmethod
def _redirect_destination(response):
"""
Return the final destination of the redirect chain in the response object
"""
return response.redirect_chain[-1][0]
class TestViewDispatch(TestCase): class TestViewDispatch(TestCase):
...@@ -232,26 +271,40 @@ class TestViewDispatch(TestCase): ...@@ -232,26 +271,40 @@ class TestViewDispatch(TestCase):
self.assertTrue(args, msg_no_request) self.assertTrue(args, msg_no_request)
self.assertEqual(args[0], 'request') self.assertEqual(args[0], 'request')
def _get_request(self, client_id): def _post_request(self, client_id):
""" """
Return a request with the specified client_id in the body Return a request with the specified client_id in the body
""" """
return RequestFactory().post('/', {'client_id': client_id}) return RequestFactory().post('/', {'client_id': client_id})
def test_dispatching_to_dot(self): def _get_request(self, client_id):
"""
Return a request with the specified client_id in the get parameters
"""
return RequestFactory().get('/?client_id={}'.format(client_id))
def test_dispatching_post_to_dot(self):
request = self._post_request('dot-id')
self.assertEqual(self.view.select_backend(request), self.dot_adapter.backend)
def test_dispatching_post_to_dop(self):
request = self._post_request('dop-id')
self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend)
def test_dispatching_get_to_dot(self):
request = self._get_request('dot-id') request = self._get_request('dot-id')
self.assertEqual(self.view.select_backend(request), self.dot_adapter.backend) self.assertEqual(self.view.select_backend(request), self.dot_adapter.backend)
def test_dispatching_to_dop(self): def test_dispatching_get_to_dop(self):
request = self._get_request('dop-id') request = self._get_request('dop-id')
self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend) self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend)
def test_dispatching_with_no_client(self): def test_dispatching_with_no_client(self):
request = self._get_request(None) request = self._post_request(None)
self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend) self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend)
def test_dispatching_with_invalid_client(self): def test_dispatching_with_invalid_client(self):
request = self._get_request('abcesdfljh') request = self._post_request('abcesdfljh')
self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend) self.assertEqual(self.view.select_backend(request), self.dop_adapter.backend)
def test_get_view_for_dot(self): def test_get_view_for_dot(self):
......
...@@ -11,7 +11,7 @@ from . import views ...@@ -11,7 +11,7 @@ from . import views
urlpatterns = patterns( urlpatterns = patterns(
'', '',
# TODO: authorize/ URL not yet supported for DOT (MA-2124) url(r'^authorize/?$', csrf_exempt(views.AuthorizationView.as_view()), name='authorize'),
url(r'^access_token/?$', csrf_exempt(views.AccessTokenView.as_view()), name='access_token'), url(r'^access_token/?$', csrf_exempt(views.AccessTokenView.as_view()), name='access_token'),
) )
......
...@@ -74,6 +74,9 @@ class _DispatchingView(View): ...@@ -74,6 +74,9 @@ class _DispatchingView(View):
""" """
Return the client_id from the provided request Return the client_id from the provided request
""" """
if request.method == u'GET':
return request.GET.get('client_id')
else:
return request.POST.get('client_id') return request.POST.get('client_id')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment