Allow anonymous access to courseware API, and return error message if user is unenrolled.

This commit is contained in:
Dave St.Germain
2020-02-03 13:51:18 -05:00
parent c66176da13
commit 9da8ff0f0b
4 changed files with 63 additions and 38 deletions

View File

@@ -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)

View File

@@ -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}

View File

@@ -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

View File

@@ -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**