Beispiel #1
0
    def test_filter_by_roles_course_staff(self):
        """
        Verify that course_ids are filtered by the provided roles.
        """
        # Make this user a course staff user for the course.
        course_staff_user = self.create_user(username='******', is_staff=False)
        add_users(self.global_admin, CourseStaffRole(self.course.id), course_staff_user)

        # Create a second course, along with an instructor user for it.
        alternate_course1 = self.create_course(org='test1')
        course_instructor_user = self.create_user(username='******', is_staff=False)
        add_users(self.global_admin, CourseInstructorRole(alternate_course1.id), course_instructor_user)

        # Create a third course, along with an user that has both staff and instructor for it.
        alternate_course2 = self.create_course(org='test2')
        course_instructor_staff_user = self.create_user(username='******', is_staff=False)
        add_users(self.global_admin, CourseInstructorRole(alternate_course2.id), course_instructor_staff_user)
        add_users(self.global_admin, CourseStaffRole(alternate_course2.id), course_instructor_staff_user)

        # Requesting the courses for which the course staff user is staff should return *only* the single course.
        self.setup_user(self.staff_user)
        filtered_response = self.verify_response(params={
            'username': course_staff_user.username,
            'role': 'staff'
        })
        assert len(filtered_response.data['results']) == 1
        assert filtered_response.data['results'][0].startswith(self.course.org)

        # The course staff user does *not* have the course instructor role on any courses.
        filtered_response = self.verify_response(params={
            'username': course_staff_user.username,
            'role': 'instructor'
        })
        assert len(filtered_response.data['results']) == 0

        # The course instructor user only has the course instructor role on one course.
        filtered_response = self.verify_response(params={
            'username': course_instructor_user.username,
            'role': 'instructor'
        })
        assert len(filtered_response.data['results']) == 1
        assert filtered_response.data['results'][0].startswith(alternate_course1.org)

        # The course instructor user has the inferred course staff role on one course.
        self.setup_user(course_instructor_user)
        filtered_response = self.verify_response(params={
            'username': course_instructor_user.username,
            'role': 'staff'
        })
        assert len(filtered_response.data['results']) == 1
        assert filtered_response.data['results'][0].startswith(alternate_course1.org)

        # The user with both instructor AND staff on a course has the inferred course staff role on that one course.
        self.setup_user(course_instructor_staff_user)
        filtered_response = self.verify_response(params={
            'username': course_instructor_staff_user.username,
            'role': 'staff'
        })
        assert len(filtered_response.data['results']) == 1
        assert filtered_response.data['results'][0].startswith(alternate_course2.org)
Beispiel #2
0
    def test_org_and_course_roles(self):
        """
        Test that Org and course roles don't interfere with course roles or vice versa
        """
        OrgInstructorRole(self.course_key.org).add_users(self.student)
        CourseInstructorRole(self.course_key).add_users(self.student)
        assert OrgInstructorRole(self.course_key.org).has_user(self.student), \
            f"Student doesn't have access to {six.text_type(self.course_key.org)}"
        assert CourseInstructorRole(self.course_key).has_user(self.student), \
            f"Student doesn't have access to {six.text_type(self.course_key)}"

        # remove access and confirm
        OrgInstructorRole(self.course_key.org).remove_users(self.student)
        assert not OrgInstructorRole(self.course_key.org).has_user(self.student), \
            f'Student still has access to {self.course_key.org}'
        assert CourseInstructorRole(self.course_key).has_user(self.student), \
            f"Student doesn't have access to {six.text_type(self.course_key)}"

        # ok now keep org role and get rid of course one
        OrgInstructorRole(self.course_key.org).add_users(self.student)
        CourseInstructorRole(self.course_key).remove_users(self.student)
        assert OrgInstructorRole(self.course_key.org).has_user(self.student), \
            f'Student lost has access to {self.course_key.org}'
        assert not CourseInstructorRole(self.course_key).has_user(self.student), \
            f"Student doesn't have access to {six.text_type(self.course_key)}"
Beispiel #3
0
    def test_org_and_course_roles(self):
        """
        Test that Org and course roles don't interfere with course roles or vice versa
        """
        OrgInstructorRole(self.course_key.org).add_users(self.student)
        CourseInstructorRole(self.course_key).add_users(self.student)
        self.assertTrue(
            OrgInstructorRole(self.course_key.org).has_user(self.student),
            "Student doesn't have access to {}".format(
                six.text_type(self.course_key.org)))
        self.assertTrue(
            CourseInstructorRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(
                six.text_type(self.course_key)))

        # remove access and confirm
        OrgInstructorRole(self.course_key.org).remove_users(self.student)
        self.assertFalse(
            OrgInstructorRole(self.course_key.org).has_user(self.student),
            "Student still has access to {}".format(self.course_key.org))
        self.assertTrue(
            CourseInstructorRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(
                six.text_type(self.course_key)))

        # ok now keep org role and get rid of course one
        OrgInstructorRole(self.course_key.org).add_users(self.student)
        CourseInstructorRole(self.course_key).remove_users(self.student)
        self.assertTrue(
            OrgInstructorRole(self.course_key.org).has_user(self.student),
            "Student lost has access to {}".format(self.course_key.org))
        self.assertFalse(
            CourseInstructorRole(self.course_key).has_user(self.student),
            "Student doesn't have access to {}".format(
                six.text_type(self.course_key)))
Beispiel #4
0
def remove_all_instructors(course_key):
    """
    Removes all instructor and staff users from the given course.
    """
    staff_role = CourseStaffRole(course_key)
    staff_role.remove_users(*staff_role.users_with_role())
    instructor_role = CourseInstructorRole(course_key)
    instructor_role.remove_users(*instructor_role.users_with_role())
Beispiel #5
0
    def make_instructor(self):
        """
        create instructor user.
        """
        instructor = UserFactory.create(password="******")
        role = CourseInstructorRole(self.course.id)
        role.add_users(instructor)

        return instructor
    def test_keep_instructors(self):
        course_run = CourseFactory()
        instructor = UserFactory()
        CourseInstructorRole(course_run.id).add_users(instructor)

        with mock.patch(self.YESNO_PATCH_LOCATION, return_value=True):
            call_command('delete_course', str(course_run.id), '--keep-instructors')

        assert CourseInstructorRole(course_run.id).has_user(instructor)
Beispiel #7
0
    def test_detail_delete_instructor(self):
        auth.add_users(self.user, CourseInstructorRole(self.course.id), self.ext_user, self.user)

        resp = self.client.delete(
            self.detail_url,
            HTTP_ACCEPT="application/json",
        )
        self.assertEqual(resp.status_code, 204)
        # reload user from DB
        ext_user = User.objects.get(email=self.ext_user.email)
        self.assertFalse(auth.user_has_role(ext_user, CourseInstructorRole(self.course.id)))
Beispiel #8
0
    def test_delete_last_instructor(self):
        auth.add_users(self.user, CourseInstructorRole(self.course.id), self.ext_user)

        resp = self.client.delete(
            self.detail_url,
            HTTP_ACCEPT="application/json",
        )
        self.assertEqual(resp.status_code, 400)
        result = json.loads(resp.content.decode('utf-8'))
        self.assertIn("error", result)
        # reload user from DB
        ext_user = User.objects.get(email=self.ext_user.email)
        self.assertTrue(auth.user_has_role(ext_user, CourseInstructorRole(self.course.id)))
Beispiel #9
0
    def test_add_user_to_course_group(self):
        """
        Tests adding user to course group (happy path).
        """
        # Create groups for a new course (and assign instructor role to the creator).
        self.assertFalse(user_has_role(self.creator, CourseInstructorRole(self.course_key)))
        add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator)
        add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator)
        self.assertTrue(user_has_role(self.creator, CourseInstructorRole(self.course_key)))

        # Add another user to the staff role.
        self.assertFalse(user_has_role(self.staff, CourseStaffRole(self.course_key)))
        add_users(self.creator, CourseStaffRole(self.course_key), self.staff)
        self.assertTrue(user_has_role(self.staff, CourseStaffRole(self.course_key)))
Beispiel #10
0
    def test_remove_user_from_course_group(self):
        """
        Tests removing user from course group (happy path).
        """
        add_users(self.global_admin, CourseInstructorRole(self.course_key), self.creator)
        add_users(self.global_admin, CourseStaffRole(self.course_key), self.creator)

        add_users(self.creator, CourseStaffRole(self.course_key), self.staff)
        self.assertTrue(user_has_role(self.staff, CourseStaffRole(self.course_key)))

        remove_users(self.creator, CourseStaffRole(self.course_key), self.staff)
        self.assertFalse(user_has_role(self.staff, CourseStaffRole(self.course_key)))

        remove_users(self.creator, CourseInstructorRole(self.course_key), self.creator)
        self.assertFalse(user_has_role(self.creator, CourseInstructorRole(self.course_key)))
Beispiel #11
0
    def delete(self, request, course_key_string):
        course_key = CourseKey.from_string(course_key_string)

        email = request.data.get("email", None)
        validate_param_exist(email, "email")

        try:
            user = User.objects.get(email=email)
        except Exception:  # pylint: disable=broad-except
            msg = {
                "error":
                "Could not find user by email address '{email}'".format(
                    email=email)
            }
            return Response(msg, 404)

        auth.get_user_permissions(request.user, course_key)

        auth.remove_users(request.user, CourseStaffRole(course_key), user)
        auth.remove_users(request.user, CourseInstructorRole(course_key), user)

        CourseEnrollment.unenroll(user, course_key)

        msg = "'{email}''s permissions are revoked from '{course_key}'".format(
            email=email, course_key=course_key)
        log.info(msg)

        return Response(
            {'message': "User is removed from {}.".format(course_key)})
Beispiel #12
0
    def test_add_master_course_staff_to_ccx_display_name(self):
        """
        Test add staff of master course to ccx course.
        Specific test to check that a passed display name is in the
        subject of the email sent to the enrolled users.
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(
            CourseInstructorRole(self.course.id).has_user(instructor))
        outbox = self.get_outbox()
        # create a unique display name
        display_name = 'custom_display_{}'.format(uuid.uuid4())
        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(
            self.course, 'instructor')
        self.assertEqual(len(outbox), 0)
        # give access to the course staff/instructor
        add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                       display_name)
        self.assertEqual(
            len(outbox),
            len(list_staff_master_course) + len(list_instructor_master_course))
        for email in outbox:
            self.assertIn(display_name, email.subject)
Beispiel #13
0
def _manage_users(request, course_key):
    """
    This view will return all CMS users who are editors for the specified course
    """
    # check that logged in user has permissions to this item
    user_perms = get_user_permissions(request.user, course_key)
    if not user_perms & STUDIO_VIEW_USERS:
        raise PermissionDenied()

    course_module = modulestore().get_course(course_key)
    instructors = set(CourseInstructorRole(course_key).users_with_role())
    # the page only lists staff and assumes they're a superset of instructors. Do a union to ensure.
    staff = set(
        CourseStaffRole(course_key).users_with_role()).union(instructors)

    formatted_users = []
    for user in instructors:
        formatted_users.append(user_with_role(user, 'instructor'))
    for user in staff - instructors:
        formatted_users.append(user_with_role(user, 'staff'))

    return render_to_response(
        'manage_users.html', {
            'context_course':
            course_module,
            'show_transfer_ownership_hint':
            request.user in instructors and len(instructors) == 1,
            'users':
            formatted_users,
            'allow_actions':
            bool(user_perms & STUDIO_EDIT_ROLES),
        })
Beispiel #14
0
    def get(self, request):
        """Displays course Enrollment and staffing course statistics"""

        if not request.user.is_staff:
            raise Http404
        data = []

        for course in self.get_courses():
            datum = [course.display_name, course.id]
            datum += [CourseEnrollment.objects.filter(
                course_id=course.id).count()]
            datum += [CourseStaffRole(course.id).users_with_role().count()]
            datum += [','.join([x.username for x in CourseInstructorRole(
                course.id).users_with_role()])]
            data.append(datum)

        datatable = dict(header=[_('Course Name'), _('course_id'),
                                 _('# enrolled'), _('# staff'),
                                 _('instructors')],
                         title=_('Enrollment information for all courses'),
                         data=data)
        context = {
            'datatable': datatable,
            'msg': self.msg,
            'djangopid': os.getpid(),
            'modeflag': {'staffing': 'active-section'},
        }
        return render_to_response(self.template_name, context)
Beispiel #15
0
    def setUpClass(cls):
        super().setUpClass()
        cls.store = modulestore()
        cls.course = ToyCourseFactory.create(
            end=datetime(2028, 1, 1, 1, 1, 1),
            enrollment_start=datetime(2020, 1, 1, 1, 1, 1),
            enrollment_end=datetime(2028, 1, 1, 1, 1, 1),
            emit_signals=True,
            modulestore=cls.store,
        )
        cls.chapter = ItemFactory(parent=cls.course, category='chapter')
        cls.sequence = ItemFactory(parent=cls.chapter,
                                   category='sequential',
                                   display_name='sequence')
        cls.unit = ItemFactory.create(parent=cls.sequence,
                                      category='vertical',
                                      display_name="Vertical")

        cls.user = UserFactory(username='******',
                               email=u'*****@*****.**',
                               password='******',
                               is_staff=False)
        cls.instructor = UserFactory(username='******',
                                     email=u'*****@*****.**',
                                     password='******',
                                     is_staff=False)
        CourseInstructorRole(cls.course.id).add_users(cls.instructor)
        cls.url = '/api/courseware/course/{}'.format(cls.course.id)
    def get_users(self, course_id, user_id=None):
        """
        Gets the users for a given target.

        Result is returned in the form of a queryset, and may contain duplicates.
        """
        staff_qset = CourseStaffRole(course_id).users_with_role()
        instructor_qset = CourseInstructorRole(course_id).users_with_role()
        staff_instructor_qset = (staff_qset | instructor_qset)
        enrollment_query = models.Q(is_active=True,
                                    courseenrollment__course_id=course_id,
                                    courseenrollment__is_active=True)
        enrollment_qset = User.objects.filter(enrollment_query)
        if self.target_type == SEND_TO_MYSELF:
            if user_id is None:
                raise ValueError(
                    "Must define self user to send email to self.")
            user = User.objects.filter(id=user_id)
            return use_read_replica_if_available(user)
        elif self.target_type == SEND_TO_STAFF:
            return use_read_replica_if_available(staff_instructor_qset)
        elif self.target_type == SEND_TO_LEARNERS:
            return use_read_replica_if_available(
                enrollment_qset.exclude(id__in=staff_instructor_qset))
        elif self.target_type == SEND_TO_COHORT:
            return self.cohorttarget.cohort.users.filter(
                id__in=enrollment_qset)  # lint-amnesty, pylint: disable=no-member
        elif self.target_type == SEND_TO_TRACK:
            return use_read_replica_if_available(
                User.objects.filter(
                    models.Q(courseenrollment__mode=self.coursemodetarget.
                             track.mode_slug)
                    & enrollment_query))
        else:
            raise ValueError(f"Unrecognized target type {self.target_type}")
Beispiel #17
0
    def test_get_course_list_with_invalid_course_location(self, store):
        """
        Test getting courses with invalid course location (course deleted from modulestore).
        """
        with self.store.default_store(store):
            course_key = self.store.make_course_key('Org', 'Course', 'Run')
            self._create_course_with_access_groups(course_key, self.user,
                                                   store)

        # get courses through iterating all courses
        courses_iter, __ = _accessible_courses_iter_for_tests(self.request)
        courses_list = list(courses_iter)
        self.assertEqual(len(courses_list), 1)

        courses_summary_iter, __ = _accessible_courses_summary_iter(
            self.request)
        courses_summary_list = list(courses_summary_iter)

        # Verify fetched accessible courses list is a list of CourseSummery instances and only one course
        # is returned
        self.assertTrue(
            all(
                isinstance(course, CourseSummary)
                for course in courses_summary_list))
        self.assertEqual(len(courses_summary_list), 1)

        # get courses by reversing group name formats
        courses_list_by_groups, __ = _accessible_courses_list_from_groups(
            self.request)
        self.assertEqual(len(courses_list_by_groups), 1)

        course_keys_in_course_list = [course.id for course in courses_list]
        course_keys_in_courses_list_by_groups = [
            course.id for course in courses_list_by_groups
        ]
        # check course lists have same courses
        self.assertEqual(course_keys_in_course_list,
                         course_keys_in_courses_list_by_groups)
        # now delete this course and re-add user to instructor group of this course
        delete_course(course_key, self.user.id)

        CourseInstructorRole(course_key).add_users(self.user)

        # Get courses through iterating all courses
        courses_iter, __ = _accessible_courses_iter_for_tests(self.request)

        # Get course summaries by iterating all courses
        courses_summary_iter, __ = _accessible_courses_summary_iter(
            self.request)

        # Get courses by reversing group name formats
        courses_list_by_groups, __ = _accessible_courses_list_from_groups(
            self.request)

        # Test that course list returns no course
        self.assertEqual([
            len(list(courses_iter)),
            len(courses_list_by_groups),
            len(list(courses_summary_iter))
        ], [0, 0, 0])
Beispiel #18
0
class RoleCacheTestCase(TestCase):  # lint-amnesty, pylint: disable=missing-class-docstring

    IN_KEY = CourseKey.from_string('edX/toy/2012_Fall')
    NOT_IN_KEY = CourseKey.from_string('edX/toy/2013_Fall')

    ROLES = (
        (CourseStaffRole(IN_KEY), ('staff', IN_KEY, 'edX')),
        (CourseInstructorRole(IN_KEY), ('instructor', IN_KEY, 'edX')),
        (OrgStaffRole(IN_KEY.org), ('staff', None, 'edX')),
        (OrgInstructorRole(IN_KEY.org), ('instructor', None, 'edX')),
        (CourseBetaTesterRole(IN_KEY), ('beta_testers', IN_KEY, 'edX')),
    )

    def setUp(self):
        super(RoleCacheTestCase, self).setUp()  # lint-amnesty, pylint: disable=super-with-arguments
        self.user = UserFactory()

    @ddt.data(*ROLES)
    @ddt.unpack
    def test_only_in_role(self, role, target):
        role.add_users(self.user)
        cache = RoleCache(self.user)
        assert cache.has_role(*target)

        for other_role, other_target in self.ROLES:
            if other_role == role:
                continue

            assert not cache.has_role(*other_target)

    @ddt.data(*ROLES)
    @ddt.unpack
    def test_empty_cache(self, role, target):  # lint-amnesty, pylint: disable=unused-argument
        cache = RoleCache(self.user)
        assert not cache.has_role(*target)
    def test_creation(self):
        """
        The user that creates a library should have instructor (admin) and staff permissions
        """
        # self.library has been auto-created by the staff user.
        self.assertTrue(has_studio_write_access(self.user, self.lib_key))
        self.assertTrue(has_studio_read_access(self.user, self.lib_key))
        # Make sure the user was actually assigned the instructor role and not just using is_staff superpowers:
        self.assertTrue(CourseInstructorRole(self.lib_key).has_user(self.user))

        # Now log out and ensure we are forbidden from creating a library:
        self.client.logout()
        self._assert_cannot_create_library(
            expected_code=302)  # 302 redirect to login expected

        # Now check that logged-in users without CourseCreator role cannot create libraries
        self._login_as_non_staff_user(logout_first=False)
        with patch.dict('django.conf.settings.FEATURES',
                        {'ENABLE_CREATOR_GROUP': True}):
            self._assert_cannot_create_library(
                expected_code=403)  # 403 user is not CourseCreator

        # Now check that logged-in users with CourseCreator role can create libraries
        add_user_with_status_granted(self.user, self.non_staff_user)
        with patch.dict('django.conf.settings.FEATURES',
                        {'ENABLE_CREATOR_GROUP': True}):
            lib_key2 = self._create_library(library="lib2",
                                            display_name="Test Library 2")
            library2 = modulestore().get_library(lib_key2)
            self.assertIsNotNone(library2)
Beispiel #20
0
    def setUpClass(cls):
        super().setUpClass()
        cls.store = modulestore()
        cls.course = ToyCourseFactory.create(
            end=datetime(2028, 1, 1, 1, 1, 1),
            enrollment_start=datetime(2020, 1, 1, 1, 1, 1),
            enrollment_end=datetime(2028, 1, 1, 1, 1, 1),
            emit_signals=True,
            modulestore=cls.store,
            certificate_available_date=_NEXT_WEEK,
            certificates_display_behavior=CertificatesDisplayBehaviors.
            END_WITH_DATE)
        cls.chapter = ItemFactory(parent=cls.course, category='chapter')
        cls.sequence = ItemFactory(parent=cls.chapter,
                                   category='sequential',
                                   display_name='sequence')
        cls.unit = ItemFactory.create(parent=cls.sequence,
                                      category='vertical',
                                      display_name="Vertical")

        cls.user = UserFactory(username='******',
                               email='*****@*****.**',
                               password='******',
                               is_staff=False)
        cls.instructor = UserFactory(username='******',
                                     email='*****@*****.**',
                                     password='******',
                                     is_staff=False)
        CourseInstructorRole(cls.course.id).add_users(cls.instructor)
        cls.url = f'/api/courseware/course/{cls.course.id}'
Beispiel #21
0
class RoleCacheTestCase(TestCase):

    IN_KEY = CourseKey.from_string('edX/toy/2012_Fall')
    NOT_IN_KEY = CourseKey.from_string('edX/toy/2013_Fall')

    ROLES = (
        (CourseStaffRole(IN_KEY), ('staff', IN_KEY, 'edX')),
        (CourseInstructorRole(IN_KEY), ('instructor', IN_KEY, 'edX')),
        (OrgStaffRole(IN_KEY.org), ('staff', None, 'edX')),
        (OrgInstructorRole(IN_KEY.org), ('instructor', None, 'edX')),
        (CourseBetaTesterRole(IN_KEY), ('beta_testers', IN_KEY, 'edX')),
    )

    def setUp(self):
        super(RoleCacheTestCase, self).setUp()
        self.user = UserFactory()

    @ddt.data(*ROLES)
    @ddt.unpack
    def test_only_in_role(self, role, target):
        role.add_users(self.user)
        cache = RoleCache(self.user)
        self.assertTrue(cache.has_role(*target))

        for other_role, other_target in self.ROLES:
            if other_role == role:
                continue

            self.assertFalse(cache.has_role(*other_target))

    @ddt.data(*ROLES)
    @ddt.unpack
    def test_empty_cache(self, role, target):
        cache = RoleCache(self.user)
        self.assertFalse(cache.has_role(*target))
    def test_masquerade_in_holdback(self, mock_get_course_run_details):
        mock_get_course_run_details.return_value = {'weeks_to_complete': 12}
        audit_student = UserFactory(username='******')
        enrollment = CourseEnrollmentFactory.create(user=audit_student,
                                                    course_id=self.course.id,
                                                    mode='audit')
        FBEEnrollmentExclusion.objects.create(enrollment=enrollment)
        CourseDurationLimitConfig.objects.create(
            enabled=True,
            course=CourseOverview.get_from_id(self.course.id),
            enabled_as_of=self.course.start,
        )

        instructor = UserFactory.create(username='******')
        enrollment = CourseEnrollmentFactory.create(user=instructor,
                                                    course_id=self.course.id,
                                                    mode='audit')
        CourseInstructorRole(self.course.id).add_users(instructor)
        self.client.login(username=instructor.username, password='******')

        self.update_masquerade(username='******')

        response = self.get_courseware()
        assert response.status_code == 200
        self.assertCountEqual(response.redirect_chain, [])
        banner_text = 'You lose all access to this course, including your progress,'
        self.assertNotContains(response, banner_text)
    def test_masquerade_expired(self, mock_get_course_run_details):
        mock_get_course_run_details.return_value = {'weeks_to_complete': 1}

        audit_student = UserFactory(username='******')
        enrollment = CourseEnrollmentFactory.create(
            user=audit_student,
            course_id=self.course.id,
            mode='audit',
        )
        enrollment.created = self.course.start
        enrollment.save()
        CourseDurationLimitConfig.objects.create(
            enabled=True,
            course=CourseOverview.get_from_id(self.course.id),
            enabled_as_of=self.course.start,
        )

        instructor = UserFactory.create(username='******')
        CourseEnrollmentFactory.create(user=instructor,
                                       course_id=self.course.id,
                                       mode='audit')
        CourseInstructorRole(self.course.id).add_users(instructor)
        self.client.login(username=instructor.username, password='******')

        self.update_masquerade(username='******')

        response = self.get_courseware()
        assert response.status_code == 200
        self.assertCountEqual(response.redirect_chain, [])
        banner_text = 'This learner does not have access to this course. Their access expired on'
        self.assertContains(response, banner_text)
Beispiel #24
0
def user_has_role(user, role):
    """
    Check whether this user has access to this role (either direct or implied)
    :param user:
    :param role: an AccessRole
    """
    if not user.is_active:
        return False
    # Do cheapest check first even though it's not the direct one
    if GlobalStaff().has_user(user):
        return True
    # CourseCreator is odd b/c it can be disabled via config
    if isinstance(role, CourseCreatorRole):
        # completely shut down course creation setting
        if settings.FEATURES.get('DISABLE_COURSE_CREATION', False):
            return False
        # wide open course creation setting
        if not settings.FEATURES.get('ENABLE_CREATOR_GROUP', False):
            return True

    if role.has_user(user):
        return True
    # If not, then check inferred permissions
    if (isinstance(role, (CourseStaffRole, CourseBetaTesterRole)) and
            CourseInstructorRole(role.course_key).has_user(user)):
        return True

    return False
Beispiel #25
0
def get_user_permissions(user, course_key, org=None):
    """
    Get the bitmask of permissions that this user has in the given course context.
    Can also set course_key=None and pass in an org to get the user's
    permissions for that organization as a whole.
    """
    if org is None:
        org = course_key.org
        course_key = course_key.for_branch(None)
    else:
        assert course_key is None
    # No one has studio permissions for CCX courses
    if is_ccx_course(course_key):
        return STUDIO_NO_PERMISSIONS
    all_perms = STUDIO_EDIT_ROLES | STUDIO_VIEW_USERS | STUDIO_EDIT_CONTENT | STUDIO_VIEW_CONTENT
    # global staff, org instructors, and course instructors have all permissions:
    if GlobalStaff().has_user(user) or OrgInstructorRole(org=org).has_user(user):
        return all_perms
    if course_key and user_has_role(user, CourseInstructorRole(course_key)):
        return all_perms
    # Staff have all permissions except EDIT_ROLES:
    if OrgStaffRole(org=org).has_user(user) or (course_key and user_has_role(user, CourseStaffRole(course_key))):
        return STUDIO_VIEW_USERS | STUDIO_EDIT_CONTENT | STUDIO_VIEW_CONTENT
    # Otherwise, for libraries, users can view only:
    if course_key and isinstance(course_key, LibraryLocator):
        if OrgLibraryUserRole(org=org).has_user(user) or user_has_role(user, LibraryUserRole(course_key)):
            return STUDIO_VIEW_USERS | STUDIO_VIEW_CONTENT
    return STUDIO_NO_PERMISSIONS
Beispiel #26
0
    def test_enrollment_limit(self):
        """
        Assert that in a course with max student limit set to 1, we can enroll staff and instructor along with
        student. To make sure course full check excludes staff and instructors.
        """
        assert self.course_limited.max_student_enrollments_allowed == 1
        user1 = UserFactory.create(username="******", email="*****@*****.**", password="******")
        user2 = UserFactory.create(username="******", email="*****@*****.**", password="******")

        # create staff on course.
        staff = UserFactory.create(username="******", email="*****@*****.**", password="******")
        role = CourseStaffRole(self.course_limited.id)
        role.add_users(staff)

        # create instructor on course.
        instructor = UserFactory.create(username="******", email="*****@*****.**", password="******")
        role = CourseInstructorRole(self.course_limited.id)
        role.add_users(instructor)

        CourseEnrollment.enroll(staff, self.course_limited.id, check_access=True)
        CourseEnrollment.enroll(instructor, self.course_limited.id, check_access=True)

        assert CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=staff).exists()

        assert CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=instructor).exists()

        CourseEnrollment.enroll(user1, self.course_limited.id, check_access=True)
        assert CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=user1).exists()

        with pytest.raises(CourseFullError):
            CourseEnrollment.enroll(user2, self.course_limited.id, check_access=True)

        assert not CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=user2).exists()
Beispiel #27
0
    def test_add_master_course_staff_to_ccx(self):
        """
        Test add staff of master course to ccx course
        """
        # adding staff to master course.
        staff = self.make_staff()
        assert CourseStaffRole(self.course.id).has_user(staff)

        # adding instructor to master course.
        instructor = self.make_instructor()
        assert CourseInstructorRole(self.course.id).has_user(instructor)

        add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                       self.ccx.display_name)

        # assert that staff and instructors of master course has staff and instructor roles on ccx
        list_staff_master_course = list_with_level(self.course.id, 'staff')
        list_instructor_master_course = list_with_level(
            self.course.id, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx.id, 'staff')
            assert len(list_staff_master_course) == len(list_staff_ccx_course)
            assert list_staff_master_course[0].email == list_staff_ccx_course[
                0].email

            list_instructor_ccx_course = list_with_level(
                course_ccx.id, 'instructor')
            assert len(list_instructor_ccx_course) == len(
                list_instructor_master_course)
            assert list_instructor_ccx_course[
                0].email == list_instructor_master_course[0].email
Beispiel #28
0
    def test_remove_master_course_staff_from_ccx_display_name(self):
        """
        Test remove role of staff of master course on ccx course.
        Specific test to check that a passed display name is in the
        subject of the email sent to the unenrolled users.
        """
        staff = self.make_staff()
        assert CourseStaffRole(self.course.id).has_user(staff)

        # adding instructor to master course.
        instructor = self.make_instructor()
        assert CourseInstructorRole(self.course.id).has_user(instructor)
        outbox = self.get_outbox()
        add_master_course_staff_to_ccx(self.course,
                                       self.ccx_locator,
                                       self.ccx.display_name,
                                       send_email=False)
        # create a unique display name
        display_name = f'custom_display_{uuid.uuid4()}'
        list_staff_master_course = list_with_level(self.course.id, 'staff')
        list_instructor_master_course = list_with_level(
            self.course.id, 'instructor')
        assert len(outbox) == 0
        # give access to the course staff/instructor
        remove_master_course_staff_from_ccx(self.course, self.ccx_locator,
                                            display_name)
        assert len(outbox) == (len(list_staff_master_course) +
                               len(list_instructor_master_course))
        for email in outbox:
            assert display_name in email.subject
Beispiel #29
0
    def test_add_master_course_staff_to_ccx_with_exception(self):
        """
        When exception raise from ``enroll_email`` assert that enrollment skipped for that staff or
        instructor.
        """
        staff = self.make_staff()
        assert CourseStaffRole(self.course.id).has_user(staff)

        # adding instructor to master course.
        instructor = self.make_instructor()
        assert CourseInstructorRole(self.course.id).has_user(instructor)

        with mock.patch.object(CourseEnrollment,
                               'enroll_by_email',
                               side_effect=CourseEnrollmentException()):
            add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                           self.ccx.display_name)

            assert not CourseEnrollment.objects.filter(
                course_id=self.ccx_locator, user=staff).exists()
            assert not CourseEnrollment.objects.filter(
                course_id=self.ccx_locator, user=instructor).exists()

        with mock.patch.object(CourseEnrollment,
                               'enroll_by_email',
                               side_effect=SMTPException()):
            add_master_course_staff_to_ccx(self.course, self.ccx_locator,
                                           self.ccx.display_name)

            assert not CourseEnrollment.objects.filter(
                course_id=self.ccx_locator, user=staff).exists()
            assert not CourseEnrollment.objects.filter(
                course_id=self.ccx_locator, user=instructor).exists()
Beispiel #30
0
    def test_remove_master_course_staff_from_ccx_idempotent(self):
        """
        Test remove staff of master course from ccx course
        """
        staff = self.make_staff()
        self.assertTrue(CourseStaffRole(self.course.id).has_user(staff))

        # adding instructor to master course.
        instructor = self.make_instructor()
        self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))

        outbox = self.get_outbox()
        self.assertEqual(len(outbox), 0)
        add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False)

        list_staff_master_course = list_with_level(self.course, 'staff')
        list_instructor_master_course = list_with_level(self.course, 'instructor')

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course))
            self.assertEqual(list_staff_master_course[0].email, list_staff_ccx_course[0].email)

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))
            self.assertEqual(list_instructor_ccx_course[0].email, list_instructor_master_course[0].email)

            # assert that role of staff and instructors of master course removed from ccx.
            remove_master_course_staff_from_ccx(
                self.course, self.ccx_locator, self.ccx.display_name, send_email=True
            )
            self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))

            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)

        # Run again
        remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name)
        self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course))

        with ccx_course(self.ccx_locator) as course_ccx:
            list_staff_ccx_course = list_with_level(course_ccx, 'staff')
            self.assertNotEqual(len(list_staff_master_course), len(list_staff_ccx_course))

            list_instructor_ccx_course = list_with_level(course_ccx, 'instructor')
            self.assertNotEqual(len(list_instructor_ccx_course), len(list_instructor_master_course))

            for user in list_staff_master_course:
                self.assertNotIn(user, list_staff_ccx_course)
            for user in list_instructor_master_course:
                self.assertNotIn(user, list_instructor_ccx_course)