Commit e208fad0 by Ivica Ceraj Committed by Carson Gee

OAuth signature should be verified against URL provided to the tool. There is…

OAuth signature should be verified against URL provided to the tool.  There is possibility that a proxy will change URL along the way (for example protocol from https to http) which would invalidate signature.  Supporting LTI_1 and LTI_2 use case.

(cherry picked from commit 9a1d8df737ed6f3b82cd84a1f391fb62c7d5724d)
parent ca909e3d
...@@ -787,7 +787,13 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} ...@@ -787,7 +787,13 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
oauth_params = signature.collect_parameters(headers=headers, exclude_oauth_signature=False) oauth_params = signature.collect_parameters(headers=headers, exclude_oauth_signature=False)
oauth_headers = dict(oauth_params) oauth_headers = dict(oauth_params)
oauth_signature = oauth_headers.pop('oauth_signature') oauth_signature = oauth_headers.pop('oauth_signature')
mock_request = mock.Mock( mock_request_LTI_1 = mock.Mock(
uri=unicode(urllib.unquote(self.get_outcome_service_url())),
http_method=unicode(request.method),
params=oauth_headers.items(),
signature=oauth_signature
)
mock_request_LTI_2 = mock.Mock(
uri=unicode(urllib.unquote(request.url)), uri=unicode(urllib.unquote(request.url)),
http_method=unicode(request.method), http_method=unicode(request.method),
params=oauth_headers.items(), params=oauth_headers.items(),
...@@ -795,9 +801,12 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} ...@@ -795,9 +801,12 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
) )
if oauth_body_hash != oauth_headers.get('oauth_body_hash'): if oauth_body_hash != oauth_headers.get('oauth_body_hash'):
log.error( "OAuth body hash verification failed, provided: {} calculated: {}, for url: {}, body is: {}".format(oauth_headers.get('oauth_body_hash'),oauth_body_hash,self.get_outcome_service_url(),request.body))
raise LTIError("OAuth body hash verification is failed.") raise LTIError("OAuth body hash verification is failed.")
if not signature.verify_hmac_sha1(mock_request, client_secret): if not signature.verify_hmac_sha1(mock_request_LTI_1, client_secret) \
and not signature.verify_hmac_sha1(mock_request_LTI_2, client_secret):
log.error( "OAuth signature verification failed, for headers:{} url:{} method:{}".format(oauth_headers,self.get_outcome_service_url(),unicode(request.method)))
raise LTIError("OAuth signature verification is failed.") raise LTIError("OAuth signature verification is failed.")
def get_client_key_secret(self): def get_client_key_secret(self):
......
...@@ -331,6 +331,49 @@ class LTIModuleTest(LogicTest): ...@@ -331,6 +331,49 @@ class LTIModuleTest(LogicTest):
except LTIError as err: except LTIError as err:
self.fail("verify_oauth_body_sign() raised LTIError: " + err.message) self.fail("verify_oauth_body_sign() raised LTIError: " + err.message)
@patch('xmodule.lti_module.LTIModule.get_outcome_service_url', Mock(return_value=u'https://testurl/'))
@patch('xmodule.lti_module.LTIModule.get_client_key_secret',
Mock(return_value=(u'__consumer_key__', u'__lti_secret__')))
def test_failed_verify_oauth_body_sign_proxy_mangle_url(self):
"""
Oauth signing verify fail.
"""
try:
request = self.get_signed_grade_mock_request_with_correct_signature()
self.xmodule.verify_oauth_body_sign(request)
# we should verify against get_outcome_service_url not request url
# proxy and load balancer along the way may change url presented to the method
request.uri = 'http://testurl/'
self.xmodule.verify_oauth_body_sign(request)
except LTIError as err:
self.fail("verify_oauth_body_sign() raised LTIError: " + err.message)
pass
def get_signed_grade_mock_request_with_correct_signature(self):
mock_request = Mock()
mock_request.headers = {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': u'OAuth realm="https://testurl/", oauth_body_hash="wwzA3s8gScKD1VpJ7jMt9b%2BMj9Q%3D", \
oauth_nonce="18821463", oauth_timestamp="1409321145", \
oauth_consumer_key="__consumer_key__", oauth_signature_method="HMAC-SHA1", \
oauth_version="1.0", oauth_signature="fHsE1hhIz76/msUoMR3Lyb7Aou4%3D"'
}
mock_request.url = u'https://testurl'
mock_request.http_method = u'POST'
mock_request.method = mock_request.http_method
mock_request.body = '<?xml version=\'1.0\' encoding=\'utf-8\'?>\n\
<imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">\
<imsx_POXHeader><imsx_POXRequestHeaderInfo><imsx_version>V1.0</imsx_version>\
<imsx_messageIdentifier>edX_fix</imsx_messageIdentifier></imsx_POXRequestHeaderInfo>\
</imsx_POXHeader><imsx_POXBody><replaceResultRequest><resultRecord><sourcedGUID>\
<sourcedId>MITxLTI/MITxLTI/201x:localhost%3A8000-i4x-MITxLTI-MITxLTI-lti-3751833a214a4f66a0d18f63234207f2:363979ef768ca171b50f9d1bfb322131</sourcedId>\
</sourcedGUID><result><resultScore><language>en</language><textString>0.32</textString></resultScore>\
</result></resultRecord></replaceResultRequest></imsx_POXBody></imsx_POXEnvelopeRequest>'
return mock_request
def test_wrong_xml_namespace(self): def test_wrong_xml_namespace(self):
""" """
Test wrong XML Namespace. Test wrong XML Namespace.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment