Skip to content

Enforcement: Cohorts tab (course_groups API) #492

Open

Description

@BryanttV

Permissions

  • courses.view_cohorts
  • courses.manage_cohorts

Endpoints

These endpoints are not part of the instructor v2 API. They live in openedx/core/djangoapps/course_groups:

  • /api/cohorts/v1/courses/{course_id}/cohorts/… (GET → view_cohorts, write → manage_cohorts)
  • /api/cohorts/v1/courses/{course_id}/cohorts/{id}/users/…
  • Cohort CSV upload → manage_cohorts
  • courses/{course_id}/cohorts/settings (course_cohort_settings_handler) → read or write depending on the method

Notes

  • Other consumers also use these endpoints (e.g. discussions and the legacy LMS). The change must not remove access from anyone who has it today.
  • Estimate this task separately: it's a different app, with different tests and reviewers.

Acceptance criteria

Blocked by: #486

Activity

  1. efortish commented on Oct 17, 2026

    @efortish
    Contributor

    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.

    Suggested plan once #486 unblocks this

    1. Add the LMS-equivalent legacy permission handler (reproducing IsStaffOrAdmin's current logic) wherever Shared infrastructure and exposing permissions to the MFE #486 lands it.
    2. 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.
    3. 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.
    4. 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.
    5. Leave the CourseMetadataView permissions/tab-visibility piece to Shared infrastructure and exposing permissions to the MFE #486 rather than duplicating a one-off version of it here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

willowReleased in Willow

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions