test_access.py 12.7 KB
Newer Older
1
import datetime
2
import pytz
3 4

from django.test import TestCase
5 6
from mock import Mock, patch
from opaque_keys.edx.locations import SlashSeparatedCourseKey
7

8
import courseware.access as access
9 10
from courseware.tests.factories import UserFactory, StaffFactory, InstructorFactory
from student.tests.factories import AnonymousUserFactory, CourseEnrollmentAllowedFactory
11 12
from xmodule.course_module import (
    CATALOG_VISIBILITY_CATALOG_AND_ABOUT, CATALOG_VISIBILITY_ABOUT,
13 14
    CATALOG_VISIBILITY_NONE
)
muhammad-ammar committed
15

16
# pylint: disable=missing-docstring
17
# pylint: disable=protected-access
Julia Hansbrough committed
18

19

20
class AccessTestCase(TestCase):
21 22 23
    """
    Tests for the various access controls on the student dashboard
    """
24

25
    def setUp(self):
26 27
        course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
        self.course = course_key.make_usage_key('course', course_key.run)
28 29 30
        self.anonymous_user = AnonymousUserFactory()
        self.student = UserFactory()
        self.global_staff = UserFactory(is_staff=True)
31 32
        self.course_staff = StaffFactory(course_key=self.course.course_key)
        self.course_instructor = InstructorFactory(course_key=self.course.course_key)
33

34 35 36
    def test_has_access_to_course(self):
        self.assertFalse(access._has_access_to_course(
            None, 'staff', self.course.course_key
37 38
        ))

39 40
        self.assertFalse(access._has_access_to_course(
            self.anonymous_user, 'staff', self.course.course_key
41
        ))
42 43
        self.assertFalse(access._has_access_to_course(
            self.anonymous_user, 'instructor', self.course.course_key
44 45
        ))

46 47
        self.assertTrue(access._has_access_to_course(
            self.global_staff, 'staff', self.course.course_key
48
        ))
49 50
        self.assertTrue(access._has_access_to_course(
            self.global_staff, 'instructor', self.course.course_key
51
        ))
52 53

        # A user has staff access if they are in the staff group
54 55
        self.assertTrue(access._has_access_to_course(
            self.course_staff, 'staff', self.course.course_key
56
        ))
57 58
        self.assertFalse(access._has_access_to_course(
            self.course_staff, 'instructor', self.course.course_key
59
        ))
60

61
        # A user has staff and instructor access if they are in the instructor group
62 63
        self.assertTrue(access._has_access_to_course(
            self.course_instructor, 'staff', self.course.course_key
64
        ))
65 66
        self.assertTrue(access._has_access_to_course(
            self.course_instructor, 'instructor', self.course.course_key
67
        ))
68

69
        # A user does not have staff or instructor access if they are
70
        # not in either the staff or the the instructor group
71 72
        self.assertFalse(access._has_access_to_course(
            self.student, 'staff', self.course.course_key
73
        ))
74 75
        self.assertFalse(access._has_access_to_course(
            self.student, 'instructor', self.course.course_key
76
        ))
77 78

    def test__has_access_string(self):
79 80
        user = Mock(is_staff=True)
        self.assertFalse(access._has_access_string(user, 'staff', 'not_global', self.course.course_key))
81

82 83
        user._has_global_staff_access.return_value = True
        self.assertTrue(access._has_access_string(user, 'staff', 'global', self.course.course_key))
84

85
        self.assertRaises(ValueError, access._has_access_string, user, 'not_staff', 'global', self.course.course_key)
86

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    def test__has_access_error_desc(self):
        descriptor = Mock()

        self.assertFalse(access._has_access_error_desc(self.student, 'load', descriptor, self.course.course_key))
        self.assertTrue(access._has_access_error_desc(self.course_staff, 'load', descriptor, self.course.course_key))
        self.assertTrue(access._has_access_error_desc(self.course_instructor, 'load', descriptor, self.course.course_key))

        self.assertFalse(access._has_access_error_desc(self.student, 'staff', descriptor, self.course.course_key))
        self.assertTrue(access._has_access_error_desc(self.course_staff, 'staff', descriptor, self.course.course_key))
        self.assertTrue(access._has_access_error_desc(self.course_instructor, 'staff', descriptor, self.course.course_key))

        self.assertFalse(access._has_access_error_desc(self.student, 'instructor', descriptor, self.course.course_key))
        self.assertFalse(access._has_access_error_desc(self.course_staff, 'instructor', descriptor, self.course.course_key))
        self.assertTrue(access._has_access_error_desc(self.course_instructor, 'instructor', descriptor, self.course.course_key))

        with self.assertRaises(ValueError):
            access._has_access_error_desc(self.course_instructor, 'not_load_or_staff', descriptor, self.course.course_key)

105
    def test__has_access_descriptor(self):
106
        # TODO: override DISABLE_START_DATES and test the start date branch of the method
107
        user = Mock()
108
        descriptor = Mock()
109 110

        # Always returns true because DISABLE_START_DATES is set in test.py
111 112
        self.assertTrue(access._has_access_descriptor(user, 'load', descriptor))
        self.assertTrue(access._has_access_descriptor(user, 'instructor', descriptor))
113
        with self.assertRaises(ValueError):
114
            access._has_access_descriptor(user, 'not_load_or_staff', descriptor)
115

116
    @patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    def test__has_access_descriptor_staff_lock(self):
        """
        Tests that "visible_to_staff_only" overrides start date.
        """
        mock_unit = Mock()
        mock_unit._class_tags = {}  # Needed for detached check in _has_access_descriptor

        def verify_access(student_should_have_access):
            """ Verify the expected result from _has_access_descriptor """
            self.assertEqual(student_should_have_access, access._has_access_descriptor(
                self.anonymous_user, 'load', mock_unit, course_key=self.course.course_key)
            )
            # staff always has access
            self.assertTrue(access._has_access_descriptor(
                self.course_staff, 'load', mock_unit, course_key=self.course.course_key)
            )

        # No start date, staff lock on
        mock_unit.visible_to_staff_only = True
        verify_access(False)

        # No start date, staff lock off.
        mock_unit.visible_to_staff_only = False
        verify_access(True)

        # Start date in the past, staff lock on.
        mock_unit.start = datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1)
        mock_unit.visible_to_staff_only = True
        verify_access(False)

        # Start date in the past, staff lock off.
        mock_unit.visible_to_staff_only = False
        verify_access(True)

        # Start date in the future, staff lock on.
        mock_unit.start = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)  # release date in the future
        mock_unit.visible_to_staff_only = True
        verify_access(False)

        # Start date in the future, staff lock off.
        mock_unit.visible_to_staff_only = False
        verify_access(False)

160
    def test__has_access_course_desc_can_enroll(self):
161 162
        yesterday = datetime.datetime.now(pytz.utc) - datetime.timedelta(days=1)
        tomorrow = datetime.datetime.now(pytz.utc) + datetime.timedelta(days=1)
163

164
        # Non-staff can enroll if authenticated and specifically allowed for that course
165
        # even outside the open enrollment period
166
        user = UserFactory.create()
167 168 169 170 171 172
        course = Mock(
            enrollment_start=tomorrow, enrollment_end=tomorrow,
            id=SlashSeparatedCourseKey('edX', 'test', '2012_Fall'), enrollment_domain=''
        )
        CourseEnrollmentAllowedFactory(email=user.email, course_id=course.id)
        self.assertTrue(access._has_access_course_desc(user, 'enroll', course))
173 174

        # Staff can always enroll even outside the open enrollment period
175 176
        user = StaffFactory.create(course_key=course.id)
        self.assertTrue(access._has_access_course_desc(user, 'enroll', course))
177

178 179
        # Non-staff cannot enroll if it is between the start and end dates and invitation only
        # and not specifically allowed
180
        course = Mock(
181 182 183 184 185 186 187 188 189 190 191 192
            enrollment_start=yesterday, enrollment_end=tomorrow,
            id=SlashSeparatedCourseKey('edX', 'test', '2012_Fall'), enrollment_domain='',
            invitation_only=True
        )
        user = UserFactory.create()
        self.assertFalse(access._has_access_course_desc(user, 'enroll', course))

        # Non-staff can enroll if it is between the start and end dates and not invitation only
        course = Mock(
            enrollment_start=yesterday, enrollment_end=tomorrow,
            id=SlashSeparatedCourseKey('edX', 'test', '2012_Fall'), enrollment_domain='',
            invitation_only=False
193 194
        )
        self.assertTrue(access._has_access_course_desc(user, 'enroll', course))
195 196

        # Non-staff cannot enroll outside the open enrollment period if not specifically allowed
197 198 199 200 201 202
        course = Mock(
            enrollment_start=tomorrow, enrollment_end=tomorrow,
            id=SlashSeparatedCourseKey('edX', 'test', '2012_Fall'), enrollment_domain='',
            invitation_only=False
        )
        self.assertFalse(access._has_access_course_desc(user, 'enroll', course))
203 204 205

    def test__user_passed_as_none(self):
        """Ensure has_access handles a user being passed as null"""
206 207
        access.has_access(None, 'staff', 'global', None)

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    def test__catalog_visibility(self):
        """
        Tests the catalog visibility tri-states
        """
        user = UserFactory.create()
        course_id = SlashSeparatedCourseKey('edX', 'test', '2012_Fall')
        staff = StaffFactory.create(course_key=course_id)

        course = Mock(
            id=course_id,
            catalog_visibility=CATALOG_VISIBILITY_CATALOG_AND_ABOUT
        )
        self.assertTrue(access._has_access_course_desc(user, 'see_in_catalog', course))
        self.assertTrue(access._has_access_course_desc(user, 'see_about_page', course))
        self.assertTrue(access._has_access_course_desc(staff, 'see_in_catalog', course))
        self.assertTrue(access._has_access_course_desc(staff, 'see_about_page', course))

        # Now set visibility to just about page
        course = Mock(
            id=SlashSeparatedCourseKey('edX', 'test', '2012_Fall'),
            catalog_visibility=CATALOG_VISIBILITY_ABOUT
        )
        self.assertFalse(access._has_access_course_desc(user, 'see_in_catalog', course))
        self.assertTrue(access._has_access_course_desc(user, 'see_about_page', course))
        self.assertTrue(access._has_access_course_desc(staff, 'see_in_catalog', course))
        self.assertTrue(access._has_access_course_desc(staff, 'see_about_page', course))

        # Now set visibility to none, which means neither in catalog nor about pages
        course = Mock(
            id=SlashSeparatedCourseKey('edX', 'test', '2012_Fall'),
            catalog_visibility=CATALOG_VISIBILITY_NONE
        )
        self.assertFalse(access._has_access_course_desc(user, 'see_in_catalog', course))
        self.assertFalse(access._has_access_course_desc(user, 'see_about_page', course))
        self.assertTrue(access._has_access_course_desc(staff, 'see_in_catalog', course))
        self.assertTrue(access._has_access_course_desc(staff, 'see_about_page', course))

245 246 247 248 249 250

class UserRoleTestCase(TestCase):
    """
    Tests for user roles.
    """
    def setUp(self):
251
        self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
252 253 254
        self.anonymous_user = AnonymousUserFactory()
        self.student = UserFactory()
        self.global_staff = UserFactory(is_staff=True)
255 256
        self.course_staff = StaffFactory(course_key=self.course_key)
        self.course_instructor = InstructorFactory(course_key=self.course_key)
257 258 259 260 261

    def test_user_role_staff(self):
        """Ensure that user role is student for staff masqueraded as student."""
        self.assertEqual(
            'staff',
262
            access.get_user_role(self.course_staff, self.course_key)
263 264 265 266 267
        )
        # Masquerade staff
        self.course_staff.masquerade_as_student = True
        self.assertEqual(
            'student',
268
            access.get_user_role(self.course_staff, self.course_key)
269 270 271 272 273 274
        )

    def test_user_role_instructor(self):
        """Ensure that user role is student for instructor masqueraded as student."""
        self.assertEqual(
            'instructor',
275
            access.get_user_role(self.course_instructor, self.course_key)
276 277 278 279 280
        )
        # Masquerade instructor
        self.course_instructor.masquerade_as_student = True
        self.assertEqual(
            'student',
281
            access.get_user_role(self.course_instructor, self.course_key)
282 283 284 285 286 287
        )

    def test_user_role_anonymous(self):
        """Ensure that user role is student for anonymous user."""
        self.assertEqual(
            'student',
288
            access.get_user_role(self.anonymous_user, self.course_key)
289
        )