def post(self): """ Kickstarts the LTI integration flow. """ if not current_app.config.get('LTI_LOGIN_ENABLED'): abort(403, title="Log In Failed", message="Please try an alternate way of logging in. The LTI login has been disabled by your system administrator.") try: tool_provider = FlaskToolProvider.from_flask_request(request=request) except InvalidLaunchParamError as e: return "Invalid Request: {}".format(str(e)), 400 validator = ComPAIRRequestValidator() if not tool_provider.is_valid_request(validator): return _return_validation_error(tool_provider, "Invalid Request: There is something wrong with the LTI tool consumer's request.") if tool_provider.resource_link_id == None: return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to provide a resource link id.") if tool_provider.user_id == None: return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to provide a user's user_id.") if tool_provider.lti_version not in ["LTI-1p0"]: return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to use the LTI 1.0 or 1.1 specification.") if tool_provider.lti_message_type != "basic-lti-launch-request": return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to send a basic lti launch request.") lti_consumer = LTIConsumer.get_by_tool_provider(tool_provider) params = lti_launch_parser.parse_args() # override custom_assignment if not set in launch body but is in querystring if not tool_provider.custom_assignment and params.get('assignment'): tool_provider.custom_assignment = params.get('assignment') # log current user out if needed logout_user() sess.clear() sess['LTI'] = True sess['lti_consumer'] = lti_consumer.id lti_user = LTIUser.get_by_tool_provider(lti_consumer, tool_provider) sess['lti_user'] = lti_user.id lti_context = LTIContext.get_by_tool_provider(lti_consumer, tool_provider) if lti_context: sess['lti_context'] = lti_context.id lti_resource_link = LTIResourceLink.get_by_tool_provider(lti_consumer, tool_provider, lti_context) sess['lti_resource_link'] = lti_resource_link.id lti_user_resource_link = LTIUserResourceLink.get_by_tool_provider(lti_resource_link, lti_user, tool_provider) sess['lti_user_resource_link'] = lti_user_resource_link.id setup_required = False angular_route = None # if user linked if lti_user.is_linked_to_user(): authenticate(lti_user.compair_user, login_method='LTI') # upgrade user system role if needed lti_user.upgrade_system_role() lti_user.update_user_profile() # create/update enrollment if context exists if lti_context and lti_context.is_linked_to_course(): lti_context.update_enrolment(lti_user.compair_user_id, lti_user_resource_link.course_role) else: # need to create user link sess['lti_create_user_link'] = True setup_required = True if not lti_context: # no context, redriect to home page angular_route = "/" elif lti_context.is_linked_to_course(): # redirect to course page or assignment page if available angular_route = "/course/{}".format(lti_context.compair_course_uuid) if lti_resource_link.is_linked_to_assignment(): angular_route += "/assignment/{}".format(lti_resource_link.compair_assignment_uuid) else: # instructors can select course, students will recieve a warning message setup_required = True if setup_required: # if account/course setup required, redirect to lti controller angular_route = "/lti" elif angular_route == None: # set angular route to home page by default angular_route = "/" return current_app.make_response(redirect("/app/#{}".format(angular_route)))
def post(self): """ Kickstarts the LTI integration flow. """ if not current_app.config.get('LTI_LOGIN_ENABLED'): abort(403, title="Log In Failed", message="Please try an alternate way of logging in. The LTI login has been disabled by your system administrator.") try: tool_provider = FlaskToolProvider.from_flask_request(request=request) except InvalidLaunchParamError as e: return "Invalid Request: {}".format(e.message), 400 validator = ComPAIRRequestValidator() if not tool_provider.is_valid_request(validator): return _return_validation_error(tool_provider, "Invalid Request: There is something wrong with the LTI tool consumer's request.") if tool_provider.resource_link_id == None: return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to provide a resource link id.") if tool_provider.user_id == None: return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to provide a user's user_id.") if tool_provider.lti_version not in ["LTI-1p0"]: return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to use the LTI 1.0 or 1.1 specification.") if tool_provider.lti_message_type != "basic-lti-launch-request": return _return_validation_error(tool_provider, "ComPAIR requires the LTI tool consumer to send a basic lti launch request.") lti_consumer = LTIConsumer.get_by_tool_provider(tool_provider) params = lti_launch_parser.parse_args() # override custom_assignment if not set in launch body but is in querystring if not tool_provider.custom_assignment and params.get('assignment'): tool_provider.custom_assignment = params.get('assignment') # log current user out if needed logout_user() sess.clear() sess['LTI'] = True sess['lti_consumer'] = lti_consumer.id lti_user = LTIUser.get_by_tool_provider(lti_consumer, tool_provider) sess['lti_user'] = lti_user.id lti_context = LTIContext.get_by_tool_provider(lti_consumer, tool_provider) if lti_context: sess['lti_context'] = lti_context.id lti_resource_link = LTIResourceLink.get_by_tool_provider(lti_consumer, tool_provider, lti_context) sess['lti_resource_link'] = lti_resource_link.id lti_user_resource_link = LTIUserResourceLink.get_by_tool_provider(lti_resource_link, lti_user, tool_provider) sess['lti_user_resource_link'] = lti_user_resource_link.id setup_required = False angular_route = None # if user linked if lti_user.is_linked_to_user(): authenticate(lti_user.compair_user, login_method='LTI') # upgrade user system role if needed lti_user.upgrade_system_role() lti_user.update_user_profile() # create/update enrollment if context exists if lti_context and lti_context.is_linked_to_course(): lti_context.update_enrolment(lti_user.compair_user_id, lti_user_resource_link.course_role) else: # need to create user link sess['lti_create_user_link'] = True setup_required = True if not lti_context: # no context, redriect to home page angular_route = "/" elif lti_context.is_linked_to_course(): # redirect to course page or assignment page if available angular_route = "/course/{}".format(lti_context.compair_course_uuid) if lti_resource_link.is_linked_to_assignment(): angular_route += "/assignment/{}".format(lti_resource_link.compair_assignment_uuid) else: # instructors can select course, students will recieve a warning message setup_required = True if setup_required: # if account/course setup required, redirect to lti controller angular_route = "/lti" elif angular_route == None: # set angular route to home page by default angular_route = "/" return current_app.make_response(redirect("/app/#{}".format(angular_route)))
def _update_membership_for_context(cls, lti_context, members): from compair.models import SystemRole, CourseRole, \ LTIUser, LTIUserResourceLink lti_resource_links = lti_context.lti_resource_links # remove old membership rows LTIMembership.query \ .filter_by( lti_context_id=lti_context.id ) \ .delete() # retrieve existing lti_user rows user_ids = [] for member in members: user_ids.append(member['user_id']) existing_lti_users = [] if len(user_ids) > 0: existing_lti_users = LTIUser.query \ .filter(and_( LTIUser.lti_consumer_id == lti_context.lti_consumer_id, LTIUser.user_id.in_(user_ids) )) \ .all() # get existing lti_user_resource_link if there there exists lti users and known resource links for context existing_lti_user_resource_links = [] if len(existing_lti_users) > 0 and len(lti_resource_links) > 0: lti_resource_link_ids = [ lti_resource_link.id for lti_resource_link in lti_resource_links ] existing_lti_user_ids = [ existing_lti_user.id for existing_lti_user in existing_lti_users ] existing_lti_user_resource_links = LTIUserResourceLink.query \ .filter(and_( LTIUserResourceLink.lti_resource_link_id.in_(lti_resource_link_ids), LTIUserResourceLink.lti_user_id.in_(existing_lti_user_ids) )) \ .all() new_lti_users = [] new_lti_user_resource_links = [] lti_memberships = [] for member in members: # get lti user if exists lti_user = next((lti_user for lti_user in existing_lti_users if lti_user.user_id == member.get('user_id')), None) roles = member.get('roles') has_instructor_role = any( role.lower().find("instructor") >= 0 or role.lower().find( "faculty") >= 0 or role.lower().find("staff") >= 0 for role in roles) has_ta_role = any(role.lower().find("teachingassistant") >= 0 for role in roles) # create lti user if doesn't exist if not lti_user: lti_user = LTIUser(lti_consumer_id=lti_context.lti_consumer_id, user_id=member.get('user_id')) new_lti_users.append(lti_user) # update/set fields if needed lti_user.system_role = SystemRole.instructor if has_instructor_role else SystemRole.student lti_user.lis_person_name_given = member.get('person_name_given') lti_user.lis_person_name_family = member.get('person_name_family') lti_user.lis_person_name_full = member.get('person_name_full') lti_user.handle_fullname_with_missing_first_and_last_name() lti_user.lis_person_contact_email_primary = member.get( 'person_contact_email_primary') lti_user.lis_person_sourcedid = member.get('lis_person_sourcedid') if member.get('global_unique_identifier'): lti_user.global_unique_identifier = member.get( 'global_unique_identifier') if member.get('student_number'): lti_user.student_number = member.get('student_number') if not lti_user.is_linked_to_user( ) and lti_user.global_unique_identifier: lti_user.generate_or_link_user_account() course_role = CourseRole.student if has_instructor_role: course_role = CourseRole.instructor elif has_ta_role: course_role = CourseRole.teaching_assistant # create new lti membership row lti_membership = LTIMembership( lti_user=lti_user, lti_context=lti_context, roles=text_type(roles), lis_result_sourcedid=member.get('lis_result_sourcedid'), lis_result_sourcedids=json.dumps( member.get('lis_result_sourcedids')) if member.get('lis_result_sourcedids') else None, course_role=course_role) lti_memberships.append(lti_membership) # if membership includes lis_result_sourcedids, create/update lti user resource links if member.get('lis_result_sourcedids'): for lis_result_sourcedid_set in member.get( 'lis_result_sourcedids'): lti_resource_link = next( lti_resource_link for lti_resource_link in lti_resource_links if lti_resource_link.resource_link_id == lis_result_sourcedid_set['resource_link_id']) if not lti_resource_link: continue lti_user_resource_link = None if len(existing_lti_user_resource_links ) > 0 and lti_user.id: # get lti user resource link if exists lti_user_resource_link = next( (lti_user_resource_link for lti_user_resource_link in existing_lti_user_resource_links if lti_user_resource_link.lti_user_id == lti_user. id and lti_user_resource_link.lti_resource_link_id == lti_resource_link.id), None) # create new lti user resource link if needed if not lti_user_resource_link: lti_user_resource_link = LTIUserResourceLink( lti_resource_link=lti_resource_link, lti_user=lti_user, roles=text_type(roles), course_role=course_role) new_lti_user_resource_links.append( lti_user_resource_link) # finally update the lis_result_sourcedid value for the user resource link lti_user_resource_link.lis_result_sourcedid = lis_result_sourcedid_set[ 'lis_result_sourcedid'] db.session.add_all(new_lti_users) db.session.add_all(existing_lti_users) db.session.add_all(lti_memberships) db.session.add_all(new_lti_user_resource_links) db.session.add_all(existing_lti_user_resource_links) # save new lti users db.session.commit() return lti_memberships
def _update_membership_for_context(cls, lti_context, members): from compair.models import SystemRole, CourseRole, \ LTIUser, LTIUserResourceLink lti_resource_links = lti_context.lti_resource_links # remove old membership rows LTIMembership.query \ .filter_by( lti_context_id=lti_context.id ) \ .delete() # retrieve existing lti_user rows user_ids = [] for member in members: user_ids.append(member['user_id']) existing_lti_users = [] if len(user_ids) > 0: existing_lti_users = LTIUser.query \ .filter(and_( LTIUser.lti_consumer_id == lti_context.lti_consumer_id, LTIUser.user_id.in_(user_ids) )) \ .all() # get existing lti_user_resource_link if there there exists lti users and known resource links for context existing_lti_user_resource_links = [] if len(existing_lti_users) > 0 and len(lti_resource_links) > 0: lti_resource_link_ids = [lti_resource_link.id for lti_resource_link in lti_resource_links] existing_lti_user_ids = [existing_lti_user.id for existing_lti_user in existing_lti_users] existing_lti_user_resource_links = LTIUserResourceLink.query \ .filter(and_( LTIUserResourceLink.lti_resource_link_id.in_(lti_resource_link_ids), LTIUserResourceLink.lti_user_id.in_(existing_lti_user_ids) )) \ .all() new_lti_users = [] new_lti_user_resource_links = [] lti_memberships = [] for member in members: # get lti user if exists lti_user = next( (lti_user for lti_user in existing_lti_users if lti_user.user_id == member.get('user_id')), None ) roles = member.get('roles') has_instructor_role = any( role.lower().find("instructor") >= 0 or role.lower().find("faculty") >= 0 or role.lower().find("staff") >= 0 for role in roles ) has_ta_role = any(role.lower().find("teachingassistant") >= 0 for role in roles) # create lti user if doesn't exist if not lti_user: lti_user = LTIUser( lti_consumer_id=lti_context.lti_consumer_id, user_id=member.get('user_id') ) new_lti_users.append(lti_user) # update/set fields if needed lti_user.system_role = SystemRole.instructor if has_instructor_role else SystemRole.student lti_user.lis_person_name_given = member.get('person_name_given') lti_user.lis_person_name_family = member.get('person_name_family') lti_user.lis_person_name_full = member.get('person_name_full') lti_user.handle_fullname_with_missing_first_and_last_name() lti_user.lis_person_contact_email_primary = member.get('person_contact_email_primary') lti_user.lis_person_sourcedid = member.get('lis_person_sourcedid') if member.get('global_unique_identifier'): lti_user.global_unique_identifier = member.get('global_unique_identifier') if member.get('student_number'): lti_user.student_number = member.get('student_number') if not lti_user.is_linked_to_user() and lti_user.global_unique_identifier: lti_user.generate_or_link_user_account() course_role = CourseRole.student if has_instructor_role: course_role = CourseRole.instructor elif has_ta_role: course_role = CourseRole.teaching_assistant # create new lti membership row lti_membership = LTIMembership( lti_user=lti_user, lti_context=lti_context, roles=text_type(roles), lis_result_sourcedid=member.get('lis_result_sourcedid'), lis_result_sourcedids=json.dumps(member.get('lis_result_sourcedids')) if member.get('lis_result_sourcedids') else None, course_role=course_role ) lti_memberships.append(lti_membership) # if membership includes lis_result_sourcedids, create/update lti user resource links if member.get('lis_result_sourcedids'): for lis_result_sourcedid_set in member.get('lis_result_sourcedids'): lti_resource_link = next( lti_resource_link for lti_resource_link in lti_resource_links if lti_resource_link.resource_link_id == lis_result_sourcedid_set['resource_link_id'] ) if not lti_resource_link: continue lti_user_resource_link = None if len(existing_lti_user_resource_links) > 0 and lti_user.id: # get lti user resource link if exists lti_user_resource_link = next( (lti_user_resource_link for lti_user_resource_link in existing_lti_user_resource_links if lti_user_resource_link.lti_user_id == lti_user.id and lti_user_resource_link.lti_resource_link_id == lti_resource_link.id), None ) # create new lti user resource link if needed if not lti_user_resource_link: lti_user_resource_link = LTIUserResourceLink( lti_resource_link=lti_resource_link, lti_user=lti_user, roles=text_type(roles), course_role=course_role ) new_lti_user_resource_links.append(lti_user_resource_link) # finally update the lis_result_sourcedid value for the user resource link lti_user_resource_link.lis_result_sourcedid = lis_result_sourcedid_set['lis_result_sourcedid'] db.session.add_all(new_lti_users) db.session.add_all(existing_lti_users) db.session.add_all(lti_memberships) db.session.add_all(new_lti_user_resource_links) db.session.add_all(existing_lti_user_resource_links) # save new lti users db.session.commit() return lti_memberships
def post(self): """ Kickstarts the LTI integration flow. """ if not current_app.config.get("LTI_LOGIN_ENABLED"): return abort(403) tool_provider = FlaskToolProvider.from_flask_request(request=request) validator = ComPAIRRequestValidator() ok = tool_provider.is_valid_request(validator) if ok and tool_provider.user_id != None: # log current user out if needed logout_user() sess.clear() sess["LTI"] = True lti_consumer = LTIConsumer.get_by_tool_provider(tool_provider) sess["lti_consumer"] = lti_consumer.id lti_user = LTIUser.get_by_tool_provider(lti_consumer, tool_provider) sess["lti_user"] = lti_user.id lti_context = LTIContext.get_by_tool_provider(lti_consumer, tool_provider) if lti_context: sess["lti_context"] = lti_context.id lti_resource_link = LTIResourceLink.get_by_tool_provider(lti_consumer, tool_provider, lti_context) sess["lti_resource_link"] = lti_resource_link.id lti_user_resource_link = LTIUserResourceLink.get_by_tool_provider( lti_resource_link, lti_user, tool_provider ) sess["lti_user_resource_link"] = lti_user_resource_link.id setup_required = False angular_route = None # if user linked if lti_user.is_linked_to_user(): authenticate(lti_user.compair_user, login_method="LTI") # create/update enrollment if context exists if lti_context and lti_context.is_linked_to_course(): lti_context.update_enrolment(lti_user.compair_user_id, lti_user_resource_link.course_role) else: # need to create user link sess["oauth_create_user_link"] = True setup_required = True if not lti_context: # no context, redriect to home page angular_route = "/" elif lti_context.is_linked_to_course(): # redirect to course page or assignment page if available angular_route = "/course/" + lti_context.compair_course_uuid if lti_resource_link.is_linked_to_assignment(): angular_route += "/assignment/" + lti_resource_link.compair_assignment_uuid else: # instructors can select course, students will recieve a warning message setup_required = True if setup_required: # if account/course setup required, redirect to lti controller angular_route = "/lti" elif angular_route == None: # set angular route to home page by default angular_route = "/" # clear cookies in case they exist from previous user response = current_app.make_response(redirect("/app/#" + angular_route)) response.set_cookie("current.permissions", value="", path="/static") response.set_cookie("current.lti.status", value="", path="/static") response.set_cookie("current.user", value="", path="/static") return response else: display_message = "Invalid Request" if ok and tool_provider.user_id == None: display_message = "ComPAIR requires the LTI tool consumer to provide a user_id." tool_provider.lti_errormsg = display_message return_url = tool_provider.build_return_url() if return_url: return redirect(return_url) else: return display_message, 400