From 6fa89e610a679b76701d97d87c9bf65935622f38 Mon Sep 17 00:00:00 2001 From: alangsto <46360176+alangsto@users.noreply.github.com> Date: Tue, 24 Aug 2021 09:14:01 -0400 Subject: [PATCH] fix: Allow masquerading users to see content (#28458) If a user with staff access is masquerading as a specific student, they should be able to see content that would normally be gated for that student. --- common/lib/xmodule/xmodule/seq_module.py | 4 +- lms/djangoapps/courseware/tests/test_views.py | 41 +++++++++++++++++++ lms/djangoapps/courseware/views/views.py | 5 ++- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index 2854e512f2..9eec6ea374 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -638,7 +638,7 @@ class SequenceBlock( return None - def descendants_are_gated(self): + def descendants_are_gated(self, context): """ Sequences do their own access gating logic as to whether their content should be viewable, based on things like pre-reqs and time exam starts. @@ -664,7 +664,7 @@ class SequenceBlock( comes to determining whether a student is allowed to access this, with other checks being done in has_access calls. """ - if self.runtime.user_is_staff: + if self.runtime.user_is_staff or context.get('specific_masquerade', False): return False # We're not allowed to see it because of pre-reqs that haven't been diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 0e1462671c..c3b46364f5 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -3134,6 +3134,47 @@ class TestRenderXBlock(RenderXBlockTestMixin, ModuleStoreTestCase, CompletionWaf # The Sequence itself 200s (or we risk infinite redirect loops). assert self.get_response(usage_key=self.sequence.location).status_code == 200 + def test_rendering_descendant_of_gated_sequence_with_masquerade(self): + """ + Test that if we are masquerading as a specific student, we do not redirect if content is gated + """ + with self.store.default_store(ModuleStoreEnum.Type.split): + # pylint:disable=attribute-defined-outside-init + self.course = CourseFactory.create(**self.course_options()) + self.chapter = ItemFactory.create(parent=self.course, category='chapter') + self.sequence = ItemFactory.create( + parent=self.chapter, + category='sequential', + display_name='Sequence', + is_time_limited=True, + ) + self.vertical_block = ItemFactory.create( + parent=self.sequence, + category='vertical', + display_name="Vertical", + ) + self.html_block = ItemFactory.create( + parent=self.vertical_block, + category='html', + data="
Test HTML Content
" + ) + self.problem_block = ItemFactory.create( + parent=self.vertical_block, + category='problem', + display_name='Problem' + ) + CourseOverview.load_from_module_store(self.course.id) + self.setup_user(admin=True, enroll=True, login=True) + + student = UserFactory() + CourseEnrollment.enroll(student, self.course.id) + self.update_masquerade(role='student', username=student.username) + + # Problem and Vertical response should both render successfully + for block in [self.problem_block, self.vertical_block]: + response = self.get_response(usage_key=block.location) + assert response.status_code == 200 + class TestRenderXBlockSelfPaced(TestRenderXBlock): # lint-amnesty, pylint: disable=test-inherits-tests """ diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index e62441cfa7..16a1d0ee1f 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -77,7 +77,7 @@ from lms.djangoapps.courseware.courses import ( ) from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, Redirect -from lms.djangoapps.courseware.masquerade import setup_masquerade +from lms.djangoapps.courseware.masquerade import setup_masquerade, is_masquerading_as_specific_student from lms.djangoapps.courseware.model_data import FieldDataCache from lms.djangoapps.courseware.models import BaseStudentModuleHistory, StudentModule from lms.djangoapps.courseware.permissions import ( # lint-amnesty, pylint: disable=unused-import @@ -1763,9 +1763,10 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True): # timed exam started?"). ancestor_sequence_block = enclosing_sequence_for_gating_checks(block) if ancestor_sequence_block: + context = {'specific_masquerade': is_masquerading_as_specific_student(request.user, course_key)} # If the SequenceModule feels that gating is necessary, redirect # there so we can have some kind of error message at any rate. - if ancestor_sequence_block.descendants_are_gated(): + if ancestor_sequence_block.descendants_are_gated(context): return redirect( reverse( 'render_xblock',