feat: add Waffle flag to control start date access for masquerading users
This introduces the `ENFORCE_MASQUERADE_START_DATES` flag. 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.
This commit is contained in:
committed by
Piotr Surowiec
parent
6deb4f8d05
commit
890b8268c9
@@ -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