Skip to content

fix: allow Course Admin/Staff/Editor to sync library updates into a course - #39055

Open
carlos-marquez-wgu wants to merge 1 commit into
openedx:masterfrom
WGU-Open-edX:carlos-marquez-wgu/fix-sync-downstream-when-manage_library_updates-perm
Open

fix: allow Course Admin/Staff/Editor to sync library updates into a course#39055
carlos-marquez-wgu wants to merge 1 commit into
openedx:masterfrom
WGU-Open-edX:carlos-marquez-wgu/fix-sync-downstream-when-manage_library_updates-perm

Conversation

@carlos-marquez-wgu

@carlos-marquez-wgu carlos-marquez-wgu commented Sep 1, 2026

Copy link
Copy Markdown

Description

Users with the courses.manage_library_updates permission (carried by the Course Admin, Course Staff, and Course Editor roles) were unable to sync library content into a course via POST /api/contentstore/v2/downstreams/{usage_key}/sync unless they also had explicit view permissions on the source library. This defeated the purpose of the RBAC permission, which should be the sole requirement at the course level.

This PR adds a course-level permission check in both sync_from_upstream_block and sync_from_upstream_container that bypasses the library-level permission check when the user holds manage_library_updates for the downstream's course.

A shared helper (user_has_manage_library_updates in upstream_sync.py) centralizes the check with a LegacyAuthoringPermission.WRITE fallback for environments where the AuthZ feature flag is not yet enabled.

Impacted roles: Course Admin, Course Staff, Course Editor — these users can now sync library updates without needing library-level permissions.

Supporting information

Fixes openedx/openedx-authz#419

Testing instructions

  1. Create a Content Library.
  2. Create a course.
  3. Add the Content Library to the course.
  4. Set up a test user and assign them a Course Admin/Course Staff/Course Editor role within the course context.
  5. Ensure that the test user does not have any permissions assigned on the library (neither read nor write).
  6. Authenticate as the test user and attempt to perform the synchronization by executing a POST /api/contentstore/v2/downstreams/{usage_key}/sync request.
  7. Request must be successful, return a HTTP code 200.

AI usage notice

Used Claude Opus 4.6 for help writing of unit tests and docstrings.

@openedx-webhooks

openedx-webhooks commented Sep 1, 2026

Copy link
Copy Markdown

Thanks for the pull request, @carlos-marquez-wgu!

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.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Sep 1, 2026
@github-project-automation github-project-automation Bot moved this to Needs Triage in Contributions Sep 1, 2026
…ourse

Users with the courses.manage_library_updates permission (granted by
Course Staff, Course Editor, and Course Admin roles) were unable to sync
library updates into a course unless they also had explicit view
permissions on the source library.

This adds a course-level permission check in both sync_from_upstream_block
and sync_from_upstream_container that skips the library-level permission
check when the user holds manage_library_updates for the downstream
course. A shared helper (user_has_manage_library_updates) centralizes
the check with a legacy write-access fallback.
@carlos-marquez-wgu
carlos-marquez-wgu force-pushed the carlos-marquez-wgu/fix-sync-downstream-when-manage_library_updates-perm branch from 4784bb6 to 470bc9c Compare September 2, 2026 15:36
@carlos-marquez-wgu
carlos-marquez-wgu marked this pull request as ready for review September 2, 2026 15:36
if course_key is None:
return False

from openedx.core.djangoapps.authz.decorators import ( # pylint: disable=wrong-import-order

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.

Is there any reason why we are importing this here instead of at the top of the file?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, putting those imports at the top of the file will cause a crash

Traceback (most recent call last):  
  File "/openedx/edx-platform/./manage.py", line 93, in <module>  
    execute_from_command_line([sys.argv[0]] + django_args)  
  File "/openedx/venv/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line  
    utility.execute()  
  File "/openedx/venv/lib/python3.12/site-packages/django/core/management/__init__.py", line 382, in execute  
    settings.INSTALLED_APPS  
  File "/openedx/venv/lib/python3.12/site-packages/django/conf/__init__.py", line 81, in __getattr__  
    self._setup(name)  
  File "/openedx/venv/lib/python3.12/site-packages/django/conf/__init__.py", line 68, in _setup  
    self._wrapped = Settings(settings_module)  
                    ^^^^^^^^^^^^^^^^^^^^^^^^^  
  File "/openedx/venv/lib/python3.12/site-packages/django/conf/__init__.py", line 166, in __init__  
    mod = importlib.import_module(self.SETTINGS_MODULE)  
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
  File "/opt/pyenv/versions/3.12.13/lib/python3.12/importlib/__init__.py", line 90, in import_module  
    return _bootstrap._gcd_import(name[level:], package, level)  
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import  
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load  
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked  
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked  
  File "<frozen importlib._bootstrap_external>", line 999, in exec_module  
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed  
  File "/openedx/edx-platform/cms/envs/tutor/development.py", line 3, in <module>  
    from cms.envs.devstack import *  
  File "/openedx/edx-platform/cms/envs/devstack.py", line 12, in <module>  
    from .production import *  # pylint: disable=wildcard-import, unused-wildcard-import  # noqa: F403  
    ^^^^^^^^^^^^^^^^^^^^^^^^^  
  File "/openedx/edx-platform/cms/envs/production.py", line 32, in <module>  
    from .common import *  # noqa: F403  
    ^^^^^^^^^^^^^^^^^^^^^  
  File "/openedx/edx-platform/cms/envs/common.py", line 51, in <module>  
    from cms.lib.xblock.upstream_sync import UpstreamSyncMixin  
  File "/openedx/edx-platform/cms/lib/xblock/upstream_sync.py", line 31, in <module>  
    from openedx.core.djangoapps.authz.decorators import (  
  File "/openedx/edx-platform/openedx/core/djangoapps/authz/decorators.py", line 6, in <module>  
    from django.contrib.auth.models import AbstractUser  
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/auth/models.py", line 5, in <module>  
    from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager  
  File "/openedx/venv/lib/python3.12/site-packages/django/contrib/auth/base_user.py", line 43, in <module>  
    class AbstractBaseUser(models.Model):  
  File "/openedx/venv/lib/python3.12/site-packages/django/db/models/base.py", line 131, in __new__  
    app_config = apps.get_containing_app_config(module)  
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  
  File "/openedx/venv/lib/python3.12/site-packages/django/apps/registry.py", line 260, in get_containing_app_config  
    self.check_apps_ready()  
  File "/openedx/venv/lib/python3.12/site-packages/django/apps/registry.py", line 138, in check_apps_ready  
    raise AppRegistryNotReady("Apps aren't loaded yet.")  
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.  

Alternatively I am thinking I can put that helper function, user_has_manage_library_updates in its own file or another file instead of upstream_sync.py

Any thoughts?

@carlos-marquez-wgu carlos-marquez-wgu Sep 2, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

After checking #39009 from @wgu-taylor-payne , I think it might make more sense to use a single file and share it in both solutions as we are doing the same validations

... Or just use user_has_course_permission directly instead of relying on the thin wrapper, any thoughts?

@mphilbrick211 mphilbrick211 added the mao-onboarding Reviewing this will help onboard devs from an Axim mission-aligned organization (MAO). label Sep 2, 2026
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Waiting on Author in Contributions Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mao-onboarding Reviewing this will help onboard devs from an Axim mission-aligned organization (MAO). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Waiting on Author

Development

Successfully merging this pull request may close these issues.

Course Admin/Staff/Editor cannot sync library updates into a course

4 participants