-
Notifications
You must be signed in to change notification settings - Fork 5
Open
Description
There are a number of complexities with CCX courses and RBAC, including a "CCX Coach" role that is not currently accounted for, and has a variety of distinct permissions and use cases that we likely do not have time for in Verawood. Product has said that it is ok to leave off CCX until Willow, but we still need to handle some errors and edge cases that exist with the current implementation.
Currently there are a number of issues around CCX courses with AuthZ (likely there are more we hidden behind these):
Migration errors
- Migrating a course with CCX enabled and any CCX courses underneath using
authz_migrate_course_authoring(whether or not the course has assigned any users the CCX Coach role) fails with:utils.py:228 - Unknown access level: ccx_coach for User: tyaxim - Migrating a CCX course with
authz_migrate_course_authoring --course-id-list ccx-v1:OpenedX+DemoX+DemoCourse+ccx@1falsely succeeds withMigration completed successfully with 0 roles migrated. - Migrating a CCX course without the CCX number like
authz_migrate_course_authoring --course-id-list ccx-v1:OpenedX+DemoX+DemoCourse+ccxerrors withopaque_keys.InvalidKeyError: <class 'opaque_keys.edx.locator.CourseLocator'>: ccx-v1:OpenedX+DemoX+DemoCourse+ccx
Rollback errors
- Rolling back CCX courses or their parents always reports:
Rollback completed successfully with 0 roles rolled back.
Runtime errors
Turning on the feature flag for a course that has CCX enabled and CCX courses
Instructor dashboard on the parent course, any calls to "list_course_role_members":
lms-1 | 2026-03-23 19:20:26,944 ERROR 43 [django.request] [user 4] [ip 172.19.0.1] log.py:253 - Internal Server Error: /courses/ccx-v1:OpenedX+DemoX+DemoCourse+ccx@1/instructor/api/list_course_role_members
lms-1 | Traceback (most recent call last):
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
lms-1 | response = get_response(request)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
lms-1 | response = wrapped_callback(request, *callback_args, **callback_kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/opt/pyenv/versions/3.12.12/lib/python3.12/contextlib.py", line 81, in inner
lms-1 | return func(*args, **kwds)
lms-1 | ^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
lms-1 | return view_func(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/views/generic/base.py", line 105, in view
lms-1 | return self.dispatch(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 48, in _wrapper
lms-1 | return bound_method(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/views/decorators/cache.py", line 54, in _view_wrapper
lms-1 | response = viewfunc(request, *args, **kw)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 515, in dispatch
lms-1 | response = self.handle_exception(exc)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 475, in handle_exception
lms-1 | self.raise_uncaught_exception(exc)
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
lms-1 | raise exc
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 512, in dispatch
lms-1 | response = handler(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 48, in _wrapper
lms-1 | return bound_method(*args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 192, in _view_wrapper
lms-1 | result = _process_exception(request, e)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/utils/decorators.py", line 190, in _view_wrapper
lms-1 | response = view_func(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/lms/djangoapps/instructor/views/api.py", line 1091, in post
lms-1 | users = list_with_level(course.id, rolename)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/lms/djangoapps/instructor/access.py", line 46, in list_with_level
lms-1 | return ROLES[level](course_id).users_with_role()
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 526, in users_with_role
lms-1 | return self._authz_users_with_role()
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 498, in _authz_users_with_role
lms-1 | users_data = authz_api.get_users_for_role_in_scope(
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/openedx_authz/api/users.py", line 206, in get_users_for_role_in_scope
lms-1 | ScopeData(external_key=scope_external_key),
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/openedx_authz/api/data.py", line 203, in __call__
lms-1 | scope_cls = cls.get_subclass_by_external_key(kwargs["external_key"])
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/openedx_authz/api/data.py", line 269, in get_subclass_by_external_key
lms-1 | raise ValueError(f"Unknown scope: {namespace} for external_key: {external_key}")
lms-1 | ValueError: Unknown scope: ccx-v1 for external_key: ccx-v1:OpenedX+DemoX+DemoCourse+ccx@1
Visiting course home for the CCX course:
lms-1 | 2026-03-23 19:20:36,049 ERROR 43 [django.request] [user 4] [ip 172.19.0.1] log.py:253 - Internal Server Error: /api/course_home/outline/ccx-v1:OpenedX+DemoX+DemoCourse+ccx@1
lms-1 | Traceback (most recent call last):
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
lms-1 | response = get_response(request)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
lms-1 | response = wrapped_callback(request, *callback_args, **callback_kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/opt/pyenv/versions/3.12.12/lib/python3.12/contextlib.py", line 81, in inner
lms-1 | return func(*args, **kwds)
lms-1 | ^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
lms-1 | return view_func(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/django/views/generic/base.py", line 105, in view
lms-1 | return self.dispatch(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 515, in dispatch
lms-1 | response = self.handle_exception(exc)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 475, in handle_exception
lms-1 | self.raise_uncaught_exception(exc)
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
lms-1 | raise exc
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/rest_framework/views.py", line 512, in dispatch
lms-1 | response = handler(request, *args, **kwargs)
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/lms/djangoapps/course_home_api/outline/views.py", line 304, in get
lms-1 | elif CourseEnrollment.objects.is_course_full(course_overview):
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/common/djangoapps/student/models/course_enrollment.py", line 245, in is_course_full
lms-1 | is_course_full = self.num_enrolled_in_exclude_admins(course.id) >= course.max_student_enrollments_allowed
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/common/djangoapps/student/models/course_enrollment.py", line 231, in num_enrolled_in_exclude_admins
lms-1 | coaches = CourseCcxCoachRole(course_locator).users_with_role()
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 526, in users_with_role
lms-1 | return self._authz_users_with_role()
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 498, in _authz_users_with_role
lms-1 | users_data = authz_api.get_users_for_role_in_scope(
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/openedx_authz/api/users.py", line 205, in get_users_for_role_in_scope
lms-1 | RoleData(external_key=role_external_key),
lms-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lms-1 | File "<attrs generated methods openedx_authz.api.data.RoleData>", line 5, in __init__
lms-1 | self.__attrs_post_init__()
lms-1 | File "/openedx/venv/lib/python3.12/site-packages/openedx_authz/api/data.py", line 135, in __attrs_post_init__
lms-1 | raise ValueError("Either external_key or namespaced_key must be provided.")
lms-1 | ValueError: Either external_key or namespaced_key must be provided.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels