From 1f392bdc3e10ecd87a0b960fe010ab06a569de8c Mon Sep 17 00:00:00 2001 From: Kyle McCormick <kmccormick@edx.org> Date: Mon, 15 Mar 2021 16:50:33 -0400 Subject: [PATCH] fix: render_xblock was denying access to staff masquerading as learners (#27017) The render_xblock view, which powers the Learning MFE (among other things) returned a 404 when un- enrolled course staff users tried to load it while masquerading as learners. This was because we checked course access after enabling the masquerading context, which triggered a redirect- to-enrollment exception. The fix is simply to enable the masquerading context after checking course access. Content-level behavior and access is still calculated within the masquerading context, as intended. TNL-7989 --- lms/djangoapps/courseware/testutils.py | 12 ++++++++++-- lms/djangoapps/courseware/views/views.py | 13 ++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/courseware/testutils.py b/lms/djangoapps/courseware/testutils.py index 91e8cf89e5a..35a44d7e9f1 100644 --- a/lms/djangoapps/courseware/testutils.py +++ b/lms/djangoapps/courseware/testutils.py @@ -10,7 +10,6 @@ from urllib.parse import urlencode import ddt -from lms.djangoapps.courseware.field_overrides import OverrideModulestoreFieldData from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.features.course_experience.url_helpers import get_legacy_courseware_url from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory @@ -18,9 +17,12 @@ from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls +from .field_overrides import OverrideModulestoreFieldData +from .tests.helpers import MasqueradeMixin + @ddt.ddt -class RenderXBlockTestMixin(metaclass=ABCMeta): +class RenderXBlockTestMixin(MasqueradeMixin, metaclass=ABCMeta): """ Mixin for testing the courseware.render_xblock function. It can be used for testing any higher-level endpoint that calls this method. @@ -220,6 +222,12 @@ class RenderXBlockTestMixin(metaclass=ABCMeta): self.setup_user(admin=True, enroll=False, login=True) self.verify_response() + def test_success_unenrolled_staff_masquerading_as_student(self): + self.setup_course() + self.setup_user(admin=True, enroll=False, login=True) + self.update_masquerade(role='student') + self.verify_response() + def test_success_enrolled_student(self): self.setup_course() self.setup_user(admin=False, enroll=True, login=True) diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 895c74253cc..4e3ebeab718 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -1700,7 +1700,6 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True): ) staff_access = has_access(request.user, 'staff', course_key) - _course_masquerade, request.user = setup_masquerade(request, course_key, staff_access) with modulestore().bulk_operations(course_key): # verify the user has access to the course, including enrollment check @@ -1709,6 +1708,18 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True): except CourseAccessRedirect: raise Http404("Course not found.") # lint-amnesty, pylint: disable=raise-missing-from + # with course access now verified: + # assume masquerading role, if applicable. + # (if we did this *before* the course access check, then course staff + # masquerading as learners would often be denied access, since course + # staff are generally not enrolled, and viewing a course generally + # requires enrollment.) + _course_masquerade, request.user = setup_masquerade( + request, + course_key, + staff_access, + ) + # get the block, which verifies whether the user has access to the block. recheck_access = request.GET.get('recheck_access') == '1' block, _ = get_module_by_usage_id( -- GitLab