예제 #1
0
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)):
        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
예제 #2
0
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))
    }
예제 #3
0
def course_listing(request):
    """
    List all courses available to the logged in user
    Try to get all courses by first reversing django groups and fallback to old method if it fails
    Note: overhead of pymongo reads will increase if getting courses from django groups fails
    """
    if GlobalStaff().has_user(request.user):
        # user has global access so no need to get courses from django groups
        courses = _accessible_courses_list(request)
    else:
        try:
            courses = _accessible_courses_list_from_groups(request)
        except ItemNotFoundError:
            # user have some old groups or there was some error getting courses from django groups
            # so fallback to iterating through all courses
            courses = _accessible_courses_list(request)

    def format_course_for_view(course):
        """
        return tuple of the data which the view requires for each course
        """
        return (course.display_name,
                reverse_course_url('course_handler', course.id),
                get_lms_link_for_item(course.location),
                course.display_org_with_default,
                course.display_number_with_default, course.location.name)

    return render_to_response(
        'index.html', {
            'courses': [
                format_course_for_view(c)
                for c in courses if not isinstance(c, ErrorDescriptor)
            ],
            'user':
            request.user,
            'request_course_creator_url':
            reverse('contentstore.views.request_course_creator'),
            'course_creator_status':
            _get_course_creator_status(request.user),
            'allow_unicode_course_id':
            settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False)
        })
예제 #4
0
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
예제 #5
0
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)

    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 course_keys

    return LazySequence(
        (
            course_key for course_key in course_keys
            if has_access(user, role, course_key)
        ),
        est_len=len(course_keys)
    )
예제 #6
0
    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')
예제 #7
0
파일: auth.py 프로젝트: lumsx/edx-platform
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) or caller.groups.filter(
            name=settings.EDLY_PANEL_ADMIN_USERS_GROUP).exists():
        return

    if isinstance(role, (GlobalStaff, CourseCreatorRole)):
        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
예제 #8
0
    def test_errored_course_global_staff(self, store, path_to_patch):
        """
        Test the course list for global staff when get_course returns an ErrorDescriptor
        """
        GlobalStaff().add_users(self.user)

        with self.store.default_store(store):
            course_key = self.store.make_course_key('Org1', 'Course1', 'Run1')
            self._create_course_with_access_groups(course_key, self.user, store=store)

            with patch(path_to_patch, Mock(side_effect=Exception)):
                self.assertIsInstance(self.store.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, [])
예제 #9
0
    def _get_courses_with_access_type(self, user, access_type):
        # Check the application cache and update if not present. The application
        # cache is useful since there are calls to different endpoints in close
        # succession, for example the id_token and user_info endpoints.

        key = '-'.join([str(self.__class__), str(user.id), access_type])
        course_ids = cache.get(key)

        if not course_ids:
            course_keys = CourseOverview.get_all_course_keys()

            # Global staff have access to all courses. Filter courses for non-global staff.
            if not GlobalStaff().has_user(user):
                course_keys = [course_key for course_key in course_keys if has_access(user, access_type, course_key)]

            course_ids = [six.text_type(course_key) for course_key in course_keys]

            cache.set(key, course_ids, self.COURSE_CACHE_TIMEOUT)

        return course_ids
예제 #10
0
    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)
예제 #11
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
    # 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
예제 #12
0
파일: views.py 프로젝트: jayminDj/edxPlugin
    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)
예제 #13
0
def has_course_author_access(user, course_key, role=CourseStaffRole):
    """
    Return True if user has studio (write) access to the given course.
    Note that the CMS permissions model is with respect to courses.
    There is a super-admin permissions if user.is_staff is set.
    Also, since we're unifying the user database between LMS and CAS,
    I'm presuming that the course instructor (formally known as admin)
    will not be in both INSTRUCTOR and STAFF groups, so we have to cascade our
    queries here as INSTRUCTOR has all the rights that STAFF do.

    :param user:
    :param course_key: a CourseKey
    :param role: an AccessRole
    """
    if GlobalStaff().has_user(user):
        return True
    if OrgInstructorRole(org=course_key.org).has_user(user):
        return True
    if OrgStaffRole(org=course_key.org).has_user(user):
        return True
    # temporary to ensure we give universal access given a course until we impl branch specific perms
    return has_access(user, role(course_key.for_branch(None)))
예제 #14
0
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
예제 #15
0
    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 = User.objects.get(
            id=self.runtime.service(self, 'user').get_current_user(
            ).opt_attrs.get('edx-platform.user_id'))
        if not user:
            msg = self.i18n_service.gettext('Requested user does not exists.')
            return self._json_resp({'status': 'error', 'msg': msg})

        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': user.username,
            'scope': scope
        }

        token = jwt.encode(payload,
                           annoto_auth['client_secret'],
                           algorithm='HS256')
        return self._json_resp({'status': 'ok', 'token': token})
예제 #16
0
 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)
예제 #17
0
def set_roles_for_edx_users(user, permissions, strategy):
    '''
    This function is specific functional for open-edx platform.
    It create roles for edx users from sso permissions.
    '''

    log_message = 'For User: {}, object_type {} and object_id {} there is not matched Role for Permission set: {}'

    global_perm = {
        'Read', 'Update', 'Delete', 'Publication', 'Enroll',
        'Manage(permissions)'
    }
    staff_perm = {'Read', 'Update', 'Delete', 'Publication', 'Enroll'}
    tester_perm = {'Read', 'Enroll'}

    role_ids = set(user.courseaccessrole_set.values_list('id', flat=True))
    new_role_ids = []

    is_global_staff = False
    for role in permissions:
        _log = False
        if role['obj_type'] == '*':
            if '*' in role['obj_perm'] or global_perm.issubset(
                    set(role['obj_perm'])):
                GlobalStaff().add_users(user)
                is_global_staff = True

            elif 'Create' in role['obj_perm']:
                if not CourseCreatorRole().has_user(user):
                    CourseCreatorRole().add_users(user)
                car = CourseAccessRole.objects.get(user=user,
                                                   role=CourseCreatorRole.ROLE)
                new_role_ids.append(car.id)

            if role['obj_perm'] != '*' and global_perm != set(
                    role['obj_perm']) and ['Create'] != role['obj_perm']:
                _log = True

        elif role['obj_type'] == 'edxorg':
            if '*' in role['obj_perm'] or global_perm.issubset(
                    set(role['obj_perm'])):
                if not OrgInstructorRole(role['obj_id']).has_user(user):
                    OrgInstructorRole(role['obj_id']).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=OrgInstructorRole(role['obj_id'])._role_name,
                    org=role['obj_id'])
                new_role_ids.append(car.id)

            elif staff_perm.issubset(set(role['obj_perm'])):
                if not OrgStaffRole(role['obj_id']).has_user(user):
                    OrgStaffRole(role['obj_id']).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=OrgStaffRole(role['obj_id'])._role_name,
                    org=role['obj_id'])
                new_role_ids.append(car.id)

            elif 'Read' in role['obj_perm']:
                if not OrgLibraryUserRole(role['obj_id']).has_user(user):
                    OrgLibraryUserRole(role['obj_id']).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=OrgLibraryUserRole.ROLE,
                    org=role['obj_id'])
                new_role_ids.append(car.id)

            if role['obj_perm'] != '*' and global_perm != set(role['obj_perm']) and \
                    staff_perm != set(role['obj_perm']) and 'Read' not in role['obj_perm']:
                _log = True

        elif role['obj_type'] in ['edxcourse', 'edxlibrary']:

            course_key = CourseKey.from_string(role['obj_id'])

            if '*' in role['obj_perm'] or global_perm.issubset(
                    set(role['obj_perm'])):
                if not CourseInstructorRole(course_key).has_user(user):
                    CourseInstructorRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=CourseInstructorRole.ROLE,
                    course_id=course_key)
                new_role_ids.append(car.id)

            elif staff_perm.issubset(set(role['obj_perm'])):
                if not CourseStaffRole(course_key).has_user(user):
                    CourseStaffRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(user=user,
                                                   role=CourseStaffRole.ROLE,
                                                   course_id=course_key)
                new_role_ids.append(car.id)

            elif tester_perm.issubset(set(role['obj_perm'])):
                if not CourseBetaTesterRole(course_key).has_user(user):
                    CourseBetaTesterRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=CourseBetaTesterRole.ROLE,
                    course_id=course_key)
                new_role_ids.append(car.id)

            elif role['obj_type'] == 'edxlibrary' and 'Read' in role[
                    'obj_perm']:
                if not LibraryUserRole(course_key).has_user(user):
                    LibraryUserRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=CourseBetaTesterRole.ROLE,
                    course_id=course_key)
                new_role_ids.append(car.id)

            if role['obj_perm'] != '*' and global_perm != set(role['obj_perm']) and \
                staff_perm != set(role['obj_perm']) and tester_perm != set(role['obj_perm']) and 'Read' not in role['obj_perm']:
                _log = True

        elif role['obj_type'] == 'edxcourserun':

            course_key = CourseKey.from_string(role['obj_id'])

            if '*' in role['obj_perm'] or global_perm.issubset(
                    set(role['obj_perm'])):
                if not CourseInstructorRole(course_key).has_user(user):
                    CourseInstructorRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=CourseInstructorRole.ROLE,
                    course_id=course_key)
                new_role_ids.append(car.id)
            elif staff_perm.issubset(set(role['obj_perm'])):
                if not CourseStaffRole(course_key).has_user(user):
                    CourseStaffRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(user=user,
                                                   role=CourseStaffRole.ROLE,
                                                   course_id=course_key)
                new_role_ids.append(car.id)
            elif tester_perm.issubset(set(role['obj_perm'])):
                if not CourseBetaTesterRole(course_key).has_user(user):
                    CourseBetaTesterRole(course_key).add_users(user)
                car = CourseAccessRole.objects.get(
                    user=user,
                    role=CourseBetaTesterRole.ROLE,
                    course_id=course_key)
                new_role_ids.append(car.id)

            if role['obj_perm'] != '*' and global_perm != set(role['obj_perm']) and \
                staff_perm != set(role['obj_perm']) and tester_perm != set(role['obj_perm']):
                _log = True

        if _log:
            logging.warning(
                log_message.format(user.id, role['obj_type'], role['obj_id'],
                                   str(role['obj_perm'])))

    if (not is_global_staff) and GlobalStaff().has_user(user):
        GlobalStaff().remove_users(user)

    remove_roles = role_ids - set(new_role_ids)

    if remove_roles:
        entries = CourseAccessRole.objects.filter(id__in=list(remove_roles))
        entries.delete()
예제 #18
0
def certificates_detail_handler(request, course_key_string, certificate_id):
    """
    JSON API endpoint for manipulating a course certificate via its internal identifier.
    Utilized by the Backbone.js 'certificates' application model

    POST or PUT
        json: update the specified certificate based on provided information
    DELETE
        json: remove the specified certificate from the course
    """
    course_key = CourseKey.from_string(course_key_string)
    course = _get_course_and_check_access(course_key, request.user)

    certificates_list = course.certificates.get('certificates', [])
    match_index = None
    match_cert = None
    for index, cert in enumerate(certificates_list):
        if certificate_id is not None:
            if int(cert['id']) == int(certificate_id):
                match_index = index
                match_cert = cert

    store = modulestore()
    if request.method in ('POST', 'PUT'):
        if certificate_id:
            active_certificates = CertificateManager.get_certificates(
                course, only_active=True)
            if int(certificate_id) in [
                    int(certificate["id"])
                    for certificate in active_certificates
            ]:
                # Only global staff (PMs) are able to edit active certificate configuration
                if not GlobalStaff().has_user(request.user):
                    raise PermissionDenied()
        try:
            new_certificate = CertificateManager.deserialize_certificate(
                course, request.body)
        except CertificateValidationError as err:
            return JsonResponse({"error": text_type(err)}, status=400)

        serialized_certificate = CertificateManager.serialize_certificate(
            new_certificate)
        cert_event_type = 'created'
        if match_cert:
            cert_event_type = 'modified'
            certificates_list[match_index] = serialized_certificate
        else:
            certificates_list.append(serialized_certificate)

        store.update_item(course, request.user.id)
        CertificateManager.track_event(
            cert_event_type, {
                'course_id': unicode(course.id),
                'configuration_id': serialized_certificate["id"]
            })
        return JsonResponse(serialized_certificate, status=201)

    elif request.method == "DELETE":
        if not match_cert:
            return JsonResponse(status=404)

        active_certificates = CertificateManager.get_certificates(
            course, only_active=True)
        if int(certificate_id) in [
                int(certificate["id"]) for certificate in active_certificates
        ]:
            # Only global staff (PMs) are able to delete active certificate configuration
            if not GlobalStaff().has_user(request.user):
                raise PermissionDenied()

        CertificateManager.remove_certificate(request=request,
                                              store=store,
                                              course=course,
                                              certificate_id=certificate_id)
        CertificateManager.track_event('deleted', {
            'course_id': unicode(course.id),
            'configuration_id': certificate_id
        })
        return JsonResponse(status=204)
예제 #19
0
 def _setstaff_login(self):
     """Makes the test user staff and logs them in"""
     GlobalStaff().add_users(self.user)
     self.client.login(username=self.user.username, password='******')
예제 #20
0
    def get_exclude_list_of_fields(cls, course_key):
        """
        Returns a list of fields to exclude from the Studio Advanced settings based on a
        feature flag (i.e. enabled or disabled).
        """
        # Copy the filtered list to avoid permanently changing the class attribute.
        exclude_list = list(cls.FIELDS_EXCLUDE_LIST)

        # Do not show giturl if feature is not enabled.
        if not settings.FEATURES.get('ENABLE_EXPORT_GIT'):
            exclude_list.append('giturl')

        # Do not show edxnotes if the feature is disabled.
        if not settings.FEATURES.get('ENABLE_EDXNOTES'):
            exclude_list.append('edxnotes')

        # Do not show video auto advance if the feature is disabled
        if not settings.FEATURES.get('ENABLE_OTHER_COURSE_SETTINGS'):
            exclude_list.append('other_course_settings')

        # Do not show video_upload_pipeline if the feature is disabled.
        if not settings.FEATURES.get('ENABLE_VIDEO_UPLOAD_PIPELINE'):
            exclude_list.append('video_upload_pipeline')

        # Do not show video auto advance if the feature is disabled
        if not settings.FEATURES.get('ENABLE_AUTOADVANCE_VIDEOS'):
            exclude_list.append('video_auto_advance')

        # Do not show social sharing url field if the feature is disabled.
        if (not hasattr(settings, 'SOCIAL_SHARING_SETTINGS') or
                not getattr(settings, 'SOCIAL_SHARING_SETTINGS', {}).get("CUSTOM_COURSE_URLS")):
            exclude_list.append('social_sharing_url')

        # Do not show teams configuration if feature is disabled.
        if not settings.FEATURES.get('ENABLE_TEAMS'):
            exclude_list.append('teams_configuration')

        if not settings.FEATURES.get('ENABLE_VIDEO_BUMPER'):
            exclude_list.append('video_bumper')

        # Do not show enable_ccx if feature is not enabled.
        if not settings.FEATURES.get('CUSTOM_COURSES_EDX'):
            exclude_list.append('enable_ccx')
            exclude_list.append('ccx_connector')

        # Do not show "Issue Open Badges" in Studio Advanced Settings
        # if the feature is disabled.
        if not settings.FEATURES.get('ENABLE_OPENBADGES'):
            exclude_list.append('issue_badges')

        # If the XBlockStudioConfiguration table is not being used, there is no need to
        # display the "Allow Unsupported XBlocks" setting.
        if not XBlockStudioConfigurationFlag.is_enabled():
            exclude_list.append('allow_unsupported_xblocks')

        # Do not show "Course Visibility For Unenrolled Learners" in Studio Advanced Settings
        # if the enable_anonymous_access flag is not enabled
        if not COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(course_key=course_key):
            exclude_list.append('course_visibility')

        # Do not show "Create Zendesk Tickets For Suspicious Proctored Exam Attempts" in
        # Studio Advanced Settings if the user is not edX staff.
        if not GlobalStaff().has_user(get_current_user()):
            exclude_list.append('create_zendesk_tickets')

        # Do not show "Proctortrack Exam Escalation Contact" if Proctortrack is not
        # an available proctoring backend.
        if not settings.PROCTORING_BACKENDS or settings.PROCTORING_BACKENDS.get('proctortrack') is None:
            exclude_list.append('proctoring_escalation_email')

        return exclude_list
예제 #21
0
def certificates_list_handler(request, course_key_string):
    """
    A RESTful handler for Course Certificates

    GET
        html: return Certificates list page (Backbone application)
    POST
        json: create new Certificate
    """
    course_key = CourseKey.from_string(course_key_string)
    store = modulestore()
    with store.bulk_operations(course_key):
        try:
            course = _get_course_and_check_access(course_key, request.user)
        except PermissionDenied:
            msg = _('PermissionDenied: Failed in authenticating {user}'
                    ).format(user=request.user)
            return JsonResponse({"error": msg}, status=403)

        if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
            certificate_url = reverse_course_url('certificates_list_handler',
                                                 course_key)
            course_outline_url = reverse_course_url('course_handler',
                                                    course_key)
            upload_asset_url = reverse_course_url('assets_handler', course_key)
            activation_handler_url = reverse_course_url(
                handler_name='certificate_activation_handler',
                course_key=course_key)
            course_modes = [
                mode.slug
                for mode in CourseMode.modes_for_course(course_id=course.id,
                                                        include_expired=True)
                if mode.slug != 'audit'
            ]

            has_certificate_modes = len(course_modes) > 0

            if has_certificate_modes:
                certificate_web_view_url = get_lms_link_for_certificate_web_view(
                    user_id=request.user.id,
                    course_key=course_key,
                    mode=course_modes[
                        0]  # CourseMode.modes_for_course returns default mode if doesn't find anyone.
                )
            else:
                certificate_web_view_url = None

            is_active, certificates = CertificateManager.is_activated(course)

            return render_to_response(
                'certificates.html', {
                    'context_course': course,
                    'certificate_url': certificate_url,
                    'course_outline_url': course_outline_url,
                    'upload_asset_url': upload_asset_url,
                    'certificates': certificates,
                    'has_certificate_modes': has_certificate_modes,
                    'course_modes': course_modes,
                    'certificate_web_view_url': certificate_web_view_url,
                    'is_active': is_active,
                    'is_global_staff': GlobalStaff().has_user(request.user),
                    'certificate_activation_handler_url':
                    activation_handler_url
                })
        elif "application/json" in request.META.get('HTTP_ACCEPT'):
            # Retrieve the list of certificates for the specified course
            if request.method == 'GET':
                certificates = CertificateManager.get_certificates(course)
                return JsonResponse(certificates, encoder=EdxJSONEncoder)
            elif request.method == 'POST':
                # Add a new certificate to the specified course
                try:
                    new_certificate = CertificateManager.deserialize_certificate(
                        course, request.body)
                except CertificateValidationError as err:
                    return JsonResponse({"error": text_type(err)}, status=400)
                if course.certificates.get('certificates') is None:
                    course.certificates['certificates'] = []
                course.certificates['certificates'].append(
                    new_certificate.certificate_data)
                response = JsonResponse(
                    CertificateManager.serialize_certificate(new_certificate),
                    status=201)
                response["Location"] = reverse_course_url(
                    'certificates_detail_handler',
                    course.id,
                    kwargs={'certificate_id': new_certificate.id})
                store.update_item(course, request.user.id)
                CertificateManager.track_event(
                    'created', {
                        'course_id': unicode(course.id),
                        'configuration_id': new_certificate.id
                    })
                course = _get_course_and_check_access(course_key, request.user)
                return response
        else:
            return HttpResponse(status=406)
예제 #22
0
파일: api.py 프로젝트: treigua/edx-platform
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
예제 #23
0
 def set_staff(self, create, extracted, **kwargs):
     GlobalStaff().add_users(self)
예제 #24
0
 def check_staff():
     if perm != 'global':
         debug("Deny: invalid permission '%s'", perm)
         return False
     return GlobalStaff().has_user(user)
예제 #25
0
 def test_global_staff(self):
     self.assertFalse(GlobalStaff().has_user(self.student))
     self.assertFalse(GlobalStaff().has_user(self.course_staff))
     self.assertFalse(GlobalStaff().has_user(self.course_instructor))
     self.assertTrue(GlobalStaff().has_user(self.global_staff))
예제 #26
0
    def post(self, request):
        # pylint: disable=too-many-statements
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"Course ID must be specified to create a new enrollment."
                })

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"No course '{course_id}' found for enrollment".format(
                        course_id=course_id)
                })

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions \
                and not GlobalStaff().has_user(request.user):
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode not in (CourseMode.AUDIT, CourseMode.HONOR, None) and not has_api_key_permissions \
                and not GlobalStaff().has_user(request.user):
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message":
                    u"User does not have permission to create enrollment with mode [{mode}]."
                    .format(mode=mode)
                })

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(status=status.HTTP_406_NOT_ACCEPTABLE,
                            data={
                                'message':
                                u'The user {} does not exist.'.format(username)
                            })

        embargo_response = embargo_api.get_embargo_response(
            request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message':
                        (u"'{value}' is an invalid enrollment activation status."
                         ).format(value=is_active)
                    })

            explicit_linked_enterprise = request.data.get(
                'linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled(
            ):
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(
                        username, text_type(course_id), None)
                except EnterpriseApiException as error:
                    log.exception(
                        u"An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                        u"for user [%s] in course run [%s]", username,
                        course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': text_type(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, text_type(course_id))
            mode_changed = enrollment and mode is not None and enrollment[
                'mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment[
                'is_active'] != is_active
            missing_attrs = []
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(
                    mode, [])) - set(actual_attrs)
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode)
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST,
                                    data={"message": msg})

                if missing_attrs:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode))
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST,
                                    data={"message": msg})

                response = api.update_enrollment(
                    username,
                    text_type(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions)
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    text_type(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes)

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                try:
                    add_user_to_cohort(cohort, user)
                except ValueError:
                    # user already in cohort, probably because they were un-enrolled and re-enrolled
                    log.exception('Cohort re-addition')
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info(
                u'The user [%s] has already been enrolled in course run [%s].',
                username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    (u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                     ).format(mode=mode, course_id=course_id),
                    "course_details":
                    error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"No course '{course_id}' found for enrollment".format(
                        course_id=course_id)
                })
        except CourseEnrollmentExistsError as error:
            log.warning(
                u'An enrollment already exists for user [%s] in course run [%s].',
                username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception(
                u"An error occurred while creating the new course enrollment for user "
                u"[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    (u"An error occurred while creating the new course enrollment for user "
                     u"'{username}' in course '{course_id}'").format(
                         username=username, course_id=course_id)
                })
        except CourseUserGroup.DoesNotExist:
            log.exception(u'Missing cohort [%s] in course run [%s]',
                          cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"An error occured while adding to cohort [%s]" %
                    cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(
                    username, text_type(course_id))
                audit_log('enrollment_change_requested',
                          course_id=text_type(course_id),
                          requested_mode=mode,
                          actual_mode=current_enrollment['mode']
                          if current_enrollment else None,
                          requested_activation=is_active,
                          actual_activation=current_enrollment['is_active']
                          if current_enrollment else None,
                          user_id=user.id)
예제 #27
0
 def test_request_global_staff_courses_with_claims(self):
     GlobalStaff().add_users(self.user)
     self._assert_role_using_claim('course_staff', 'staff_courses')
예제 #28
0
 def test_request_global_staff_courses_using_scope(self):
     GlobalStaff().add_users(self.user)
     self._assert_role_using_scope('course_staff',
                                   'staff_courses',
                                   assert_one_course=False)