Task - RBAC Authz - Implement taxonomy permissions
Context
openedx-platform#38436 added authz permission checks for the object tag endpoints (ObjectTagOrgView) and the xblock outline handler. However, the taxonomy-related endpoints still rely entirely on legacy org-level roles to determine access. Users with authz-only course roles (e.g. course_editor without legacy CourseStaffRole) cannot view, browse, or interact with taxonomies anywhere in course authoring.
This affects any surface in Studio that calls taxonomy endpoints — the content tags drawer, the taxonomy management pages, tag import/export, and any other course authoring feature that lists or browses taxonomies.
This is a continuation of the work started in #38143.
Endpoint inventory
Endpoints called from frontend-app-authoring.
Object tag endpoints (already fixed in #38436)
| Endpoint |
Method |
Called from |
Status |
/object_tags/{contentId}/ |
GET |
Content tags drawer — load applied tags |
✅ Fixed |
/object_tags/{contentId}/ |
PUT |
Content tags drawer — save tag changes |
✅ Fixed |
/object_tag_counts/{contentId}/ |
GET |
Content tags drawer, course outline — tag count badge |
✅ No auth checks (by design) |
/object_tags/{courseId}/export/ |
GET |
Course outline — export tags CSV |
✅ Fixed (uses has_view_object_tags_access) |
Taxonomy list and detail endpoints (affected)
| Endpoint |
Method |
Called from |
Permission layer |
Status |
/taxonomies/?enabled=true&org=X |
GET |
Content tags drawer (with org from content ID), taxonomy management page |
UserOrgFilterBackend → get_user_orgs / get_admin_orgs |
❌ Broken |
/taxonomies/{pk}/ |
GET |
Taxonomy detail page |
TaxonomyObjectPermissions → oel_tagging.view_taxonomy → can_view_taxonomy |
❌ Broken (org-scoped) |
/taxonomies/{pk}/ |
PATCH |
Taxonomy detail page — edit taxonomy |
TaxonomyObjectPermissions → oel_tagging.change_taxonomy |
❌ Likely broken |
/taxonomies/{pk}/ |
DELETE |
Taxonomy management page — delete taxonomy |
TaxonomyObjectPermissions → oel_tagging.delete_taxonomy |
❌ Likely broken |
Taxonomy tags endpoints (affected)
| Endpoint |
Method |
Called from |
Permission layer |
Status |
/taxonomies/{pk}/tags/ |
GET |
Content tags drawer (browse/search tags), taxonomy detail page (tag list) |
TaxonomyTagsObjectPermissions → oel_tagging.list_tag → can_view_taxonomy |
❌ Broken (org-scoped) |
/taxonomies/{pk}/tags/ |
POST |
Taxonomy detail page — create tag |
TaxonomyTagsObjectPermissions → oel_tagging.add_tag |
❌ Likely broken |
/taxonomies/{pk}/tags/ |
PUT |
Taxonomy detail page — update tag |
TaxonomyTagsObjectPermissions → oel_tagging.change_tag |
❌ Likely broken |
/taxonomies/{pk}/tags/ |
DELETE |
Taxonomy detail page — delete tag |
TaxonomyTagsObjectPermissions → oel_tagging.delete_tag |
❌ Likely broken |
Taxonomy import/export endpoints (affected)
| Endpoint |
Method |
Called from |
Permission layer |
Status |
/taxonomies/import/ |
POST |
Taxonomy management page — create taxonomy from import |
TaxonomyObjectPermissions |
❌ Likely broken |
/taxonomies/{pk}/tags/import/ |
PUT |
Taxonomy detail page — import tags |
TaxonomyTagsObjectPermissions |
❌ Likely broken |
/taxonomies/{pk}/tags/import/plan/ |
PUT |
Taxonomy detail page — preview import |
TaxonomyTagsObjectPermissions |
❌ Likely broken |
/taxonomies/{pk}/export/ |
GET |
Taxonomy detail page — export taxonomy |
No auth (direct download URL) |
⚠️ Needs investigation |
/taxonomies/import/template.{ext} |
GET |
Taxonomy management page — download template |
No auth |
✅ Fine |
Taxonomy org management endpoint (affected)
| Endpoint |
Method |
Called from |
Permission layer |
Status |
/taxonomies/{pk}/orgs/ |
PUT |
Taxonomy detail page — manage org associations |
oel_tagging.update_orgs |
❌ Likely broken |
Root cause
All "broken" endpoints share the same root cause: permission checks resolve the user's org membership via legacy roles (get_user_orgs, get_admin_orgs, is_org_user, is_org_admin). Authz-only users have no legacy roles, so these checks fail.
The key functions that need authz-aware paths:
UserOrgFilterBackend.filter_queryset — taxonomy list filtering
can_view_taxonomy — django-rules predicate used by multiple permission classes
is_org_user / is_org_admin — underlying org membership checks
Design considerations
- These endpoints don't always have a course context available. The taxonomy management page calls them without any course/content reference. An authz path may need to use
get_scopes_for_user_and_permission to resolve the user's orgs from their authz role assignments, or the frontend may need to pass a content ID where applicable (see companion frontend issue).
- The
can_view_taxonomy predicate is the most impactful fix — it underpins view_taxonomy, list_tag, and is used by both TaxonomyObjectPermissions and TaxonomyTagsObjectPermissions.
- Write operations (PATCH, DELETE, POST on taxonomies/tags) may need separate authz permissions (e.g.
courses.manage_taxonomies) beyond just org membership.
- Library tagging should be unaffected — non-course objects fall through to legacy permissions.
Task - RBAC Authz - Implement taxonomy permissions
Context
openedx-platform#38436 added authz permission checks for the object tag endpoints (
ObjectTagOrgView) and the xblock outline handler. However, the taxonomy-related endpoints still rely entirely on legacy org-level roles to determine access. Users with authz-only course roles (e.g.course_editorwithout legacyCourseStaffRole) cannot view, browse, or interact with taxonomies anywhere in course authoring.This affects any surface in Studio that calls taxonomy endpoints — the content tags drawer, the taxonomy management pages, tag import/export, and any other course authoring feature that lists or browses taxonomies.
This is a continuation of the work started in #38143.
Endpoint inventory
Endpoints called from
frontend-app-authoring.Object tag endpoints (already fixed in #38436)
/object_tags/{contentId}//object_tags/{contentId}//object_tag_counts/{contentId}//object_tags/{courseId}/export/has_view_object_tags_access)Taxonomy list and detail endpoints (affected)
/taxonomies/?enabled=true&org=XUserOrgFilterBackend→get_user_orgs/get_admin_orgs/taxonomies/{pk}/TaxonomyObjectPermissions→oel_tagging.view_taxonomy→can_view_taxonomy/taxonomies/{pk}/TaxonomyObjectPermissions→oel_tagging.change_taxonomy/taxonomies/{pk}/TaxonomyObjectPermissions→oel_tagging.delete_taxonomyTaxonomy tags endpoints (affected)
/taxonomies/{pk}/tags/TaxonomyTagsObjectPermissions→oel_tagging.list_tag→can_view_taxonomy/taxonomies/{pk}/tags/TaxonomyTagsObjectPermissions→oel_tagging.add_tag/taxonomies/{pk}/tags/TaxonomyTagsObjectPermissions→oel_tagging.change_tag/taxonomies/{pk}/tags/TaxonomyTagsObjectPermissions→oel_tagging.delete_tagTaxonomy import/export endpoints (affected)
/taxonomies/import/TaxonomyObjectPermissions/taxonomies/{pk}/tags/import/TaxonomyTagsObjectPermissions/taxonomies/{pk}/tags/import/plan/TaxonomyTagsObjectPermissions/taxonomies/{pk}/export//taxonomies/import/template.{ext}Taxonomy org management endpoint (affected)
/taxonomies/{pk}/orgs/oel_tagging.update_orgsRoot cause
All "broken" endpoints share the same root cause: permission checks resolve the user's org membership via legacy roles (
get_user_orgs,get_admin_orgs,is_org_user,is_org_admin). Authz-only users have no legacy roles, so these checks fail.The key functions that need authz-aware paths:
UserOrgFilterBackend.filter_queryset— taxonomy list filteringcan_view_taxonomy— django-rules predicate used by multiple permission classesis_org_user/is_org_admin— underlying org membership checksDesign considerations
get_scopes_for_user_and_permissionto resolve the user's orgs from their authz role assignments, or the frontend may need to pass a content ID where applicable (see companion frontend issue).can_view_taxonomypredicate is the most impactful fix — it underpinsview_taxonomy,list_tag, and is used by bothTaxonomyObjectPermissionsandTaxonomyTagsObjectPermissions.courses.manage_taxonomies) beyond just org membership.