Ejemplo n.º 1
0
    def delete(self, user_uuid, lti_user_uuid):
        """
        unlink lti user from compair user
        """

        user = User.get_by_uuid_or_404(user_uuid)
        lti_user = LTIUser.get_by_uuid_or_404(lti_user_uuid)

        require(
            MANAGE,
            User,
            title="User's LTI Account Links Unavailable",
            message=
            "Sorry, your system role does not allow you to remove LTI account links for this user."
        )

        lti_user.compair_user_id = None
        db.session.commit()

        on_user_lti_user_unlink.send(self,
                                     event_name=on_user_lti_user_unlink.name,
                                     user=current_user,
                                     data={
                                         'user_id': user.id,
                                         'lti_user_id': lti_user.id
                                     })

        return {'success': True}
Ejemplo n.º 2
0
    def delete(self, user_uuid, lti_user_uuid):
        """
        unlink lti user from compair user
        """

        user = User.get_by_uuid_or_404(user_uuid)
        lti_user = LTIUser.get_by_uuid_or_404(lti_user_uuid)

        require(MANAGE, User,
            title="User's LTI Account Links Unavailable",
            message="Sorry, your system role does not allow you to remove LTI account links for this user.")

        lti_user.compair_user_id = None
        db.session.commit()

        on_user_lti_user_unlink.send(
            self,
            event_name=on_user_lti_user_unlink.name,
            user=current_user,
            data={'user_id': user.id, 'lti_user_id': lti_user.id})

        return { 'success': True }
Ejemplo n.º 3
0
    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)))
Ejemplo n.º 4
0
    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)))
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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