def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() org, course_num, _ = course_id.split("/") module_store.ignore_write_events_on_courses.append('{0}/{1}'.format( org, course_num)) loc = CourseDescriptor.id_to_location(course_id) if delete_course(module_store, content_store, loc, commit): print 'removing forums permissions and roles...' unseed_permissions_roles(course_id) print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(loc) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(loc) instructor_role.remove_users( *instructor_role.users_with_role()) except Exception as err: log.error( "Error in deleting course groups for {0}: {1}".format( loc, err))
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() course_id_dict = Location.parse_course_id(course_id) module_store.ignore_write_events_on_courses.append('{org}/{course}'.format(**course_id_dict)) loc = CourseDescriptor.id_to_location(course_id) if delete_course(module_store, content_store, loc, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(loc) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(loc) instructor_role.remove_users(*instructor_role.users_with_role()) except Exception as err: log.error("Error in deleting course groups for {0}: {1}".format(loc, err)) # remove location of this course from loc_mapper and cache loc_mapper().delete_course_mapping(loc)
def test_transcript_delete_handler(self, is_staff, is_course_staff): """ Tests that transcript delete handler works as expected with combinations of staff and course's staff. """ # Setup user's roles self.user.is_staff = is_staff self.user.save() course_staff_role = CourseStaffRole(self.course.id) if is_course_staff: course_staff_role.add_users(self.user) else: course_staff_role.remove_users(self.user) # Assert the user role self.assertEqual(self.user.is_staff, is_staff) self.assertEqual(CourseStaffRole(self.course.id).has_user(self.user), is_course_staff) video_id, language_code = u'1234', u'en' # Create a real transcript in VAL. api.create_or_update_video_transcript( video_id=video_id, language_code=language_code, metadata={'file_format': 'srt'} ) # Make request to transcript deletion handler response = self.client.delete(self.get_url_for_course_key( self.course.id, edx_video_id=video_id, language_code=language_code )) self.assertEqual(response.status_code, 200) self.assertFalse(api.get_video_transcript_data(video_id=video_id, language_code=language_code))
def test_get_user_for_role(self): """ test users_for_role """ role = CourseStaffRole(self.course_key) role.add_users(self.student) self.assertGreater(len(role.users_with_role()), 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())
def make_staff(self): """ create staff user """ staff = AdminFactory.create(password="******") role = CourseStaffRole(self.course.id) role.add_users(staff) return staff
def remove_all_instructors(course_key): """ Removes given user as instructor and staff to the given course, after verifying that the requesting_user has permission to do so. """ 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())
def test_add_users_doesnt_add_duplicate_entry(self): """ Tests that calling add_users multiple times before a single call to remove_users does not result in the user remaining in the group. """ role = CourseStaffRole(self.course_key) role.add_users(self.student) self.assertTrue(role.has_user(self.student)) # Call add_users a second time, then remove just once. role.add_users(self.student) role.remove_users(self.student) self.assertFalse(role.has_user(self.student))
def assign_staff_role_to_ccx(ccx_locator, user, master_course_id): """ Check if user has ccx_coach role on master course then assign him staff role on ccx only if role is not already assigned. Because of this coach can open dashboard from master course as well as ccx. :param ccx_locator: CCX key :param user: User to whom we want to assign role. :param master_course_id: Master course key """ coach_role_on_master_course = CourseCcxCoachRole(master_course_id) # check if user has coach role on master course if coach_role_on_master_course.has_user(user): # Check if user has staff role on ccx. role = CourseStaffRole(ccx_locator) if not role.has_user(user): # assign user the staff role on ccx with ccx_course(ccx_locator) as course: allow_access(course, user, "staff", send_email=False)
def delete_course_and_groups(course_id, user_id): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore() with module_store.bulk_write_operations(course_id): module_store.delete_course(course_id, user_id) print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course try: staff_role = CourseStaffRole(course_id) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(course_id) instructor_role.remove_users(*instructor_role.users_with_role()) except Exception as err: log.error("Error in deleting course groups for {0}: {1}".format(course_id, err))
def delete_course_and_groups(course_id, commit=False): """ This deletes the courseware associated with a course_id as well as cleaning update_item the various user table stuff (groups, permissions, etc.) """ module_store = modulestore('direct') content_store = contentstore() module_store.ignore_write_events_on_courses.add(course_id) if delete_course(module_store, content_store, course_id, commit): print 'removing User permissions from course....' # in the django layer, we need to remove all the user permissions groups associated with this course if commit: try: staff_role = CourseStaffRole(course_id) staff_role.remove_users(*staff_role.users_with_role()) instructor_role = CourseInstructorRole(course_id) instructor_role.remove_users(*instructor_role.users_with_role()) except Exception as err: log.error("Error in deleting course groups for {0}: {1}".format(course_id, err))
def create_staff_context(self): """ Create staff user and course blocks accessible by that user """ # Create a staff user to be able to test visible_to_staff_only staff_user = UserFactory.create() CourseStaffRole(self.course.location.course_key).add_users(staff_user) block_structure = get_course_blocks( staff_user, self.course.location, self.transformers, ) return { 'request': MagicMock(), 'block_structure': block_structure, 'requested_fields': ['type'], }
def setUp(self): clear_existing_modulestores() self.toy = modulestore().get_course(SlashSeparatedCourseKey("edX", "toy", "2012_Fall")) # Create two accounts self.student = '*****@*****.**' self.instructor = '*****@*****.**' self.password = '******' self.create_account('u1', self.student, self.password) self.create_account('u2', self.instructor, self.password) self.activate_user(self.student) self.activate_user(self.instructor) CourseStaffRole(self.toy.id).add_users(User.objects.get(email=self.instructor)) self.logout() self.login(self.instructor, self.password) self.enroll(self.toy)
def test_render_html_view_with_preview_mode_when_user_already_has_cert( self): """ test certificate web view should render properly in preview mode even if user who is previewing already has a certificate generated with different mode. """ self._add_course_certificates(count=1, signatory_count=2) CourseStaffRole(self.course.id).add_users(self.user) test_url = get_certificate_url(user_id=self.user.id, course_id=unicode(self.course.id)) # user has already has certificate generated for 'honor' mode # so let's try to preview in 'verified' mode. response = self.client.get(test_url + '?preview=verified') self.assertNotIn(self.course.display_name, response.content) self.assertIn('course_title_0', response.content) self.assertIn('Signatory_Title 0', response.content)
def setUp(self): """ Set up tests """ super(TestCoachDashboard, self).setUp() # Login with the instructor account self.client.login(username=self.coach.username, password="******") # adding staff to master course. staff = UserFactory() allow_access(self.course, staff, 'staff') self.assertTrue(CourseStaffRole(self.course.id).has_user(staff)) # adding instructor to master course. instructor = UserFactory() allow_access(self.course, instructor, 'instructor') self.assertTrue( CourseInstructorRole(self.course.id).has_user(instructor))
def test_authorization_no_oauth_staff(self): """ Check authorization for staff users logged in without oauth """ # create a staff user staff_user = User.objects.create_user('test_staff_user', '*****@*****.**', 'test') # add staff role to the staff user CourseStaffRole(self.master_course_key).add_users(staff_user) data = {'display_name': 'CCX Title'} # the staff user can perform the request self.client.login(username=staff_user.username, password='******') resp = self.client.get(self.detail_url) self.assertEqual(resp.status_code, status.HTTP_200_OK) resp = self.client.patch(self.detail_url, data, format='json') self.assertEqual(resp.status_code, status.HTTP_204_NO_CONTENT)
def manage_library_users(request, library_key_string): """ Studio UI for editing the users within a library. Uses the /course_team/:library_key/:user_email/ REST API to make changes. """ library_key = CourseKey.from_string(library_key_string) if not isinstance(library_key, LibraryLocator): raise Http404 # This is not a library user_perms = get_user_permissions(request.user, library_key) if not user_perms & STUDIO_VIEW_USERS: raise PermissionDenied() library = modulestore().get_library(library_key) if library is None: raise Http404 # Segment all the users explicitly associated with this library, ensuring each user only has one role listed: instructors = set(CourseInstructorRole(library_key).users_with_role()) staff = set(CourseStaffRole(library_key).users_with_role()) - instructors users = set( LibraryUserRole(library_key).users_with_role()) - instructors - staff all_users = instructors | staff | users return render_to_response( 'manage_users_lib.html', { 'context_library': library, 'staff': staff, 'instructors': instructors, 'users': users, 'all_users': all_users, 'allow_actions': bool(user_perms & STUDIO_EDIT_ROLES), 'library_key': unicode(library_key), 'lib_users_url': reverse_library_url('manage_library_users', library_key_string), 'show_children_previews': library.show_children_previews })
def _has_access_to_course(user, access_level, course_key): """ Returns True if the given user has access_level (= staff or instructor) access to the course with the given course_key. This ensures the user is authenticated and checks if global staff or has staff / instructor access. access_level = string, either "staff" or "instructor" """ if user is None or (not user.is_authenticated()): debug("Deny: no user or anon user") return ACCESS_DENIED if is_masquerading_as_student(user, course_key): return ACCESS_DENIED if GlobalStaff().has_user(user): debug("Allow: user.is_staff") return ACCESS_GRANTED if access_level not in ('staff', 'instructor'): log.debug("Error in access._has_access_to_course access_level=%s unknown", access_level) debug("Deny: unknown access level") return ACCESS_DENIED staff_access = ( CourseStaffRole(course_key).has_user(user) or OrgStaffRole(course_key.org).has_user(user) ) if staff_access and access_level == 'staff': debug("Allow: user has course staff access") return ACCESS_GRANTED instructor_access = ( CourseInstructorRole(course_key).has_user(user) or OrgInstructorRole(course_key.org).has_user(user) ) if instructor_access and access_level in ('staff', 'instructor'): debug("Allow: user has course instructor access") return ACCESS_GRANTED debug("Deny: user did not have correct access") return ACCESS_DENIED
def get(self, request): """Displays course Enrollment and staffing course statistics""" if not request.user.is_staff: raise Http404 data = [] courses = self.get_courses() for (cdir, course) in courses.items(): # pylint: disable=unused-variable datum = [course.display_name, course.id] datum += [ CourseEnrollment.objects.filter(course_id=course.id).count() ] datum += [ CourseStaffRole(course.location).users_with_role().count() ] datum += [ ','.join([ x.username for x in CourseInstructorRole( course.location).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' }, 'mitx_version': getattr(settings, 'VERSION_STRING', ''), } return render_to_response(self.template_name, context)
def test_data(self): start = datetime.datetime.now(pytz.UTC) end = start + datetime.timedelta(days=30) enrollment_start = start - datetime.timedelta(days=7) enrollment_end = end - datetime.timedelta(days=14) course = CourseFactory(start=start, end=end, enrollment_start=enrollment_start, enrollment_end=enrollment_end) instructor = UserFactory() CourseInstructorRole(course.id).add_users(instructor) staff = UserFactory() CourseStaffRole(course.id).add_users(staff) request = RequestFactory().get('') serializer = CourseRunSerializer(course, context={'request': request}) expected = { 'id': str(course.id), 'title': course.display_name, 'schedule': { 'start': serialize_datetime(start), 'end': serialize_datetime(end), 'enrollment_start': serialize_datetime(enrollment_start), 'enrollment_end': serialize_datetime(enrollment_end), }, 'team': [ { 'user': instructor.username, 'role': 'instructor', }, { 'user': staff.username, 'role': 'staff', }, ], 'images': { 'card_image': request.build_absolute_uri(course_image_url(course)), } } assert serializer.data == expected
def test_remove_master_course_staff_from_ccx_no_email(self): """ Test remove role of staff of master course on ccx course without sending enrollment email. """ 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) remove_master_course_staff_from_ccx(self.course, self.ccx_locator, self.ccx.display_name, send_email=False) self.assertEqual(len(outbox), 0)
def test_add_master_course_staff_to_ccx_idempotent(self): """ Test add staff of master course to ccx course multiple time will not result in multiple enrollments. """ 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() 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) # run the assignment the first time add_master_course_staff_to_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') list_instructor_ccx_course = list_with_level(course_ccx, 'instructor') self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course)) for user in list_staff_master_course: self.assertIn(user, list_staff_ccx_course) self.assertEqual(len(list_instructor_master_course), len(list_instructor_ccx_course)) for user in list_instructor_master_course: self.assertIn(user, list_instructor_ccx_course) # run the assignment again add_master_course_staff_to_ccx(self.course, self.ccx_locator, self.ccx.display_name) # there are no new duplicated email self.assertEqual(len(outbox), len(list_staff_master_course) + len(list_instructor_master_course)) # there are no duplicated staffs with ccx_course(self.ccx_locator) as course_ccx: list_staff_ccx_course = list_with_level(course_ccx, 'staff') list_instructor_ccx_course = list_with_level(course_ccx, 'instructor') self.assertEqual(len(list_staff_master_course), len(list_staff_ccx_course)) for user in list_staff_master_course: self.assertIn(user, list_staff_ccx_course) self.assertEqual(len(list_instructor_master_course), len(list_instructor_ccx_course)) for user in list_instructor_master_course: self.assertIn(user, list_instructor_ccx_course)
def _get_recipient_queryset(user_id, to_option, course_id): """ Returns a query set of email recipients corresponding to the requested to_option category. `to_option` is either SEND_TO_MYSELF, SEND_TO_STAFF, or SEND_TO_ALL. Recipients who are in more than one category (e.g. enrolled in the course and are staff or self) will be properly deduped. """ if to_option not in TO_OPTIONS: log.error("Unexpected bulk email TO_OPTION found: %s", to_option) raise Exception( "Unexpected bulk email TO_OPTION found: {0}".format(to_option)) if to_option == SEND_TO_MYSELF: recipient_qset = User.objects.filter(id=user_id) else: staff_qset = CourseStaffRole(course_id).users_with_role() instructor_qset = CourseInstructorRole(course_id).users_with_role() recipient_qset = (staff_qset | instructor_qset).distinct() if to_option == SEND_TO_ALL: # We also require students to have activated their accounts to # provide verification that the provided email address is valid. enrollment_qset = User.objects.filter( is_active=True, courseenrollment__course_id=course_id, courseenrollment__is_active=True) # Now we do some queryset sidestepping to avoid doing a DISTINCT # query across the course staff and the enrolled students, which # forces the creation of a temporary table in the db. unenrolled_staff_qset = recipient_qset.exclude( courseenrollment__course_id=course_id, courseenrollment__is_active=True) # use read_replica if available: unenrolled_staff_qset = use_read_replica_if_available( unenrolled_staff_qset) unenrolled_staff_ids = [user.id for user in unenrolled_staff_qset] recipient_qset = enrollment_qset | User.objects.filter( id__in=unenrolled_staff_ids) # again, use read_replica if available to lighten the load for large queries return use_read_replica_if_available(recipient_qset)
def setUp(self): super(TestStaffOnCCX, self).setUp() # Create instructor account self.client.login(username=self.coach.username, password="******") # create an instance of modulestore self.mstore = modulestore() # adding staff to master course. staff = UserFactory() allow_access(self.course, staff, 'staff') self.assertTrue(CourseStaffRole(self.course.id).has_user(staff)) # adding instructor to master course. instructor = UserFactory() allow_access(self.course, instructor, 'instructor') self.assertTrue( CourseInstructorRole(self.course.id).has_user(instructor))
def setUp(self): """ Add courses with the end date set to various values """ super(TestCourseIndexArchived, self).setUp() # Base course has no end date (so is active) self.course.end = None self.course.display_name = 'Active Course 1' self.ORG = self.course.location.org self.save_course() # Active course has end date set to tomorrow self.active_course = CourseFactory.create( display_name='Active Course 2', org=self.ORG, end=self.TOMORROW, ) # Archived course has end date set to yesterday self.archived_course = CourseFactory.create( display_name='Archived Course', org=self.ORG, end=self.YESTERDAY, ) # Base user has global staff access self.assertTrue(GlobalStaff().has_user(self.user)) # Staff user just has course staff access self.staff, self.staff_password = self.create_non_staff_user() for course in (self.course, self.active_course, self.archived_course): CourseStaffRole(course.id).add_users(self.staff) # Make sure we've cached data which could change the query counts # depending on test execution order WaffleSwitchNamespace(name=COURSE_WAFFLE_NAMESPACE).is_enabled( u'enable_global_staff_optimization') WaffleSwitchNamespace( name=STUDIO_WAFFLE_NAMESPACE).is_enabled(u'enable_policy_page') WaffleSwitchNamespace(name=DJANGO_UTILS_NAMESPACE).is_enabled( u'enable_memory_middleware')
def setUpClass(cls): super(TeamAccessTests, cls).setUpClass() cls.user_audit = UserFactory.create(username='******') cls.user_staff = UserFactory.create(username='******') cls.user_masters = UserFactory.create(username='******') cls.user_unenrolled = UserFactory.create(username='******') cls.users = { 'user_audit': cls.user_audit, 'user_staff': cls.user_staff, 'user_masters': cls.user_masters, 'user_unenrolled': cls.user_unenrolled, } for user in (cls.user_audit, cls.user_staff): CourseEnrollmentFactory.create(user=user, course_id=COURSE_KEY1) CourseEnrollmentFactory.create(user=cls.user_masters, course_id=COURSE_KEY1, mode=CourseMode.MASTERS) CourseStaffRole(COURSE_KEY1).add_users(cls.user_staff) cls.topic_id = 'RANDOM TOPIC' cls.team_unprotected_1 = CourseTeamFactory( course_id=COURSE_KEY1, topic_id=cls.topic_id, team_id='team_unprotected_1') cls.team_unprotected_2 = CourseTeamFactory( course_id=COURSE_KEY1, topic_id=cls.topic_id, team_id='team_unprotected_2') cls.team_unprotected_3 = CourseTeamFactory( course_id=COURSE_KEY1, topic_id=cls.topic_id, team_id='team_unprotected_3') cls.team_protected_1 = CourseTeamFactory(course_id=COURSE_KEY1, team_id='team_protected_1', topic_id=cls.topic_id, organization_protected=True) cls.team_protected_2 = CourseTeamFactory(course_id=COURSE_KEY1, team_id='team_protected_2', topic_id=cls.topic_id, organization_protected=True)
def setUp(self): super(TestEolCourseEmailView, self).setUp() # create a course self.course = CourseFactory.create(org='mss', course='999', display_name='eol course email') # Create users, enroll self.users = [UserFactory.create() for _ in range(USER_COUNT)] for user in self.users: CourseEnrollmentFactory.create(user=user, course_id=self.course.id) # Patch the comment client user save method so it does not try # to create a new cc user when creating a django user with patch('student.models.cc.User.save'): # Create the student self.student = UserFactory(username='******', password='******', email='*****@*****.**') # Enroll the student in the course CourseEnrollmentFactory(user=self.student, course_id=self.course.id) # Create and Enroll staff user self.staff_user = UserFactory(username='******', password='******', email='*****@*****.**', is_staff=True) CourseEnrollmentFactory(user=self.staff_user, course_id=self.course.id) CourseStaffRole(self.course.id).add_users(self.staff_user) # Log the student in self.client = Client() self.assertTrue( self.client.login(username='******', password='******')) # Log the user staff in self.staff_client = Client() self.assertTrue( self.staff_client.login(username='******', password='******'))
def test_staff_csv(self): """Download and validate staff CSV""" self._setstaff_login() self._add_edx4edx() def_ms = modulestore() course = def_ms.get_course(SlashSeparatedCourseKey('MITx', 'edx4edx', 'edx4edx')) CourseStaffRole(course.id).add_users(self.user) response = self.client.post(reverse('sysadmin_staffing'), {'action': 'get_staff_csv', }) self.assertIn('attachment', response['Content-Disposition']) self.assertEqual('text/csv', response['Content-Type']) columns = [_('course_id'), _('role'), _('username'), _('email'), _('full_name'), ] self.assertIn(','.join('"' + c + '"' for c in columns), response.content) self._rm_edx4edx()
def setUp(self): self.toy = CourseFactory.create(org='edX', course='toy', display_name='2012_Fall') # Create two accounts self.student = '*****@*****.**' self.instructor = '*****@*****.**' self.password = '******' self.create_account('u1', self.student, self.password) self.create_account('u2', self.instructor, self.password) self.activate_user(self.student) self.activate_user(self.instructor) CourseStaffRole(self.toy.id).add_users( User.objects.get(email=self.instructor)) self.logout() self.login(self.instructor, self.password) self.enroll(self.toy)
def test_course_staff_courses_with_claims(self): CourseStaffRole(self.course_key).add_users(self.user) course_id = unicode(self.course_key) nonexistent_course_id = 'some/other/course' claims = { 'staff_courses': { 'values': [course_id, nonexistent_course_id], 'essential': True, } } scopes, claims = self.get_id_token_values(scope='openid course_staff', claims=claims) self.assertIn('course_staff', scopes) self.assertIn('staff_courses', claims) self.assertEqual(len(claims['staff_courses']), 1) self.assertIn(course_id, claims['staff_courses']) self.assertNotIn(nonexistent_course_id, claims['staff_courses'])
def setUp(self): """ Set up a simple course for testing basic grading functionality. """ super(TestRawGradeCSV, self).setUp() self.instructor = '*****@*****.**' self.student_user2 = self.create_account('u2', self.instructor, self.password) self.activate_user(self.instructor) CourseStaffRole(self.course.id).add_users(User.objects.get(email=self.instructor)) self.logout() self.login(self.instructor, self.password) self.enroll(self.course) # set up a simple course with four problems self.homework = self.add_graded_section_to_course('homework', late=False, reset=False, showanswer=False) self.add_dropdown_to_section(self.homework.location, 'p1', 1) self.add_dropdown_to_section(self.homework.location, 'p2', 1) self.add_dropdown_to_section(self.homework.location, 'p3', 1) self.refresh_course()
def test_access_course_team_users(self): """ Test that members of the course team do not lose access to graded content """ # There are two types of course team members: instructor and staff # they have different privileges, but for the purpose of this test the important thing is that they should both # have access to all graded content instructor = UserFactory.create() CourseInstructorRole(self.course.id).add_users(instructor) staff = UserFactory.create() CourseStaffRole(self.course.id).add_users(staff) # assert that all course team members have access to graded content for course_team_member in [instructor, staff]: self._assert_block_is_gated( block=self.blocks_dict['problem'], user_id=course_team_member.id, course=self.course, is_gated=False )
def has_team_api_access(user, course_key, access_username=None): """Returns True if the user has access to the Team API for the course given by `course_key`. The user must either be enrolled in the course, be course staff, or be global staff. Args: user (User): The user to check access for. course_key (CourseKey): The key to the course which we are checking access to. access_username (string): If provided, access_username must match user.username for non staff access. Returns: bool: True if the user has access, False otherwise. """ if user.is_staff: return True if CourseStaffRole(course_key).has_user(user): return True if not access_username or access_username == user.username: return CourseEnrollment.is_enrolled(user, course_key) return False
def _manage_users(request, locator): """ This view will return all CMS users who are editors for the specified course """ old_location = loc_mapper().translate_locator_to_location(locator) # check that logged in user has permissions to this item if not has_course_access(request.user, locator): raise PermissionDenied() course_module = modulestore().get_item(old_location) instructors = CourseInstructorRole(locator).users_with_role() # the page only lists staff and assumes they're a superset of instructors. Do a union to ensure. staff = set(CourseStaffRole(locator).users_with_role()).union(instructors) return render_to_response('manage_users.html', { 'context_course': course_module, 'staff': staff, 'instructors': instructors, 'allow_actions': has_course_access(request.user, locator, role=CourseInstructorRole), })
def test_errored_course_regular_access(self): """ Test the course list for regular staff when get_course returns an ErrorDescriptor """ GlobalStaff().remove_users(self.user) CourseStaffRole(SlashSeparatedCourseKey('Non', 'Existent', 'Course')).add_users(self.user) course_key = SlashSeparatedCourseKey('Org1', 'Course1', 'Run1') self._create_course_with_access_groups(course_key, self.user) with patch('xmodule.modulestore.mongo.base.MongoKeyValueStore', Mock(side_effect=Exception)): self.assertIsInstance(modulestore().get_course(course_key), ErrorDescriptor) # get courses through iterating all courses courses_list, __ = _accessible_courses_list(self.request) self.assertEqual(courses_list, []) # get courses by reversing group name formats courses_list_by_groups, __ = _accessible_courses_list_from_groups(self.request) self.assertEqual(courses_list_by_groups, []) self.assertEqual(courses_list, courses_list_by_groups)
def has_permission(self, request, view): """ This method is assuming that a `master_course_id` parameter is available in the request as a GET parameter, a POST parameter or it is in the JSON payload included in the request. The reason is because this permission class is going to check if the user making the request is an instructor for the specified course. """ master_course_id = (request.GET.get('master_course_id') or request.POST.get('master_course_id') or request.data.get('master_course_id')) if master_course_id is not None: try: course_key = CourseKey.from_string(master_course_id) except InvalidKeyError: raise Http404() return (hasattr(request, 'user') and (CourseInstructorRole(course_key).has_user(request.user) or CourseStaffRole(course_key).has_user(request.user))) return False
def get_num_enrolled_in_exclude_admins(course_id, date_for): """ Copied over from CourseEnrollmentManager.num_enrolled_in_exclude_admins method and modified to filter on date LT """ course_locator = course_id if getattr(course_id, 'ccx', None): course_locator = course_id.to_course_locator() staff = CourseStaffRole(course_locator).users_with_role() admins = CourseInstructorRole(course_locator).users_with_role() coaches = CourseCcxCoachRole(course_locator).users_with_role() return CourseEnrollment.objects.filter( course_id=course_id, is_active=1, created__lt=as_datetime(next_day(date_for)), ).exclude(user__in=staff).exclude(user__in=admins).exclude( user__in=coaches).count()
def test_authorization_no_oauth_staff(self): """ Check authorization for staff users logged in without oauth """ # create a staff user staff_user = User.objects.create_user('test_staff_user', '*****@*****.**', 'test') # add staff role to the staff user CourseStaffRole(self.master_course_key).add_users(staff_user) data = { 'master_course_id': self.master_course_key_str, 'max_students_allowed': 111, 'display_name': 'CCX Test Title', 'coach_email': self.coach.email } # the staff user can perform the request self.client.login(username=staff_user.username, password='******') resp = self.client.get(self.list_url_master_course) self.assertEqual(resp.status_code, status.HTTP_200_OK) resp = self.client.post(self.list_url, data, format='json') self.assertEqual(resp.status_code, status.HTTP_201_CREATED)
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 # Finally, check if user is linked directly to the organization: user_org = OrganizationUser.objects.filter( active=True, organization__short_name=org, user_id=user.id).values().first() if user_org: if user_org['is_staff']: return all_perms else: return STUDIO_EDIT_CONTENT | STUDIO_VIEW_CONTENT return STUDIO_NO_PERMISSIONS
def test_enrollment_list_permissions(self): """ Test that the correct list of enrollments is returned, depending on the permissions of the requesting user. """ # Create another course, and enroll self.user in both courses. other_course = CourseFactory.create(emit_signals=True) for course in self.course, other_course: CourseModeFactory.create( course_id=unicode(course.id), mode_slug=CourseMode.DEFAULT_MODE_SLUG, mode_display_name=CourseMode.DEFAULT_MODE_SLUG, ) self.assert_enrollment_status( course_id=unicode(course.id), max_mongo_calls=0, ) # Verify the user himself can see both of his enrollments. self._assert_enrollments_visible_in_list([self.course, other_course]) # Verify that self.other_user can't see any of the enrollments. self.client.login(username=self.OTHER_USERNAME, password=self.PASSWORD) self._assert_enrollments_visible_in_list([]) # Create a staff user for self.course (but nor for other_course) and log her in. staff_user = UserFactory.create(username='******', email='*****@*****.**', password=self.PASSWORD) CourseStaffRole(self.course.id).add_users(staff_user) self.client.login(username='******', password=self.PASSWORD) # Verify that she can see only the enrollment in the course she has staff privileges for. self._assert_enrollments_visible_in_list([self.course]) # Create a global staff user, and verify she can see all enrollments. AdminFactory(username='******', email='*****@*****.**', password=self.PASSWORD) self.client.login(username='******', password=self.PASSWORD) self._assert_enrollments_visible_in_list([self.course, other_course]) # Verify the server can see all enrollments. self.client.logout() self._assert_enrollments_visible_in_list([self.course, other_course], use_server_key=True)
def get(self, request): """Gets a list of all course enrollments for a user. Returns a list for the currently logged in user, or for the user named by the 'user' GET parameter. If the username does not match that of the currently logged in user, only courses for which the currently logged in user has the Staff or Admin role are listed. As a result, a course team member can find out which of his or her own courses a particular learner is enrolled in. Only the Staff or Admin role (granted on the Django administrative console as the staff or instructor permission) in individual courses gives the requesting user access to enrollment data. Permissions granted at the organizational level do not give a user access to enrollment data for all of that organization's courses. Users who have the global staff permission can access all enrollment data for all courses. """ username = request.GET.get('user', request.user.username) try: enrollment_data = api.get_enrollments(username) print enrollment_data except CourseEnrollmentError: return Response( status=status.HTTP_400_BAD_REQUEST, data={ "message": (u"An error occurred while retrieving enrollments for user '{username}'" ).format(username=username) }) if username == request.user.username or GlobalStaff().has_user(request.user) or \ self.has_api_key_permissions(request): return Response(enrollment_data) filtered_data = [] for enrollment in enrollment_data: course_key = CourseKey.from_string( enrollment["course_details"]["course_id"]) if user_has_role(request.user, CourseStaffRole(course_key)): filtered_data.append(enrollment) return Response(filtered_data)
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. """ self.assertEqual(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) self.assertTrue( CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=staff).exists() ) self.assertTrue( CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=instructor).exists() ) CourseEnrollment.enroll(user1, self.course_limited.id, check_access=True) self.assertTrue( CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=user1).exists() ) with self.assertRaises(CourseFullError): CourseEnrollment.enroll(user2, self.course_limited.id, check_access=True) self.assertFalse( CourseEnrollment.objects.filter(course_id=self.course_limited.id, user=user2).exists() )