From bb952e148f0221442f3e929afd3e8414d34845fd Mon Sep 17 00:00:00 2001
From: muzaffaryousaf <muzaffar.yousaf@arbisoft.com>
Date: Thu, 17 Mar 2016 18:57:14 +0500
Subject: [PATCH] Restrice non-staff users to access preview content.

TNL-4194
---
 lms/djangoapps/courseware/access.py           |  55 ++++++-
 .../courseware/tests/test_access.py           | 138 +++++++++++++++---
 2 files changed, 169 insertions(+), 24 deletions(-)

diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py
index 178568dd8ed..98306e7a602 100644
--- a/lms/djangoapps/courseware/access.py
+++ b/lms/djangoapps/courseware/access.py
@@ -61,7 +61,10 @@ from courseware.access_response import (
     MobileAvailabilityError,
     VisibilityError,
 )
-from courseware.access_utils import adjust_start_date, check_start_date, debug, ACCESS_GRANTED, ACCESS_DENIED
+from courseware.access_utils import (
+    adjust_start_date, check_start_date, debug, ACCESS_GRANTED, ACCESS_DENIED,
+    in_preview_mode
+)
 
 from lms.djangoapps.ccx.custom_exception import CCXLocatorValidationException
 from lms.djangoapps.ccx.models import CustomCourseForEdX
@@ -135,6 +138,10 @@ def has_access(user, action, obj, course_key=None):
     if isinstance(course_key, CCXLocator):
         course_key = course_key.to_course_locator()
 
+    if in_preview_mode():
+        if not bool(has_staff_access_to_preview_mode(user=user, obj=obj, course_key=course_key)):
+            return ACCESS_DENIED
+
     # delegate the work to type-specific functions.
     # (start with more specific types, then get more general)
     if isinstance(obj, CourseDescriptor):
@@ -172,6 +179,52 @@ def has_access(user, action, obj, course_key=None):
 
 
 # ================ Implementation helpers ================================
+
+def has_staff_access_to_preview_mode(user, obj, course_key=None):
+    """
+    Returns whether user has staff access to specified modules or not.
+
+    Arguments:
+
+        user: a Django user object.
+
+        obj: The object to check access for.
+
+        course_key: A course_key specifying which course this access is for.
+
+    Returns an AccessResponse object.
+    """
+    if course_key is None:
+        if isinstance(obj, CourseDescriptor) or isinstance(obj, CourseOverview):
+            course_key = obj.id
+
+        elif isinstance(obj, ErrorDescriptor):
+            course_key = obj.location.course_key
+
+        elif isinstance(obj, XModule):
+            course_key = obj.descriptor.course_key
+
+        elif isinstance(obj, XBlock):
+            course_key = obj.location.course_key
+
+        elif isinstance(obj, CCXLocator):
+            course_key = obj.to_course_locator()
+
+        elif isinstance(obj, CourseKey):
+            course_key = obj
+
+        elif isinstance(obj, UsageKey):
+            course_key = obj.course_key
+
+    if course_key is None:
+        if GlobalStaff().has_user(user):
+            return ACCESS_GRANTED
+        else:
+            return ACCESS_DENIED
+
+    return _has_access_to_course(user, 'staff', course_key=course_key)
+
+
 def _can_access_descriptor_with_start_date(user, descriptor, course_key):  # pylint: disable=invalid-name
     """
     Checks if a user has access to a descriptor based on its start date.
diff --git a/lms/djangoapps/courseware/tests/test_access.py b/lms/djangoapps/courseware/tests/test_access.py
index add8c738ee7..f261b562820 100644
--- a/lms/djangoapps/courseware/tests/test_access.py
+++ b/lms/djangoapps/courseware/tests/test_access.py
@@ -17,6 +17,7 @@ from mock import Mock, patch
 from nose.plugins.attrib import attr
 from opaque_keys.edx.locations import SlashSeparatedCourseKey
 
+from ccx.tests.factories import CcxFactory
 import courseware.access as access
 import courseware.access_response as access_response
 from courseware.masquerade import CourseMasquerade
@@ -39,17 +40,21 @@ from student.tests.factories import (
     CourseEnrollmentAllowedFactory,
     CourseEnrollmentFactory,
 )
+
 from xmodule.course_module import (
     CATALOG_VISIBILITY_CATALOG_AND_ABOUT,
     CATALOG_VISIBILITY_ABOUT,
     CATALOG_VISIBILITY_NONE,
 )
-from xmodule.modulestore.tests.factories import CourseFactory
+from xmodule.error_module import ErrorDescriptor
+from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
 from xmodule.modulestore.tests.django_utils import (
     ModuleStoreTestCase,
     SharedModuleStoreTestCase,
     TEST_DATA_SPLIT_MODULESTORE
 )
+from xmodule.modulestore.xml import CourseLocationManager
+from xmodule.tests import get_test_system
 
 from util.milestones_helpers import (
     set_prerequisite_courses,
@@ -157,23 +162,23 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
     """
     TOMORROW = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)
     YESTERDAY = datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1)
+    MODULESTORE = TEST_DATA_SPLIT_MODULESTORE
 
     def setUp(self):
         super(AccessTestCase, self).setUp()
-        course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
-        self.course = course_key.make_usage_key('course', course_key.run)
+        self.course = CourseFactory.create(org='edX', course='toy', run='test_run')
         self.anonymous_user = AnonymousUserFactory()
-        self.beta_user = BetaTesterFactory(course_key=self.course.course_key)
+        self.beta_user = BetaTesterFactory(course_key=self.course.id)
         self.student = UserFactory()
         self.global_staff = UserFactory(is_staff=True)
-        self.course_staff = StaffFactory(course_key=self.course.course_key)
-        self.course_instructor = InstructorFactory(course_key=self.course.course_key)
+        self.course_staff = StaffFactory(course_key=self.course.id)
+        self.course_instructor = InstructorFactory(course_key=self.course.id)
         self.staff = GlobalStaffFactory()
 
     def verify_access(self, mock_unit, student_should_have_access, expected_error_type=None):
         """ Verify the expected result from _has_access_descriptor """
         response = access._has_access_descriptor(self.anonymous_user, 'load',
-                                                 mock_unit, course_key=self.course.course_key)
+                                                 mock_unit, course_key=self.course.id)
         self.assertEqual(student_should_have_access, bool(response))
 
         if expected_error_type is not None:
@@ -181,55 +186,142 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
             self.assertIsNotNone(response.to_json()['error_code'])
 
         self.assertTrue(
-            access._has_access_descriptor(self.course_staff, 'load', mock_unit, course_key=self.course.course_key)
+            access._has_access_descriptor(self.course_staff, 'load', mock_unit, course_key=self.course.id)
         )
 
+    def test_has_staff_access_to_preview_mode(self):
+        """
+        Tests users have right access to content in preview mode.
+        """
+        course_key = self.course.id
+        usage_key = self.course.scope_ids.usage_id
+        chapter = ItemFactory.create(category="chapter", parent_location=self.course.location)
+        overview = CourseOverview.get_from_id(course_key)
+        test_system = get_test_system()
+
+        ccx = CcxFactory(course_id=course_key)
+        ccx_locator = CCXLocator.from_course_locator(course_key, ccx.id)
+
+        error_descriptor = ErrorDescriptor.from_xml(
+            u"<problem>ABC \N{SNOWMAN}</problem>",
+            test_system,
+            CourseLocationManager(course_key),
+            "error msg"
+        )
+        # Enroll student to the course
+        CourseEnrollmentFactory(user=self.student, course_id=self.course.id)
+
+        modules = [
+            self.course,
+            overview,
+            chapter,
+            ccx_locator,
+            error_descriptor,
+            course_key,
+            usage_key,
+        ]
+        # Course key is not None
+        self.assertTrue(
+            bool(access.has_staff_access_to_preview_mode(self.global_staff, obj=self.course, course_key=course_key))
+        )
+
+        for user in [self.global_staff, self.course_staff, self.course_instructor]:
+            for obj in modules:
+                self.assertTrue(bool(access.has_staff_access_to_preview_mode(user, obj=obj)))
+                self.assertFalse(bool(access.has_staff_access_to_preview_mode(self.student, obj=obj)))
+
+    def test_student_has_access(self):
+        """
+        Tests course student have right access to content w/o preview.
+        """
+        course_key = self.course.id
+        chapter = ItemFactory.create(category="chapter", parent_location=self.course.location)
+        overview = CourseOverview.get_from_id(course_key)
+
+        # Enroll student to the course
+        CourseEnrollmentFactory(user=self.student, course_id=self.course.id)
+
+        modules = [
+            self.course,
+            overview,
+            chapter,
+        ]
+        with patch('courseware.access.in_preview_mode') as mock_preview:
+            mock_preview.return_value = False
+            for obj in modules:
+                self.assertTrue(bool(access.has_access(self.student, 'load', obj, course_key=self.course.id)))
+
+        with patch('courseware.access.in_preview_mode') as mock_preview:
+            mock_preview.return_value = True
+            for obj in modules:
+                self.assertFalse(bool(access.has_access(self.student, 'load', obj, course_key=self.course.id)))
+
+    def test_string_has_staff_access_to_preview_mode(self):
+        """
+        Tests different users has right access to string content in preview mode.
+        """
+        self.assertTrue(bool(access.has_staff_access_to_preview_mode(self.global_staff, obj='global')))
+        self.assertFalse(bool(access.has_staff_access_to_preview_mode(self.course_staff, obj='global')))
+        self.assertFalse(bool(access.has_staff_access_to_preview_mode(self.course_instructor, obj='global')))
+        self.assertFalse(bool(access.has_staff_access_to_preview_mode(self.student, obj='global')))
+
+    @patch('courseware.access.in_preview_mode', Mock(return_value=True))
+    def test_has_access_with_preview_mode(self):
+        """
+        Tests particular user's can access content via has_access in preview mode.
+        """
+        self.assertTrue(bool(access.has_access(self.global_staff, 'staff', self.course, course_key=self.course.id)))
+        self.assertTrue(bool(access.has_access(self.course_staff, 'staff', self.course, course_key=self.course.id)))
+        self.assertTrue(bool(access.has_access(self.course_instructor, 'staff', self.course, course_key=self.course.id)))
+        self.assertFalse(bool(access.has_access(self.student, 'staff', self.course, course_key=self.course.id)))
+        self.assertFalse(bool(access.has_access(self.student, 'load', self.course, course_key=self.course.id)))
+
     def test_has_access_to_course(self):
         self.assertFalse(access._has_access_to_course(
-            None, 'staff', self.course.course_key
+            None, 'staff', self.course.id
         ))
 
         self.assertFalse(access._has_access_to_course(
-            self.anonymous_user, 'staff', self.course.course_key
+            self.anonymous_user, 'staff', self.course.id
         ))
         self.assertFalse(access._has_access_to_course(
-            self.anonymous_user, 'instructor', self.course.course_key
+            self.anonymous_user, 'instructor', self.course.id
         ))
 
         self.assertTrue(access._has_access_to_course(
-            self.global_staff, 'staff', self.course.course_key
+            self.global_staff, 'staff', self.course.id
         ))
         self.assertTrue(access._has_access_to_course(
-            self.global_staff, 'instructor', self.course.course_key
+            self.global_staff, 'instructor', self.course.id
         ))
 
         # A user has staff access if they are in the staff group
         self.assertTrue(access._has_access_to_course(
-            self.course_staff, 'staff', self.course.course_key
+            self.course_staff, 'staff', self.course.id
         ))
         self.assertFalse(access._has_access_to_course(
-            self.course_staff, 'instructor', self.course.course_key
+            self.course_staff, 'instructor', self.course.id
         ))
 
         # A user has staff and instructor access if they are in the instructor group
         self.assertTrue(access._has_access_to_course(
-            self.course_instructor, 'staff', self.course.course_key
+            self.course_instructor, 'staff', self.course.id
         ))
         self.assertTrue(access._has_access_to_course(
-            self.course_instructor, 'instructor', self.course.course_key
+            self.course_instructor, 'instructor', self.course.id
         ))
 
         # A user does not have staff or instructor access if they are
         # not in either the staff or the the instructor group
         self.assertFalse(access._has_access_to_course(
-            self.student, 'staff', self.course.course_key
+            self.student, 'staff', self.course.id
         ))
         self.assertFalse(access._has_access_to_course(
-            self.student, 'instructor', self.course.course_key
+            self.student, 'instructor', self.course.id
         ))
 
         self.assertFalse(access._has_access_to_course(
-            self.student, 'not_staff_or_instructor', self.course.course_key
+            self.student, 'not_staff_or_instructor', self.course.id
         ))
 
     def test__has_access_string(self):
@@ -256,12 +348,12 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
                 (self.course_instructor, expected_instructor)
         ):
             self.assertEquals(
-                bool(access._has_access_error_desc(user, action, descriptor, self.course.course_key)),
+                bool(access._has_access_error_desc(user, action, descriptor, self.course.id)),
                 expected_response
             )
 
         with self.assertRaises(ValueError):
-            access._has_access_error_desc(self.course_instructor, 'not_load_or_staff', descriptor, self.course.course_key)
+            access._has_access_error_desc(self.course_instructor, 'not_load_or_staff', descriptor, self.course.id)
 
     def test__has_access_descriptor(self):
         # TODO: override DISABLE_START_DATES and test the start date branch of the method
@@ -304,7 +396,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
         mock_unit.visible_to_staff_only = False
 
         self.assertTrue(bool(access._has_access_descriptor(
-            self.beta_user, 'load', mock_unit, course_key=self.course.course_key)))
+            self.beta_user, 'load', mock_unit, course_key=self.course.id)))
 
     @ddt.data(None, YESTERDAY, TOMORROW)
     @patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
-- 
GitLab