def test_allow(self): user = UserFactory() allow_access(self.course, user, 'staff') group = Group.objects.get( name=get_access_group_name(self.course, 'staff') ) self.assertIn(user, group.user_set.all())
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 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)
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, unicode(ccx.id)) with ccx_course(ccx_locator) as course: 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".', unicode(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) )
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 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)) with ccx_course(ccx_locator) as course: 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))
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)
def StaffFactory(course): # pylint: disable=invalid-name """ Given a course object, returns a User object with staff permissions for `course`. """ user = StudentUserFactory.create(last_name="Staff") allow_access(course, user, "staff") return user
def setUp(self): super(TestInstructorAccessRevoke, self).setUp() self.staff = [UserFactory.create() for _ in xrange(4)] for user in self.staff: allow_access(self.course, user, 'staff') self.beta_testers = [UserFactory.create() for _ in xrange(4)] for user in self.beta_testers: allow_access(self.course, user, 'beta')
def setUp(self): super(TestInstructorAccessList, self).setUp() self.instructors = [UserFactory.create() for _ in xrange(4)] for user in self.instructors: allow_access(self.course, user, 'instructor') self.beta_testers = [UserFactory.create() for _ in xrange(4)] for user in self.beta_testers: allow_access(self.course, user, 'beta')
def setUp(self): self.course = CourseFactory.create() self.staff = [UserFactory.create() for _ in xrange(4)] for user in self.staff: allow_access(self.course, user, 'staff') self.beta_testers = [UserFactory.create() for _ in xrange(4)] for user in self.beta_testers: allow_access(self.course, user, 'beta')
def setUp(self): self.course = CourseFactory.create() self.instructors = [UserFactory.create() for _ in xrange(4)] for user in self.instructors: allow_access(self.course, user, "instructor") self.beta_testers = [UserFactory.create() for _ in xrange(4)] for user in self.beta_testers: allow_access(self.course, user, "beta")
def setUp(self): super(TestCoachDashboardSchedule, self).setUp() self.course = course = CourseFactory.create() # Create a course outline self.mooc_start = start = datetime.datetime( 2010, 5, 12, 2, 42, tzinfo=pytz.UTC ) self.mooc_due = due = datetime.datetime( 2010, 7, 7, 0, 0, tzinfo=pytz.UTC ) self.chapters = [ ItemFactory.create(start=start, parent=course) for _ in xrange(2) ] self.sequentials = flatten([ [ ItemFactory.create(parent=chapter) for _ in xrange(2) ] for chapter in self.chapters ]) self.verticals = flatten([ [ ItemFactory.create( start=start, due=due, parent=sequential, graded=True, format='Homework', category=u'vertical' ) for _ in xrange(2) ] for sequential in self.sequentials ]) # Trying to wrap the whole thing in a bulk operation fails because it # doesn't find the parents. But we can at least wrap this part... with self.store.bulk_operations(course.id, emit_signals=False): blocks = flatten([ # pylint: disable=unused-variable [ ItemFactory.create(parent=vertical) for _ in xrange(2) ] for vertical in self.verticals ]) # Create instructor account self.coach = UserFactory.create() # create an instance of modulestore self.mstore = modulestore() # Login with the instructor account self.client.login(username=self.coach.username, password="******") # adding staff to master course. staff = UserFactory() allow_access(self.course, staff, 'staff') self.assertTrue(CourseStaffRole(self.course.id).has_user(staff)) # adding instructor to master course. instructor = UserFactory() allow_access(self.course, instructor, 'instructor') self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor)) self.assertTrue(modulestore().has_course(self.course.id))
def setUp(self): self.instructor = AdminFactory.create() self.course = CourseFactory.create() self.client.login(username=self.instructor.username, password='******') self.other_instructor = UserFactory() allow_access(self.course, self.other_instructor, 'instructor') self.other_staff = UserFactory() allow_access(self.course, self.other_staff, 'staff') self.other_user = UserFactory()
def add_affiliate_course_enrollments(sender, instance, created, **kwargs): 'Allow all affiliate staff and instructors access to this course.' # do this only for new CCX courses if not created: return from courseware.courses import get_course_by_id course = get_course_by_id(instance.ccx_course_id) for membership in instance.affiliate.memberships.exclude(role='ccx_coach'): allow_access(course, membership.member, membership.role, False)
def test_ccx_tab_visibility_for_instructor_ccx_course(self): """ Instructor can access coach dashboard on ccx course. """ self.make_coach() ccx = self.make_ccx() ccx_key = CCXLocator.from_course_locator(self.course.id, unicode(ccx.id)) instructor = self.make_instructor() with ccx_course(ccx_key) as course_ccx: allow_access(course_ccx, instructor, 'instructor') self.assertTrue(self.check_ccx_tab(course_ccx, instructor))
def test_ccx_tab_visibility_for_staff_ccx_course(self): """ Staff can access coach dashboard on ccx course. """ self.make_coach() ccx = self.make_ccx() ccx_key = CCXLocator.from_course_locator(self.course.id, unicode(ccx.id)) staff = self.make_staff() with ccx_course(ccx_key) as course_ccx: allow_access(course_ccx, staff, 'staff') self.assertTrue(self.check_ccx_tab(course_ccx, staff))
def i_am_staff_member_for_the_course(step, course_number): # Create the course create_course(step, course_number) course = get_course_by_id(course_id(course_number)) # Create the user world.create_user('robot', 'test') user = User.objects.get(username='******') # Add user as a course staff. allow_access(course, user, "staff") world.log_in(username='******', password='******')
def test_is_user_staff(self): """ Test to assert that the user is staff or not """ result = self.service.is_course_staff(self.student, unicode(self.course.id)) self.assertFalse(result) # allow staff access to the student allow_access(self.course, self.student, 'staff') result = self.service.is_course_staff(self.student, unicode(self.course.id)) self.assertTrue(result)
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)
def setUp(self): """ Set up tests """ super(TestCoachDashboard, self).setUp() # Login with the instructor account self.client.login(username=self.coach.username, password="******") # adding staff to master course. staff = UserFactory() allow_access(self.course, staff, 'staff') self.assertTrue(CourseStaffRole(self.course.id).has_user(staff)) # adding instructor to master course. instructor = UserFactory() allow_access(self.course, instructor, 'instructor') self.assertTrue(CourseInstructorRole(self.course.id).has_user(instructor))
def assign_coach_role_to_ccx(ccx_locator, user, master_course_id): """ Check if user has ccx_coach role on master course then assign him coach role on ccx only if role is not already assigned. Because of this coach can open dashboard from master course as well as ccx. :param ccx_locator: CCX key :param user: User to whom we want to assign role. :param master_course_id: Master course key """ coach_role_on_master_course = CourseCcxCoachRole(master_course_id) # check if user has coach role on master course if coach_role_on_master_course.has_user(user): # Check if user has coach role on ccx. role = CourseCcxCoachRole(ccx_locator) if not role.has_user(user): # assign user role coach on ccx with ccx_course(ccx_locator) as course: allow_access(course, user, "ccx_coach", send_email=False)
def add_master_course_staff_to_ccx(master_course, ccx_key, display_name): """ Added staff role on ccx to all the staff 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 """ 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 allow_access(course_ccx, staff, 'staff') # Enroll the staff in the ccx enroll_email( course_id=ccx_key, student_email=staff.email, auto_enroll=True, email_students=True, email_params=email_params, ) for instructor in list_instructor: # allow 'instructor' access on ccx to instructor of master course allow_access(course_ccx, instructor, 'instructor') # Enroll the instructor in the ccx enroll_email( course_id=ccx_key, student_email=instructor.email, auto_enroll=True, email_students=True, email_params=email_params, )
def setUp(self): """ Set up tests """ super(CcxRestApiTest, self).setUp() # add some info about the course for easy access self.master_course_key = self.course.location.course_key self.master_course_key_str = unicode(self.master_course_key) # OAUTH2 setup # create a specific user for the application app_user = UserFactory(username='******', email='*****@*****.**', password='******') # add staff role to the app user CourseStaffRole(self.master_course_key).add_users(app_user) # adding instructor to master course. instructor = UserFactory() allow_access(self.course, instructor, 'instructor') # create an oauth client app entry self.app_client = Client.objects.create( user=app_user, name='test client', url='http://localhost//', redirect_uri='http://localhost//', client_type=CONFIDENTIAL ) # create an authorization code self.app_grant = Grant.objects.create( user=app_user, client=self.app_client, redirect_uri='http://localhost//' ) self.course.enable_ccx = True self.mstore.update_item(self.course, self.coach.id) self.auth = self.get_auth_token() # making the master course chapters easily available self.master_course_chapters = get_course_chapters(self.master_course_key)
def add_affiliate_course_enrollments(sender, instance, **kwargs): # pylint: disable=unused-argument """ Allow staff or instructor level access to affiliate member into all affiliate courses if they are staff or instructor member. """ if not instance.role == AffiliateMembership.CCX_COACH: for ccx in instance.affiliate.courses: ccx_locator = CCXLocator.from_course_locator(ccx.course_id, ccx.id) course = get_course_by_id(ccx_locator) try: with transaction.atomic(): allow_access(course, instance.member, instance.role, False) except IntegrityError: LOG.error('IntegrityError: Allow access failed.') # FastTrac main course and Facilitator Guide course course_overviews = CourseOverview.objects.exclude(id__startswith='ccx-') # Program Director and Course Manager needs to be a CCX coach on FastTrac course if instance.role in AffiliateMembership.STAFF_ROLES: for course_overview in course_overviews: course_id = course_overview.id course = get_course_by_id(course_id) try: with transaction.atomic(): allow_access(course, instance.member, AffiliateMembership.CCX_COACH, False) except IntegrityError: LOG.error('IntegrityError: CCX coach failed.') elif instance.role == AffiliateMembership.CCX_COACH: for course_overview in course_overviews: course_id = course_overview.id enroll_email(course_id, instance.member.email, auto_enroll=True)
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)
def test_allow_badlevel(self): user = UserFactory() allow_access(self.course, user, 'robot-not-a-level') group = Group.objects.get(name=get_access_group_name(self.course, 'robot-not-a-level')) self.assertIn(user, group.user_set.all())
def test_allow_ccx_coach(self): user = UserFactory() allow_access(self.course, user, 'ccx_coach') self.assertTrue(CourseCcxCoachRole(self.course.id).has_user(user))
def test_allow_noneuser(self): user = None allow_access(self.course, user, 'staff')
def test_allow_badlevel(self): user = UserFactory() allow_access(self.course, user, 'robot-not-a-level')
def test_allow_beta(self): """ Test allow beta against list beta. """ user = UserFactory() allow_access(self.course, user, 'beta') self.assertTrue(CourseBetaTesterRole(self.course.id).has_user(user))
def test_allow_twice(self): user = UserFactory() allow_access(self.course, user, 'staff') allow_access(self.course, user, 'staff') self.assertTrue(CourseStaffRole(self.course.id).has_user(user))
def add_master_course_staff_to_ccx(master_course, ccx_key, display_name, send_email=True): """ Add 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 access grant. """ 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) list_staff_ccx = list_with_level(course_ccx, 'staff') list_instructor_ccx = list_with_level(course_ccx, 'instructor') for staff in list_staff: # this call should be idempotent if staff not in list_staff_ccx: try: # Enroll the staff in the ccx enroll_email( course_id=ccx_key, student_email=staff.email, auto_enroll=True, email_students=send_email, email_params=email_params, ) # allow 'staff' access on ccx to staff of master course allow_access(course_ccx, staff, 'staff') except CourseEnrollmentException: log.warning( "Unable to enroll staff %s to course with id %s", staff.email, ccx_key) continue except SMTPException: continue for instructor in list_instructor: # this call should be idempotent if instructor not in list_instructor_ccx: try: # Enroll the instructor in the ccx enroll_email( course_id=ccx_key, student_email=instructor.email, auto_enroll=True, email_students=send_email, email_params=email_params, ) # allow 'instructor' access on ccx to instructor of master course allow_access(course_ccx, instructor, 'instructor') except CourseEnrollmentException: log.warning( "Unable to enroll instructor %s to course with id %s", instructor.email, ccx_key) continue except SMTPException: continue
def test_allow_noneuser(self): user = None allow_access(self.course, user, 'staff') group = Group.objects.get(name=get_access_group_name(self.course, 'staff')) self.assertIn(user, group.user_set.all())
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)