def test_libraries_on_index(self): """ Test that the library tab is present. """ def _assert_library_tab_present(response): """ Asserts there's a library tab. """ parsed_html = lxml.html.fromstring(response.content) library_tab = parsed_html.find_class('react-library-listing') self.assertEqual(len(library_tab), 1) # Add a library: lib1 = LibraryFactory.create() # lint-amnesty, pylint: disable=unused-variable index_url = '/home/' index_response = self.client.get(index_url, {}, HTTP_ACCEPT='text/html') _assert_library_tab_present(index_response) # Make sure libraries are visible to non-staff users too self.client.logout() non_staff_user, non_staff_userpassword = self.create_non_staff_user() lib2 = LibraryFactory.create(user_id=non_staff_user.id) LibraryUserRole(lib2.location.library_key).add_users(non_staff_user) self.client.login(username=non_staff_user.username, password=non_staff_userpassword) index_response = self.client.get(index_url, {}, HTTP_ACCEPT='text/html') _assert_library_tab_present(index_response)
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
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 formatted_users = [] for user in instructors: formatted_users.append(user_with_role(user, 'instructor')) for user in staff: formatted_users.append(user_with_role(user, 'staff')) for user in users: formatted_users.append(user_with_role(user, 'library_user')) return render_to_response( 'manage_users_lib.html', { 'context_library': library, 'users': formatted_users, 'allow_actions': bool(user_perms & STUDIO_EDIT_ROLES), 'library_key': str(library_key), 'lib_users_url': reverse_library_url('manage_library_users', library_key_string), 'show_children_previews': library.show_children_previews })
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 # mcdaniel dec-2020: all approved course creators receive all_perms to the course templates. if str(course_key)[-8:] == 'Template': log.info( 'common.djangoapps.student.auth.get_user_permissions() - template found. granting all_perms on: {course_key}' .format(course_key=course_key)) return all_perms # 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
def test_read_only_role(self, use_org_level_role): """ Test the read-only role (LibraryUserRole and its org-level equivalent) """ # As staff user, add a block to self.library: block = self._add_simple_content_block() # Login as a non_staff_user: self._login_as_non_staff_user() self.assertFalse(self._can_access_library(self.library)) block_url = reverse_usage_url('xblock_handler', block.location) def can_read_block(): """ Check if studio lets us view the XBlock in the library """ response = self.client.get_json(block_url) self.assertIn(response.status_code, (200, 403)) # 400 would be ambiguous return response.status_code == 200 def can_edit_block(): """ Check if studio lets us edit the XBlock in the library """ response = self.client.ajax_post(block_url) self.assertIn(response.status_code, (200, 403)) # 400 would be ambiguous return response.status_code == 200 def can_delete_block(): """ Check if studio lets us delete the XBlock in the library """ response = self.client.delete(block_url) self.assertIn(response.status_code, (200, 403)) # 400 would be ambiguous return response.status_code == 200 def can_copy_block(): """ Check if studio lets us duplicate the XBlock in the library """ response = self.client.ajax_post( reverse_url('xblock_handler'), { 'parent_locator': str(self.library.location), 'duplicate_source_locator': str(block.location), }) self.assertIn(response.status_code, (200, 403)) # 400 would be ambiguous return response.status_code == 200 def can_create_block(): """ Check if studio lets us make a new XBlock in the library """ response = self.client.ajax_post( reverse_url('xblock_handler'), { 'parent_locator': str(self.library.location), 'category': 'html', }) self.assertIn(response.status_code, (200, 403)) # 400 would be ambiguous return response.status_code == 200 # Check that we do not have read or write access to block: self.assertFalse(can_read_block()) self.assertFalse(can_edit_block()) self.assertFalse(can_delete_block()) self.assertFalse(can_copy_block()) self.assertFalse(can_create_block()) # Give non_staff_user read-only permission: if use_org_level_role: OrgLibraryUserRole(self.lib_key.org).add_users(self.non_staff_user) else: LibraryUserRole(self.lib_key).add_users(self.non_staff_user) self.assertTrue(self._can_access_library(self.library)) self.assertTrue(can_read_block()) self.assertFalse(can_edit_block()) self.assertFalse(can_delete_block()) self.assertFalse(can_copy_block()) self.assertFalse(can_create_block())