Allow anonymous access to courseware API, and return error message if user is unenrolled.
This commit is contained in:
@@ -796,6 +796,13 @@ class CourseOverview(TimeStampedModel):
|
||||
"""
|
||||
return self._original_course.enable_ccx
|
||||
|
||||
@property
|
||||
def course_visibility(self):
|
||||
"""
|
||||
TODO: move this to the model.
|
||||
"""
|
||||
return self._original_course.course_visibility
|
||||
|
||||
def __str__(self):
|
||||
"""Represent ourselves with the course key."""
|
||||
return six.text_type(self.id)
|
||||
|
||||
@@ -7,7 +7,6 @@ from rest_framework import serializers
|
||||
|
||||
from lms.djangoapps.courseware.tabs import get_course_tab_list
|
||||
from openedx.core.lib.api.fields import AbsoluteURLField
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
|
||||
class _MediaSerializer(serializers.Serializer): # pylint: disable=abstract-method
|
||||
@@ -78,7 +77,8 @@ class CourseInfoSerializer(serializers.Serializer): # pylint: disable=abstract-
|
||||
start_display = serializers.CharField()
|
||||
start_type = serializers.CharField()
|
||||
pacing = serializers.CharField()
|
||||
enrollment = serializers.SerializerMethodField()
|
||||
enrollment = serializers.DictField()
|
||||
user_has_access = serializers.BooleanField()
|
||||
tabs = serializers.SerializerMethodField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -108,13 +108,3 @@ class CourseInfoSerializer(serializers.Serializer): # pylint: disable=abstract-
|
||||
'url': tab.link_func(course_overview, reverse),
|
||||
})
|
||||
return tabs
|
||||
|
||||
def get_enrollment(self, course_overview):
|
||||
"""
|
||||
Return the enrollment for the logged in user.
|
||||
"""
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(
|
||||
course_overview.effective_user,
|
||||
course_overview.id
|
||||
)
|
||||
return {'mode': mode, 'is_active': is_active}
|
||||
|
||||
@@ -4,6 +4,7 @@ Tests for courseware API
|
||||
from datetime import datetime
|
||||
import unittest
|
||||
import ddt
|
||||
import mock
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
@@ -52,36 +53,43 @@ class BaseCoursewareTests(SharedModuleStoreTestCase):
|
||||
super().setUp()
|
||||
self.client.login(username=self.user.username, password='foo')
|
||||
|
||||
def test_unauth(self):
|
||||
self.client.logout()
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
# pylint: disable=test-inherits-tests
|
||||
@ddt.ddt
|
||||
class CourseApiTestViews(BaseCoursewareTests):
|
||||
"""
|
||||
Tests for the courseware REST API
|
||||
"""
|
||||
@ddt.data((None,), ('audit',), ('verified',))
|
||||
@ddt.data(
|
||||
(True, None, False),
|
||||
(True, 'audit', False),
|
||||
(True, 'verified', False),
|
||||
(False, None, False),
|
||||
(False, None, True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_course_metadata(self, enrollment_mode):
|
||||
if enrollment_mode:
|
||||
CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode)
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
enrollment = response.data['enrollment']
|
||||
if enrollment_mode:
|
||||
assert enrollment_mode == enrollment['mode']
|
||||
assert enrollment['is_active']
|
||||
assert len(response.data['tabs']) == 4
|
||||
else:
|
||||
assert len(response.data['tabs']) == 2
|
||||
assert not enrollment['is_active']
|
||||
def test_course_metadata(self, logged_in, enrollment_mode, enable_anonymous):
|
||||
allow_public_access = mock.Mock()
|
||||
allow_public_access.return_value = enable_anonymous
|
||||
with mock.patch('openedx.core.djangoapps.courseware_api.views.allow_public_access', allow_public_access):
|
||||
if not logged_in:
|
||||
self.client.logout()
|
||||
if enrollment_mode:
|
||||
CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode)
|
||||
response = self.client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
if enrollment_mode:
|
||||
enrollment = response.data['enrollment']
|
||||
assert enrollment_mode == enrollment['mode']
|
||||
assert enrollment['is_active']
|
||||
assert len(response.data['tabs']) == 4
|
||||
elif enable_anonymous and not logged_in:
|
||||
allow_public_access.assert_called_once()
|
||||
assert response.data['enrollment']['mode'] is None
|
||||
assert response.data['user_has_access']
|
||||
else:
|
||||
assert not response.data['user_has_access']
|
||||
|
||||
|
||||
# pylint: disable=test-inherits-tests
|
||||
class SequenceApiTestViews(BaseCoursewareTests):
|
||||
"""
|
||||
Tests for the sequence REST API
|
||||
|
||||
@@ -10,14 +10,17 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from lms.djangoapps.course_api.api import course_detail
|
||||
from lms.djangoapps.courseware.courses import allow_public_access
|
||||
from lms.djangoapps.courseware.module_render import get_module_by_usage_id
|
||||
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC
|
||||
|
||||
from .serializers import CourseInfoSerializer
|
||||
|
||||
|
||||
@view_auth_classes(is_authenticated=True)
|
||||
class CoursewareInformation(DeveloperErrorViewMixin, RetrieveAPIView):
|
||||
class CoursewareInformation(RetrieveAPIView):
|
||||
"""
|
||||
**Use Cases**
|
||||
|
||||
@@ -57,6 +60,7 @@ class CoursewareInformation(DeveloperErrorViewMixin, RetrieveAPIView):
|
||||
* enrollment: Enrollment status of authenticated user
|
||||
* mode: `audit`, `verified`, etc
|
||||
* is_active: boolean
|
||||
* user_has_access: Whether the user can view the course
|
||||
|
||||
**Parameters:**
|
||||
|
||||
@@ -80,11 +84,28 @@ class CoursewareInformation(DeveloperErrorViewMixin, RetrieveAPIView):
|
||||
Return the requested course object, if the user has appropriate
|
||||
permissions.
|
||||
"""
|
||||
return course_detail(
|
||||
|
||||
overview = course_detail(
|
||||
self.request,
|
||||
self.request.user.username,
|
||||
CourseKey.from_string(self.kwargs['course_key_string']),
|
||||
)
|
||||
if self.request.user.is_anonymous:
|
||||
mode = None
|
||||
is_active = False
|
||||
else:
|
||||
mode, is_active = CourseEnrollment.enrollment_mode_for_user(
|
||||
overview.effective_user,
|
||||
overview.id
|
||||
)
|
||||
|
||||
overview.enrollment = {'mode': mode, 'is_active': is_active}
|
||||
if not is_active:
|
||||
user_has_access = allow_public_access(overview, [COURSE_VISIBILITY_PUBLIC])
|
||||
else:
|
||||
user_has_access = True
|
||||
overview.user_has_access = user_has_access
|
||||
return overview
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""
|
||||
@@ -95,7 +116,6 @@ class CoursewareInformation(DeveloperErrorViewMixin, RetrieveAPIView):
|
||||
return context
|
||||
|
||||
|
||||
@view_auth_classes(is_authenticated=True)
|
||||
class SequenceMetadata(DeveloperErrorViewMixin, APIView):
|
||||
"""
|
||||
**Use Cases**
|
||||
|
||||
Reference in New Issue
Block a user