Merge pull request #37547 from open-craft/agrendalath/start-date-masquerade
feat: add Waffle flag to control start date access for masquerading users
This commit is contained in:
@@ -23,7 +23,11 @@ from lms.djangoapps.courseware.access_response import (
|
||||
StartDateError,
|
||||
)
|
||||
from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_student
|
||||
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, COURSE_PRE_START_ACCESS_FLAG
|
||||
from openedx.features.course_experience import (
|
||||
COURSE_ENABLE_UNENROLLED_ACCESS_FLAG,
|
||||
COURSE_PRE_START_ACCESS_FLAG,
|
||||
ENFORCE_MASQUERADE_START_DATES,
|
||||
)
|
||||
from xmodule.course_block import COURSE_VISIBILITY_PUBLIC # lint-amnesty, pylint: disable=wrong-import-order
|
||||
|
||||
DEBUG_ACCESS = False
|
||||
@@ -137,7 +141,10 @@ def check_start_date(user, days_early_for_beta, start, course_key, display_error
|
||||
if start_dates_disabled and not masquerading_as_student:
|
||||
return ACCESS_GRANTED
|
||||
else:
|
||||
if start is None or get_course_masquerade(user, course_key):
|
||||
if start is None:
|
||||
return ACCESS_GRANTED
|
||||
|
||||
if not ENFORCE_MASQUERADE_START_DATES.is_enabled(course_key) and get_course_masquerade(user, course_key):
|
||||
return ACCESS_GRANTED
|
||||
|
||||
if now is None:
|
||||
|
||||
@@ -17,6 +17,7 @@ from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
|
||||
@@ -30,6 +31,7 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_experience import ENFORCE_MASQUERADE_START_DATES
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.roles import CourseCcxCoachRole, CourseStaffRole
|
||||
from common.djangoapps.student.tests.factories import (
|
||||
@@ -465,6 +467,50 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
|
||||
self.verify_access(mock_unit, expected_access, expected_error_type)
|
||||
|
||||
@ddt.data(
|
||||
# Flag inactive (default)
|
||||
(False, True, None), # Masquerading, no start date
|
||||
(False, True, YESTERDAY), # Masquerading, past start date
|
||||
(False, False, TOMORROW), # Not masquerading, future start date
|
||||
(False, True, TOMORROW), # Masquerading, future start date
|
||||
# Flag active
|
||||
(True, True, None), # Masquerading, no start date
|
||||
(True, True, YESTERDAY), # Masquerading, past start date
|
||||
(True, False, TOMORROW), # Not masquerading, future start date
|
||||
(True, True, TOMORROW, False), # Masquerading, future start date - no access
|
||||
)
|
||||
@ddt.unpack
|
||||
@patch.dict("django.conf.settings.FEATURES", {"DISABLE_START_DATES": False})
|
||||
def test_enforce_masquerade_start_dates_flag(self, flag_active, is_masquerading, start, expected_access=True):
|
||||
"""
|
||||
Test that the ENFORCE_MASQUERADE_START_DATES flag controls whether masquerading bypasses start date
|
||||
restrictions.
|
||||
|
||||
When the flag is disabled (default), masquerading users bypass start dates.
|
||||
When the flag is enabled, masquerading users see the same start date restrictions as regular students.
|
||||
"""
|
||||
mock_unit = Mock(
|
||||
location=self.course.location,
|
||||
user_partitions=[],
|
||||
_class_tags={},
|
||||
start=self.DATES[start],
|
||||
visible_to_staff_only=False,
|
||||
merged_group_access={},
|
||||
)
|
||||
|
||||
if is_masquerading:
|
||||
self.course_staff.masquerade_settings = {self.course.id: CourseMasquerade(self.course.id, role="student")}
|
||||
|
||||
with override_waffle_flag(ENFORCE_MASQUERADE_START_DATES, active=flag_active):
|
||||
response = access._has_access_to_block(self.course_staff, "load", mock_unit, course_key=self.course.id)
|
||||
|
||||
if expected_access:
|
||||
assert response == access.ACCESS_GRANTED
|
||||
else:
|
||||
assert isinstance(response, access_response.StartDateError)
|
||||
assert response.to_json()["error_code"] is not None
|
||||
assert str(self.DATES[start]) in response.developer_message
|
||||
|
||||
def test__has_access_course_can_enroll(self):
|
||||
yesterday = datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1)
|
||||
tomorrow = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)
|
||||
|
||||
@@ -83,6 +83,23 @@ RELATIVE_DATES_DISABLE_RESET_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.r
|
||||
# .. toggle_tickets: https://openedx.atlassian.net/browse/AA-36
|
||||
CALENDAR_SYNC_FLAG = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.calendar_sync', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation
|
||||
|
||||
# .. toggle_name: course_experience.enforce_masquerade_start_dates
|
||||
# .. toggle_implementation: CourseWaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: When enabled, staff masquerading as students will see the same start date
|
||||
# restrictions as actual students. This provides a more accurate preview experience by enforcing
|
||||
# section and subsection start dates even when viewing the course as a masqueraded user.
|
||||
# When disabled (default), masquerading continues to bypass start date restrictions as before.
|
||||
# .. toggle_use_cases: opt_in
|
||||
# .. toggle_creation_date: 2025-10-08
|
||||
# .. toggle_warning: Enabling this flag means staff members masquerading as students will not be able to access course
|
||||
# content before its start date, which may impact course testing workflows.
|
||||
# Also, when you masquerade as a student in a course that starts in the future, you will lock yourself out of the
|
||||
# course in the current Django session. To revert this, you need to log out and log back in.
|
||||
ENFORCE_MASQUERADE_START_DATES = CourseWaffleFlag(
|
||||
f'{WAFFLE_FLAG_NAMESPACE}.enforce_masquerade_start_dates', __name__
|
||||
)
|
||||
|
||||
|
||||
def course_home_page_title(_course):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user