Skip to content

Enforcement: Cohorts tab (course_groups API) #492

Open
eduNEXT/edx-platform
#328

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.
  2. efortish commented on Oct 20, 2026

    @efortish
    Contributor

    @BryanttV Could you take a look of this?

  3. BryanttV commented on Oct 20, 2026

    @BryanttV
    ContributorAuthor

    Hi @efortish, thanks for the analysis!

    The PR for #486 is now up, and the explanation on how to migrate a view is in the README. I'd love to get your review as well. If you have any questions, please let me know.

    We need to migrate all the views used by the Instructor Dashboard MFE for the Cohorts tab. I've left a list of the views here: Cohorts Tab, but if you spot any other endpoint used by the MFE, please point it out so we can take it into account for the migration.

    Views used by the legacy UI, such as course_cohort_settings_handler, won't be migrated for now.

  4. efortish commented on Oct 20, 2026

    @efortish
    Contributor

    Reviewed #39255, left my notes there: openedx/openedx-platform#39255 (review)

    Good news for this issue specifically: legacy.py's has_cohort_view_access/has_cohort_manage_access already reproduce IsStaffOrAdmin's current logic, which is exactly the legacy-equivalent gap I flagged in my earlier comment here. So once #486 merges, wiring the three DRF views (CohortSettings, CohortHandler, CohortUsers), CohortCSV, and course_cohort_settings_handler over to InstructorAuthzPermission/user_has_instructor_permission should be mostly mechanical, following the README's own cohorts example.

    One gap that isn't specific to this issue: courses.view_cohorts/courses.manage_cohorts aren't defined in the openedx-authz schema yet (same as every Instructor Dashboard permission except course team and grading settings). That'll need to land in course_permissions.yaml before this can be wired for real.

  5. moved this from Blocked to In Progress in RBAC AuthZ Boardon Oct 21, 2026
  6. BryanttV commented on Oct 21, 2026

    @BryanttV
    ContributorAuthor

    Thanks, @efortish. For now, we can move forward with the PR based on the branch from openedx/openedx-platform#39255, which should already have everything needed to migrate a view. Regarding the permissions, they will be included in this issue: #484. In the meantime, we can work by adding the specific permissions we need directly in Casbin, just for testing purposes.

  7. efortish commented on Oct 21, 2026

    @efortish
    Contributor

    Opened a PR covering this: eduNEXT/edx-platform#328

    Targeting bav/instructor-authz-shared-infra directly (same approach as openedx-authz#491) so the diff only shows this issue's changes. The PR description has the full detail: which endpoint maps to which permission, why no legacy_permission_check was needed for the three DRF views (the per-method default checks already registered reproduce IsStaffOrAdmin exactly), the two endpoints whose legacy fallback I deliberately kept narrower than the others (course_cohort_settings_handler and CohortCSV, both flagged with the reasoning), and a shared-infra fix (InstructorAuthzPermission needed a configurable course_id_url_kwarg, since cohorts uses course_key_string). Draft until #39255 merges.

  8. self-assigned this
    on Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

willowReleased in Willow

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions