Beispiel #1
0
def lti_launch(request, course_id, usage_id):
    """
    Endpoint for all requests to embed edX content via the LTI protocol. This
    endpoint will be called by a POST message that contains the parameters for
    an LTI launch (we support version 1.2 of the LTI specification):
        http://www.imsglobal.org/lti/ltiv1p2/ltiIMGv1p2.html

    An LTI launch is successful if:
        - The launch contains all the required parameters
        - The launch data is correctly signed using a known client key/secret
          pair
    """
    if not settings.FEATURES['ENABLE_LTI_PROVIDER']:
        return HttpResponseForbidden()

    # Check the LTI parameters, and return 400 if any required parameters are
    # missing
    params = get_required_parameters(request.POST)
    if not params:
        return HttpResponseBadRequest()
    params.update(get_optional_parameters(request.POST))

    # Get the consumer information from either the instance GUID or the consumer
    # key
    try:
        lti_consumer = LtiConsumer.get_or_supplement(
            params.get('tool_consumer_instance_guid', None),
            params['oauth_consumer_key']
        )
    except LtiConsumer.DoesNotExist:
        return HttpResponseForbidden()

    # Check the OAuth signature on the message
    if not SignatureValidator(lti_consumer).verify(request):
        return HttpResponseForbidden()

    # Add the course and usage keys to the parameters array
    try:
        course_key, usage_key = parse_course_and_usage_keys(course_id, usage_id)
    except InvalidKeyError:
        log.error(
            u'Invalid course key %s or usage key %s from request %s',
            course_id,
            usage_id,
            request
        )
        raise Http404()
    params['course_key'] = course_key
    params['usage_key'] = usage_key

    # Create an edX account if the user identifed by the LTI launch doesn't have
    # one already, and log the edX account into the platform.
    authenticate_lti_user(request, params['user_id'], lti_consumer)

    # Store any parameters required by the outcome service in order to report
    # scores back later. We know that the consumer exists, since the record was
    # used earlier to verify the oauth signature.
    store_outcome_parameters(params, request.user, lti_consumer)

    return render_courseware(request, params['usage_key'])
Beispiel #2
0
def lti_launch(request, course_id, usage_id):
    """
    Endpoint for all requests to embed edX content via the LTI protocol. This
    endpoint will be called by a POST message that contains the parameters for
    an LTI launch (we support version 1.2 of the LTI specification):
        http://www.imsglobal.org/lti/ltiv1p2/ltiIMGv1p2.html

    An LTI launch is successful if:
        - The launch contains all the required parameters
        - The launch data is correctly signed using a known client key/secret
          pair
    """
    if not settings.FEATURES['ENABLE_LTI_PROVIDER']:
        return HttpResponseForbidden()

    # Check the LTI parameters, and return 400 if any required parameters are
    # missing
    params = get_required_parameters(request.POST)
    if not params:
        return HttpResponseBadRequest()
    params.update(get_optional_parameters(request.POST))

    # Get the consumer information from either the instance GUID or the consumer
    # key
    try:
        lti_consumer = LtiConsumer.get_or_supplement(
            params.get('tool_consumer_instance_guid', None),
            params['oauth_consumer_key']
        )
    except LtiConsumer.DoesNotExist:
        return HttpResponseForbidden()

    # Check the OAuth signature on the message
    if not SignatureValidator(lti_consumer).verify(request):
        return HttpResponseForbidden()

    # Add the course and usage keys to the parameters array
    try:
        course_key, usage_key = parse_course_and_usage_keys(course_id, usage_id)
    except InvalidKeyError:
        log.error(
            'Invalid course key %s or usage key %s from request %s',
            course_id,
            usage_id,
            request
        )
        raise Http404()
    params['course_key'] = course_key
    params['usage_key'] = usage_key

    # Create an edX account if the user identifed by the LTI launch doesn't have
    # one already, and log the edX account into the platform.
    authenticate_lti_user(request, params['user_id'], lti_consumer)

    # Store any parameters required by the outcome service in order to report
    # scores back later. We know that the consumer exists, since the record was
    # used earlier to verify the oauth signature.
    store_outcome_parameters(params, request.user, lti_consumer)

    return render_courseware(request, params['usage_key'])
 def test_db_record_created_without_consumer_id(self):
     params = self.get_valid_request_params()
     del params['tool_consumer_instance_guid']
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     self.assertEqual(GradedAssignment.objects.count(), 1)
     self.assertEqual(OutcomeService.objects.count(), 1)
Beispiel #4
0
 def test_db_record_created_without_consumer_id(self):
     params = self.get_valid_request_params()
     del params['tool_consumer_instance_guid']
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     self.assertEqual(GradedAssignment.objects.count(), 1)
     self.assertEqual(OutcomeService.objects.count(), 1)
Beispiel #5
0
def lti_run(request):
    """
    This method can be reached in two ways, and must always follow a POST to
    lti_launch:
     - The user was logged in, so this method was called by lti_launch
     - The user was not logged in, so the login process redirected them back here.

    In either case, the session was populated by lti_launch, so all the required
    LTI parameters will be stored there. Note that the request passed here may
    or may not contain the LTI parameters (depending on how the user got here),
    and so we should only use LTI parameters from the session.

    Users should never call this view directly; if a user attempts to call it
    without having first gone through lti_launch (and had the LTI parameters
    stored in the session) they will get a 403 response.
    """

    # Check the parameters to make sure that the session is associated with a
    # valid LTI launch
    params = restore_params_from_session(request)
    if not params:
        # This view has been called without first setting the session
        return HttpResponseForbidden()
    # Remove the parameters from the session to prevent replay
    del request.session[LTI_SESSION_KEY]

    # Store any parameters required by the outcome service in order to report
    # scores back later. We know that the consumer exists, since the record was
    # used earlier to verify the oauth signature.
    lti_consumer = LtiConsumer.get_or_supplement(
        params.get('tool_consumer_instance_guid', None),
        params['oauth_consumer_key'])
    store_outcome_parameters(params, request.user, lti_consumer)

    return render_courseware(request, params['usage_key'])
Beispiel #6
0
 def test_outcome_service_created(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(8):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     outcome = OutcomeService.objects.get(
         lti_consumer=self.consumer
     )
     self.assertEqual(outcome.lti_consumer, self.consumer)
Beispiel #7
0
 def test_outcome_service_created(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     outcome = OutcomeService.objects.get(
         lti_consumer=self.consumer
     )
     self.assertEqual(outcome.lti_consumer, self.consumer)
Beispiel #8
0
 def test_graded_assignment_references_outcome_service(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(8):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     outcome = OutcomeService.objects.get(lti_consumer=self.consumer)
     assignment = GradedAssignment.objects.get(
         lis_result_sourcedid=params['lis_result_sourcedid'])
     self.assertEqual(assignment.outcome_service, outcome)
Beispiel #9
0
 def test_no_duplicate_outcome_services(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(8):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     with self.assertNumQueries(2):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     outcome_services = OutcomeService.objects.filter(
         lti_consumer=self.consumer)
     self.assertEqual(len(outcome_services), 1)
Beispiel #10
0
 def test_no_duplicate_graded_assignments(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(8):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     with self.assertNumQueries(2):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     assignments = GradedAssignment.objects.filter(
         lis_result_sourcedid=params['lis_result_sourcedid'])
     self.assertEqual(len(assignments), 1)
Beispiel #11
0
 def test_graded_assignment_created(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(8):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     assignment = GradedAssignment.objects.get(
         lis_result_sourcedid=params['lis_result_sourcedid'])
     self.assertEqual(assignment.course_key, self.course_key)
     self.assertEqual(assignment.usage_key, self.usage_key)
     self.assertEqual(assignment.user, self.user)
Beispiel #12
0
 def test_no_duplicate_graded_assignments(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     with self.assertNumQueries(2):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     assignments = GradedAssignment.objects.filter(
         lis_result_sourcedid=params['lis_result_sourcedid']
     )
     self.assertEqual(len(assignments), 1)
Beispiel #13
0
 def test_no_duplicate_outcome_services(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     with self.assertNumQueries(2):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     outcome_services = OutcomeService.objects.filter(
         lti_consumer=self.consumer
     )
     self.assertEqual(len(outcome_services), 1)
Beispiel #14
0
 def test_graded_assignment_created(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     assignment = GradedAssignment.objects.get(
         lis_result_sourcedid=params['lis_result_sourcedid']
     )
     self.assertEqual(assignment.course_key, self.course_key)
     self.assertEqual(assignment.usage_key, self.usage_key)
     self.assertEqual(assignment.user, self.user)
Beispiel #15
0
 def test_graded_assignment_references_outcome_service(self):
     params = self.get_valid_request_params()
     with self.assertNumQueries(4):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
     outcome = OutcomeService.objects.get(
         lti_consumer=self.consumer
     )
     assignment = GradedAssignment.objects.get(
         lis_result_sourcedid=params['lis_result_sourcedid']
     )
     self.assertEqual(assignment.outcome_service, outcome)
Beispiel #16
0
def lti_run(request):
    """
    This method can be reached in two ways, and must always follow a POST to
    lti_launch:
     - The user was logged in, so this method was called by lti_launch
     - The user was not logged in, so the login process redirected them back here.

    In either case, the session was populated by lti_launch, so all the required
    LTI parameters will be stored there. Note that the request passed here may
    or may not contain the LTI parameters (depending on how the user got here),
    and so we should only use LTI parameters from the session.

    Users should never call this view directly; if a user attempts to call it
    without having first gone through lti_launch (and had the LTI parameters
    stored in the session) they will get a 403 response.
    """

    # Check the parameters to make sure that the session is associated with a
    # valid LTI launch
    params = restore_params_from_session(request)
    if not params:
        # This view has been called without first setting the session
        return HttpResponseForbidden()
    # Remove the parameters from the session to prevent replay
    del request.session[LTI_SESSION_KEY]

    # Store any parameters required by the outcome service in order to report
    # scores back later. We know that the consumer exists, since the record was
    # used earlier to verify the oauth signature.
    lti_consumer = LtiConsumer.get_or_supplement(
        params.get('tool_consumer_instance_guid', None),
        params['oauth_consumer_key']
    )
    store_outcome_parameters(params, request.user, lti_consumer)

    return render_courseware(request, params['usage_key'])
Beispiel #17
0
 def test_no_db_update_for_ungraded_assignment(self):
     params = self.get_valid_request_params()
     del params['lis_result_sourcedid']
     with self.assertNumQueries(0):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
Beispiel #18
0
 def test_no_db_update_for_bad_request(self):
     params = self.get_valid_request_params()
     del params['lis_outcome_service_url']
     with self.assertNumQueries(0):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
Beispiel #19
0
 def test_no_db_update_for_ungraded_assignment(self):
     params = self.get_valid_request_params()
     del params['lis_result_sourcedid']
     with self.assertNumQueries(0):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)
Beispiel #20
0
 def test_no_db_update_for_bad_request(self):
     params = self.get_valid_request_params()
     del params['lis_outcome_service_url']
     with self.assertNumQueries(0):
         outcomes.store_outcome_parameters(params, self.user, self.consumer)