Repository navigation
Conversation
|
Thanks for the pull request, @BryanttV! This repository is currently maintained by 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 approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo 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:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere 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:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
89a0a76 to
a113060
Compare
rodmgwgu
left a comment
There was a problem hiding this comment.
Code is looking good, added a couple of nits and comments. Thanks!
| return CourseOverviewData(external_key=str(course_key)) | ||
|
|
||
| @override_waffle_flag(AUTHZ_COURSE_AUTHORING_FLAG, active=True) | ||
| def test_course_scope_grants_its_org(self, mock_get_scopes): |
There was a problem hiding this comment.
nit: can we add docstrings to all the tests to keep consistency please?
| key = str(course_id) | ||
| if key not in authz_course_cache: | ||
| try: | ||
| authz_course_cache[key] = enable_authz_course_authoring(CourseKey.from_string(key)) |
There was a problem hiding this comment.
I had the impression that waffle flags were already cached, do you know if that would also be the case here when querying for a specific course?
There was a problem hiding this comment.
You're right, thanks for catching that. WaffleFlagCourseOverrideModel.override_value is already @request_cached, Removed it in: 2618148
rodmgwgu
left a comment
There was a problem hiding this comment.
Tested manually against a my local env. All three testing-instruction cases pass:
- authz-only
course_editor, course switched —GET /taxonomies/?org=OpenedX→ 200, lists the org + global taxonomies; opening a taxonomy detail → 200 (was 403 before this PR). ✅ - course not switched — the authz grant alone returns nothing (empty list, detail denied), and a legacy
CourseStaffRolestill lists and opens as before. ✅ - course switched, legacy role only — a user with only a legacy
CourseAccessRoleand nocourses.manage_tagsno longer gets org membership (empty list, detail denied). ✅
One note: a denied taxonomy detail returns 404, not 403 as the testing instructions say — the view filters the queryset by get_user_orgs, so an invisible taxonomy is simply absent and DRF returns 404. The access behavior is correct; only the status code differs from the description.
aa780cc to
4acbaa1
Compare
| access_role.org == org_name and | ||
| not (access_role.course_id and course_uses_authz(access_role.course_id)) |
There was a problem hiding this comment.
How is this change related to this PR? Are we adding any tests related to this update? I couldn't find other references to user_has_role_ignore_course_id
There was a problem hiding this comment.
This change is what makes legacy roles consistent with the authz switch. Once a course has switched to openedx-authz, tagging an object in it requires courses.manage_tags (see can_change_object_tag_objectid), so a legacy CourseAccessRole there is no longer authoritative. Before this change, such a user still got org membership through _get_course_user_orgs, so they could list and open the org's taxonomies but then couldn't tag anything in that course.
| scopes = rules_cache.get_authz_manage_tags_scopes(user) | ||
|
|
||
| org_names = set() | ||
| all_orgs = False | ||
| glob_authz_enabled = None | ||
| for scope in scopes: | ||
| if isinstance(scope, CourseOverviewData): | ||
| # Skip the per-course flag lookup when the org is already known. | ||
| if scope.org not in org_names and enable_authz_course_authoring(scope.course_key): | ||
| org_names.add(scope.org) | ||
| elif isinstance(scope, OrgCourseOverviewGlobData | PlatformCourseOverviewGlobData): | ||
| if glob_authz_enabled is None: | ||
| glob_authz_enabled = enable_authz_course_authoring() | ||
| if not glob_authz_enabled: | ||
| continue | ||
| if isinstance(scope, PlatformCourseOverviewGlobData): | ||
| all_orgs = True | ||
| break | ||
| if scope.org: | ||
| org_names.add(scope.org) | ||
|
|
||
| if all_orgs: | ||
| return list(orgs) | ||
| return [org for org in orgs if org.short_name in org_names] |
There was a problem hiding this comment.
Could we extract or reuse the scope-expansion logic already implemented in _get_course_keys_from_scopes? It already resolves course, organization, and platform-wide Authz scopes into concrete course keys.
I think get_authz_manage_tags_scopes needs similar behavior, so sharing this logic would keep the scope semantics consistent and avoid implementing the same expansion twice. We may need to generalize it if the tagging case also covers library scopes. What do you think?
Similar comment for get_authz_manage_tags_scopes
There was a problem hiding this comment.
Good call, thanks! I extracted the expansion into a shared helper (get_course_keys_from_scopes) and _get_course_keys_from_scopes in cms now uses it. The tagging rules reuse it too. get_authz_manage_tags_scopes became get_authz_manage_tags_org_names; it expands the user's courses.manage_tags scopes (course, org, and platform) with the helper and caches the resulting orgs per request.
TaxonomyOrgView filtered taxonomies using only legacy role resolution (get_admin_orgs/get_user_orgs), so a user holding only an openedx-authz role like course_editor, with no legacy CourseAccessRole, got an empty org list and hit queryset.none(), blocking them from seeing even global taxonomies. Add get_authz_manage_tags_orgs, which resolves the orgs where the user holds courses.manage_tags via get_scopes_for_user_and_permission, and OR it into UserOrgFilterBackend's existing user_orgs set alongside the legacy roles, the same additive pattern already used for object tags in ObjectTagTaxonomyOrgFilterBackend. A scope only counts once its course has actually switched to openedx-authz, so a policy assignment ahead of that course's own toggle doesn't grant early access. Closes openedx-authz#448
… rules The previous commit only widened UserOrgFilterBackend, so a user with an authz-only role (e.g. course_editor) could list an org's taxonomies but got 403 opening them, because can_view_taxonomy still resolved orgs through legacy roles alone. Fold the authz lookup into get_user_orgs via _get_authz_manage_tags_orgs, so the list filters, is_org_user and can_view_taxonomy share one source, and drop the duplicated set-union in UserOrgFilterBackend. - Count org-wide and platform-wide course glob scopes, which were silently dropped; they are checked against the global authz flag as they have no single course to toggle. - Skip the per-course flag lookup once an org is already resolved. - Ignore legacy course roles in courses that have switched to openedx-authz, so visibility matches can_change_object_tag_objectid, which requires courses.manage_tags there. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add TestRulesAuthzManageTagsOrgs for get_user_orgs and can_view_taxonomy: course, org-glob and platform-glob scopes, the course toggle gating, a single flag lookup per org, authz-only users viewing their org's taxonomies, and legacy course roles being ignored once a course has switched to openedx-authz. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ules get_user_orgs is evaluated once per taxonomy, and each call re-queried the authz policy store for the user's courses.manage_tags scopes. Cache the scopes in the tagging rules request cache and update the query counts asserted in the content_tagging view tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Course waffle flag overrides are already request-cached, so the local dict in _get_course_user_orgs added nothing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…angoapps.authz Make the logic of _get_course_keys_from_scopes reusable outside of cms so other apps (e.g. content_tagging) can share the same scope semantics. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Resolve courses.manage_tags scopes with get_course_keys_from_scopes instead of a bespoke expansion, so tagging follows the same course, org and platform scope semantics as the rest of the authz-aware code. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
4acbaa1 to
0f43c71
Compare
CourseOverviewFactory defaults the org column to "edX" regardless of the id, so org-wide scope expansion, which filters on that column, found no courses. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Description
Builds on #39159, which made
UserOrgFilterBackendrecognizeopenedx-authzcourses.manage_tagsroles so authz-only users (e.g.course_editor, no legacyCourseAccessRole) can list their org's taxonomies. That change only widened the list filter, so the rest of the tagging rules still resolved orgs through legacy roles alone. This PR makes the authz lookup part of the shared org resolution:get_user_orgsvia_get_authz_manage_tags_orgs, so the list filters,is_org_userandcan_view_taxonomyall agree. Previously, an authz-only user could list an org's taxonomies but got a 403 when opening them. The duplicated set union inUserOrgFilterBackendis dropped.openedx-authzno longer grant org membership, so visibility matchescan_change_object_tag_objectid, which requirescourses.manage_tagsthere.The legacy path is unchanged for Content Libraries and for courses where the authz toggle is off. The authz lookup is additive on top of it.
Impacted roles: Course Author / Course Staff / Course Admin using Studio taxonomies and tagging, and Operators using the API directly.
Supporting information
TaxonomyOrgViewauthz-aware to allow access for users withcourses.manage_tagspermission openedx-authz#448Testing instructions
openedx-authzand a user holding only an authz role withcourses.manage_tags(e.g.course_editor) and no legacy role:GET /api/content_tagging/v1/taxonomies/?org=<org>lists the org's taxonomies and the global ones.