Esempio n. 1
0
 def test_revoke_twice(self):
     user = self.staff[0]
     revoke_access(self.course, user, 'staff')
     group = Group.objects.get(
         name=get_access_group_name(self.course, 'staff')
     )
     self.assertNotIn(user, group.user_set.all())
Esempio n. 2
0
def bulk_beta_modify_access(request, course_id):
    """
    Enroll or unenroll users in beta testing program.

    Query parameters:
    - emails is string containing a list of emails separated by anything split_input_list can handle.
    - action is one of ['add', 'remove']
    """
    action = request.GET.get('action')
    emails_raw = request.GET.get('emails')
    emails = _split_input_list(emails_raw)
    email_students = request.GET.get('email_students') in ['true', 'True', True]
    results = []
    rolename = 'beta'
    course = get_course_by_id(course_id)

    email_params = {}
    if email_students:
        email_params = get_email_params(course, auto_enroll=False)

    for email in emails:
        try:
            error = False
            user_does_not_exist = False
            user = User.objects.get(email=email)

            if action == 'add':
                allow_access(course, user, rolename)
            elif action == 'remove':
                revoke_access(course, user, rolename)
            else:
                return HttpResponseBadRequest(strip_tags(
                    "Unrecognized action '{}'".format(action)
                ))
        except User.DoesNotExist:
            error = True
            user_does_not_exist = True
        # catch and log any unexpected exceptions
        # so that one error doesn't cause a 500.
        except Exception as exc:  # pylint: disable=broad-except
            log.exception("Error while #{}ing student")
            log.exception(exc)
            error = True
        else:
            # If no exception thrown, see if we should send an email
            if email_students:
                send_beta_role_email(action, user, email_params)
        finally:
            # Tabulate the action result of this email address
            results.append({
                'email': email,
                'error': error,
                'userDoesNotExist': user_does_not_exist
            })

    response_payload = {
        'action': action,
        'results': results,
    }
    return JsonResponse(response_payload)
Esempio n. 3
0
 def test_revoke_badrolename(self):
     user = UserFactory()
     revoke_access(self.course, user, 'robot-not-a-level')
     group = Group.objects.get(
         name=get_access_group_name(self.course, 'robot-not-a-level')
     )
     self.assertNotIn(user, group.user_set.all())
def change_existing_ccx_coaches_to_staff(apps, schema_editor):
    """
    Modify all coaches of CCX courses so that they have the staff role on the
    CCX course they coach, but retain the CCX Coach role on the parent course.

    Arguments:
        apps (Applications): Apps in edX platform.
        schema_editor (SchemaEditor): For editing database schema (unused)

    """
    CustomCourseForEdX = apps.get_model('ccx', 'CustomCourseForEdX')
    db_alias = schema_editor.connection.alias
    if not db_alias == 'default':
        # This migration is not intended to run against the student_module_history database and
        # will fail if it does. Ensure that it'll only run against the default database.
        return
    list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
    for ccx in list_ccx:
        ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
        try:
            course = get_course_by_id(ccx_locator)
        except Http404:
            log.error('Could not migrate access for CCX course: %s', six.text_type(ccx_locator))
        else:
            coach = User.objects.get(id=ccx.coach.id)
            allow_access(course, coach, 'staff', send_email=False)
            revoke_access(course, coach, 'ccx_coach', send_email=False)
            log.info(
                'The CCX coach of CCX %s has been switched from "CCX Coach" to "Staff".',
                six.text_type(ccx_locator)
            )
def revert_ccx_staff_to_coaches(apps, schema_editor):
    """
    Modify all staff on CCX courses so that they no longer have the staff role
    on the course that they coach.

    Arguments:
        apps (Applications): Apps in edX platform.
        schema_editor (SchemaEditor): For editing database schema (unused)

    """
    CustomCourseForEdX = apps.get_model('ccx', 'CustomCourseForEdX')
    db_alias = schema_editor.connection.alias
    if not db_alias == 'default':
        return
    list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
    for ccx in list_ccx:
        ccx_locator = CCXLocator.from_course_locator(ccx.course_id, six.text_type(ccx.id))
        try:
            course = get_course_by_id(ccx_locator)
        except Http404:
            log.error('Could not migrate access for CCX course: %s', six.text_type(ccx_locator))
        else:
            coach = User.objects.get(id=ccx.coach.id)
            allow_access(course, coach, 'ccx_coach', send_email=False)
            revoke_access(course, coach, 'staff', send_email=False)
            log.info(
                'The CCX coach of CCX %s has been switched from "Staff" to "CCX Coach".',
                six.text_type(ccx_locator)
            )
Esempio n. 6
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    unique_student_identifer is the target user's username or email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    course = get_course_with_access(request.user, "instructor", course_id, depth=None)
    try:
        user = get_student_from_identifier(request.GET.get("unique_student_identifier"))
    except User.DoesNotExist:
        response_payload = {
            "unique_student_identifier": request.GET.get("unique_student_identifier"),
            "userDoesNotExist": True,
        }
        return JsonResponse(response_payload)

    # Check that user is active, because add_users
    # in common/djangoapps/student/roles.py fails
    # silently when we try to add an inactive user.
    if not user.is_active:
        response_payload = {"unique_student_identifier": user.username, "inactiveUser": True}
        return JsonResponse(response_payload)

    rolename = request.GET.get("rolename")
    action = request.GET.get("action")

    if not rolename in ["instructor", "staff", "beta"]:
        return HttpResponseBadRequest(strip_tags("unknown rolename '{}'".format(rolename)))

    # disallow instructors from removing their own instructor access.
    if rolename == "instructor" and user == request.user and action != "allow":
        response_payload = {
            "unique_student_identifier": user.username,
            "rolename": rolename,
            "action": action,
            "removingSelfAsInstructor": True,
        }
        return JsonResponse(response_payload)

    if action == "allow":
        allow_access(course, user, rolename)
    elif action == "revoke":
        revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest(strip_tags("unrecognized action '{}'".format(action)))

    response_payload = {
        "unique_student_identifier": user.username,
        "rolename": rolename,
        "action": action,
        "success": "yes",
    }
    return JsonResponse(response_payload)
def change_existing_ccx_coaches_to_staff(apps, schema_editor):
    """
    Modify all coaches of CCX courses so that they have the staff role on the
    CCX course they coach, but retain the CCX Coach role on the parent course.

    Arguments:
        apps (Applications): Apps in edX platform.
        schema_editor (SchemaEditor): For editing database schema (unused)

    """
    CustomCourseForEdX = apps.get_model('ccx', 'CustomCourseForEdX')
    db_alias = schema_editor.connection.alias
    if not db_alias == 'default':
        # This migration is not intended to run against the student_module_history database and
        # will fail if it does. Ensure that it'll only run against the default database.
        return
    list_ccx = CustomCourseForEdX.objects.using(db_alias).all()
    for ccx in list_ccx:
        ccx_locator = CCXLocator.from_course_locator(ccx.course_id,
                                                     unicode(ccx.id))
        try:
            course = get_course_by_id(ccx_locator)
        except Http404:
            log.error('Could not migrate access for CCX course: %s',
                      unicode(ccx_locator))
        else:
            coach = User.objects.get(id=ccx.coach.id)
            allow_access(course, coach, 'staff', send_email=False)
            revoke_access(course, coach, 'ccx_coach', send_email=False)
            log.info(
                'The CCX coach of CCX %s has been switched from "CCX Coach" to "Staff".',
                unicode(ccx_locator))
Esempio n. 8
0
def bulk_beta_modify_access(request, course_id):
    """
    Enroll or unenroll users in beta testing program.

    Query parameters:
    - identifiers is string containing a list of emails and/or usernames separated by
      anything split_input_list can handle.
    - action is one of ['add', 'remove']
    """
    action = request.GET.get("action")
    identifiers_raw = request.GET.get("identifiers")
    identifiers = _split_input_list(identifiers_raw)
    email_students = request.GET.get("email_students") in ["true", "True", True]
    auto_enroll = request.GET.get("auto_enroll") in ["true", "True", True]
    results = []
    rolename = "beta"
    course = get_course_by_id(course_id)

    email_params = {}
    if email_students:
        email_params = get_email_params(course, auto_enroll=auto_enroll)

    for identifier in identifiers:
        try:
            error = False
            user_does_not_exist = False
            user = get_student_from_identifier(identifier)

            if action == "add":
                allow_access(course, user, rolename)
            elif action == "remove":
                revoke_access(course, user, rolename)
            else:
                return HttpResponseBadRequest(strip_tags("Unrecognized action '{}'".format(action)))
        except User.DoesNotExist:
            error = True
            user_does_not_exist = True
        # catch and log any unexpected exceptions
        # so that one error doesn't cause a 500.
        except Exception as exc:  # pylint: disable=broad-except
            log.exception("Error while #{}ing student")
            log.exception(exc)
            error = True
        else:
            # If no exception thrown, see if we should send an email
            if email_students:
                send_beta_role_email(action, user, email_params)
            # See if we should autoenroll the student
            if auto_enroll:
                # Check if student is already enrolled
                if not CourseEnrollment.is_enrolled(user, course_id):
                    CourseEnrollment.enroll(user, course_id)

        finally:
            # Tabulate the action result of this email address
            results.append({"identifier": identifier, "error": error, "userDoesNotExist": user_does_not_exist})

    response_payload = {"action": action, "results": results}
    return JsonResponse(response_payload)
Esempio n. 9
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    email is the target users email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(
        request.user, course_id, 'instructor', depth=None
    )

    email = request.GET.get('email')
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(
            "unknown rolename '{}'".format(rolename)
        )

    user = User.objects.get(email=email)

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        return HttpResponseBadRequest(
            "An instructor cannot remove their own instructor access."
        )

    if action == 'allow':
        access.allow_access(course, user, rolename)
    elif action == 'revoke':
        access.revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest("unrecognized action '{}'".format(action))

    response_payload = {
        'email': email,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    response = HttpResponse(
        json.dumps(response_payload), content_type="application/json"
    )
    return response
def remove_master_course_staff_from_ccx(master_course,
                                        ccx_key,
                                        display_name,
                                        send_email=True):
    """
    Remove staff and instructor roles on ccx to all the staff and instructors members of master course.

    Arguments:
        master_course (CourseDescriptorWithMixins): Master course instance.
        ccx_key (CCXLocator): CCX course key.
        display_name (str): ccx display name for email.
        send_email (bool): flag to switch on or off email to the users on revoke access.

    """
    list_staff = list_with_level(master_course, 'staff')
    list_instructor = list_with_level(master_course, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        list_staff_ccx = list_with_level(course_ccx, 'staff')
        list_instructor_ccx = list_with_level(course_ccx, 'instructor')
        email_params = get_email_params(course_ccx,
                                        auto_enroll=True,
                                        course_key=ccx_key,
                                        display_name=display_name)
        for staff in list_staff:
            if staff in list_staff_ccx:
                # revoke 'staff' access on ccx.
                revoke_access(course_ccx, staff, 'staff')

                # Unenroll the staff on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=staff.email,
                    email_students=send_email,
                    email_params=email_params,
                )

        for instructor in list_instructor:
            if instructor in list_instructor_ccx:
                # revoke 'instructor' access on ccx.
                revoke_access(course_ccx, instructor, 'instructor')

                # Unenroll the instructor on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=instructor.email,
                    email_students=send_email,
                    email_params=email_params,
                )
Esempio n. 11
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    unique_student_identifer is the target user's username or email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(
        request.user, course_id, 'instructor', depth=None
    )

    user = get_student_from_identifier(request.GET.get('unique_student_identifier'))
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(strip_tags(
            "unknown rolename '{}'".format(rolename)
        ))

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        return HttpResponseBadRequest(
            "An instructor cannot remove their own instructor access."
        )

    if action == 'allow':
        allow_access(course, user, rolename)
    elif action == 'revoke':
        revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest(strip_tags(
            "unrecognized action '{}'".format(action)
        ))

    response_payload = {
        'unique_student_identifier': user.username,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    return JsonResponse(response_payload)
Esempio n. 12
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    email is the target users email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(
        request.user, course_id, 'instructor', depth=None
    )

    email = strip_if_string(request.GET.get('email'))
    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(
            "unknown rolename '{}'".format(rolename)
        )

    user = User.objects.get(email=email)

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        return HttpResponseBadRequest(
            "An instructor cannot remove their own instructor access."
        )

    if action == 'allow':
        allow_access(course, user, rolename)
    elif action == 'revoke':
        revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest("unrecognized action '{}'".format(action))

    response_payload = {
        'email': email,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    return JsonResponse(response_payload)
Esempio n. 13
0
def remove_master_course_staff_from_ccx(master_course, ccx_key, display_name, send_email=True):
    """
    Remove staff and instructor roles on ccx to all the staff and instructors members of master course.

    Arguments:
        master_course (CourseDescriptorWithMixins): Master course instance.
        ccx_key (CCXLocator): CCX course key.
        display_name (str): ccx display name for email.
        send_email (bool): flag to switch on or off email to the users on revoke access.

    """
    list_staff = list_with_level(master_course, 'staff')
    list_instructor = list_with_level(master_course, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        list_staff_ccx = list_with_level(course_ccx, 'staff')
        list_instructor_ccx = list_with_level(course_ccx, 'instructor')
        email_params = get_email_params(course_ccx, auto_enroll=True, course_key=ccx_key, display_name=display_name)
        for staff in list_staff:
            if staff in list_staff_ccx:
                # revoke 'staff' access on ccx.
                revoke_access(course_ccx, staff, 'staff')

                # Unenroll the staff on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=staff.email,
                    email_students=send_email,
                    email_params=email_params,
                )

        for instructor in list_instructor:
            if instructor in list_instructor_ccx:
                # revoke 'instructor' access on ccx.
                revoke_access(course_ccx, instructor, 'instructor')

                # Unenroll the instructor on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=instructor.email,
                    email_students=send_email,
                    email_params=email_params,
                )
def remove_affiliate_course_enrollments(sender, instance, **kwargs):  # pylint: disable=unused-argument
    'Remove all privileges over all affiliate courses.'
    for ccx in instance.affiliate.courses:
        ccx_locator = CCXLocator.from_course_locator(ccx.course_id, ccx.id)
        course = get_course_by_id(ccx_locator)

        revoke_access(course, instance.member, instance.role, False)

    # Remove CCX coach on FastTrac course if the user is a staff member in ONLY the affiliate
    # for which the membership has been deleted.
    is_staff_in_other_affiliate = AffiliateMembership.objects.filter(
        member=instance.member, role__in=AffiliateMembership.STAFF_ROLES
    ).exists()
    if instance.role in AffiliateMembership.STAFF_ROLES and not is_staff_in_other_affiliate:
        course_overviews = CourseOverview.objects.exclude(id__startswith='ccx-')
        for course_overview in course_overviews:
            course_id = course_overview.id
            course = get_course_by_id(course_id)

            revoke_access(course, instance.member, AffiliateMembership.CCX_COACH, False)
Esempio n. 15
0
def reverse_add_master_course_staff_to_ccx(master_course, ccx_key,
                                           display_name):
    """
    Remove staff of ccx.

    Arguments:
        master_course (CourseDescriptorWithMixins): Master course instance
        ccx_key (CCXLocator): CCX course key
        display_name (str): ccx display name for email
    """
    list_staff = list_with_level(master_course, 'staff')
    list_instructor = list_with_level(master_course, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        email_params = get_email_params(course_ccx,
                                        auto_enroll=True,
                                        course_key=ccx_key,
                                        display_name=display_name)
        for staff in list_staff:
            # allow 'staff' access on ccx to staff of master course
            revoke_access(course_ccx, staff, 'staff')

            # Enroll the staff in the ccx
            unenroll_email(
                course_id=ccx_key,
                student_email=staff.email,
                email_students=True,
                email_params=email_params,
            )

        for instructor in list_instructor:
            # allow 'instructor' access on ccx to instructor of master course
            revoke_access(course_ccx, instructor, 'instructor')

            # Enroll the instructor in the ccx
            unenroll_email(
                course_id=ccx_key,
                student_email=instructor.email,
                email_students=True,
                email_params=email_params,
            )
Esempio n. 16
0
def reverse_add_master_course_staff_to_ccx(master_course, ccx_key, display_name):
    """
    Remove staff of ccx.

    Arguments:
        master_course (CourseDescriptorWithMixins): Master course instance
        ccx_key (CCXLocator): CCX course key
        display_name (str): ccx display name for email
    """
    list_staff = list_with_level(master_course, 'staff')
    list_instructor = list_with_level(master_course, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        email_params = get_email_params(course_ccx, auto_enroll=True, course_key=ccx_key, display_name=display_name)
        for staff in list_staff:
            # allow 'staff' access on ccx to staff of master course
            revoke_access(course_ccx, staff, 'staff')

            # Enroll the staff in the ccx
            unenroll_email(
                course_id=ccx_key,
                student_email=staff.email,
                email_students=True,
                email_params=email_params,
            )

        for instructor in list_instructor:
            # allow 'instructor' access on ccx to instructor of master course
            revoke_access(course_ccx, instructor, 'instructor')

            # Enroll the instructor in the ccx
            unenroll_email(
                course_id=ccx_key,
                student_email=instructor.email,
                email_students=True,
                email_params=email_params,
            )
Esempio n. 17
0
 def test_revoke_beta(self):
     user = self.beta_testers[0]
     revoke_access(self.course, user, 'beta')
     self.assertFalse(CourseBetaTesterRole(self.course.id).has_user(user))
Esempio n. 18
0
 def test_revoke_badrolename(self):
     user = UserFactory()
     revoke_access(self.course, user, 'robot-not-a-level')
     group = Group.objects.get(
         name=get_access_group_name(self.course, 'robot-not-a-level'))
     self.assertNotIn(user, group.user_set.all())
Esempio n. 19
0
def edit_ccx(request, course, ccx=None, **kwargs):
    if not ccx:
        raise Http404

    name = request.POST.get('name')
    delivery_mode = request.POST.get('delivery_mode')
    location_city = request.POST.get('city')
    location_state = request.POST.get('state')
    location_postal_code = request.POST.get('postal_code')
    time = '{} {}Z'.format(request.POST.get('date'), request.POST.get('time'))
    enrollment_end_date = '{} {}Z'.format(
        request.POST.get('enrollment_end_date'),
        request.POST.get('enrollment_end_time'))
    end_date = '{} {}Z'.format(request.POST.get('end_date'),
                               request.POST.get('end_time'))
    fee = request.POST.get('fee')
    course_description = request.POST.get('course_description')
    enrollment_type = request.POST.get('enrollment_type')
    facilitators = dict(request.POST).get('facilitators')

    ccx.display_name = name
    ccx.delivery_mode = delivery_mode
    ccx.location_city = location_city
    ccx.location_state = location_state
    ccx.location_postal_code = location_postal_code
    ccx.enrollment_type = enrollment_type
    ccx.time = time
    ccx.enrollment_end_date = enrollment_end_date
    ccx.end_date = end_date
    ccx.fee = ast.literal_eval(fee)
    ccx.course_description = course_description
    ccx.save()

    current_facilitator_ids = CourseAccessRole.objects.filter(
        course_id=ccx.ccx_course_id,
        role=AffiliateMembership.CCX_COACH).values_list('user_id', flat=True)
    removed_facilitator_ids = set(current_facilitator_ids).difference(
        set(facilitators))
    added_facilitator_ids = set(facilitators).difference(
        set(current_facilitator_ids))

    ccx_id = CCXLocator.from_course_locator(course.id, ccx.pk)
    course_obj = get_course_by_id(ccx.ccx_course_id, depth=None)

    for facilitator_id in removed_facilitator_ids:
        user = User.objects.get(id=facilitator_id)
        revoke_access(course_obj, user, AffiliateMembership.CCX_COACH, False)

    email_params = get_email_params(course,
                                    auto_enroll=True,
                                    course_key=ccx_id,
                                    display_name=ccx.display_name)

    for facilitator_id in added_facilitator_ids:
        user = User.objects.get(id=facilitator_id)
        enroll_email(course_id=ccx_id,
                     student_email=user.email,
                     auto_enroll=True,
                     email_students=True,
                     email_params=email_params)
        allow_access(course_obj, user, AffiliateMembership.CCX_COACH, False)

    url = reverse('ccx_coach_dashboard', kwargs={'course_id': ccx_id})
    return redirect(url)
Esempio n. 20
0
 def test_revoke_twice(self):
     user = self.staff[0]
     revoke_access(self.course, user, 'staff')
     self.assertFalse(CourseStaffRole(self.course.id).has_user(user))
Esempio n. 21
0
 def test_revoke_beta(self):
     user = self.beta_testers[0]
     revoke_access(self.course, user, 'beta')
     self.assertFalse(CourseBetaTesterRole(self.course.id).has_user(user))
Esempio n. 22
0
def populate_user(user, authentication_response):

    attr = authentication_response.find(CAS + 'authenticationSuccess/' + CAS +
                                        'attributes',
                                        namespaces=NSMAP)

    if attr is not None:

        staff_flag = attr.find(CAS + 'is_staff', NSMAP)
        if staff_flag is not None:
            user.is_staff = (staff_flag.text or '').upper() == 'TRUE'

        superuser_flag = attr.find(CAS + 'is_superuser', NSMAP)
        if superuser_flag is not None:
            user.is_superuser = (superuser_flag.text or '').upper() == 'TRUE'

        active_flag = attr.find(CAS + 'is_active', NSMAP)
        if active_flag is not None:
            user.is_active = (active_flag.text or '').upper() == 'TRUE'

        # Limiting by maximum lengths.
        # Max length of firstname/lastname is 30.
        # Max length of a email is 75.

        first_name = attr.find(CAS + 'givenName', NSMAP)
        if first_name is not None:
            user.first_name = (first_name.text or '')[0:30]

        last_name = attr.find(CAS + 'sn', NSMAP)
        if last_name is not None:
            user.last_name = (last_name.text or '')[0:30]

        email = attr.find(CAS + 'email', NSMAP)
        if email is not None:
            user.email = (email.text or '')[0:75]

        # Here we handle things that go into UserProfile instead.

        # This is a dirty hack and you shouldn't do that.
        # However, I don't think it's going to work when imported outside of the function body.

        from student.models import UserProfile

        # Make the user's password unusable. But only if they don't have an unusable password already,
        # to prevent SessionAuthenticationMiddleware from logging them out because their password changed.
        if user.has_usable_password():
            user.set_unusable_password()
        user.save()

        # If the user doesn't yet have a profile, it means it's a new one and we need to create it a profile.
        # but we need to save the user first.
        user_profile, created = UserProfile.objects.get_or_create(
            user=user, defaults={'name': user.username})

        # There should be more variables, but let's settle on the actual model first.
        full_name = attr.find(CAS + 'fullName', NSMAP)
        if full_name is not None:
            user_profile.name = full_name.text or ''

        user_profile.save()

        # Now the really fun bit. Signing the user up for courses given.
        coursetag = attr.find(CAS + 'courses', NSMAP)

        from student.models import CourseEnrollment
        from opaque_keys.edx.locator import CourseLocator
        from opaque_keys import InvalidKeyError
        from xmodule.modulestore.django import modulestore
        from xmodule.modulestore.exceptions import ItemNotFoundError

        if coursetag is not None:
            try:
                courses = json.loads(coursetag.text)
                assert isinstance(courses, list)
            except (ValueError, AssertionError):
                # We failed to parse the tag and get a list, so we leave.
                log.error("Course list failed to parse.")
                return

            # We got a list. Compare it to existing enrollments.
            existing_enrollments = CourseEnrollment.objects.filter(
                user=user, is_active=True).values_list('course_id', flat=True)

            for course in courses:
                if course and not course in existing_enrollments:
                    try:
                        locator = CourseLocator.from_string(course)
                    except (InvalidKeyError, AttributeError) as e:
                        log.error(
                            "Invalid course identifier {}".format(course))
                        continue
                    try:
                        course = modulestore().get_course(locator)
                    except ItemNotFoundError:
                        log.error("Course {} does not exist.".format(course))
                        continue
                    CourseEnrollment.enroll(user, locator)
            # Now we need to unsub the user from courses for which they are not enrolled.
            for course in existing_enrollments:
                if not course in courses:
                    try:
                        locator = CourseLocator.from_string(course)
                    except (InvalidKeyError, AttributeError) as e:
                        log.error(
                            "Invalid course identifier {} in existing enrollments."
                            .format(course))
                        continue
                    CourseEnrollment.unenroll(user, locator)

        # Now implement CourseEnrollmentAllowed objects, because otherwise they will only ever fire when
        # users click a link in the registration email -- which can never happen here.
        # Considering the new setup, I doubt this will ever be useful.
        if created:
            from student.models import CourseEnrollmentAllowed
            for cea in CourseEnrollmentAllowed.objects.filter(
                    email=user.email, auto_enroll=True):
                CourseEnrollment.enroll(user, cea.course_id)

        # Now, deal with course administration packets.
        course_admin_tag = attr.find(CAS + 'course_administration_update',
                                     NSMAP)

        if course_admin_tag is not None:
            try:
                courses = json.loads(course_admin_tag.text)
                assert isinstance(courses, dict)
            except (ValueError, AssertionError):
                # We failed to parse the tag, so we leave.
                log.error(
                    "Could not parse course administration block: <<{}>>".
                    format(course_admin_tag.text))
                return

            from instructor.access import list_with_level, allow_access, revoke_access
            from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA
            from django.contrib.auth.models import User

            for course_id, admin_block in courses.iteritems():
                try:
                    locator = CourseLocator.from_string(course_id)
                except (InvalidKeyError, AttributeError) as e:
                    log.error("Invalid course identifier {}".format(course_id))
                    continue
                try:
                    course = modulestore().get_course(locator)
                except ItemNotFoundError:
                    log.error("Course {} does not exist.".format(course_id))
                    continue

                if not course:
                    continue

                # Course roles are relatively easy.
                for block_name, role in [('admin', 'instructor'),
                                         ('staff', 'staff'), ('beta', 'beta')]:
                    role_list = admin_block.get(block_name, [])
                    existing = list_with_level(course, role)

                    for username in role_list:
                        try:
                            user = User.objects.get(username=username)
                        except User.DoesNotExist:
                            continue
                        if not user in existing:
                            allow_access(course, user, role)
                            try:
                                CourseEnrollment.enroll(user, locator)
                            except:
                                pass
                    for user in existing:
                        if not user.username in role_list:
                            revoke_access(course, user, role)

                # Forum roles, considerably different.

                for block_name, rolename in [
                    ('forum_admin', FORUM_ROLE_ADMINISTRATOR),
                    ('forum_moderator', FORUM_ROLE_MODERATOR),
                    ('forum_assistant', FORUM_ROLE_COMMUNITY_TA)
                ]:
                    role_list = admin_block.get(block_name, [])
                    try:
                        role = Role.objects.get(course_id=locator,
                                                name=rolename)
                    except Role.DoesNotExist:
                        continue
                    existing = role.users.all()

                    for user in existing:
                        if not user.username in role_list:
                            role.users.remove(user)
                    for username in role_list:
                        try:
                            user = User.objects.get(username=username)
                        except User.DoesNotExist:
                            continue
                        if not user in existing:
                            role.users.add(user)
                            try:
                                CourseEnrollment.enroll(user, locator)
                            except:
                                pass

    pass
Esempio n. 23
0
 def test_revoke_badrolename(self):
     user = UserFactory()
     revoke_access(self.course, user, 'robot-not-a-level')
Esempio n. 24
0
def bulk_beta_modify_access(request, course_id):
    """
    Enroll or unenroll users in beta testing program.

    Query parameters:
    - identifiers is string containing a list of emails and/or usernames separated by
      anything split_input_list can handle.
    - action is one of ['add', 'remove']
    """
    action = request.GET.get('action')
    identifiers_raw = request.GET.get('identifiers')
    identifiers = _split_input_list(identifiers_raw)
    email_students = request.GET.get('email_students') in [
        'true', 'True', True
    ]
    auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]
    results = []
    rolename = 'beta'
    course = get_course_by_id(course_id)

    email_params = {}
    if email_students:
        email_params = get_email_params(course, auto_enroll=auto_enroll)

    for identifier in identifiers:
        try:
            error = False
            user_does_not_exist = False
            user = get_student_from_identifier(identifier)

            if action == 'add':
                allow_access(course, user, rolename)
            elif action == 'remove':
                revoke_access(course, user, rolename)
            else:
                return HttpResponseBadRequest(
                    strip_tags("Unrecognized action '{}'".format(action)))
        except User.DoesNotExist:
            error = True
            user_does_not_exist = True
        # catch and log any unexpected exceptions
        # so that one error doesn't cause a 500.
        except Exception as exc:  # pylint: disable=broad-except
            log.exception("Error while #{}ing student")
            log.exception(exc)
            error = True
        else:
            # If no exception thrown, see if we should send an email
            if email_students:
                send_beta_role_email(action, user, email_params)
            # See if we should autoenroll the student
            if auto_enroll:
                # Check if student is already enrolled
                if not CourseEnrollment.is_enrolled(user, course_id):
                    CourseEnrollment.enroll(user, course_id)

        finally:
            # Tabulate the action result of this email address
            results.append({
                'identifier': identifier,
                'error': error,
                'userDoesNotExist': user_does_not_exist
            })

    response_payload = {
        'action': action,
        'results': results,
    }
    return JsonResponse(response_payload)
Esempio n. 25
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    unique_student_identifer is the target user's username or email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(request.user,
                                    course_id,
                                    'instructor',
                                    depth=None)
    try:
        user = get_student_from_identifier(
            request.GET.get('unique_student_identifier'))
    except User.DoesNotExist:
        response_payload = {
            'unique_student_identifier':
            request.GET.get('unique_student_identifier'),
            'userDoesNotExist':
            True,
        }
        return JsonResponse(response_payload)

    # Check that user is active, because add_users
    # in common/djangoapps/student/roles.py fails
    # silently when we try to add an inactive user.
    if not user.is_active:
        response_payload = {
            'unique_student_identifier': user.username,
            'inactiveUser': True,
        }
        return JsonResponse(response_payload)

    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(
            strip_tags("unknown rolename '{}'".format(rolename)))

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        response_payload = {
            'unique_student_identifier': user.username,
            'rolename': rolename,
            'action': action,
            'removingSelfAsInstructor': True,
        }
        return JsonResponse(response_payload)

    if action == 'allow':
        allow_access(course, user, rolename)
    elif action == 'revoke':
        revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest(
            strip_tags("unrecognized action '{}'".format(action)))

    response_payload = {
        'unique_student_identifier': user.username,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    return JsonResponse(response_payload)
Esempio n. 26
0
 def test_revoke_beta(self):
     user = self.beta_testers[0]
     revoke_access(self.course, user, 'beta')
     self.assertNotIn(user, list_with_level(self.course, 'beta'))
Esempio n. 27
0
def populate_user(user, authentication_response):

    attr = authentication_response.find(CAS + 'authenticationSuccess/'  + CAS + 'attributes'  , namespaces=NSMAP)

    if attr is not None:

        staff_flag = attr.find(CAS + 'is_staff', NSMAP)
        if staff_flag is not None:
            user.is_staff = (staff_flag.text or '').upper() == 'TRUE'

        superuser_flag = attr.find(CAS + 'is_superuser', NSMAP)
        if superuser_flag is not None:
            user.is_superuser = (superuser_flag.text or '').upper() == 'TRUE'

        active_flag = attr.find(CAS + 'is_active', NSMAP)
        if active_flag is not None:
            user.is_active = (active_flag.text or '').upper() == 'TRUE'

        # Limiting by maximum lengths.
        # Max length of firstname/lastname is 30.
        # Max length of a email is 75.

        first_name = attr.find(CAS + 'givenName', NSMAP)
        if first_name is not None:
            user.first_name = (first_name.text or '')[0:30]

        last_name = attr.find(CAS + 'sn', NSMAP)
        if last_name is not None:
            user.last_name = (last_name.text or '')[0:30]

        email = attr.find(CAS + 'email', NSMAP)
        if email is not None:
            user.email = (email.text or '')[0:75]

        # Here we handle things that go into UserProfile instead.

        # This is a dirty hack and you shouldn't do that.
        # However, I don't think it's going to work when imported outside of the function body.

        from student.models import UserProfile

        # Make the user's password unusable. But only if they don't have an unusable password already,
        # to prevent SessionAuthenticationMiddleware from logging them out because their password changed.
        if user.has_usable_password():
            user.set_unusable_password()
        user.save()

        # If the user doesn't yet have a profile, it means it's a new one and we need to create it a profile.
        # but we need to save the user first.
        user_profile, created = UserProfile.objects.get_or_create(user=user, defaults={'name':user.username})

        # There should be more variables, but let's settle on the actual model first.
        full_name = attr.find(CAS + 'fullName', NSMAP)
        if full_name is not None:
            user_profile.name = full_name.text or ''

        user_profile.save()

        # Now the really fun bit. Signing the user up for courses given.
        coursetag = attr.find(CAS + 'courses', NSMAP)

        from student.models import CourseEnrollment
        from opaque_keys.edx.locator import CourseLocator
        from opaque_keys import InvalidKeyError
        from xmodule.modulestore.django import modulestore
        from xmodule.modulestore.exceptions import ItemNotFoundError

        if coursetag is not None:
            try:
                courses = json.loads(coursetag.text)
                assert isinstance(courses,list)
            except (ValueError, AssertionError):
                # We failed to parse the tag and get a list, so we leave.
                log.error("Course list failed to parse.")
                return

            # We got a list. Compare it to existing enrollments.
            existing_enrollments = CourseEnrollment.objects.filter(user=user, is_active=True).values_list('course_id',flat=True)

            for course in courses:
                if course and not course in existing_enrollments:
                    try:
                        locator = CourseLocator.from_string(course)
                    except (InvalidKeyError, AttributeError) as e:
                        log.error("Invalid course identifier {}".format(course))
                        continue
                    try:
                        course = modulestore().get_course(locator)
                    except ItemNotFoundError:
                        log.error("Course {} does not exist.".format(course))
                        continue
                    CourseEnrollment.enroll(user,locator)
            # Now we need to unsub the user from courses for which they are not enrolled.
            for course in existing_enrollments:
                if not course in courses:
                    try:
                        locator = CourseLocator.from_string(course)
                    except (InvalidKeyError, AttributeError) as e:
                        log.error("Invalid course identifier {} in existing enrollments.".format(course))
                        continue
                    CourseEnrollment.unenroll(user, locator)

        # Now implement CourseEnrollmentAllowed objects, because otherwise they will only ever fire when
        # users click a link in the registration email -- which can never happen here.
        # Considering the new setup, I doubt this will ever be useful.
        if created:
            from student.models import CourseEnrollmentAllowed
            for cea in CourseEnrollmentAllowed.objects.filter(email=user.email, auto_enroll=True):
                    CourseEnrollment.enroll(user, cea.course_id)

        # Now, deal with course administration packets.
        course_admin_tag = attr.find(CAS + 'course_administration_update', NSMAP)

        if course_admin_tag is not None:
            try:
                courses = json.loads(course_admin_tag.text)
                assert isinstance(courses,dict)
            except (ValueError, AssertionError):
                # We failed to parse the tag, so we leave.
                log.error("Could not parse course administration block: <<{}>>".format(course_admin_tag.text))
                return

            from instructor.access import list_with_level, allow_access, revoke_access
            from django_comment_common.models import Role, FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA
            from django.contrib.auth.models import User

            for course_id, admin_block in courses.iteritems():
                try:
                    locator = CourseLocator.from_string(course_id)
                except (InvalidKeyError, AttributeError) as e:
                    log.error("Invalid course identifier {}".format(course_id))
                    continue
                try:
                    course = modulestore().get_course(locator)
                except ItemNotFoundError:
                    log.error("Course {} does not exist.".format(course_id))
                    continue

                if not course:
                    continue

                # Course roles are relatively easy.
                for block_name, role in [('admin','instructor'), ('staff','staff'), ('beta','beta')]:
                    role_list = admin_block.get(block_name,[])
                    existing = list_with_level(course,role)

                    for username in role_list:
                        try:
                            user = User.objects.get(username=username)
                        except User.DoesNotExist:
                            continue
                        if not user in existing:
                            allow_access(course, user, role)
                            try:
                                CourseEnrollment.enroll(user, locator)
                            except:
                                pass
                    for user in existing:
                        if not user.username in role_list:
                            revoke_access(course, user, role)

                # Forum roles, considerably different.

                for block_name, rolename in [('forum_admin',FORUM_ROLE_ADMINISTRATOR), ('forum_moderator',FORUM_ROLE_MODERATOR), ('forum_assistant',FORUM_ROLE_COMMUNITY_TA)]:
                    role_list = admin_block.get(block_name,[])
                    try:
                        role = Role.objects.get(course_id=locator, name=rolename)
                    except Role.DoesNotExist:
                        continue
                    existing = role.users.all()

                    for user in existing:
                        if not user.username in role_list:
                            role.users.remove(user)
                    for username in role_list:
                        try:
                            user = User.objects.get(username=username)
                        except User.DoesNotExist:
                            continue
                        if not user in existing:
                            role.users.add(user)
                            try:
                                CourseEnrollment.enroll(user, locator)
                            except:
                                pass

    pass
Esempio n. 28
0
 def test_revoke_beta(self):
     user = self.beta_testers[0]
     revoke_access(self.course, user, 'beta')
     self.assertNotIn(user, list_with_level(self.course, 'beta'))
 def test_revoke_twice(self):
     user = self.staff[0]
     revoke_access(self.course, user, "staff")
     self.assertFalse(CourseStaffRole(self.course.location).has_user(user))
Esempio n. 30
0
def modify_access(request, course_id):
    """
    Modify staff/instructor access of other user.
    Requires instructor access.

    NOTE: instructors cannot remove their own instructor access.

    Query parameters:
    unique_student_identifer is the target user's username or email
    rolename is one of ['instructor', 'staff', 'beta']
    action is one of ['allow', 'revoke']
    """
    course = get_course_with_access(
        request.user, course_id, 'instructor', depth=None
    )
    try:
        user = get_student_from_identifier(request.GET.get('unique_student_identifier'))
    except User.DoesNotExist:
        response_payload = {
            'unique_student_identifier': request.GET.get('unique_student_identifier'),
            'userDoesNotExist': True,
        }
        return JsonResponse(response_payload)

    # Check that user is active, because add_users
    # in common/djangoapps/student/roles.py fails
    # silently when we try to add an inactive user.
    if not user.is_active:
        response_payload = {
            'unique_student_identifier': user.username,
            'inactiveUser': True,
        }
        return JsonResponse(response_payload)

    rolename = request.GET.get('rolename')
    action = request.GET.get('action')

    if not rolename in ['instructor', 'staff', 'beta']:
        return HttpResponseBadRequest(strip_tags(
            "unknown rolename '{}'".format(rolename)
        ))

    # disallow instructors from removing their own instructor access.
    if rolename == 'instructor' and user == request.user and action != 'allow':
        response_payload = {
            'unique_student_identifier': user.username,
            'rolename': rolename,
            'action': action,
            'removingSelfAsInstructor': True,
        }
        return JsonResponse(response_payload)

    if action == 'allow':
        allow_access(course, user, rolename)
    elif action == 'revoke':
        revoke_access(course, user, rolename)
    else:
        return HttpResponseBadRequest(strip_tags(
            "unrecognized action '{}'".format(action)
        ))

    response_payload = {
        'unique_student_identifier': user.username,
        'rolename': rolename,
        'action': action,
        'success': 'yes',
    }
    return JsonResponse(response_payload)
Esempio n. 31
0
 def test_revoke_badrolename(self):
     user = UserFactory()
     revoke_access(self.course, user, 'robot-not-a-level')
Esempio n. 32
0
 def test_revoke(self):
     user = self.staff[0]
     revoke_access(self.course, user, 'staff')
     group = Group.objects.get(
         name=get_access_group_name(self.course, 'staff'))
     self.assertNotIn(user, group.user_set.all())
Esempio n. 33
0
 def test_revoke_twice(self):
     user = self.staff[0]
     revoke_access(self.course, user, 'staff')
     self.assertFalse(CourseStaffRole(self.course.id).has_user(user))
 def test_revoke_beta(self):
     user = self.beta_testers[0]
     revoke_access(self.course, user, "beta")
     self.assertFalse(CourseBetaTesterRole(self.course.location).has_user(user))