def post(self): if not current_app.config.get('DEMO_INSTALLATION', False): abort(404, title="Demo Accounts Unavailable", message="Sorry, the system settings do now allow the use of demo accounts.") params = new_user_demo_parser.parse_args() user = User() user.password = "******" system_role = params.get("system_role") check_valid_system_role(system_role) user.system_role = SystemRole(system_role) user_count = User.query \ .filter_by(system_role=user.system_role) \ .count() user_count += 1 # username while True: if user.system_role == SystemRole.sys_admin: user.username="******"+str(user_count) elif user.system_role == SystemRole.instructor: user.username="******"+str(user_count) else: user.username="******"+str(user_count) username_exists = User.query.filter_by(username=user.username).first() if not username_exists: break else: user_count+=1 if user.system_role == SystemRole.sys_admin: user.firstname = "Admin" user.lastname = str(user_count) user.displayname = "Admin "+str(user_count) elif user.system_role == SystemRole.instructor: user.firstname = "Instructor" user.lastname = str(user_count) user.displayname = "Instructor "+str(user_count) # create new enrollment new_user_course = UserCourse( user=user, course_id=1, course_role=CourseRole.instructor ) db.session.add(new_user_course) else: user.firstname = "Student" user.lastname = str(user_count) user.displayname = display_name_generator() while True: user.student_number = random_generator(8, string.digits) student_number_exists = User.query.filter_by(student_number=user.student_number).first() if not student_number_exists: break # create new enrollment new_user_course = UserCourse( user=user, course_id=1, course_role=CourseRole.student ) db.session.add(new_user_course) try: db.session.add(user) db.session.commit() on_user_demo_create.send( self, event_name=on_user_demo_create.name, user=current_user, data=marshal(user, dataformat.get_full_user())) except exc.IntegrityError: db.session.rollback() current_app.logger.error("Failed to add new user. Duplicate.") return {'error': 'A user with the same identifier already exists.'}, 400 authenticate(user, login_method="Demo") return marshal(user, dataformat.get_full_user())
def post(self): # login_required when lti_create_user_link not set if not sess.get('lti_create_user_link') and not current_user.is_authenticated: return current_app.login_manager.unauthorized() user = User() params = new_user_parser.parse_args() user.student_number = params.get("student_number", None) user.email = params.get("email") user.firstname = params.get("firstname") user.lastname = params.get("lastname") user.displayname = params.get("displayname") email_notification_method = params.get("email_notification_method") check_valid_email_notification_method(email_notification_method) user.email_notification_method = EmailNotificationMethod(email_notification_method) if not current_app.config.get('APP_LOGIN_ENABLED'): # if APP_LOGIN_ENABLED is not enabled, allow blank username and password user.username = None user.password = None else: # else enforce required password and unique username user.password = params.get("password") if user.password == None: abort(400, title="User Not Saved", message="A password is required. Please enter a password and try saving again.") elif len(params.get("password")) < 4: abort(400, title="User Not Saved", message="The password must be at least 4 characters long.") user.username = params.get("username") if user.username == None: abort(400, title="User Not Saved", message="A username is required. Please enter a username and try saving again.") username_exists = User.query.filter_by(username=user.username).first() if username_exists: abort(409, title="User Not Saved", message="Sorry, this username already exists and usernames must be unique in ComPAIR. Please enter another username and try saving again.") student_number_exists = User.query.filter_by(student_number=user.student_number).first() # if student_number is not left blank and it exists -> 409 error if user.student_number is not None and student_number_exists: abort(409, title="User Not Saved", message="Sorry, this student number already exists and student numbers must be unique in ComPAIR. Please enter another number and try saving again.") # handle lti_create_user_link setup for third party logins if sess.get('lti_create_user_link') and sess.get('LTI'): lti_user = LTIUser.query.get_or_404(sess['lti_user']) lti_user.compair_user = user user.system_role = lti_user.system_role lti_user.update_user_profile() if sess.get('lti_context') and sess.get('lti_user_resource_link'): lti_context = LTIContext.query.get_or_404(sess['lti_context']) lti_user_resource_link = LTIUserResourceLink.query.get_or_404(sess['lti_user_resource_link']) if lti_context.is_linked_to_course(): # create new enrollment new_user_course = UserCourse( user=user, course_id=lti_context.compair_course_id, course_role=lti_user_resource_link.course_role ) db.session.add(new_user_course) else: system_role = params.get("system_role") check_valid_system_role(system_role) user.system_role = SystemRole(system_role) require(CREATE, user, title="User Not Saved", message="Sorry, your role does not allow you to save users.") # only students can have student numbers if user.system_role != SystemRole.student: user.student_number = None try: db.session.add(user) db.session.commit() if current_user.is_authenticated: on_user_create.send( self, event_name=on_user_create.name, user=current_user, data=marshal(user, dataformat.get_full_user())) else: on_user_create.send( self, event_name=on_user_create.name, data=marshal(user, dataformat.get_full_user())) except exc.IntegrityError: db.session.rollback() current_app.logger.error("Failed to add new user. Duplicate.") abort(409, title="User Not Saved", message="Sorry, this ID already exists and IDs must be unique in ComPAIR. Please try addding another user.") # handle lti_create_user_link teardown for third party logins if sess.get('lti_create_user_link'): authenticate(user, login_method='LTI') sess.pop('lti_create_user_link') return marshal_user_data(user)
def import_users(import_type, course, users): invalids = [] # invalid entries - eg. invalid # of columns count = 0 # store number of successful enrolments imported_users = [] set_user_passwords = [] # store unique user identifiers - eg. student number - throws error if duplicate in file import_usernames = [] import_student_numbers = [] # store unique user identifiers - eg. student number - throws error if duplicate in file existing_system_usernames = _get_existing_users_by_identifier( import_type, users) existing_system_student_numbers = _get_existing_users_by_student_number( import_type, users) groups = course.groups.all() groups_by_name = {} for group in groups: groups_by_name[group.name] = group # create / update users in file for user_row in users: if len(user_row) < 1: continue # skip empty row user = _parse_user_row(import_type, user_row) # validate unique identifier username = user.get('username') password = user.get( 'password' ) #always None for CAS/SAML import, can be None for existing users on ComPAIR import student_number = user.get('student_number') u = existing_system_usernames.get(username, None) if not username: invalids.append({ 'user': User(username=username), 'message': 'The username is required.' }) continue elif username in import_usernames: invalids.append({ 'user': User(username=username), 'message': 'This username already exists in the file.' }) continue if u: # overwrite password if user has not logged in yet if u.last_online == None and not password in [None, '*']: set_user_passwords.append((u, password)) else: u = User(username=None, password=None, student_number=user.get('student_number'), firstname=user.get('firstname'), lastname=user.get('lastname'), email=user.get('email')) if import_type == ThirdPartyType.cas.value or import_type == ThirdPartyType.saml.value: # CAS/SAML login u.third_party_auths.append( ThirdPartyUser( unique_identifier=username, third_party_type=ThirdPartyType(import_type))) else: # ComPAIR login u.username = username if password in [None, '*']: invalids.append({ 'user': u, 'message': 'The password is required.' }) continue elif len(password) < 4: invalids.append({ 'user': u, 'message': 'The password must be at least 4 characters long.' }) continue else: set_user_passwords.append((u, password)) # validate student number (if not None) if student_number: # invalid if already showed up in file if student_number in import_student_numbers: u.username = username invalids.append({ 'user': u, 'message': 'This student number already exists in the file.' }) continue # invalid if student number already exists in the system elif student_number in existing_system_student_numbers: u.username = username invalids.append({ 'user': u, 'message': 'This student number already exists in the system.' }) continue u.system_role = SystemRole.student u.displayname = user.get('displayname') if user.get( 'displayname') else display_name_generator() db.session.add(u) import_usernames.append(username) if student_number: import_student_numbers.append(student_number) imported_users.append((u, user.get('group'))) db.session.commit() enroled = UserCourse.query \ .filter_by(course_id=course.id) \ .all() enroled = {e.user_id: e for e in enroled} students = UserCourse.query \ .filter_by( course_id=course.id, course_role=CourseRole.student ) \ .all() students = {s.user_id: s for s in students} # enrol valid users in file for user, group_name in imported_users: enrol = enroled.get(user.id, UserCourse(course_id=course.id, user_id=user.id)) enrol.group = None if group_name: group = groups_by_name.get(group_name) # add new groups if needed if not group: group = Group(course=course, name=group_name) groups_by_name[group_name] = group db.session.add(group) enrol.group = group # do not overwrite instructor or teaching assistant roles if enrol.course_role not in [ CourseRole.instructor, CourseRole.teaching_assistant ]: enrol.course_role = CourseRole.student if user.id in students: del students[user.id] count += 1 db.session.add(enrol) db.session.commit() # unenrol users not in file anymore for user_id in students: enrolment = students.get(user_id) # skip users that are already dropped if enrolment.course_role == CourseRole.dropped: continue enrolment.course_role = CourseRole.dropped enrolment.group_id = None db.session.add(enrolment) db.session.commit() # wait until user ids are generated before starting background jobs # perform password update in chunks of 100 chunk_size = 100 chunks = [ set_user_passwords[index:index + chunk_size] for index in range(0, len(set_user_passwords), chunk_size) ] for chunk in chunks: set_passwords.delay({user.id: password for (user, password) in chunk}) on_classlist_upload.send(current_app._get_current_object(), event_name=on_classlist_upload.name, user=current_user, course_id=course.id) return { 'success': count, 'invalids': marshal(invalids, dataformat.get_import_users_results(False)) }
def post(self): if not current_app.config.get('DEMO_INSTALLATION', False): abort( 404, title="Demo Accounts Unavailable", message= "Sorry, the system settings do now allow the use of demo accounts." ) params = new_user_demo_parser.parse_args() user = User() user.password = "******" system_role = params.get("system_role") check_valid_system_role(system_role) user.system_role = SystemRole(system_role) user_count = User.query \ .filter_by(system_role=user.system_role) \ .count() user_count += 1 # username while True: if user.system_role == SystemRole.sys_admin: user.username = "******" + str(user_count) elif user.system_role == SystemRole.instructor: user.username = "******" + str(user_count) else: user.username = "******" + str(user_count) username_exists = User.query.filter_by( username=user.username).first() if not username_exists: break else: user_count += 1 if user.system_role == SystemRole.sys_admin: user.firstname = "Admin" user.lastname = str(user_count) user.displayname = "Admin " + str(user_count) elif user.system_role == SystemRole.instructor: user.firstname = "Instructor" user.lastname = str(user_count) user.displayname = "Instructor " + str(user_count) # create new enrollment new_user_course = UserCourse(user=user, course_id=1, course_role=CourseRole.instructor) db.session.add(new_user_course) else: user.firstname = "Student" user.lastname = str(user_count) user.displayname = display_name_generator() while True: user.student_number = random_generator(8, string.digits) student_number_exists = User.query.filter_by( student_number=user.student_number).first() if not student_number_exists: break # create new enrollment new_user_course = UserCourse(user=user, course_id=1, course_role=CourseRole.student) db.session.add(new_user_course) try: db.session.add(user) db.session.commit() on_user_demo_create.send(self, event_name=on_user_demo_create.name, user=current_user, data=marshal(user, dataformat.get_full_user())) except exc.IntegrityError: db.session.rollback() current_app.logger.error("Failed to add new user. Duplicate.") return { 'error': 'A user with the same identifier already exists.' }, 400 authenticate(user, login_method="Demo") return marshal(user, dataformat.get_full_user())
def post(self): # login_required when oauth_create_user_link not set if not sess.get('oauth_create_user_link'): if not current_app.login_manager._login_disabled and \ not current_user.is_authenticated: return current_app.login_manager.unauthorized() user = User() params = new_user_parser.parse_args() user.student_number = params.get("student_number", None) user.email = params.get("email") user.firstname = params.get("firstname") user.lastname = params.get("lastname") user.displayname = params.get("displayname") email_notification_method = params.get("email_notification_method") check_valid_email_notification_method(email_notification_method) user.email_notification_method = EmailNotificationMethod( email_notification_method) # if creating a cas user, do not set username or password if sess.get('oauth_create_user_link') and sess.get('LTI') and sess.get( 'CAS_CREATE'): user.username = None user.password = None else: # else enforce required password and unique username user.password = params.get("password") if user.password == None: abort( 400, title="User Not Saved", message= "A password is required. Please enter a password and try saving again." ) user.username = params.get("username") if user.username == None: abort( 400, title="User Not Saved", message= "A username is required. Please enter a username and try saving again." ) username_exists = User.query.filter_by( username=user.username).first() if username_exists: abort( 409, title="User Not Saved", message= "Sorry, this username already exists and usernames must be unique in ComPAIR. Please enter another username and try saving again." ) student_number_exists = User.query.filter_by( student_number=user.student_number).first() # if student_number is not left blank and it exists -> 409 error if user.student_number is not None and student_number_exists: abort( 409, title="User Not Saved", message= "Sorry, this student number already exists and student numbers must be unique in ComPAIR. Please enter another number and try saving again." ) # handle oauth_create_user_link setup for third party logins if sess.get('oauth_create_user_link'): login_method = None if sess.get('LTI'): lti_user = LTIUser.query.get_or_404(sess['lti_user']) lti_user.compair_user = user user.system_role = lti_user.system_role login_method = 'LTI' if sess.get('lti_context') and sess.get( 'lti_user_resource_link'): lti_context = LTIContext.query.get_or_404( sess['lti_context']) lti_user_resource_link = LTIUserResourceLink.query.get_or_404( sess['lti_user_resource_link']) if lti_context.is_linked_to_course(): # create new enrollment new_user_course = UserCourse( user=user, course_id=lti_context.compair_course_id, course_role=lti_user_resource_link.course_role) db.session.add(new_user_course) if sess.get('CAS_CREATE'): thirdpartyuser = ThirdPartyUser( third_party_type=ThirdPartyType.cas, unique_identifier=sess.get('CAS_UNIQUE_IDENTIFIER'), params=sess.get('CAS_PARAMS'), user=user) login_method = ThirdPartyType.cas.value db.session.add(thirdpartyuser) else: system_role = params.get("system_role") check_valid_system_role(system_role) user.system_role = SystemRole(system_role) require( CREATE, user, title="User Not Saved", message="Sorry, your role does not allow you to save users.") # only students can have student numbers if user.system_role != SystemRole.student: user.student_number = None try: db.session.add(user) db.session.commit() if current_user.is_authenticated: on_user_create.send(self, event_name=on_user_create.name, user=current_user, data=marshal(user, dataformat.get_user(False))) else: on_user_create.send(self, event_name=on_user_create.name, data=marshal(user, dataformat.get_user(False))) except exc.IntegrityError: db.session.rollback() current_app.logger.error("Failed to add new user. Duplicate.") abort( 409, title="User Not Saved", message= "Sorry, this ID already exists and IDs must be unique in ComPAIR. Please try addding another user." ) # handle oauth_create_user_link teardown for third party logins if sess.get('oauth_create_user_link'): authenticate(user, login_method=login_method) sess.pop('oauth_create_user_link') if sess.get('CAS_CREATE'): sess.pop('CAS_CREATE') sess.pop('CAS_UNIQUE_IDENTIFIER') sess['CAS_LOGIN'] = True return marshal(user, dataformat.get_user())
def import_users(import_type, course, users): invalids = [] # invalid entries - eg. invalid # of columns count = 0 # store number of successful enrolments imported_users = [] set_user_passwords = [] # store unique user identifiers - eg. student number - throws error if duplicate in file import_usernames = [] import_student_numbers = [] # store unique user identifiers - eg. student number - throws error if duplicate in file existing_system_usernames = _get_existing_users_by_identifier(import_type, users) existing_system_student_numbers = _get_existing_users_by_student_number(import_type, users) groups = course.groups.all() groups_by_name = {} for group in groups: groups_by_name[group.name] = group # create / update users in file for user_row in users: if len(user_row) < 1: continue # skip empty row user = _parse_user_row(import_type, user_row) # validate unique identifier username = user.get('username') password = user.get('password') #always None for CAS/SAML import, can be None for existing users on ComPAIR import student_number = user.get('student_number') u = existing_system_usernames.get(username, None) if not username: invalids.append({'user': User(username=username), 'message': 'The username is required.'}) continue elif username in import_usernames: invalids.append({'user': User(username=username), 'message': 'This username already exists in the file.'}) continue if u: # overwrite password if user has not logged in yet if u.last_online == None and not password in [None, '*']: set_user_passwords.append((u, password)) else: u = User( username=None, password=None, student_number=user.get('student_number'), firstname=user.get('firstname'), lastname=user.get('lastname'), email=user.get('email') ) if import_type == ThirdPartyType.cas.value or import_type == ThirdPartyType.saml.value: # CAS/SAML login u.third_party_auths.append(ThirdPartyUser( unique_identifier=username, third_party_type=ThirdPartyType(import_type) )) else: # ComPAIR login u.username = username if password in [None, '*']: invalids.append({'user': u, 'message': 'The password is required.'}) continue elif len(password) < 4: invalids.append({'user': u, 'message': 'The password must be at least 4 characters long.'}) continue else: set_user_passwords.append((u, password)) # validate student number (if not None) if student_number: # invalid if already showed up in file if student_number in import_student_numbers: u.username = username invalids.append({'user': u, 'message': 'This student number already exists in the file.'}) continue # invalid if student number already exists in the system elif student_number in existing_system_student_numbers: u.username = username invalids.append({'user': u, 'message': 'This student number already exists in the system.'}) continue u.system_role = SystemRole.student u.displayname = user.get('displayname') if user.get('displayname') else display_name_generator() db.session.add(u) import_usernames.append(username) if student_number: import_student_numbers.append(student_number) imported_users.append( (u, user.get('group')) ) db.session.commit() enroled = UserCourse.query \ .filter_by(course_id=course.id) \ .all() enroled = {e.user_id: e for e in enroled} students = UserCourse.query \ .filter_by( course_id=course.id, course_role=CourseRole.student ) \ .all() students = {s.user_id: s for s in students} # enrol valid users in file for user, group_name in imported_users: enrol = enroled.get(user.id, UserCourse(course_id=course.id, user_id=user.id)) enrol.group = None if group_name: group = groups_by_name.get(group_name) # add new groups if needed if not group: group = Group( course=course, name=group_name ) groups_by_name[group_name] = group db.session.add(group) enrol.group = group # do not overwrite instructor or teaching assistant roles if enrol.course_role not in [CourseRole.instructor, CourseRole.teaching_assistant]: enrol.course_role = CourseRole.student if user.id in students: del students[user.id] count += 1 db.session.add(enrol) db.session.commit() # unenrol users not in file anymore for user_id in students: enrolment = students.get(user_id) # skip users that are already dropped if enrolment.course_role == CourseRole.dropped: continue enrolment.course_role = CourseRole.dropped enrolment.group_id = None db.session.add(enrolment) db.session.commit() # wait until user ids are generated before starting background jobs # perform password update in chunks of 100 chunk_size = 100 chunks = [set_user_passwords[index:index + chunk_size] for index in range(0, len(set_user_passwords), chunk_size)] for chunk in chunks: set_passwords.delay({ user.id: password for (user, password) in chunk }) on_classlist_upload.send( current_app._get_current_object(), event_name=on_classlist_upload.name, user=current_user, course_id=course.id ) return { 'success': count, 'invalids': marshal(invalids, dataformat.get_import_users_results(False)) }
def post(self): # login_required when oauth_create_user_link not set if not sess.get('oauth_create_user_link'): if not current_app.login_manager._login_disabled and \ not current_user.is_authenticated: return current_app.login_manager.unauthorized() user = User() params = new_user_parser.parse_args() user.student_number = params.get("student_number", None) user.email = params.get("email") user.firstname = params.get("firstname") user.lastname = params.get("lastname") user.displayname = params.get("displayname") # if creating a cas user, do not set username or password if sess.get('oauth_create_user_link') and sess.get('LTI') and sess.get('CAS_CREATE'): user.username = None user.password = None else: # else enforce required password and unique username user.password = params.get("password") if user.password == None: return {"error": "Missing required parameter: password."}, 400 user.username = params.get("username") if user.username == None: return {"error": "Missing required parameter: username."}, 400 username_exists = User.query.filter_by(username=user.username).first() if username_exists: return {"error": "This username already exists. Please pick another."}, 409 student_number_exists = User.query.filter_by(student_number=user.student_number).first() # if student_number is not left blank and it exists -> 409 error if user.student_number is not None and student_number_exists: return {"error": "This student number already exists. Please pick another."}, 409 # handle oauth_create_user_link setup for third party logins if sess.get('oauth_create_user_link'): login_method = None if sess.get('LTI'): lti_user = LTIUser.query.get_or_404(sess['lti_user']) lti_user.compair_user = user user.system_role = lti_user.system_role login_method = 'LTI' if sess.get('lti_context') and sess.get('lti_user_resource_link'): lti_context = LTIContext.query.get_or_404(sess['lti_context']) lti_user_resource_link = LTIUserResourceLink.query.get_or_404(sess['lti_user_resource_link']) if lti_context.is_linked_to_course(): # create new enrollment new_user_course = UserCourse( user=user, course_id=lti_context.compair_course_id, course_role=lti_user_resource_link.course_role ) db.session.add(new_user_course) if sess.get('CAS_CREATE'): thirdpartyuser = ThirdPartyUser( third_party_type=ThirdPartyType.cas, unique_identifier=sess.get('CAS_UNIQUE_IDENTIFIER'), params=sess.get('CAS_ADDITIONAL_PARAMS'), user=user ) login_method = ThirdPartyType.cas.value db.session.add(thirdpartyuser) else: system_role = params.get("system_role") check_valid_system_role(system_role) user.system_role = SystemRole(system_role) require(CREATE, user) # only students can have student numbers if user.system_role != SystemRole.student: user.student_number = None try: db.session.add(user) db.session.commit() if current_user.is_authenticated: on_user_create.send( self, event_name=on_user_create.name, user=current_user, data=marshal(user, dataformat.get_user(False))) else: on_user_create.send( self, event_name=on_user_create.name, data=marshal(user, dataformat.get_user(False))) except exc.IntegrityError: db.session.rollback() current_app.logger.error("Failed to add new user. Duplicate.") return {'error': 'A user with the same identifier already exists.'}, 400 # handle oauth_create_user_link teardown for third party logins if sess.get('oauth_create_user_link'): authenticate(user, login_method=login_method) sess.pop('oauth_create_user_link') if sess.get('CAS_CREATE'): sess.pop('CAS_CREATE') sess.pop('CAS_UNIQUE_IDENTIFIER') sess['CAS_LOGIN'] = True return marshal(user, dataformat.get_user())