Implement an endpoint to get id_tokens for programs.
ECOM-2440
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
"""Programs views for use with Studio."""
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import Http404
|
||||
from django.http import Http404, JsonResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import View
|
||||
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.lib.token_utils import get_id_token
|
||||
|
||||
|
||||
class ProgramAuthoringView(View):
|
||||
@@ -18,13 +19,6 @@ class ProgramAuthoringView(View):
|
||||
"""
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, *args, **kwargs):
|
||||
"""Relays requests to matching methods.
|
||||
|
||||
Decorated to require login before accessing the authoring app.
|
||||
"""
|
||||
return super(ProgramAuthoringView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Populate the template context with values required for the authoring app to run."""
|
||||
programs_config = ProgramsApiConfig.current()
|
||||
@@ -38,3 +32,16 @@ class ProgramAuthoringView(View):
|
||||
})
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
|
||||
class ProgramsIdTokenView(View):
|
||||
"""Provides id tokens to JavaScript clients for use with the Programs API."""
|
||||
|
||||
@method_decorator(login_required)
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Generate and return a token, if the integration is enabled."""
|
||||
if ProgramsApiConfig.current().is_studio_tab_enabled:
|
||||
id_token = get_id_token(request.user, 'programs')
|
||||
return JsonResponse({'id_token': id_token})
|
||||
else:
|
||||
raise Http404
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
"""Tests covering the Programs listing on the Studio home."""
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
import httpretty
|
||||
import mock
|
||||
from oauth2_provider.tests.factories import ClientFactory
|
||||
from provider.constants import CONFIDENTIAL
|
||||
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
|
||||
|
||||
class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, ModuleStoreTestCase):
|
||||
class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, SharedModuleStoreTestCase):
|
||||
"""Verify Program listing behavior."""
|
||||
def setUp(self):
|
||||
super(TestProgramListing, self).setUp()
|
||||
@@ -70,7 +73,7 @@ class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, ModuleStoreT
|
||||
self.assertIn(program_name, response.content)
|
||||
|
||||
|
||||
class TestProgramAuthoringView(ProgramsApiConfigMixin, ModuleStoreTestCase):
|
||||
class TestProgramAuthoringView(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
|
||||
"""Verify the behavior of the program authoring app's host view."""
|
||||
def setUp(self):
|
||||
super(TestProgramAuthoringView, self).setUp()
|
||||
@@ -118,3 +121,43 @@ class TestProgramAuthoringView(ProgramsApiConfigMixin, ModuleStoreTestCase):
|
||||
student = UserFactory(is_staff=False)
|
||||
self.client.login(username=student.username, password='test')
|
||||
self._assert_status(404)
|
||||
|
||||
|
||||
class TestProgramsIdTokenView(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
|
||||
"""Tests for the programs id_token endpoint."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestProgramsIdTokenView, self).setUp()
|
||||
self.user = UserFactory()
|
||||
self.client.login(username=self.user.username, password='test')
|
||||
self.path = reverse('programs_id_token')
|
||||
|
||||
def test_config_disabled(self):
|
||||
"""Ensure the endpoint returns 404 when Programs authoring is disabled."""
|
||||
self.create_config(enable_studio_tab=False)
|
||||
response = self.client.get(self.path)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_not_logged_in(self):
|
||||
"""Ensure the endpoint denies access to unauthenticated users."""
|
||||
self.create_config()
|
||||
self.client.logout()
|
||||
response = self.client.get(self.path)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn(settings.LOGIN_URL, response['Location'])
|
||||
|
||||
@mock.patch('cms.djangoapps.contentstore.views.program.get_id_token', return_value='test-id-token')
|
||||
def test_config_enabled(self, mock_get_id_token):
|
||||
"""
|
||||
Ensure the endpoint responds with a valid JSON payload when authoring
|
||||
is enabled.
|
||||
"""
|
||||
self.create_config()
|
||||
response = self.client.get(self.path)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
payload = json.loads(response.content)
|
||||
self.assertEqual(payload, {"id_token": "test-id-token"})
|
||||
# this comparison is a little long-handed because we need to compare user instances directly
|
||||
user, client_name = mock_get_id_token.call_args[0]
|
||||
self.assertEqual(user, self.user)
|
||||
self.assertEqual(client_name, "programs")
|
||||
|
||||
@@ -3,7 +3,7 @@ from django.conf.urls import patterns, include, url
|
||||
# There is a course creators admin table.
|
||||
from ratelimitbackend import admin
|
||||
|
||||
from cms.djangoapps.contentstore.views.program import ProgramAuthoringView
|
||||
from cms.djangoapps.contentstore.views.program import ProgramAuthoringView, ProgramsIdTokenView
|
||||
|
||||
|
||||
admin.autodiscover()
|
||||
@@ -185,9 +185,10 @@ if settings.FEATURES.get('CERTIFICATES_HTML_VIEW'):
|
||||
)
|
||||
|
||||
urlpatterns += (
|
||||
# These views use a configuration model to determine whether or not to
|
||||
# display the Programs authoring app. If disabled, a 404 is returned.
|
||||
url(r'^programs/id_token/$', ProgramsIdTokenView.as_view(), name='programs_id_token'),
|
||||
# Drops into the Programs authoring app, which handles its own routing.
|
||||
# The view uses a configuration model to determine whether or not to
|
||||
# display the authoring app. If disabled, a 404 is returned.
|
||||
url(r'^program/', ProgramAuthoringView.as_view(), name='programs'),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user