def can_view_courses_for_username(requesting_user, target_username): """ Determine whether `requesting_user` has permission to view courses available to the user identified by `target_username`. Arguments: requesting_user (User): The user requesting permission to view another target_username (string): The name of the user `requesting_user` would like to access. Return value: Boolean: `True` if `requesting_user` is authorized to view courses as `target_username`. Otherwise, `False` Raises: TypeError if target_username is empty or None. """ # AnonymousUser has no username, so we test for requesting_user's own # username before prohibiting an empty target_username. if requesting_user.username == target_username: return True elif not target_username: raise TypeError("target_username must be specified") else: staff = GlobalStaff() return staff.has_user(requesting_user)
def test_staff_course_listing(self, default_store, mongo_calls): """ Create courses and verify they take certain amount of mongo calls to call get_courses_accessible_to_user. Also verify that fetch accessible courses list for staff user returns CourseSummary instances. """ # Assign & verify staff role to the user GlobalStaff().add_users(self.user) self.assertTrue(GlobalStaff().has_user(self.user)) with self.store.default_store(default_store): # Create few courses for num in range(TOTAL_COURSES_COUNT): course_location = self.store.make_course_key('Org', 'CreatedCourse' + str(num), 'Run') self._create_course_with_access_groups(course_location, self.user, default_store) # Fetch accessible courses list & verify their count courses_list_by_staff, __ = get_courses_accessible_to_user(self.request) self.assertEqual(len(list(courses_list_by_staff)), TOTAL_COURSES_COUNT) # Verify fetched accessible courses list is a list of CourseSummery instances self.assertTrue(all(isinstance(course, CourseSummary) for course in courses_list_by_staff)) # Now count the db queries for staff with check_mongo_calls(mongo_calls): list(_accessible_courses_summary_iter(self.request))
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
def wrapped(request, *args, **kwargs): if GlobalStaff().has_user(request.user): return func(request, *args, **kwargs) else: return HttpResponseForbidden( "Must be {platform_name} staff to perform this action.".format( platform_name=settings.PLATFORM_NAME))
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 setUp(self): """ Add courses with the end date set to various values """ super().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)
def get_child_locations(section_desc, request, course_id): """ Returns all child locations for a section. If user is learner or masquerading as learner, staff only blocks are excluded. """ is_staff_user = GlobalStaff().has_user(request.user) if request else False def is_masquerading_as_student(): """ Return True if user is masquerading as learner. """ masquerade_settings = request.session.get(MASQUERADE_SETTINGS_KEY, {}) course_info = masquerade_settings.get(course_id) return masquerade_settings and course_info and getattr( course_info, 'role', '') == 'student' def is_user_staff_and_not_masquerading_learner(): """ Return True if user is staff and not masquerading as learner. """ return is_staff_user and not is_masquerading_as_student() def is_child_appendable(child_instance): """ Return True if child is appendable based on request and request's user type. """ return (request and is_user_staff_and_not_masquerading_learner() ) or not child_instance.visible_to_staff_only child_locs = [] for child in section_desc.get_children(): if not is_child_appendable(child): continue child_locs.append(child.location) return child_locs
def all_permissions_for_user_in_course(user, course_id): """ Returns all the permissions the user has in the given course. """ if not user.is_authenticated: return {} course = modulestore().get_course(course_id) if course is None: raise ItemNotFoundError(course_id) roles = Role.objects.filter(users=user, course_id=course_id) role_names = {role.name for role in roles} permission_names = set() for role in roles: # Intentional n+1 query pattern to get permissions for each role because # Aurora's query optimizer can't handle the join proplerly on 30M+ row # tables (EDUCATOR-3374). Fortunately, there are very few forum roles. for permission in role.permissions.all(): if not permission_blacked_out(course, role_names, permission.name): permission_names.add(permission.name) # Prevent a circular import from openedx.core.djangoapps.django_comment_common.utils import GLOBAL_STAFF_ROLE_PERMISSIONS if GlobalStaff().has_user(user): for permission in GLOBAL_STAFF_ROLE_PERMISSIONS: permission_names.add(permission) return permission_names
def user_permissions_for_course(course, user): """ Return the user's permissions over the discussion configuration of the course. """ return { "change_provider": not course.has_started() or GlobalStaff().has_user(user), }
def check_staff(): """ Checks for staff access """ if perm != 'global': debug("Deny: invalid permission '%s'", perm) return ACCESS_DENIED return ACCESS_GRANTED if GlobalStaff().has_user(user) else ACCESS_DENIED
def has_permission(self, request, view): """Returns true if the user is enrolled or is staff.""" course_key = CourseKey.from_string(view.kwargs.get('course_id')) return (GlobalStaff().has_user(request.user) or CourseStaffRole(course_key).has_user(request.user) or CourseInstructorRole(course_key).has_user(request.user) or CourseEnrollment.is_enrolled(request.user, course_key) or has_discussion_privileges(request.user, course_key))
def check_support(): """Check that the user has access to the support UI. """ if perm != 'global': return ACCESS_DENIED return ( ACCESS_GRANTED if GlobalStaff().has_user(user) or SupportStaffRole().has_user(user) else ACCESS_DENIED )
def has_permission(self, request, view): course_key_string = view.kwargs.get('course_id') course_key = validate_course_key(course_key_string) if GlobalStaff().has_user(request.user): return True return (CourseInstructorRole(course_key).has_user(request.user) or CourseStaffRole(course_key).has_user(request.user))
def can_call_public_api(requesting_user, course_key): """ Global staff can always call the public API. Otherwise, check waffle flag. This is only intended for rollout purposes, and eventually everyone will be able to call the public API for all courses. """ return GlobalStaff().has_user( requesting_user) or USE_FOR_OUTLINES.is_enabled(course_key)
def can_see_all_content(requesting_user, course_key): """ Global staff, course staff, and instructors can see everything. There's no need to run processors to restrict results for these users. """ return (GlobalStaff().has_user(requesting_user) or CourseStaffRole(course_key).has_user(requesting_user) or CourseInstructorRole(course_key).has_user(requesting_user))
def setUp(self): """ Add a student & teacher """ super(TestCourseListing, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments self.student = UserFactory() self.teacher = UserFactory() GlobalStaff().add_users(self.teacher) self.client = Client() self.client.login(username=self.teacher.username, password='******')
def get_user_roles(self) -> str: """ Returns comma-separated roles for the given user """ basic_role = self.DEFAULT_ROLE if GlobalStaff().has_user(self.request.user): basic_role = self.ADMIN_ROLE all_roles = [basic_role] return ','.join(all_roles)
def is_enrolled_or_staff(self, request, program_uuid): """Returns true if the user is enrolled in the program or staff""" if GlobalStaff().has_user(request.user): return True try: get_program_enrollment(program_uuid=program_uuid, user=request.user) except ObjectDoesNotExist: return False return True
def setUp(self): """ Add a student & teacher """ super().setUp() self.student = UserFactory() self.teacher = UserFactory() GlobalStaff().add_users(self.teacher) self.client = Client() self.client.login(username=self.teacher.username, password='******')
def has_permission(self, request, view): """Returns true if the user is admin or staff and request method is GET.""" course_key = CourseKey.from_string(view.kwargs.get('course_id')) user_roles = get_user_role_names(request.user, course_key) is_user_staff = bool( user_roles & { FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA, }) return (GlobalStaff().has_user(request.user) or request.user.is_staff or is_user_staff and request.method == "GET")
def is_user_staff_or_instructor_in_course(user, course_key): """ Determines if a user is an Instructor or part of the given course's course staff. Also returns true for GlobalStaff. """ if not isinstance(course_key, CourseKey): course_key = CourseKey.from_string(course_key) return (GlobalStaff().has_user(user) or CourseStaffRole(course_key).has_user(user) or CourseInstructorRole(course_key).has_user(user))
def administrative_accesses_to_course_for_user(user, course_key): """ Returns types of access a user have for given course. """ global_staff = GlobalStaff().has_user(user) staff_access = (CourseStaffRole(course_key).has_user(user) or OrgStaffRole(course_key.org).has_user(user)) instructor_access = (CourseInstructorRole(course_key).has_user(user) or OrgInstructorRole(course_key.org).has_user(user)) return global_staff, staff_access, instructor_access
def user_can_preview_themes(user): """ Returns true if the specified user is allowed to preview themes. """ if not user or user.is_anonymous: return False # In development mode, all users can preview themes if settings.DEBUG: return True # Otherwise, only global staff can preview themes return GlobalStaff().has_user(user)
def get_jwt_token(self, request, suffix=''): """Generate JWT token for SSO authorization""" annoto_auth = self.get_annoto_settings() if not annoto_auth: msg = self.i18n_service.gettext( 'Annoto authorization is not provided in "LTI Passports".') return self._json_resp({'status': 'error', 'msg': msg}) user = self._get_user() if not user: msg = self.i18n_service.gettext('Requested user does not exists.') return self._json_resp({'status': 'error', 'msg': msg}) profile_name = hasattr( user, 'profile') and user.profile and user.profile.name name = profile_name or user.get_full_name() or user.username photo = self._build_absolute_uri( request, get_profile_image_urls_for_user(user)['small']) roles = user.courseaccessrole_set.filter( course_id=self.course_id).values_list('role', flat=True) if CourseStaffRole.ROLE in roles or GlobalStaff().has_user(user): scope = 'super-mod' elif CourseInstructorRole.ROLE in roles: scope = 'moderator' else: scope = 'user' payload = { 'exp': int(time.time() + 60 * 20), 'iss': annoto_auth['client_id'], 'jti': user.id, 'name': name, 'email': user.email, 'photoUrl': photo, 'scope': scope } token = jwt.encode(payload, annoto_auth['client_secret'], algorithm='HS256') return self._json_resp({ 'status': 'ok', 'token': getattr(token, 'decode', lambda: token)() })
def _check_caller_authority(caller, role): """ Internal function to check whether the caller has authority to manipulate this role :param caller: a user :param role: an AccessRole """ if not (caller.is_authenticated and caller.is_active): raise PermissionDenied # superuser if GlobalStaff().has_user(caller): return if isinstance(role, (GlobalStaff, CourseCreatorRole, OrgContentCreatorRole)): # lint-amnesty, pylint: disable=no-else-raise raise PermissionDenied elif isinstance(role, CourseRole): # instructors can change the roles w/in their course if not user_has_role(caller, CourseInstructorRole(role.course_key)): raise PermissionDenied
def get_ability(course_id, content, user): """ Return a dictionary of forums-oriented actions and the user's permission to perform them """ (user_group_id, content_user_group_id) = get_user_group_ids(course_id, content, user) return { 'editable': check_permissions_by_view( user, course_id, content, "update_thread" if content['type'] == 'thread' else "update_comment", user_group_id, content_user_group_id ), 'can_reply': check_permissions_by_view( user, course_id, content, "create_comment" if content['type'] == 'thread' else "create_sub_comment", ), 'can_delete': check_permissions_by_view( user, course_id, content, "delete_thread" if content['type'] == 'thread' else "delete_comment", user_group_id, content_user_group_id ), 'can_openclose': check_permissions_by_view( user, course_id, content, "openclose_thread" if content['type'] == 'thread' else False, user_group_id, content_user_group_id ), 'can_vote': not is_content_authored_by(content, user) and check_permissions_by_view( user, course_id, content, "vote_for_thread" if content['type'] == 'thread' else "vote_for_comment" ), 'can_report': not is_content_authored_by(content, user) and (check_permissions_by_view( user, course_id, content, "flag_abuse_for_thread" if content['type'] == 'thread' else "flag_abuse_for_comment" ) or GlobalStaff().has_user(user)) }
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 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 their 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) except CourseEnrollmentError: return Response( status=status.HTTP_400_BAD_REQUEST, data={ "message": ("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 has_staff_roles(user, course_key): """ Return true if a user has any of the following roles Staff, Instructor, Beta Tester, Forum Community TA, Forum Group Moderator, Forum Moderator, Forum Administrator """ forum_roles = [ FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_GROUP_MODERATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_ADMINISTRATOR ] is_staff = CourseStaffRole(course_key).has_user(user) is_instructor = CourseInstructorRole(course_key).has_user(user) is_beta_tester = CourseBetaTesterRole(course_key).has_user(user) is_org_staff = OrgStaffRole(course_key.org).has_user(user) is_org_instructor = OrgInstructorRole(course_key.org).has_user(user) is_global_staff = GlobalStaff().has_user(user) has_forum_role = Role.user_has_role_for_course(user, course_key, forum_roles) if any([ is_staff, is_instructor, is_beta_tester, is_org_staff, is_org_instructor, is_global_staff, has_forum_role ]): return True return False
def list_course_keys(request, username, role): """ Yield all available CourseKeys for the user having the given role. The courses returned include those for which the user identified by `username` has the given role. Additionally, the logged in user should have permission to view courses available to that user. Note: This function does not use branding to determine courses. Arguments: request (HTTPRequest): Used to identify the logged-in user and to instantiate the course module to retrieve the course about description username (string): The name of the user the logged-in user would like to be identified as Keyword Arguments: role (string): Course keys are filtered such that only those for which the user has the specified role are returned. Return value: Yield `CourseKey` objects representing the collection of courses. """ user = get_effective_user(request.user, username) all_course_keys = CourseOverview.get_all_course_keys() # Global staff have access to all courses. Filter courses for non-global staff. if GlobalStaff().has_user(user): return all_course_keys if role == 'staff': # This short-circuit implementation bypasses has_access() which we think is too slow for some users when # evaluating staff-level course access for Insights. Various tickets have context on this issue: CR-2487, # TNL-7448, DESUPPORT-416, and probably more. # # This is a simplified implementation that does not consider org-level access grants (e.g. when course_id is # empty). filtered_course_keys = ( CourseAccessRole.objects.filter( user=user, # Having the instructor role implies staff access. role__in=['staff', 'instructor'], ) # We need to check against CourseOverview so that we don't return any Libraries. .extra(tables=['course_overviews_courseoverview'], where=['course_id = course_overviews_courseoverview.id']) # For good measure, make sure we don't return empty course IDs. .exclude(course_id=CourseKeyField.Empty).order_by( 'course_id').values_list('course_id', flat=True).distinct()) else: # This is the original implementation which still covers the case where role = "instructor": filtered_course_keys = LazySequence( (course_key for course_key in all_course_keys if has_access(user, role, course_key)), est_len=len(all_course_keys)) return filtered_course_keys