fix: allow Course Admin/Staff/Editor to sync library updates into a course - #39055
Conversation
|
Thanks for the pull request, @carlos-marquez-wgu! 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. |
…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.
4784bb6 to
470bc9c
Compare
| if course_key is None: | ||
| return False | ||
|
|
||
| from openedx.core.djangoapps.authz.decorators import ( # pylint: disable=wrong-import-order |
There was a problem hiding this comment.
Is there any reason why we are importing this here instead of at the top of the file?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
Description
Users with the
courses.manage_library_updatespermission (carried by the Course Admin, Course Staff, and Course Editor roles) were unable to sync library content into a course viaPOST/api/contentstore/v2/downstreams/{usage_key}/syncunless 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_blockandsync_from_upstream_containerthat bypasses the library-level permission check when the user holdsmanage_library_updatesfor the downstream's course.A shared helper (
user_has_manage_library_updatesinupstream_sync.py) centralizes the check with aLegacyAuthoringPermission.WRITEfallback 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
POST/api/contentstore/v2/downstreams/{usage_key}/syncrequest.HTTP code 200.AI usage notice
Used Claude Opus 4.6 for help writing of unit tests and docstrings.