From 1e4dc51ac7267f213cb4be6b2f8c8c4366bd7130 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Sat, 10 Nov 2018 18:03:59 -0800 Subject: [PATCH] Allow calling xblock_view API with OAuth creds --- lms/djangoapps/courseware/module_render.py | 8 +-- .../courseware/tests/test_module_render.py | 65 ++++++++++++++----- openedx/core/lib/xblock_utils/__init__.py | 3 + 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 7b95e99c6b..072b0d9d6e 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -15,7 +15,6 @@ from django.conf import settings from django.contrib.auth.models import User from django.core.cache import cache from django.template.context_processors import csrf -from django.core.exceptions import PermissionDenied from django.urls import reverse from django.http import Http404, HttpResponse, HttpResponseForbidden from django.utils.text import slugify @@ -27,6 +26,7 @@ from edx_proctoring.services import ProctoringService from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey from requests.auth import HTTPBasicAuth +from rest_framework.decorators import api_view from six import text_type from xblock.core import XBlock 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.util.user_utils import SystemUser 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.license import wrap_with_license from openedx.core.lib.url_utils import quote_slashes, unquote_slashes @@ -1169,6 +1170,8 @@ def hash_resource(resource): return md5.hexdigest() +@api_view(['GET']) +@view_auth_classes(is_authenticated=True) def xblock_view(request, course_id, usage_id, view_name): """ 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']") raise Http404 - if not request.user.is_authenticated: - raise PermissionDenied - try: course_key = CourseKey.from_string(course_id) except InvalidKeyError: diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index a5bb5e132c..dacfa40d91 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -743,21 +743,33 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas with self.assertRaises(BlockCompletion.DoesNotExist): 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) - request.user = self.mock_user - response = render.xblock_view(request, *args) +@attr(shard=1) +@ddt.ddt +@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) expected = ['csrf_token', 'html', 'resources'] @@ -765,7 +777,30 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas for section in expected: self.assertIn(section, content) 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) diff --git a/openedx/core/lib/xblock_utils/__init__.py b/openedx/core/lib/xblock_utils/__init__.py index e63688926d..26698af512 100644 --- a/openedx/core/lib/xblock_utils/__init__.py +++ b/openedx/core/lib/xblock_utils/__init__.py @@ -309,6 +309,9 @@ def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context) 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 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