feat: make can_tag_object's course check authorization-service aware - #39112
alezconsultant wants to merge 1 commit into
Conversation
can_change_object_tag_objectid in content_tagging/rules.py -- the predicate every direct
has_perm('oel_tagging.can_tag_object', ...) caller resolves through -- already checked the
new openedx-authz service for Content Library objects, but for course objects it only ever
checked the legacy has_studio_write_access role, regardless of whether that course had been
switched to the new authorization service. ObjectTagOrgView (Studio's own tag-editing
endpoint) already had the correct, toggle-aware behavior, but only inside that one view
class, so every other caller (including the CBE create-criterion endpoint, openedx-core#665)
silently got the wrong, legacy-only answer for switched-over courses.
Add a branch to can_change_object_tag_objectid, checked after the Content Library case and
before the legacy fallback: when should_use_course_authz_for_object says the course has been
switched, return authz_api.is_user_allowed directly, with no
fallback to legacy roles or org-admin access -- an exclusive switch, matching
ObjectTagOrgView's existing behavior exactly.
Delete ObjectTagOrgView.ensure_user_has_can_tag_object_permissions: the parent
ObjectTagView's own version (openedx-core) already resolves through
has_perm(oel_tagging.can_tag_object, ...), so this override had become pure duplication of
logic that now lives in rules.py. get_permissions and _authz_check stay, since
ensure_has_view_object_tag_permission (the separate, untouched view permission) and
ObjectTagTaxonomyOrgFilterBackend.filter_queryset both still read them directly.
Related to openedx/openedx-core#795
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Thanks for the pull request, @alezconsultant! 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. |
Description
Makes
oel_tagging.can_tag_object's course check authorization-service aware(openedx/openedx-core#795).
can_change_object_tag_objectidincontent_tagging/rules.py— the predicate every directhas_perm('oel_tagging.can_tag_object', ...)caller resolves through — already checked the new openedx-authz service for ContentLibrary objects, but for course objects it only ever checked the legacy
has_studio_write_accessrole, regardless of whether that course had been switched to the new authorization service.
ObjectTagOrgViewalready had the correct, toggle-awarebehavior, but only inside that one view class — every other caller, including the CBE
create-criterion endpoint (openedx-core#665), silently got the wrong (legacy-only) answer for
switched-over courses. This closes that gap by giving the shared predicate the same
toggle-aware branch
ObjectTagOrgViewalready had, then removing the view's own now-duplicateoverride so the toggle check lives in one place.
Changes
content_tagging/rules.py:can_change_object_tag_objectidgets a new branch, checkedafter the existing Content Library case and before the legacy
has_studio_write_accessfallback. It calls
should_use_course_authz_for_object(object_id)(already used byObjectTagOrgView, fromcontent_tagging/auth.py); when the course has been switched, itreturns
authz_api.is_user_allowed(user.username, COURSES_MANAGE_TAGS.identifier, str(course_key))directly, with no fallback to legacy roles or org-admin access — anexclusive switch, matching
ObjectTagOrgView's existing behavior exactly. Docstring updatedto describe the new branch.
content_tagging/rest_api/v1/views.py:ObjectTagOrgView.ensure_user_has_can_tag_object_permissionsdeleted. Verified the parent
ObjectTagView.ensure_user_has_can_tag_object_permissions(openedx-core) already resolves through
user.has_perm("oel_tagging.can_tag_object", ...),so this override became pure duplication of the same logic now living in
rules.py.get_permissionsand_authz_checkare unchanged —_authz_check's docstring nowexplains why:
ensure_has_view_object_tag_permission(the separate, untouched viewpermission) and
ObjectTagTaxonomyOrgFilterBackend.filter_querysetboth still read itdirectly, so only the tagging-permission override became redundant, not this.
content_tagging/tests/test_rules.py: newTestRulesCourseAuthzPermissions, covering theticket's acceptance criteria directly against the predicate: a course switched to authz
grants access via
authz_api.is_user_allowedalone; a course not yet switched keeps resolving through thelegacy check unaffected; Content Library objects are
unaffected.
Tests
is_user_allowedcalled with the right identifier/scope.CourseStaffRolebut authz denies → denied; provesthe switch is exclusive, not an OR with a legacy course-level role.
exclusivity holds against org-admin fallback too, not just the course-level role.
is_user_allowedis never even called.MANAGE_LIBRARY_TAGS(regression guard on the untouched branch).Verification
pytest openedx/core/djangoapps/content_tagging/tests/test_rules.py openedx/core/djangoapps/content_tagging/rest_api/v1/tests/test_views.py --no-cov:575 passed, 0 failed.
pylinton both changed source files and the changed test file: clean.Manual testing
Since #665 not merged I've walked current tagging flow. Walked both paths live in Studio: added a fresh user via Course Team with the course's authz
waffle override off (routes through a real legacy
CourseAccessRole) and again with it on — tagging succeeded both ways.Related to openedx/openedx-core#795 and openedx/openedx-core#665
🤖 Generated with Claude Code