Commit 1e460073 by muzaffaryousaf

Restrice non-staff users to access preview content.

TNL-4194
parent eebae966
...@@ -60,7 +60,10 @@ from courseware.access_response import ( ...@@ -60,7 +60,10 @@ from courseware.access_response import (
MobileAvailabilityError, MobileAvailabilityError,
VisibilityError, 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
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -102,6 +105,10 @@ def has_access(user, action, obj, course_key=None): ...@@ -102,6 +105,10 @@ def has_access(user, action, obj, course_key=None):
if isinstance(course_key, CCXLocator): if isinstance(course_key, CCXLocator):
course_key = course_key.to_course_locator() 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. # delegate the work to type-specific functions.
# (start with more specific types, then get more general) # (start with more specific types, then get more general)
if isinstance(obj, CourseDescriptor): if isinstance(obj, CourseDescriptor):
...@@ -139,6 +146,52 @@ def has_access(user, action, obj, course_key=None): ...@@ -139,6 +146,52 @@ def has_access(user, action, obj, course_key=None):
# ================ Implementation helpers ================================ # ================ 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 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. Checks if a user has access to a descriptor based on its start date.
......
...@@ -12,6 +12,9 @@ from django.core.urlresolvers import reverse ...@@ -12,6 +12,9 @@ from django.core.urlresolvers import reverse
from mock import Mock, patch from mock import Mock, patch
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from ccx_keys.locator import CCXLocator
from ccx.tests.factories import CcxFactory
import courseware.access as access import courseware.access as access
import courseware.access_response as access_response import courseware.access_response as access_response
...@@ -35,8 +38,15 @@ from xmodule.course_module import ( ...@@ -35,8 +38,15 @@ from xmodule.course_module import (
CATALOG_VISIBILITY_ABOUT, CATALOG_VISIBILITY_ABOUT,
CATALOG_VISIBILITY_NONE, CATALOG_VISIBILITY_NONE,
) )
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.error_module import ErrorDescriptor
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase 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 ( from util.milestones_helpers import (
set_prerequisite_courses, set_prerequisite_courses,
...@@ -55,23 +65,23 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -55,23 +65,23 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
""" """
TOMORROW = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1) TOMORROW = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)
YESTERDAY = 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): def setUp(self):
super(AccessTestCase, self).setUp() super(AccessTestCase, self).setUp()
course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall') self.course = CourseFactory.create(org='edX', course='toy', run='test_run')
self.course = course_key.make_usage_key('course', course_key.run)
self.anonymous_user = AnonymousUserFactory() 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.student = UserFactory()
self.global_staff = UserFactory(is_staff=True) self.global_staff = UserFactory(is_staff=True)
self.course_staff = StaffFactory(course_key=self.course.course_key) self.course_staff = StaffFactory(course_key=self.course.id)
self.course_instructor = InstructorFactory(course_key=self.course.course_key) self.course_instructor = InstructorFactory(course_key=self.course.id)
self.staff = GlobalStaffFactory() self.staff = GlobalStaffFactory()
def verify_access(self, mock_unit, student_should_have_access, expected_error_type=None): def verify_access(self, mock_unit, student_should_have_access, expected_error_type=None):
""" Verify the expected result from _has_access_descriptor """ """ Verify the expected result from _has_access_descriptor """
response = access._has_access_descriptor(self.anonymous_user, 'load', 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)) self.assertEqual(student_should_have_access, bool(response))
if expected_error_type is not None: if expected_error_type is not None:
...@@ -79,55 +89,142 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -79,55 +89,142 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
self.assertIsNotNone(response.to_json()['error_code']) self.assertIsNotNone(response.to_json()['error_code'])
self.assertTrue( 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): def test_has_access_to_course(self):
self.assertFalse(access._has_access_to_course( 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.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.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.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.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 # A user has staff access if they are in the staff group
self.assertTrue(access._has_access_to_course( 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.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 # A user has staff and instructor access if they are in the instructor group
self.assertTrue(access._has_access_to_course( 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.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 # A user does not have staff or instructor access if they are
# not in either the staff or the the instructor group # not in either the staff or the the instructor group
self.assertFalse(access._has_access_to_course( 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.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.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): def test__has_access_string(self):
...@@ -154,12 +251,12 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -154,12 +251,12 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
(self.course_instructor, expected_instructor) (self.course_instructor, expected_instructor)
): ):
self.assertEquals( 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 expected_response
) )
with self.assertRaises(ValueError): 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): def test__has_access_descriptor(self):
# TODO: override DISABLE_START_DATES and test the start date branch of the method # TODO: override DISABLE_START_DATES and test the start date branch of the method
...@@ -202,7 +299,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -202,7 +299,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
mock_unit.visible_to_staff_only = False mock_unit.visible_to_staff_only = False
self.assertTrue(bool(access._has_access_descriptor( 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) @ddt.data(None, YESTERDAY, TOMORROW)
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False}) @patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment