Skip to content

feat: enforce AuthZ permissions in the Date Extensions tab endpoints - #39362

Open
BryanttV wants to merge 5 commits into
masterfrom
ks/issue-491-date-extensions-authz
Open

BryanttV wants to merge 5 commits into
masterfrom
ks/issue-491-date-extensions-authz

Conversation

@BryanttV

@BryanttV BryanttV commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

Migrates the five endpoints behind the Date Extensions tab to the shared Instructor Dashboard AuthZ infrastructure, following the migration guide in lms/djangoapps/instructor/authz/README.rst.

With authz.enable_course_authoring on, InstructorAuthzPermission is the only permission check, so a user whose AuthZ role has no legacy equivalent but holds the date extensions permissions can use the whole tab. With the flag off, behavior is unchanged.

Affected roles: Course Admin, Course Staff, legacy Instructor, legacy Staff, Course Limited Staff, Data Researcher, Beta Tester.

Endpoints (base path /api/instructor/v2)

Endpoint Method AuthZ permission Legacy fallback (flag off)
/courses/<course_id>/graded_subsections GET courses.view_learner_extensions VIEW_DASHBOARD
/courses/<course_id>/unit_extensions GET courses.view_learner_extensions VIEW_DASHBOARD
/courses/<course_id>/learners/<email_or_username> (learner search, shared with other tabs) GET courses.view_learner_extensions VIEW_DASHBOARD
/courses/<course_id>/change_due_date POST courses.manage_learner_extensions GIVE_STUDENT_EXTENSION
/courses/<course_id>/instructor/api/reset_due_date (legacy v1, used by the tab to rescind an extension) POST courses.manage_learner_extensions GIVE_STUDENT_EXTENSION

Notes

  • ResetDueDate: the MFE calls this legacy v1 endpoint to rescind an extension. It used InstructorPermission, which only sees legacy roles, so it returned 403 for AuthZ-only users while the rest of the tab worked. The other legacy v1 views (change_due_date, show_unit_extensions, show_student_extensions) are not used by the MFE and are not migrated.
  • Single permission migration: each view keeps its existing permission_name as the fallback. LEGACY_PERMISSION_CHECKS in lms/djangoapps/instructor/authz/legacy.py already maps both permissions to those checks (added in feat: enforce AuthZ permissions in the Instructor Dashboard API v2 #39255), so no new legacy-check code is needed.
  • No extra course access check: the views only load the course with get_course_by_id, so InstructorAuthzPermission is the single barrier. A user holding only the AuthZ permission gets access without any legacy role.
  • view does not imply manage: a user with only courses.view_learner_extensions gets 200 on the three GETs and 403 on change_due_date, and the other way around.
  • Legacy-equivalent roles: with the flag on, Course Limited Staff, Data Researcher and Beta Tester are evaluated with their legacy rule (compat.py), so they keep exactly the access they had. This includes the Data Researcher reaching the two GETs through VIEW_DASHBOARD while the tab stays hidden for them; that is existing legacy behavior and is intentionally preserved here.
  • Tab visibility: the date_extensions tab logic in serializers_v2.py was already wired up in feat: enforce AuthZ permissions in the Instructor Dashboard API v2 #39255 and is not touched.
  • Non-existent course: the permission check does not load the course, so a user without access to a non-existent course now gets 403 instead of 404, as for any other course. Only a user who passes the check (global staff) reaches the view and gets the 404. The existing tests are adjusted, and a 403 test is added per GET endpoint.

Other changes

  • openedx-authz: bumped to 3.2.0.
  • Imports: AuthZ applet constants are imported from openedx_authz.authz instead of openedx_authz.applets.*.

Supporting Information

Testing Instructions

  1. Enable authz.enable_course_authoring for a test course.

  2. Flag on. Call the five endpoints above as each of these users:

    • Course Admin and Course Staff: 200 on all five.
    • A user whose AuthZ role has no legacy equivalent but grants the permissions: 200 on the endpoints the permission covers (view for the three GETs, manage for the two POSTs).
    • Course Editor and Course Auditor (no date extensions permissions): 403 on all five.
    • Course Limited Staff: 200 on all five. Data Researcher: 200 on the GETs, 403 on the POSTs. Beta Tester: 403 on all five (legacy rules).
    • A user with no AuthZ role: 403.
    • A legacy instructor with the AuthZ permission explicitly denied: 403 (AuthZ wins over the legacy role).
  3. Flag off. Behavior is unchanged:

    • Course Staff, Course Limited Staff, and legacy Instructor: 200 on all five.
    • Data Researcher: 200 on the GETs, 403 on the POSTs.
    • Beta Tester and an enrolled learner with no staff role: 403.
  4. Run the tests:

    pytest lms/djangoapps/instructor/tests/test_api_v2_authz.py -k DateExtensionsAuthzTest
    pytest lms/djangoapps/instructor/tests/test_api_v2.py -k "GradedSubsectionsViewTest or UnitExtensionsViewTest"
    pytest lms/djangoapps/instructor/tests/test_api.py -k "TestChangeDueDateV2 or TestDueDateExtensions"  # includes reset_due_date
    pytest lms/djangoapps/instructor/authz

Automated coverage added: DateExtensionsAuthzTest covers all five endpoints for Course Admin, Course Staff, no permission, AuthZ denying a legacy instructor, an AuthZ role without a legacy equivalent (including each permission held alone), Course Editor and Auditor, the legacy-equivalent roles with the flag on, each legacy role with the flag off, and that the flag off never queries AuthZ (is_user_allowed.assert_not_called()).

Deadline

Willow

Concerns

  • LearnerView is a shared endpoint: GET /api/instructor/v2/courses/<course_id>/learners/<email_or_username> (learner search) is also used by other tabs of the MFE, such as Grading and Special Exams. It is migrated here using only the Date Extensions permission, courses.view_learner_extensions, since that is the one this PR can justify. As a consequence, an AuthZ-only user (a role with no legacy equivalent) needs that permission to search learners, even from the other tabs. For example, a role that can manage grades but does not hold view_learner_extensions gets 403 on the search. This behavior could change in the future by a product decision, for instance to accept the permissions of the other tabs too (authz_permissions already accepts several, any one is enough). Special Exams has no AuthZ permission yet.

Other Information

  • The Data Researcher can read the graded subsections and the unit extensions (learner names and due dates) through the two GETs, although the tab is hidden for them. It is legacy behavior, preserved on purpose. It is worth tracking separately whether those GETs should require staff access.
  • Not migrated here, and used by other tabs of the MFE: list_instructor_tasks and get_student_enrollment_status. They still use InstructorPermission, so AuthZ-only users get 403 on them.
  • The PR is based on master before the openedx-authz 3.2.0 bump and the applet imports change in the Cohorts PR, so the same lines may need a trivial rebase depending on the merge order.

@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Nov 10, 2026
@openedx-webhooks

openedx-webhooks commented Nov 10, 2026 •

Copy link
Copy Markdown

Thanks for the pull request, @BryanttV!

This repository is currently maintained by @openedx/wg-maintenance-openedx-platform-oncall.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@github-project-automation github-project-automation Bot moved this to Needs Triage in Contributions Nov 10, 2026
@BryanttV BryanttV linked an issue Nov 10, 2026 that may be closed by this pull request
1 task
@BryanttV
BryanttV marked this pull request as draft November 10, 2026 17:06
@BryanttV
BryanttV changed the base branch from bav/instructor-authz-shared-infra to master November 10, 2026 17:15
@BryanttV
BryanttV force-pushed the ks/issue-491-date-extensions-authz branch 2 times, most recently from 70a526b to ac947d1 Compare November 10, 2026 17:25
@BryanttV
BryanttV marked this pull request as ready for review November 10, 2026 20:19
@BryanttV
BryanttV force-pushed the ks/issue-491-date-extensions-authz branch from 6a8fae3 to ee176dd Compare November 12, 2026 16:35
@BryanttV
BryanttV changed the base branch from master to bav/update-openedx-authz November 12, 2026 16:35
@BryanttV
BryanttV marked this pull request as draft November 12, 2026 16:59
Base automatically changed from bav/update-openedx-authz to master November 12, 2026 17:31
efortish and others added 5 commits November 12, 2026 12:41
Migrate ChangeDueDateView, GradedSubsectionsView and UnitExtensionsView
to InstructorAuthzPermission, following the shared infrastructure from
openedx-authz#486. GET endpoints require courses.view_learner_extensions,
the POST endpoint requires courses.manage_learner_extensions. Both
already had a matching default legacy check registered in
LEGACY_PERMISSION_CHECKS, so the existing permission_name on each view
is reused unchanged as the fallback when the authz.enable_course_authoring
flag is disabled for the course.

Add DateExtensionsAuthzTest covering Course Admin, Course Staff, a user
with no permission, AuthZ denying a legacy instructor, and the legacy
path with the flag off, for all three endpoints.

Closes openedx-authz#491.
…s in Date Extensions

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
LearnerView is shared with other tabs of the Instructor Dashboard MFE. It
is migrated using only the Date Extensions permission,
courses.view_learner_extensions, with VIEW_DASHBOARD as the legacy
fallback.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@BryanttV
BryanttV force-pushed the ks/issue-491-date-extensions-authz branch from ee176dd to 9a2ebdb Compare November 12, 2026 17:41
@BryanttV
BryanttV marked this pull request as ready for review November 12, 2026 18:50

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

Enforcement: Date Extensions tab

3 participants