Skip to content

feat: apply OEP-66 queryset-scoping pattern to Enrollments v2 API - #39037

Open
Abdul-Muqadim-Arbisoft wants to merge 1 commit into
openedx:masterfrom
edly-io:feat/apply-queryset-scoping-to-enrollments-v2
Open

feat: apply OEP-66 queryset-scoping pattern to Enrollments v2 API#39037
Abdul-Muqadim-Arbisoft wants to merge 1 commit into
openedx:masterfrom
edly-io:feat/apply-queryset-scoping-to-enrollments-v2

Conversation

@Abdul-Muqadim-Arbisoft

Copy link
Copy Markdown
Contributor

Adopt the OEP-66 "Separating Authorization Concerns in List Endpoints" record-visibility layer on the admin enrollment list, using the shared building blocks now published in edx-drf-extensions.

EnrollmentsAdminListView is an ORM-backed ListAPIView, so it wires the three authorization concerns separately:

  • Endpoint access: permission_classes = (IsAdminUser,).
  • Record visibility: ScopedQuerysetMixin applies AdminEnrollmentScopingPolicy to the base queryset in get_queryset(). The policy is an intentional pass-through for platform admins today, documented as the single seam where an openedx-authz scope-set filter (e.g. restricting a delegated, org-scoped admin to their organization's enrollments) will plug in later; behavior is unchanged.
  • User-driven filtering: the form-based course_key/course_keys/username/ email/ordering filtering moves from get_queryset() into filter_queryset(), so it runs after scoping and only narrows the already-authorized queryset.

Bumps edx-drf-extensions 10.6.0 -> 10.7.0, the release that adds the reusable ScopingPolicy (a typing.Protocol) and ScopedQuerysetMixin. Adds TestEnrollmentsAdminListView regression tests covering endpoint access (401/403), pass-through scoping (admin sees all rows), the course_key/username filters, the 400-on-invalid-params path, the ADR 0033 Deprecation header, and the scoping-policy pass-through.

Follows up the closed PR #38847: per review, the shared ScopingPolicy / ScopedQuerysetMixin tooling moved to edx-drf-extensions (#569) as a subject-based typing.Protocol with a duck-typed mixin check, and this change consumes it rather than defining it locally.

@Abdul-Muqadim-Arbisoft
Abdul-Muqadim-Arbisoft requested a review from a team as a code owner August 27, 2026 13:28
Adopt the OEP-66 "Separating Authorization Concerns in List Endpoints"
record-visibility layer on the admin enrollment list, using the shared
building blocks now published in edx-drf-extensions.

EnrollmentsAdminListView is an ORM-backed ListAPIView, so it wires the three
authorization concerns separately:

  * Endpoint access — permission_classes = (IsAdminUser,).
  * Record visibility — ScopedQuerysetMixin applies AdminEnrollmentScopingPolicy
    to the base queryset in get_queryset(). The policy is an intentional
    pass-through for platform admins today, documented as the single seam where
    an openedx-authz scope-set filter (e.g. restricting a delegated, org-scoped
    admin to their organization's enrollments) will plug in later; behavior is
    unchanged.
  * User-driven filtering — the form-based course_key/course_keys/username/
    email/ordering filtering moves from get_queryset() into filter_queryset(),
    so it runs after scoping and only narrows the already-authorized queryset.

Bumps edx-drf-extensions 10.6.0 -> 10.7.0, the release that adds the reusable
ScopingPolicy (a typing.Protocol) and ScopedQuerysetMixin. Adds
TestEnrollmentsAdminListView regression tests covering endpoint access
(401/403), pass-through scoping (admin sees all rows), the course_key/username
filters, the 400-on-invalid-params path, the ADR 0033 Deprecation header, and
the scoping-policy pass-through.

Follows up the closed PR openedx#38847: per review, the shared ScopingPolicy /
ScopedQuerysetMixin tooling moved to edx-drf-extensions (#569) as a
subject-based typing.Protocol with a duck-typed mixin check, and this change
consumes it rather than defining it locally.
@Abdul-Muqadim-Arbisoft
Abdul-Muqadim-Arbisoft force-pushed the feat/apply-queryset-scoping-to-enrollments-v2 branch from 78d3f46 to 3678f4c Compare August 30, 2026 10:04
def test_filter_by_username_narrows(self):
"""User-driven filter (filter_queryset) still narrows by username."""
self.client.force_authenticate(user=self.admin)
response = self.client.get(self.url, {"username": self.learner_b.username})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should only work for admins correct? we should have the inverted test validating that users can't see eachothers enrollments by passing eachothers names in.

Comment on lines +633 to +637
OEP-66 record-visibility policy for the admin enrollment list.

Implements the ``ScopingPolicy`` protocol from
``edx_rest_framework_extensions.scoping`` by duck typing (a ``scope``
method); it does not need to inherit from it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all relevant to our review but not really relevant as a long term comment is it?

I'm seeing this in a bunch of places, where LLMs drop in content relevant to the current task in the comments that won't really be relevant long term to the code they are adding it to. We should re-read these and make sure the comments are useful and relevant to future readers.


def scope(self, queryset, subject):
# Platform admins (IsAdminUser) see all enrollments; nothing to narrow yet.
return queryset

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of boilerplate for essentially a no-op on scoping, can we simplify this? Maybe we have some sort of a "FullScopePolicy" that can be used in many places for things like this?

"""
Admin-only paginated enrollment list with OEP-68 filter aliases.

OEP-66 — this ORM-backed list endpoint wires the three authorization layers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need the names of our OEPs in the code, the code should just follow the standards.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants