Permalink
Browse files

Updating web service verification method to work for LTI messages tha…

…t don't send a source id.

Adding extension so other plugins can handle LTI web service calls.
  • Loading branch information...
1 parent d66865d commit 020eea1be8144626cc376f186522d9bad0d42b8c @scriby scriby committed Oct 10, 2011
Showing with 81 additions and 24 deletions.
  1. +32 −0 mod/lti/locallib.php
  2. +36 −3 mod/lti/service.php
  3. +13 −21 mod/lti/servicelib.php
View
@@ -608,6 +608,38 @@ function lti_get_best_tool_by_url($url, $tools, $courseid = null){
return $bestmatch;
}
+function lti_get_shared_secrets_by_key($key){
+ global $DB;
+
+ //Look up the shared secret for the specified key in both the types_config table (for configured tools)
+ //And in the lti resource table for ad-hoc tools
+ $query = <<<QUERY
+ SELECT t2.value
+ FROM {lti_types_config} t1
+ INNER JOIN {lti_types_config} t2 ON t1.typeid = t2.typeid
+ WHERE
+ t1.name = 'resourcekey'
+ AND t1.value = :key1
+ AND t2.name = 'password'
+
+ UNION
+
+ SELECT password
+ FROM {lti}
+ WHERE resourcekey = :key2
+QUERY;
+
+ $sharedsecrets = $DB->get_records_sql($query, array('key1' => $key, 'key2' => $key));
+
+ $values = array_map(function($item){
+ return $item->value;
+ }, $sharedsecrets);
+
+ //There should really only be one shared secret per key. But, we can't prevent
+ //more than one getting entered. For instance, if the same key is used for two tool providers.
+ return $values;
+}
+
/**
* Prints the various configured tool types
*
View
@@ -3,7 +3,29 @@
require_once($CFG->dirroot.'/mod/lti/locallib.php');
require_once($CFG->dirroot.'/mod/lti/servicelib.php');
+use moodle\mod\lti as lti;
+
$rawbody = file_get_contents("php://input");
+
+foreach(getallheaders() as $name => $value){
+ if($name === 'Authorization'){
+ $oauthparams = lti\OAuthUtil::split_header($value);
+
+ $consumerkey = $oauthparams['oauth_consumer_key'];
+ break;
+ }
+}
+
+if(empty($consumerkey)){
+ throw new Exception('Consumer key is missing.');
+}
+
+$sharedsecret = lti_verify_message($consumerkey, lti_get_shared_secrets_by_key($consumerkey), $rawbody);
+
+if($sharedsecret === false){
+ throw new Exception('Message signature not valid');
+}
+
$xml = new SimpleXMLElement($rawbody);
$body = $xml->imsx_POXBody;
@@ -18,7 +40,6 @@
$ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
lti_verify_sourcedid($ltiinstance, $parsed);
- lti_verify_message($ltiinstance, $rawbody);
$gradestatus = lti_update_grade($ltiinstance, $parsed->userid, $parsed->launchid, $parsed->gradeval);
@@ -43,7 +64,6 @@
$PAGE->set_context($context);
lti_verify_sourcedid($ltiinstance, $parsed);
- lti_verify_message($ltiinstance, $rawbody);
$grade = lti_read_grade($ltiinstance, $parsed->userid);
@@ -69,7 +89,6 @@
$ltiinstance = $DB->get_record('lti', array('id' => $parsed->instanceid));
lti_verify_sourcedid($ltiinstance, $parsed);
- lti_verify_message($ltiinstance, $rawbody);
$gradestatus = lti_delete_grade($ltiinstance, $parsed->userid);
@@ -83,6 +102,20 @@
echo $responsexml->asXML();
break;
+
+ default:
+ //Fire an event if we get a web service request which we don't support directly.
+ //This will allow others to extend the LTI services, which I expect to be a common
+ //use case, at least until the spec matures.
+ $data = new stdClass();
+ $data->body = $rawbody;
+ $data->messagetype = $messagetype;
+ $data->consumerkey = $consumerkey;
+ $data->sharedsecret = $sharedsecret;
+
+ events_trigger('lti_unknown_service_api_call', $data);
+
+ break;
}
View
@@ -161,31 +161,23 @@ function lti_delete_grade($ltiinstance, $userid){
return $status == GRADE_UPDATE_OK || $status == GRADE_UPDATE_ITEM_DELETED; //grade_update seems to return ok now, but could reasonably return deleted in the future
}
-function lti_verify_message($ltiinstance, $body, $headers = null){
- //Use the key / secret configured on the tool, or look it up from the admin config
- if(empty($ltiinstance->resourcekey) || empty($ltiinstance->password)){
- if($ltiinstance->typeid){
- $typeid = $ltiinstance->typeid;
- } else {
- $tool = lti_get_tool_by_url_match($ltiinstance->toolurl, $ltiinstance->course);
-
- if(!$tool){
- throw new Exception('Tool configuration not found for tool instance ' . $ltiinstance->id);
- }
-
- $typeid = $tool->id;
+function lti_verify_message($key, $sharedsecrets, $body, $headers = null){
+ foreach($sharedsecrets as $secret){
+ $signaturefailed = false;
+
+ try{
+ lti\handleOAuthBodyPOST($key, $secret, $body, $headers);
+ }
+ catch(Exception $e){
+ $signaturefailed = true;
}
-
- $typeconfig = lti_get_type_config($typeid);//Consider only fetching the 2 necessary settings here
- $key = $typeconfig['resourcekey'];
- $secret = $typeconfig['password'];
- } else {
- $key = $ltiinstance->resourcekey;
- $secret = $ltiinstance->password;
+ if(!$signaturefailed){
+ return $secret;//Return the secret used to sign the message)
+ }
}
- lti\handleOAuthBodyPOST($key, $secret, $body, $headers);
+ return false;
}
function lti_verify_sourcedid($ltiinstance, $parsed){

0 comments on commit 020eea1

Please sign in to comment.