Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions cms/djangoapps/contentstore/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.edxmako.services import MakoService
from common.djangoapps.student import auth
from common.djangoapps.student.auth import STUDIO_EDIT_ROLES, has_studio_read_access, has_studio_write_access
from common.djangoapps.student.auth import STUDIO_EDIT_ROLES, has_studio_write_access
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole, GlobalStaff
from common.djangoapps.track import contexts
Expand All @@ -71,6 +71,7 @@
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from openedx.core import toggles as core_toggles
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.services import StudioPermissionsService
from openedx.core.djangoapps.content_libraries.api import get_container
from openedx.core.djangoapps.content_tagging.toggles import is_tagging_feature_disabled
from openedx.core.djangoapps.credit.api import get_credit_requirements, is_credit_course
Expand Down Expand Up @@ -2243,27 +2244,6 @@ def get_group_configurations_context(course, store):
return context


class StudioPermissionsService:
"""
Service that can provide information about a user's permissions.

Deprecated. To be replaced by a more general authorization service.

Only used by LegacyLibraryContentBlock (and library_tools.py).
"""

def __init__(self, user):
self._user = user

def can_read(self, course_key):
""" Does the user have read access to the given course/library? """
return has_studio_read_access(self._user, course_key)

def can_write(self, course_key):
""" Does the user have read access to the given course/library? """
return has_studio_write_access(self._user, course_key)


def track_course_update_event(course_key, user, course_update_content=None):
"""
Track course update event
Expand Down
3 changes: 2 additions & 1 deletion cms/djangoapps/contentstore/views/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from common.djangoapps.student.models import anonymous_id_for_user
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from openedx.core.djangoapps.content.services import StudioPermissionsService
from openedx.core.djangoapps.discussions.services import DiscussionConfigService
from openedx.core.djangoapps.video_config.services import VideoConfigService
from openedx.core.lib.cache_utils import CacheService
Expand All @@ -44,7 +45,7 @@
from xmodule.util.sandboxing import SandboxService
from xmodule.x_module import AUTHOR_VIEW, PREVIEW_VIEWS, STUDENT_VIEW, XModuleMixin

from ..utils import StudioPermissionsService, get_visibility_partition_info
from ..utils import get_visibility_partition_info
from .access import get_user_role
from .session_kv_store import SessionKeyValueStore

Expand Down
35 changes: 33 additions & 2 deletions common/djangoapps/student/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator
from django.conf import settings
from django.core.exceptions import PermissionDenied
from opaque_keys.edx.locator import LibraryLocator
from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2
from openedx_authz import api as authz_api
from openedx_authz.constants.permissions import (
COURSES_MANAGE_ADVANCED_SETTINGS,
Expand All @@ -32,6 +32,7 @@
strict_role_checking,
)
from openedx.core import toggles as core_toggles
from openedx.core.types.user import User as UserType

# Studio permissions:
STUDIO_EDIT_ROLES = 8
Expand Down Expand Up @@ -80,6 +81,21 @@ def user_has_role(user, role):
return False


def lib_perm_check_to_bool(
library_key: LibraryLocatorV2, user: UserType, permission: str | authz_api.data.PermissionData
):
"""
Returns True if the API call to library permissions does not raise,
and False otherwise.
"""
from openedx.core.djangoapps.content_libraries.api import require_permission_for_library_key
try:
require_permission_for_library_key(library_key, user, permission)
return True
except PermissionError:
return False


def get_user_permissions(user, course_key, org=None, service_variant=None):
"""
Get the bitmask of permissions that this user has in the given course context.
Expand All @@ -92,6 +108,10 @@ def get_user_permissions(user, course_key, org=None, service_variant=None):
:param service_variant: the variant of the service (lms or cms). Permissions may differ between the two,
see the HACK comment in the function for more details.
"""
from openedx.core.djangoapps.content_libraries.permissions import (
CAN_EDIT_THIS_CONTENT_LIBRARY,
CAN_VIEW_THIS_CONTENT_LIBRARY,
)
if org is None:
org = course_key.org
course_key = course_key.for_branch(None)
Expand Down Expand Up @@ -126,7 +146,17 @@ def get_user_permissions(user, course_key, org=None, service_variant=None):
if OrgStaffRole(org=org).has_user(user) or (course_key and user_has_role(user, CourseStaffRole(course_key))):
return STUDIO_VIEW_USERS | STUDIO_EDIT_CONTENT | STUDIO_VIEW_CONTENT

# Otherwise, for libraries, users can view only:
# For libraries v2, permissions can be specific to the library
if course_key and isinstance(course_key, LibraryLocatorV2):
read_value = STUDIO_NO_PERMISSIONS
write_value = STUDIO_NO_PERMISSIONS
if lib_perm_check_to_bool(course_key, user, CAN_VIEW_THIS_CONTENT_LIBRARY):
read_value = STUDIO_VIEW_CONTENT
if lib_perm_check_to_bool(course_key, user, CAN_EDIT_THIS_CONTENT_LIBRARY):
write_value = STUDIO_EDIT_CONTENT
return read_value | write_value

# Otherwise, for legacy libraries, users can view only:
if course_key and isinstance(course_key, LibraryLocator):
if OrgLibraryUserRole(org=org).has_user(user) or user_has_role(user, LibraryUserRole(course_key)):
return STUDIO_VIEW_USERS | STUDIO_VIEW_CONTENT
Expand Down Expand Up @@ -180,6 +210,7 @@ def has_studio_read_access(user, course_key):
There is currently no such thing as read-only course access in studio, but
there is read-only access to content libraries.
"""
print("STUDIO_VIEW_CONTENT is ", STUDIO_VIEW_CONTENT)
return bool(STUDIO_VIEW_CONTENT & get_user_permissions(user, course_key))


Expand Down
27 changes: 27 additions & 0 deletions openedx/core/djangoapps/content/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
Services for learning content
"""
from __future__ import annotations

from common.djangoapps.student.auth import has_studio_read_access, has_studio_write_access


class StudioPermissionsService:
"""
Service that can provide information about a user's permissions.

Deprecated. To be replaced by a more general authorization service.

Only used by LegacyLibraryContentBlock (and library_tools.py).
"""

def __init__(self, user):
self._user = user

def can_read(self, course_key):
""" Does the user have read access to the given course/library? """
return has_studio_read_access(self._user, course_key)

def can_write(self, course_key):
""" Does the user have read access to the given course/library? """
return has_studio_write_access(self._user, course_key)
3 changes: 3 additions & 0 deletions openedx/core/djangoapps/xblock/runtime/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,9 @@ def service(self, block: XBlock, service_name: str):
return DiscussionConfigService()
elif service_name == 'xqueue':
return XQueueService(block)
elif service_name == 'studio_user_permissions':
from openedx.core.djangoapps.content.services import StudioPermissionsService
return StudioPermissionsService(self.user)

# Otherwise, fall back to the base implementation which loads services
# defined in the constructor:
Expand Down
Loading