You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Looked into this to scope the work ahead of time. #486 (the blocker) is still open with no PR or branch from anyone, so there's nothing to pick up there yet. Leaving the findings here so whoever picks this up next doesn't have to re-derive them.
Endpoints in scope
In openedx/core/djangoapps/course_groups, all three use the same DRF permission class today, permission_classes = (IsAuthenticated, IsStaffOrAdmin):
CohortSettings (views.py, around line 485): GET/PATCH, /api/cohorts/v1/settings/{course_id}
CohortHandler (views.py, around line 525): GET should map to view_cohorts, POST/PATCH to manage_cohorts, /api/cohorts/v1/courses/{course_id}/cohorts/{id}
CohortUsers (views.py, around line 675): GET to view_cohorts, POST/DELETE to manage_cohorts, .../cohorts/{id}/users/{username}
Outside that DRF group:
CohortCSV (lms/djangoapps/instructor/views/api.py): should require manage_cohorts
course_cohort_settings_handler (course_groups/views.py, around line 124): a legacy function-based view, also used by Studio and discussions. GET should map to view_cohorts, PATCH to manage_cohorts
Out of scope (used by Discussions and the legacy LMS UI, not the Cohorts tab): cohort_handler, users_in_cohort, add_users_to_cohort, remove_user_from_cohort, debug_cohort_mgmt, all under the non-/api/courses/{course_id}/cohorts/... routes. These should stay untouched per the "must not remove access from anyone who has it today" note above.
What #486 already gives us, and what it doesn't yet
openedx/core/djangoapps/authz/decorators.py already has the combined check #486 describes: authz_permission_required / user_has_course_permission check AuthZ when AUTHZ_COURSE_AUTHORING_FLAG is enabled for the course, and otherwise fall back to a legacy_permission callable.
What's missing is a legacy-permission table that actually fits this tab. LegacyAuthoringPermission / LEGACY_PERMISSION_HANDLER_MAP (authz/constants.py) only map READ/WRITE to has_studio_read_access/has_studio_write_access, which is Studio authoring logic. The equivalent for this tab is what IsStaffOrAdmin (course_groups/permissions.py) already does: GET is allowed to forum admin/moderator/community TA in addition to staff/instructor, writes are staff/instructor only. That logic needs an LMS-side home, either as new entries on the existing enum/map or a separate one, as #486 itself leaves open ("Extending LegacyAuthoringPermission and LEGACY_PERMISSION_HANDLER_MAP, or creating an LMS equivalent").
CourseMetadataView (lms/djangoapps/instructor/views/api_v2.py) also doesn't expose any AuthZ permission yet, and tab visibility (get_course_tab_list) isn't wired to AuthZ either. That's explicitly #486's responsibility, not something worth solving per-tab.
Swap IsStaffOrAdmin for authz_permission_required / user_has_course_permission with courses.view_cohorts / courses.manage_cohorts plus that legacy fallback, across the three DRF views, CohortCSV, and course_cohort_settings_handler.
Tests per the common criteria in Backend permission enforcement (LMS) #485: Course Admin, Course Staff, a user with no permission, and a user with the legacy role (e.g. forum admin on GET) but no AuthZ role.
Confirm Discussions and the legacy LMS cohort UI still work, since they share some of this app's code even though their views aren't touched here.
Activity
efortish commentedon Oct 17, 2026
Looked into this to scope the work ahead of time. #486 (the blocker) is still open with no PR or branch from anyone, so there's nothing to pick up there yet. Leaving the findings here so whoever picks this up next doesn't have to re-derive them.
Endpoints in scope
In
openedx/core/djangoapps/course_groups, all three use the same DRF permission class today,permission_classes = (IsAuthenticated, IsStaffOrAdmin):CohortSettings(views.py, around line 485): GET/PATCH,/api/cohorts/v1/settings/{course_id}CohortHandler(views.py, around line 525): GET should map toview_cohorts, POST/PATCH tomanage_cohorts,/api/cohorts/v1/courses/{course_id}/cohorts/{id}CohortUsers(views.py, around line 675): GET toview_cohorts, POST/DELETE tomanage_cohorts,.../cohorts/{id}/users/{username}Outside that DRF group:
CohortCSV(lms/djangoapps/instructor/views/api.py): should requiremanage_cohortscourse_cohort_settings_handler(course_groups/views.py, around line 124): a legacy function-based view, also used by Studio and discussions. GET should map toview_cohorts, PATCH tomanage_cohortsOut of scope (used by Discussions and the legacy LMS UI, not the Cohorts tab):
cohort_handler,users_in_cohort,add_users_to_cohort,remove_user_from_cohort,debug_cohort_mgmt, all under the non-/api/courses/{course_id}/cohorts/...routes. These should stay untouched per the "must not remove access from anyone who has it today" note above.What #486 already gives us, and what it doesn't yet
openedx/core/djangoapps/authz/decorators.pyalready has the combined check #486 describes:authz_permission_required/user_has_course_permissioncheck AuthZ whenAUTHZ_COURSE_AUTHORING_FLAGis enabled for the course, and otherwise fall back to alegacy_permissioncallable.What's missing is a legacy-permission table that actually fits this tab.
LegacyAuthoringPermission/LEGACY_PERMISSION_HANDLER_MAP(authz/constants.py) only mapREAD/WRITEtohas_studio_read_access/has_studio_write_access, which is Studio authoring logic. The equivalent for this tab is whatIsStaffOrAdmin(course_groups/permissions.py) already does: GET is allowed to forum admin/moderator/community TA in addition to staff/instructor, writes are staff/instructor only. That logic needs an LMS-side home, either as new entries on the existing enum/map or a separate one, as #486 itself leaves open ("ExtendingLegacyAuthoringPermissionandLEGACY_PERMISSION_HANDLER_MAP, or creating an LMS equivalent").CourseMetadataView(lms/djangoapps/instructor/views/api_v2.py) also doesn't expose any AuthZ permission yet, and tab visibility (get_course_tab_list) isn't wired to AuthZ either. That's explicitly #486's responsibility, not something worth solving per-tab.Suggested plan once #486 unblocks this
IsStaffOrAdmin's current logic) wherever Shared infrastructure and exposing permissions to the MFE #486 lands it.IsStaffOrAdminforauthz_permission_required/user_has_course_permissionwithcourses.view_cohorts/courses.manage_cohortsplus that legacy fallback, across the three DRF views,CohortCSV, andcourse_cohort_settings_handler.CourseMetadataViewpermissions/tab-visibility piece to Shared infrastructure and exposing permissions to the MFE #486 rather than duplicating a one-off version of it here.