Merge pull request #19260 from open-craft/xblock-view-oauth

Allow calling xblock_view API with OAuth creds
This commit is contained in:
David Ormsbee
2018-11-28 13:01:54 -05:00
committed by GitHub
3 changed files with 57 additions and 19 deletions

View File

@@ -15,7 +15,6 @@ from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.cache import cache from django.core.cache import cache
from django.template.context_processors import csrf from django.template.context_processors import csrf
from django.core.exceptions import PermissionDenied
from django.urls import reverse from django.urls import reverse
from django.http import Http404, HttpResponse, HttpResponseForbidden from django.http import Http404, HttpResponse, HttpResponseForbidden
from django.utils.text import slugify from django.utils.text import slugify
@@ -27,6 +26,7 @@ from edx_proctoring.services import ProctoringService
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.keys import CourseKey, UsageKey
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
from rest_framework.decorators import api_view
from six import text_type from six import text_type
from xblock.core import XBlock from xblock.core import XBlock
from xblock.django.request import django_to_webob_request, webob_to_django_response from xblock.django.request import django_to_webob_request, webob_to_django_response
@@ -59,6 +59,7 @@ from openedx.core.djangoapps.crawlers.models import CrawlersConfig
from openedx.core.djangoapps.credit.services import CreditService from openedx.core.djangoapps.credit.services import CreditService
from openedx.core.djangoapps.util.user_utils import SystemUser from openedx.core.djangoapps.util.user_utils import SystemUser
from openedx.core.djangolib.markup import HTML from openedx.core.djangolib.markup import HTML
from openedx.core.lib.api.view_utils import view_auth_classes
from openedx.core.lib.gating.services import GatingService from openedx.core.lib.gating.services import GatingService
from openedx.core.lib.license import wrap_with_license from openedx.core.lib.license import wrap_with_license
from openedx.core.lib.url_utils import quote_slashes, unquote_slashes from openedx.core.lib.url_utils import quote_slashes, unquote_slashes
@@ -1169,6 +1170,8 @@ def hash_resource(resource):
return md5.hexdigest() return md5.hexdigest()
@api_view(['GET'])
@view_auth_classes(is_authenticated=True)
def xblock_view(request, course_id, usage_id, view_name): def xblock_view(request, course_id, usage_id, view_name):
""" """
Returns the rendered view of a given XBlock, with related resources Returns the rendered view of a given XBlock, with related resources
@@ -1183,9 +1186,6 @@ def xblock_view(request, course_id, usage_id, view_name):
" see FEATURES['ENABLE_XBLOCK_VIEW_ENDPOINT']") " see FEATURES['ENABLE_XBLOCK_VIEW_ENDPOINT']")
raise Http404 raise Http404
if not request.user.is_authenticated:
raise PermissionDenied
try: try:
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
except InvalidKeyError: except InvalidKeyError:

View File

@@ -743,21 +743,33 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas
with self.assertRaises(BlockCompletion.DoesNotExist): with self.assertRaises(BlockCompletion.DoesNotExist):
BlockCompletion.objects.get(block_key=block.scope_ids.usage_id) BlockCompletion.objects.get(block_key=block.scope_ids.usage_id)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_XBLOCK_VIEW_ENDPOINT': True})
def test_xblock_view_handler(self):
args = [
'edX/toy/2012_Fall',
quote_slashes('i4x://edX/toy/videosequence/Toy_Videos'),
'student_view'
]
xblock_view_url = reverse(
'xblock_view',
args=args
)
request = self.request_factory.get(xblock_view_url) @attr(shard=1)
request.user = self.mock_user @ddt.ddt
response = render.xblock_view(request, *args) @patch.dict('django.conf.settings.FEATURES', {'ENABLE_XBLOCK_VIEW_ENDPOINT': True})
class TestXBlockView(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Test the handle_xblock_callback function
"""
@classmethod
def setUpClass(cls):
super(TestXBlockView, cls).setUpClass()
cls.course_key = ToyCourseFactory.create().id
cls.toy_course = modulestore().get_course(cls.course_key)
def setUp(self):
super(TestXBlockView, self).setUp()
self.location = unicode(self.course_key.make_usage_key('html', 'toyhtml'))
self.request_factory = RequestFactory()
self.view_args = [unicode(self.course_key), quote_slashes(self.location), 'student_view']
self.xblock_view_url = reverse('xblock_view', args=self.view_args)
def test_xblock_view_handler(self):
request = self.request_factory.get(self.xblock_view_url)
request.user = UserFactory.create()
response = render.xblock_view(request, *self.view_args)
self.assertEquals(200, response.status_code) self.assertEquals(200, response.status_code)
expected = ['csrf_token', 'html', 'resources'] expected = ['csrf_token', 'html', 'resources']
@@ -765,7 +777,30 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas
for section in expected: for section in expected:
self.assertIn(section, content) self.assertIn(section, content)
doc = PyQuery(content['html']) doc = PyQuery(content['html'])
self.assertEquals(len(doc('div.xblock-student_view-videosequence')), 1) self.assertEquals(len(doc('div.xblock-student_view-html')), 1)
@ddt.data(True, False)
def test_hide_staff_markup(self, hide):
"""
When xblock_view gets 'hide_staff_markup' in its context, the staff markup
should not be included. See 'add_staff_markup' in xblock_utils/__init__.py
"""
request = self.request_factory.get(self.xblock_view_url)
request.user = GlobalStaffFactory.create()
request.session = {}
if hide:
request.GET = {'hide_staff_markup': 'true'}
response = render.xblock_view(request, *self.view_args)
self.assertEquals(200, response.status_code)
html = json.loads(response.content)['html']
self.assertEqual('Staff Debug Info' in html, not hide)
def test_xblock_view_handler_not_authenticated(self):
request = self.request_factory.get(self.xblock_view_url)
request.user = AnonymousUser()
response = render.xblock_view(request, *self.view_args)
self.assertEquals(401, response.status_code)
@attr(shard=1) @attr(shard=1)

View File

@@ -309,6 +309,9 @@ def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context)
Does nothing if module is a SequenceModule. Does nothing if module is a SequenceModule.
""" """
if context and context.get('hide_staff_markup', False):
# If hide_staff_markup is passed, don't add the markup
return frag
# TODO: make this more general, eg use an XModule attribute instead # TODO: make this more general, eg use an XModule attribute instead
if isinstance(block, VerticalBlock) and (not context or not context.get('child_of_vertical', False)): if isinstance(block, VerticalBlock) and (not context or not context.get('child_of_vertical', False)):
# check that the course is a mongo backed Studio course before doing work # check that the course is a mongo backed Studio course before doing work