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_authentication_with_new_user(self, _create_user, switch_user): lti_user = MagicMock() lti_user.edx_user_id = self.edx_user_id with patch('lti_provider.users.create_lti_user', return_value=lti_user) as create_user: users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer) create_user.assert_called_with(self.lti_user_id, self.lti_consumer) switch_user.assert_called_with(self.request, lti_user, self.lti_consumer)
def test_authentication_with_wrong_user(self, create_user, switch_user): lti_user = self.create_lti_user_model() self.request.user = self.old_user self.request.user.is_authenticated = MagicMock(return_value=True) users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer) self.assertFalse(create_user.called) switch_user.assert_called_with(self.request, lti_user, self.lti_consumer)
def test_authentication_with_authenticated_user(self, create_user, switch_user): lti_user = self.create_lti_user_model() self.request.user = lti_user.edx_user assert self.request.user.is_authenticated users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer) self.assertFalse(create_user.called) self.assertFalse(switch_user.called)
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'])
def test_authentication_with_unauthenticated_user(self, create_user, switch_user): lti_user = self.create_lti_user_model() self.request.user = lti_user.edx_user with patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock) as mock_is_auth: mock_is_auth.return_value = False users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer) self.assertFalse(create_user.called) switch_user.assert_called_with(self.request, lti_user, self.lti_consumer)
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 - The user is logged into the edX instance Authentication in this view is a little tricky, since clients use a POST with parameters to fetch it. We can't just use @login_required since in the case where a user is not logged in it will redirect back after login using a GET request, which would lose all of our LTI parameters. Instead, we verify the LTI launch in this view before checking if the user is logged in, and store the required LTI parameters in the session. Then we do the authentication check, and if login is required we redirect back to the lti_run view. If the user is already logged in, we just call that view directly. """ if not settings.FEATURES['ENABLE_LTI_PROVIDER']: return HttpResponseForbidden() # Check the OAuth signature on the message try: if not SignatureValidator().verify(request): return HttpResponseForbidden() except LtiConsumer.DoesNotExist: return HttpResponseForbidden() params = get_required_parameters(request.POST) if not params: return HttpResponseBadRequest() params.update(get_optional_parameters(request.POST)) # Store the course, and usage ID in the session to prevent privilege # escalation if a staff member in one course tries to access material in # another. 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 try: lti_consumer = LtiConsumer.get_or_supplement( params.get('tool_consumer_instance_guid', None), params['oauth_consumer_key'] ) except LtiConsumer.DoesNotExist: return HttpResponseForbidden() # 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) request.session[LTI_SESSION_KEY] = params return lti_run(request)
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 - The user is logged into the edX instance Authentication in this view is a little tricky, since clients use a POST with parameters to fetch it. We can't just use @login_required since in the case where a user is not logged in it will redirect back after login using a GET request, which would lose all of our LTI parameters. Instead, we verify the LTI launch in this view before checking if the user is logged in, and store the required LTI parameters in the session. Then we do the authentication check, and if login is required we redirect back to the lti_run view. If the user is already logged in, we just call that view directly. """ if not settings.FEATURES['ENABLE_LTI_PROVIDER']: return HttpResponseForbidden() # Check the OAuth signature on the message try: if not SignatureValidator().verify(request): return HttpResponseForbidden() except LtiConsumer.DoesNotExist: return HttpResponseForbidden() params = get_required_parameters(request.POST) if not params: return HttpResponseBadRequest() params.update(get_optional_parameters(request.POST)) # Store the course, and usage ID in the session to prevent privilege # escalation if a staff member in one course tries to access material in # another. 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 try: lti_consumer = LtiConsumer.get_or_supplement( params.get('tool_consumer_instance_guid', None), params['oauth_consumer_key']) except LtiConsumer.DoesNotExist: return HttpResponseForbidden() # 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) request.session[LTI_SESSION_KEY] = params return lti_run(request)