-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #35919 from mitodl/asad/backport-35655
[Backport] fix: bypass access checks when populating course blocks cache
- Loading branch information
Showing
3 changed files
with
169 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,4 +108,5 @@ def get_course_blocks( | |
transformers, | ||
starting_block_usage_key, | ||
collected_block_structure, | ||
user, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# pylint: disable=attribute-defined-outside-init | ||
""" | ||
Tests for course_blocks API | ||
""" | ||
|
||
from unittest.mock import Mock, patch | ||
|
||
import ddt | ||
from django.http.request import HttpRequest | ||
|
||
from common.djangoapps.student.tests.factories import UserFactory | ||
from lms.djangoapps.course_blocks.api import get_course_blocks | ||
from lms.djangoapps.course_blocks.transformers.tests.helpers import CourseStructureTestCase | ||
from lms.djangoapps.course_blocks.transformers.tests.test_user_partitions import UserPartitionTestMixin | ||
from lms.djangoapps.courseware.block_render import make_track_function, prepare_runtime_for_user | ||
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers | ||
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort | ||
from xmodule.modulestore.django import modulestore | ||
|
||
|
||
def get_block_side_effect(block_locator, user_known): | ||
""" | ||
Side effect for `CachingDescriptorSystem.get_block` | ||
""" | ||
store = modulestore() | ||
course = store.get_course(block_locator.course_key) | ||
block = store.get_item(block_locator) | ||
runtime = block.runtime | ||
user = UserFactory.create() | ||
user.known = user_known | ||
|
||
prepare_runtime_for_user( | ||
user=user, | ||
student_data=Mock(), | ||
runtime=runtime, | ||
course_id=block_locator.course_key, | ||
track_function=make_track_function(HttpRequest()), | ||
request_token=Mock(), | ||
course=course, | ||
) | ||
return block.runtime.get_block_for_descriptor(block) | ||
|
||
|
||
def get_block_side_effect_for_known_user(self, *args, **kwargs): | ||
""" | ||
Side effect for known user test. | ||
""" | ||
return get_block_side_effect(self, True) | ||
|
||
|
||
def get_block_side_effect_for_unknown_user(self, *args, **kwargs): | ||
""" | ||
Side effect for unknown user test. | ||
""" | ||
return get_block_side_effect(self, False) | ||
|
||
|
||
@ddt.ddt | ||
class TestGetCourseBlocks(UserPartitionTestMixin, CourseStructureTestCase): | ||
""" | ||
Tests `get_course_blocks` API | ||
""" | ||
|
||
def setup_partitions_and_course(self): | ||
""" | ||
Setup course structure. | ||
""" | ||
# Set up user partitions and groups. | ||
self.setup_groups_partitions(active=True, num_groups=1) | ||
self.user_partition = self.user_partitions[0] | ||
|
||
# Build course. | ||
self.course_hierarchy = self.get_course_hierarchy() | ||
self.blocks = self.build_course(self.course_hierarchy) | ||
self.course = self.blocks['course'] | ||
|
||
# Set up cohorts. | ||
self.setup_cohorts(self.course) | ||
|
||
def get_course_hierarchy(self): | ||
""" | ||
Returns a course hierarchy to test with. | ||
""" | ||
# course | ||
# / \ | ||
# / \ | ||
# A[0] B | ||
# | | ||
# | | ||
# O | ||
|
||
return [ | ||
{ | ||
'org': 'UserPartitionTransformer', | ||
'course': 'UP101F', | ||
'run': 'test_run', | ||
'user_partitions': [self.user_partition], | ||
'#type': 'course', | ||
'#ref': 'course', | ||
'#children': [ | ||
{ | ||
'#type': 'vertical', | ||
'#ref': 'A', | ||
'metadata': {'group_access': {self.user_partition.id: [0]}}, | ||
}, | ||
{'#type': 'vertical', '#ref': 'B'}, | ||
], | ||
}, | ||
{ | ||
'#type': 'vertical', | ||
'#ref': 'O', | ||
'#parents': ['B'], | ||
}, | ||
] | ||
|
||
@ddt.data( | ||
(1, ('course', 'B', 'O'), True), | ||
(1, ('course', 'A', 'B', 'O'), False), | ||
(None, ('course', 'B', 'O'), True), | ||
(None, ('course', 'A', 'B', 'O'), False), | ||
) | ||
@ddt.unpack | ||
def test_get_course_blocks(self, group_id, expected_blocks, user_known): | ||
""" | ||
Tests that `get_course_blocks` returns blocks without access checks for unknown users. | ||
Access checks are done through the transformers and through Runtime get_block_for_descriptor. Due | ||
to the runtime limitations during the tests, the Runtime access checks are not performed as | ||
get_block_for_descriptor is never called and Block is returned by CachingDescriptorSystem.get_block. | ||
In this test, we mock the CachingDescriptorSystem.get_block and check block access for known and unknown users. | ||
For known users, it performs the Runtime access checks through get_block_for_descriptor. For unknown, it | ||
skips the access checks. | ||
""" | ||
self.setup_partitions_and_course() | ||
if group_id: | ||
cohort = self.partition_cohorts[self.user_partition.id - 1][group_id - 1] | ||
add_user_to_cohort(cohort, self.user.username) | ||
|
||
side_effect = get_block_side_effect_for_known_user if user_known else get_block_side_effect_for_unknown_user | ||
with patch('xmodule.modulestore.split_mongo.split.CachingDescriptorSystem.get_block', side_effect=side_effect): | ||
block_structure = get_course_blocks( | ||
self.user, | ||
self.course.location, | ||
BlockStructureTransformers([]), | ||
) | ||
self.assertSetEqual( | ||
set(block_structure.get_block_keys()), | ||
self.get_block_key_set(self.blocks, *expected_blocks) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters