CourseTeamManageAPIView at
lms/djangoapps/support/rest_api/v1/views.py declared only
`permission_classes = (IsAuthenticated,)`, so any authenticated
user — including a plain enrolled learner — could invoke GET and
use the 404-vs-200 response for user enumeration against arbitrary
email/username/user_id values, confirming which accounts exist and
are active. Callers holding any course/org `instructor` role
additionally received the target user's `staff`/`instructor` role
map across the caller's scope.
Gate `get()` on `_caller_can_manage_course_team()`, a new helper
that returns True only for:
- is_staff (GlobalStaff), or
- is_superuser, or
- SupportStaffRole, or
- any user with a CourseAccessRole of role="instructor"
This is the union of PUT's current authorization set (admin/staff/
superuser/instructor) and the global SupportStaffRole. Read access
must match write access — a user who can PUT role changes must be
able to see the current state before making them — and the endpoint
lives in the support module, so support staff belong in the set
too. Also extend `get_accessible_courses_for_user` so that
SupportStaffRole users get the same all-courses view as admin/
staff, otherwise they'd fall through to the instructor branch and
receive an empty result set.
Anonymous callers still get 401 from DRF's IsAuthenticated; plain
learners now get 403 before the target lookup runs, so the 404-vs-
200 enumeration discrepancy (CWE-204) is no longer reachable by
unauthorized callers. Course instructors retain their scoped view.
`put()` retains its existing authorization for now; it lacks
SupportStaffRole in the set — a separate design gap best handled in
a public follow-up after publication (cross-linked to
GHSA-95xv-3c54-c3pw), where the `_caller_can_manage_course_team`
helper can also become the shared PUT check.
Fixes GHSA-95xv-3c54-c3pw. Credit: 5ud0 / Tarmo Technologies.