Fix lis_outcome_service_url sending.
This commit is contained in:
committed by
Alexander Kryklia
parent
f6867da502
commit
cc2e4bfeae
@@ -5,6 +5,7 @@ These are notable changes in edx-platform. This is a rolling list of changes,
|
|||||||
in roughly chronological order, most recent first. Add your entries at or near
|
in roughly chronological order, most recent first. Add your entries at or near
|
||||||
the top. Include a label indicating the component affected.
|
the top. Include a label indicating the component affected.
|
||||||
|
|
||||||
|
Blades: Make LTI module not send grade_back_url if has_score=False. BLD-561.
|
||||||
Blades: LTI additional Python tests. LTI must use HTTPS for
|
Blades: LTI additional Python tests. LTI must use HTTPS for
|
||||||
lis_outcome_service_url. BLD-564.
|
lis_outcome_service_url. BLD-564.
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class AnonymousUserId(models.Model):
|
|||||||
|
|
||||||
Purpose of this table is to provide user by anonymous_user_id.
|
Purpose of this table is to provide user by anonymous_user_id.
|
||||||
|
|
||||||
We are generating anonymous_user_id using md5 algorithm, so resulting length will always be 16 bytes.
|
We generate anonymous_user_id using md5 algorithm,
|
||||||
http://docs.python.org/2/library/md5.html#md5.digest_size
|
and use result in hex form, so its length is equal to 32 bytes.
|
||||||
"""
|
"""
|
||||||
user = models.ForeignKey(User, db_index=True)
|
user = models.ForeignKey(User, db_index=True)
|
||||||
anonymous_user_id = models.CharField(unique=True, max_length=32)
|
anonymous_user_id = models.CharField(unique=True, max_length=32)
|
||||||
|
|||||||
@@ -355,11 +355,15 @@ class LTIModule(LTIFields, XModule):
|
|||||||
|
|
||||||
# Parameters required for grading:
|
# Parameters required for grading:
|
||||||
u'resource_link_id': self.get_resource_link_id(),
|
u'resource_link_id': self.get_resource_link_id(),
|
||||||
u'lis_outcome_service_url': self.get_outcome_service_url(),
|
|
||||||
u'lis_result_sourcedid': self.get_lis_result_sourcedid(),
|
u'lis_result_sourcedid': self.get_lis_result_sourcedid(),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.has_score:
|
||||||
|
body.update({
|
||||||
|
u'lis_outcome_service_url': self.get_outcome_service_url()
|
||||||
|
})
|
||||||
|
|
||||||
# Appending custom parameter for signing.
|
# Appending custom parameter for signing.
|
||||||
body.update(custom_parameters)
|
body.update(custom_parameters)
|
||||||
|
|
||||||
@@ -483,6 +487,8 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
|
|||||||
try:
|
try:
|
||||||
imsx_messageIdentifier, sourcedId, score, action = self.parse_grade_xml_body(request.body)
|
imsx_messageIdentifier, sourcedId, score, action = self.parse_grade_xml_body(request.body)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
log.debug("[LTI]: Request body XML parsing error.")
|
||||||
|
failure_values['imsx_description'] = 'Request body XML parsing error.'
|
||||||
return Response(response_xml_template.format(**failure_values), content_type="application/xml")
|
return Response(response_xml_template.format(**failure_values), content_type="application/xml")
|
||||||
|
|
||||||
# Verify OAuth signing.
|
# Verify OAuth signing.
|
||||||
@@ -490,10 +496,15 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
|
|||||||
self.verify_oauth_body_sign(request)
|
self.verify_oauth_body_sign(request)
|
||||||
except (ValueError, LTIError):
|
except (ValueError, LTIError):
|
||||||
failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
|
failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
|
||||||
|
failure_values['imsx_description'] = 'OAuth verification error.'
|
||||||
return Response(response_xml_template.format(**failure_values), content_type="application/xml")
|
return Response(response_xml_template.format(**failure_values), content_type="application/xml")
|
||||||
|
|
||||||
|
|
||||||
real_user = self.system.get_real_user(urllib.unquote(sourcedId.split(':')[-1]))
|
real_user = self.system.get_real_user(urllib.unquote(sourcedId.split(':')[-1]))
|
||||||
|
if not real_user: # that means we can't save to database, as we do not have real user id.
|
||||||
|
failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
|
||||||
|
failure_values['imsx_description'] = 'User not found.'
|
||||||
|
return Response(response_xml_template.format(**failure_values), content_type="application/xml")
|
||||||
|
|
||||||
if action == 'replaceResultRequest':
|
if action == 'replaceResultRequest':
|
||||||
self.system.publish(
|
self.system.publish(
|
||||||
event={
|
event={
|
||||||
@@ -510,9 +521,11 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
|
|||||||
'imsx_messageIdentifier': escape(imsx_messageIdentifier),
|
'imsx_messageIdentifier': escape(imsx_messageIdentifier),
|
||||||
'response': '<replaceResultResponse/>'
|
'response': '<replaceResultResponse/>'
|
||||||
}
|
}
|
||||||
|
log.debug("[LTI]: Grade is saved.")
|
||||||
return Response(response_xml_template.format(**values), content_type="application/xml")
|
return Response(response_xml_template.format(**values), content_type="application/xml")
|
||||||
|
|
||||||
unsupported_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
|
unsupported_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier)
|
||||||
|
log.debug("[LTI]: Incorrect action.")
|
||||||
return Response(response_xml_template.format(**unsupported_values), content_type='application/xml')
|
return Response(response_xml_template.format(**unsupported_values), content_type='application/xml')
|
||||||
|
|
||||||
|
|
||||||
@@ -541,6 +554,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
|
|||||||
# Raise exception if score is not float or not in range 0.0-1.0 regarding spec.
|
# Raise exception if score is not float or not in range 0.0-1.0 regarding spec.
|
||||||
score = float(score)
|
score = float(score)
|
||||||
if not 0 <= score <= 1:
|
if not 0 <= score <= 1:
|
||||||
|
log.debug("[LTI]: Score not in range.")
|
||||||
raise LTIError
|
raise LTIError
|
||||||
|
|
||||||
return imsx_messageIdentifier, sourcedId, score, action
|
return imsx_messageIdentifier, sourcedId, score, action
|
||||||
@@ -582,8 +596,11 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
|
|||||||
params=oauth_headers.items(),
|
params=oauth_headers.items(),
|
||||||
signature=oauth_signature
|
signature=oauth_signature
|
||||||
)
|
)
|
||||||
if (oauth_body_hash != oauth_headers.get('oauth_body_hash') or
|
if oauth_body_hash != oauth_headers.get('oauth_body_hash'):
|
||||||
not signature.verify_hmac_sha1(mock_request, client_secret)):
|
log.debug("[LTI]: OAuth body hash verification is failed.")
|
||||||
|
raise LTIError
|
||||||
|
if not signature.verify_hmac_sha1(mock_request, client_secret):
|
||||||
|
log.debug("[LTI]: OAuth signature verification is failed.")
|
||||||
raise LTIError
|
raise LTIError
|
||||||
|
|
||||||
def get_client_key_secret(self):
|
def get_client_key_secret(self):
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class LTIModuleTest(LogicTest):
|
|||||||
expected_response = {
|
expected_response = {
|
||||||
'action': None,
|
'action': None,
|
||||||
'code_major': 'failure',
|
'code_major': 'failure',
|
||||||
'description': 'The request has failed.',
|
'description': 'OAuth verification error.',
|
||||||
'messageIdentifier': self.DEFAULTS['messageIdentifier'],
|
'messageIdentifier': self.DEFAULTS['messageIdentifier'],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,27 @@ class LTIModuleTest(LogicTest):
|
|||||||
expected_response = {
|
expected_response = {
|
||||||
'action': None,
|
'action': None,
|
||||||
'code_major': 'failure',
|
'code_major': 'failure',
|
||||||
'description': 'The request has failed.',
|
'description': 'OAuth verification error.',
|
||||||
|
'messageIdentifier': self.DEFAULTS['messageIdentifier'],
|
||||||
|
}
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertDictEqual(expected_response, real_response)
|
||||||
|
|
||||||
|
def test_real_user_is_none(self):
|
||||||
|
"""
|
||||||
|
If we have no real user, we should send back failure response.
|
||||||
|
"""
|
||||||
|
self.xmodule.verify_oauth_body_sign = Mock()
|
||||||
|
self.xmodule.has_score = True
|
||||||
|
self.system.get_real_user = Mock(return_value=None)
|
||||||
|
request = Request(self.environ)
|
||||||
|
request.body = self.get_request_body()
|
||||||
|
response = self.xmodule.grade_handler(request, '')
|
||||||
|
real_response = self.get_response_values(response)
|
||||||
|
expected_response = {
|
||||||
|
'action': None,
|
||||||
|
'code_major': 'failure',
|
||||||
|
'description': 'User not found.',
|
||||||
'messageIdentifier': self.DEFAULTS['messageIdentifier'],
|
'messageIdentifier': self.DEFAULTS['messageIdentifier'],
|
||||||
}
|
}
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -151,7 +171,7 @@ class LTIModuleTest(LogicTest):
|
|||||||
expected_response = {
|
expected_response = {
|
||||||
'action': None,
|
'action': None,
|
||||||
'code_major': 'failure',
|
'code_major': 'failure',
|
||||||
'description': 'The request has failed.',
|
'description': 'Request body XML parsing error.',
|
||||||
'messageIdentifier': 'unknown',
|
'messageIdentifier': 'unknown',
|
||||||
}
|
}
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
@@ -169,7 +189,7 @@ class LTIModuleTest(LogicTest):
|
|||||||
expected_response = {
|
expected_response = {
|
||||||
'action': None,
|
'action': None,
|
||||||
'code_major': 'failure',
|
'code_major': 'failure',
|
||||||
'description': 'The request has failed.',
|
'description': 'Request body XML parsing error.',
|
||||||
'messageIdentifier': 'unknown',
|
'messageIdentifier': 'unknown',
|
||||||
}
|
}
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|||||||
@@ -1016,6 +1016,9 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
|
|||||||
|
|
||||||
error_descriptor_class - The class to use to render XModules with errors
|
error_descriptor_class - The class to use to render XModules with errors
|
||||||
|
|
||||||
|
get_real_user - function that takes `anonymous_student_id` and returns real user_id,
|
||||||
|
associated with `anonymous_student_id`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Right now, usage_store is unused, and field_data is always supplanted
|
# Right now, usage_store is unused, and field_data is always supplanted
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
|||||||
protocol = "HTTP/1.0"
|
protocol = "HTTP/1.0"
|
||||||
callback_url = None
|
callback_url = None
|
||||||
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
"""Log an arbitrary message."""
|
"""Log an arbitrary message."""
|
||||||
# Code copied from BaseHTTPServer.py. Changed to write to sys.stdout
|
# Code copied from BaseHTTPServer.py. Changed to write to sys.stdout
|
||||||
@@ -49,18 +48,13 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
Used for checking LTI Provider started correctly.
|
Used for checking LTI Provider started correctly.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.send_response(200, 'OK')
|
self.send_response(200, 'OK')
|
||||||
self.send_header('Content-type', 'html')
|
self.send_header('Content-type', 'html')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
response_str = """<html><head><title>TEST TITLE</title></head>
|
response_str = """<html><head><title>TEST TITLE</title></head>
|
||||||
<body>This is LTI Provider.</body></html>"""
|
<body>This is LTI Provider.</body></html>"""
|
||||||
|
|
||||||
self.wfile.write(response_str)
|
self.wfile.write(response_str)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
'''
|
'''
|
||||||
Handle a POST request from the client and sends response back.
|
Handle a POST request from the client and sends response back.
|
||||||
@@ -72,38 +66,17 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
|||||||
# Respond to request with correct lti endpoint:
|
# Respond to request with correct lti endpoint:
|
||||||
elif self._is_correct_lti_request():
|
elif self._is_correct_lti_request():
|
||||||
self.post_dict = self._post_dict()
|
self.post_dict = self._post_dict()
|
||||||
correct_keys = [
|
params = {k: v for k, v in self.post_dict.items() if k != 'oauth_signature'}
|
||||||
'user_id',
|
if self.server.check_oauth_signature(params, self.post_dict.get('oauth_signature', "")):
|
||||||
'role',
|
status_message = "This is LTI tool. Success."
|
||||||
'oauth_nonce',
|
# set data for grades what need to be stored as server data
|
||||||
'oauth_timestamp',
|
if 'lis_outcome_service_url' in self.post_dict:
|
||||||
'oauth_consumer_key',
|
self.server.grade_data = {
|
||||||
'lti_version',
|
'callback_url': self.post_dict.get('lis_outcome_service_url'),
|
||||||
'oauth_signature_method',
|
'sourcedId': self.post_dict.get('lis_result_sourcedid')
|
||||||
'oauth_version',
|
}
|
||||||
'oauth_signature',
|
|
||||||
'lti_message_type',
|
|
||||||
'oauth_callback',
|
|
||||||
'lis_outcome_service_url',
|
|
||||||
'lis_result_sourcedid',
|
|
||||||
'launch_presentation_return_url',
|
|
||||||
# 'lis_person_sourcedid', optional, not used now.
|
|
||||||
'resource_link_id',
|
|
||||||
]
|
|
||||||
if sorted(correct_keys) != sorted(self.post_dict.keys()):
|
|
||||||
status_message = "Incorrect LTI header"
|
|
||||||
else:
|
else:
|
||||||
params = {k: v for k, v in self.post_dict.items() if k != 'oauth_signature'}
|
status_message = "Wrong LTI signature"
|
||||||
if self.server.check_oauth_signature(params, self.post_dict['oauth_signature']):
|
|
||||||
status_message = "This is LTI tool. Success."
|
|
||||||
else:
|
|
||||||
status_message = "Wrong LTI signature"
|
|
||||||
# set data for grades
|
|
||||||
# what need to be stored as server data
|
|
||||||
self.server.grade_data = {
|
|
||||||
'callback_url': self.post_dict.get('lis_outcome_service_url'),
|
|
||||||
'sourcedId': self.post_dict.get('lis_result_sourcedid')
|
|
||||||
}
|
|
||||||
self._send_response(status_message, 200)
|
self._send_response(status_message, 200)
|
||||||
else:
|
else:
|
||||||
status_message = "Invalid request URL"
|
status_message = "Invalid request URL"
|
||||||
@@ -141,17 +114,17 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
|||||||
self.server.cookie = {}
|
self.server.cookie = {}
|
||||||
referer = urlparse.urlparse(self.headers.getheader('referer'))
|
referer = urlparse.urlparse(self.headers.getheader('referer'))
|
||||||
self.server.referer_host = "{}://{}".format(referer.scheme, referer.netloc)
|
self.server.referer_host = "{}://{}".format(referer.scheme, referer.netloc)
|
||||||
self.server.referer_netloc = referer.netloc
|
|
||||||
return post_dict
|
return post_dict
|
||||||
|
|
||||||
def _send_graded_result(self):
|
def _send_graded_result(self):
|
||||||
|
"""
|
||||||
|
Send grade request.
|
||||||
|
"""
|
||||||
values = {
|
values = {
|
||||||
'textString': 0.5,
|
'textString': 0.5,
|
||||||
'sourcedId': self.server.grade_data['sourcedId'],
|
'sourcedId': self.server.grade_data['sourcedId'],
|
||||||
'imsx_messageIdentifier': uuid4().hex,
|
'imsx_messageIdentifier': uuid4().hex,
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = textwrap.dedent("""
|
payload = textwrap.dedent("""
|
||||||
<?xml version = "1.0" encoding = "UTF-8"?>
|
<?xml version = "1.0" encoding = "UTF-8"?>
|
||||||
<imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
|
<imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
|
||||||
@@ -208,40 +181,53 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
|||||||
Send message back to the client
|
Send message back to the client
|
||||||
'''
|
'''
|
||||||
self._send_head(status_code)
|
self._send_head(status_code)
|
||||||
if self.server.grade_data['callback_url']:
|
if getattr(self.server, 'grade_data', False): # lti can be graded
|
||||||
response_str = """<html><head><title>TEST TITLE</title></head>
|
response_str = textwrap.dedent("""
|
||||||
<body>
|
<html>
|
||||||
<div><h2>Graded IFrame loaded</h2> \
|
<head>
|
||||||
<h3>Server response is:</h3>\
|
<title>TEST TITLE</title>
|
||||||
<h3 class="result">{}</h3></div>
|
</head>
|
||||||
<form action="{url}/grade" method="post">
|
<body>
|
||||||
<input type="submit" name="submit-button" value="Submit">
|
<div>
|
||||||
</form>
|
<h2>Graded IFrame loaded</h2>
|
||||||
|
<h3>Server response is:</h3>
|
||||||
|
<h3 class="result">{}</h3>
|
||||||
|
</div>
|
||||||
|
<form action="{url}/grade" method="post">
|
||||||
|
<input type="submit" name="submit-button" value="Submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""").format(message, url="http://%s:%s" % self.server.server_address)
|
||||||
|
else: # lti can't be graded
|
||||||
|
response_str = textwrap.dedent("""
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>TEST TITLE</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<h2>IFrame loaded</h2>
|
||||||
|
<h3>Server response is:</h3>
|
||||||
|
<h3 class="result">{}</h3>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""").format(message)
|
||||||
|
|
||||||
</body></html>""".format(message, url="http://%s:%s" % self.server.server_address)
|
|
||||||
else:
|
|
||||||
response_str = """<html><head><title>TEST TITLE</title></head>
|
|
||||||
<body>
|
|
||||||
<div><h2>IFrame loaded</h2> \
|
|
||||||
<h3>Server response is:</h3>\
|
|
||||||
<h3 class="result">{}</h3></div>
|
|
||||||
</body></html>""".format(message)
|
|
||||||
|
|
||||||
# Log the response
|
|
||||||
logger.debug("LTI: sent response {}".format(response_str))
|
logger.debug("LTI: sent response {}".format(response_str))
|
||||||
|
|
||||||
self.wfile.write(response_str)
|
self.wfile.write(response_str)
|
||||||
|
|
||||||
def _is_correct_lti_request(self):
|
def _is_correct_lti_request(self):
|
||||||
'''If url to LTI tool is correct.'''
|
'''
|
||||||
|
If url to LTI tool is correct.
|
||||||
|
'''
|
||||||
return self.server.oauth_settings['lti_endpoint'] in self.path
|
return self.server.oauth_settings['lti_endpoint'] in self.path
|
||||||
|
|
||||||
def oauth_sign(self, url, body):
|
def oauth_sign(self, url, body):
|
||||||
"""
|
"""
|
||||||
Signs request and returns signed body and headers.
|
Signs request and returns signed body and headers.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
client = oauthlib.oauth1.Client(
|
client = oauthlib.oauth1.Client(
|
||||||
client_key=unicode(self.server.oauth_settings['client_key']),
|
client_key=unicode(self.server.oauth_settings['client_key']),
|
||||||
client_secret=unicode(self.server.oauth_settings['client_secret'])
|
client_secret=unicode(self.server.oauth_settings['client_secret'])
|
||||||
|
|||||||
@@ -49,10 +49,9 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_wrong_header(self):
|
def test_wrong_header(self):
|
||||||
"""
|
"""
|
||||||
Tests that LTI server processes request with right program
|
Tests that LTI server processes request with right program path but with wrong header.
|
||||||
path and responses with wrong header.
|
|
||||||
"""
|
"""
|
||||||
#wrong number of params
|
#wrong number of params and no signature
|
||||||
payload = {
|
payload = {
|
||||||
'user_id': 'default_user_id',
|
'user_id': 'default_user_id',
|
||||||
'role': 'student',
|
'role': 'student',
|
||||||
@@ -62,7 +61,7 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
uri = self.server.oauth_settings['lti_base'] + self.server.oauth_settings['lti_endpoint']
|
uri = self.server.oauth_settings['lti_base'] + self.server.oauth_settings['lti_endpoint']
|
||||||
headers = {'referer': 'http://localhost:8000/'}
|
headers = {'referer': 'http://localhost:8000/'}
|
||||||
response = requests.post(uri, data=payload, headers=headers)
|
response = requests.post(uri, data=payload, headers=headers)
|
||||||
self.assertIn('Incorrect LTI header', response.content)
|
self.assertIn('Wrong LTI signature', response.content)
|
||||||
|
|
||||||
def test_wrong_signature(self):
|
def test_wrong_signature(self):
|
||||||
"""
|
"""
|
||||||
@@ -74,7 +73,7 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
'role': 'student',
|
'role': 'student',
|
||||||
'oauth_nonce': '',
|
'oauth_nonce': '',
|
||||||
'oauth_timestamp': '',
|
'oauth_timestamp': '',
|
||||||
'oauth_consumer_key': 'client_key',
|
'oauth_consumer_key': 'test_client_key',
|
||||||
'lti_version': 'LTI-1p0',
|
'lti_version': 'LTI-1p0',
|
||||||
'oauth_signature_method': 'HMAC-SHA1',
|
'oauth_signature_method': 'HMAC-SHA1',
|
||||||
'oauth_version': '1.0',
|
'oauth_version': '1.0',
|
||||||
@@ -101,7 +100,7 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
'role': 'student',
|
'role': 'student',
|
||||||
'oauth_nonce': '',
|
'oauth_nonce': '',
|
||||||
'oauth_timestamp': '',
|
'oauth_timestamp': '',
|
||||||
'oauth_consumer_key': 'client_key',
|
'oauth_consumer_key': 'test_client_key',
|
||||||
'lti_version': 'LTI-1p0',
|
'lti_version': 'LTI-1p0',
|
||||||
'oauth_signature_method': 'HMAC-SHA1',
|
'oauth_signature_method': 'HMAC-SHA1',
|
||||||
'oauth_version': '1.0',
|
'oauth_version': '1.0',
|
||||||
@@ -112,7 +111,6 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
'lis_outcome_service_url': '',
|
'lis_outcome_service_url': '',
|
||||||
'lis_result_sourcedid': '',
|
'lis_result_sourcedid': '',
|
||||||
'resource_link_id':'',
|
'resource_link_id':'',
|
||||||
"lis_outcome_service_url": '',
|
|
||||||
}
|
}
|
||||||
self.server.check_oauth_signature = Mock(return_value=True)
|
self.server.check_oauth_signature = Mock(return_value=True)
|
||||||
|
|
||||||
@@ -128,7 +126,7 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
'role': 'student',
|
'role': 'student',
|
||||||
'oauth_nonce': '',
|
'oauth_nonce': '',
|
||||||
'oauth_timestamp': '',
|
'oauth_timestamp': '',
|
||||||
'oauth_consumer_key': 'client_key',
|
'oauth_consumer_key': 'test_client_key',
|
||||||
'lti_version': 'LTI-1p0',
|
'lti_version': 'LTI-1p0',
|
||||||
'oauth_signature_method': 'HMAC-SHA1',
|
'oauth_signature_method': 'HMAC-SHA1',
|
||||||
'oauth_version': '1.0',
|
'oauth_version': '1.0',
|
||||||
@@ -139,7 +137,6 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
'lis_outcome_service_url': '',
|
'lis_outcome_service_url': '',
|
||||||
'lis_result_sourcedid': '',
|
'lis_result_sourcedid': '',
|
||||||
'resource_link_id':'',
|
'resource_link_id':'',
|
||||||
"lis_outcome_service_url": '',
|
|
||||||
}
|
}
|
||||||
self.server.check_oauth_signature = Mock(return_value=True)
|
self.server.check_oauth_signature = Mock(return_value=True)
|
||||||
|
|
||||||
@@ -147,8 +144,8 @@ class MockLTIServerTest(unittest.TestCase):
|
|||||||
#this is the uri for sending grade from lti
|
#this is the uri for sending grade from lti
|
||||||
headers = {'referer': 'http://localhost:8000/'}
|
headers = {'referer': 'http://localhost:8000/'}
|
||||||
response = requests.post(uri, data=payload, headers=headers)
|
response = requests.post(uri, data=payload, headers=headers)
|
||||||
|
self.assertIn('This is LTI tool. Success.', response.content)
|
||||||
|
|
||||||
self.assertTrue('This is LTI tool. Success.' in response.content)
|
|
||||||
self.server.grade_data['TC answer'] = "Test response"
|
self.server.grade_data['TC answer'] = "Test response"
|
||||||
graded_response = requests.post('http://127.0.0.1:8034/grade')
|
graded_response = requests.post('http://127.0.0.1:8034/grade')
|
||||||
self.assertIn('Test response', graded_response.content)
|
self.assertIn('Test response', graded_response.content)
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class TestLTI(BaseTestXmodule):
|
|||||||
u'role': u'student',
|
u'role': u'student',
|
||||||
|
|
||||||
u'resource_link_id': module_id,
|
u'resource_link_id': module_id,
|
||||||
u'lis_outcome_service_url': lis_outcome_service_url,
|
|
||||||
u'lis_result_sourcedid': sourcedId,
|
u'lis_result_sourcedid': sourcedId,
|
||||||
|
|
||||||
u'oauth_nonce': mocked_nonce,
|
u'oauth_nonce': mocked_nonce,
|
||||||
|
|||||||
Reference in New Issue
Block a user