diff --git a/AUTHORS b/AUTHORS index e37dd7e395..450188c8fb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -179,3 +179,4 @@ Henry Tareque Eugeny Kolpakov Omar Al-Ithawi Louis Pilfold +Akiva Leffert diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py index 06046d72f5..775660a25e 100644 --- a/lms/djangoapps/mobile_api/users/tests.py +++ b/lms/djangoapps/mobile_api/users/tests.py @@ -1,6 +1,8 @@ """ Tests for users API """ + +import ddt from rest_framework.test import APITestCase from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -9,8 +11,10 @@ from courseware.tests.factories import UserFactory from django.core.urlresolvers import reverse from mobile_api.users.serializers import CourseEnrollmentSerializer from student.models import CourseEnrollment +from student import auth +@ddt.ddt class TestUserApi(ModuleStoreTestCase, APITestCase): """ Test the user info API @@ -26,44 +30,70 @@ class TestUserApi(ModuleStoreTestCase, APITestCase): super(TestUserApi, self).tearDown() self.client.logout() - def _enroll(self): + def _enrollment_url(self): + """ + api url that gets the current user's course enrollments + """ + return reverse('courseenrollment-detail', kwargs={'username': self.user.username}) + + def _enroll(self, course): """ enroll test user in test course """ resp = self.client.post(reverse('change_enrollment'), { 'enrollment_action': 'enroll', - 'course_id': self.course.id.to_deprecated_string(), + 'course_id': course.id.to_deprecated_string(), 'check_access': True, }) self.assertEqual(resp.status_code, 200) - def test_user_enrollments(self): - url = reverse('courseenrollment-detail', kwargs={'username': self.user.username}) + def _verify_single_course_enrollment(self, course): + """ + check that enrolling in course adds us to it + """ + url = self._enrollment_url() self.client.login(username=self.username, password=self.password) + self._enroll(course) response = self.client.get(url) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.data, []) # pylint: disable=E1103 - self._enroll() - response = self.client.get(url) self.assertEqual(response.status_code, 200) - courses = response.data # pylint: disable=E1103 + courses = response.data # pylint: disable=maybe-no-member + self.assertEqual(len(courses), 1) - self.assertTrue(len(courses), 1) - course = courses[0]['course'] - self.assertTrue('video_outline' in course) - self.assertTrue('course_handouts' in course) - self.assertEqual(course['id'], self.course.id.to_deprecated_string()) + found_course = courses[0]['course'] + self.assertTrue('video_outline' in found_course) + self.assertTrue('course_handouts' in found_course) + self.assertEqual(found_course['id'], unicode(course.id)) self.assertEqual(courses[0]['mode'], 'honor') + def test_non_mobile_enrollments(self): + url = self._enrollment_url() + non_mobile_course = CourseFactory.create(mobile_available=False) + self.client.login(username=self.username, password=self.password) + + self._enroll(non_mobile_course) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, []) # pylint: disable=maybe-no-member + + @ddt.data(auth.CourseBetaTesterRole, auth.CourseStaffRole, auth.CourseInstructorRole) + def test_privileged_enrollments(self, role): + non_mobile_course = CourseFactory.create(mobile_available=False) + role(non_mobile_course.id).add_users(self.user) + + self._verify_single_course_enrollment(non_mobile_course) + + def test_mobile_enrollments(self): + self._verify_single_course_enrollment(self.course) + def test_user_overview(self): self.client.login(username=self.username, password=self.password) url = reverse('user-detail', kwargs={'username': self.user.username}) response = self.client.get(url) self.assertEqual(response.status_code, 200) - data = response.data # pylint: disable=E1103 + data = response.data # pylint: disable=maybe-no-member self.assertEqual(data['username'], self.user.username) self.assertEqual(data['email'], self.user.email) @@ -90,7 +120,7 @@ class TestUserApi(ModuleStoreTestCase, APITestCase): def test_course_serializer(self): self.client.login(username=self.username, password=self.password) - self._enroll() + self._enroll(self.course) serialized = CourseEnrollmentSerializer(CourseEnrollment.enrollments_for_user(self.user)[0]).data # pylint: disable=E1101 self.assertEqual(serialized['course']['video_outline'], None) self.assertEqual(serialized['course']['name'], self.course.display_name) diff --git a/lms/djangoapps/mobile_api/users/views.py b/lms/djangoapps/mobile_api/users/views.py index 708f18691b..11d47f2cb8 100644 --- a/lms/djangoapps/mobile_api/users/views.py +++ b/lms/djangoapps/mobile_api/users/views.py @@ -8,8 +8,10 @@ from rest_framework.authentication import OAuth2Authentication, SessionAuthentic from rest_framework.decorators import api_view, authentication_classes, permission_classes from rest_framework.permissions import IsAuthenticated -from courseware.access import has_access +from courseware import access from student.models import CourseEnrollment, User +from student.roles import CourseBetaTesterRole +from student import auth from .serializers import CourseEnrollmentSerializer, UserSerializer @@ -119,12 +121,18 @@ def my_user_info(request): def mobile_course_enrollments(enrollments, user): """ - Return enrollments only if courses are mobile_available (or if the user has staff access) - enrollments is a list of CourseEnrollments. + Return enrollments only if courses are mobile_available (or if the user has + privileged (beta, staff, instructor) access) + + :param enrollments is a list of CourseEnrollments. """ for enr in enrollments: course = enr.course + + # Implicitly includes instructor role via the following has_access check + role = CourseBetaTesterRole(course.id) + # The course doesn't always really exist -- we can have bad data in the enrollments # pointing to non-existent (or removed) courses, in which case `course` is None. - if course and (course.mobile_available or has_access(user, 'staff', course)): + if course and (course.mobile_available or auth.has_access(user, role) or access.has_access(user, 'staff', course)): yield enr