Skip to content
Open
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
54 changes: 38 additions & 16 deletions cms/djangoapps/contentstore/rest_api/v0/views/xblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
from rest_framework.generics import RetrieveUpdateDestroyAPIView, CreateAPIView
from django.views.decorators.csrf import csrf_exempt

from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin
from rest_framework.permissions import IsAuthenticated
from common.djangoapps.util.json_request import expect_json_in_class_view

from cms.djangoapps.contentstore.api import course_author_access_required
from cms.djangoapps.contentstore.views.permissions import HasCourseAuthorAccess
from cms.djangoapps.contentstore.xblock_storage_handlers import view_handlers

from ..serializers import XblockSerializer
Expand All @@ -19,54 +23,72 @@
handle_xblock = view_handlers.handle_xblock


@view_auth_classes()
class XblockView(DeveloperErrorViewMixin, RetrieveUpdateDestroyAPIView):
"""
Public rest API endpoints for the CMS API.
course_key: required argument, needed to authorize course authors.
course_id: required argument, needed to authorize course authors.
usage_key_string (optional):
xblock identifier, for example in the form of "block-v1:<course id>+type@<type>+block@<block id>"

ADR 0025 compliance notes:
- ``serializer_class`` is declared and used for input validation via
``@validate_request_with_serializer`` on mutating methods.
- Response formatting is delegated to ``handle_xblock()`` which produces
its own JSON shape; wrapping its output in ``XblockSerializer`` requires
a deeper refactor and is tracked as a follow-up task.
"""
authentication_classes = (
JwtAuthentication,
BearerAuthenticationAllowInactiveUser,
SessionAuthenticationAllowInactiveUser,
)
permission_classes = (IsAuthenticated, HasCourseAuthorAccess)
serializer_class = XblockSerializer

# pylint: disable=arguments-differ
@course_author_access_required
@expect_json_in_class_view
def retrieve(self, request, course_key, usage_key_string=None):
def retrieve(self, request, course_id, usage_key_string=None):
return handle_xblock(request, usage_key_string)

@course_author_access_required
@expect_json_in_class_view
@validate_request_with_serializer
def update(self, request, course_key, usage_key_string=None):
def update(self, request, course_id, usage_key_string=None):
return handle_xblock(request, usage_key_string)

@course_author_access_required
@expect_json_in_class_view
@validate_request_with_serializer
def partial_update(self, request, course_key, usage_key_string=None):
def partial_update(self, request, course_id, usage_key_string=None):
return handle_xblock(request, usage_key_string)

@course_author_access_required
@expect_json_in_class_view
def destroy(self, request, course_key, usage_key_string=None):
def destroy(self, request, course_id, usage_key_string=None):
return handle_xblock(request, usage_key_string)


@view_auth_classes()
class XblockCreateView(DeveloperErrorViewMixin, CreateAPIView):
"""
Public rest API endpoints for the CMS API.
course_key: required argument, needed to authorize course authors.
course_id: required argument, needed to authorize course authors.
usage_key_string (optional):
xblock identifier, for example in the form of "block-v1:<course id>+type@<type>+block@<block id>"

ADR 0025 compliance notes:
- ``serializer_class`` is declared and used for input validation via
``@validate_request_with_serializer``.
- Response formatting is delegated to ``handle_xblock()``; full response
serialization is tracked as a follow-up task.
"""
authentication_classes = (
JwtAuthentication,
BearerAuthenticationAllowInactiveUser,
SessionAuthenticationAllowInactiveUser,
)
permission_classes = (IsAuthenticated, HasCourseAuthorAccess) # ADR 0026
serializer_class = XblockSerializer

# pylint: disable=arguments-differ
@csrf_exempt
@course_author_access_required
@expect_json_in_class_view
@validate_request_with_serializer
def create(self, request, course_key, usage_key_string=None):
def create(self, request, course_id, usage_key_string=None):
return handle_xblock(request, usage_key_string)
19 changes: 18 additions & 1 deletion cms/djangoapps/contentstore/views/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from rest_framework.permissions import BasePermission

from common.djangoapps.student.auth import has_studio_write_access
from common.djangoapps.student.auth import has_course_author_access, has_studio_write_access
from openedx.core.lib.api.view_utils import validate_course_key


Expand All @@ -20,3 +20,20 @@ def has_permission(self, request, view):
course_key_string = view.kwargs.get("course_key_string")
course_key = validate_course_key(course_key_string)
return has_studio_write_access(request.user, course_key)


class HasCourseAuthorAccess(BasePermission):
"""
Check if the user has course author access.

ADR 0026: replaces the ``@course_author_access_required`` method decorator.
Expects a ``course_id`` URL kwarg.
"""

def has_permission(self, request, view):
"""
Check if the user has course author access.
"""
course_key_string = view.kwargs.get("course_id")
course_key = validate_course_key(course_key_string)
return has_course_author_access(request.user, course_key)
Loading