Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * This file contains an abstract definition of an LTI service 19 * 20 * @package mod_lti 21 * @copyright 2014 Vital Source Technologies http://vitalsource.com 22 * @author Stephen Vickers 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 27 namespace mod_lti\local\ltiservice; 28 29 defined('MOODLE_INTERNAL') || die(); 30 31 global $CFG; 32 require_once($CFG->dirroot . '/mod/lti/locallib.php'); 33 require_once($CFG->dirroot . '/mod/lti/OAuthBody.php'); 34 35 // TODO: Switch to core oauthlib once implemented - MDL-30149. 36 use moodle\mod\lti as lti; 37 use stdClass; 38 39 40 /** 41 * The mod_lti\local\ltiservice\service_base class. 42 * 43 * @package mod_lti 44 * @since Moodle 2.8 45 * @copyright 2014 Vital Source Technologies http://vitalsource.com 46 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 47 */ 48 abstract class service_base { 49 50 /** Label representing an LTI 2 message type */ 51 const LTI_VERSION2P0 = 'LTI-2p0'; 52 /** Service enabled */ 53 const SERVICE_ENABLED = 1; 54 55 /** @var string ID for the service. */ 56 protected $id; 57 /** @var string Human readable name for the service. */ 58 protected $name; 59 /** @var boolean <code>true</code> if requests for this service do not need to be signed. */ 60 protected $unsigned; 61 /** @var stdClass Tool proxy object for the current service request. */ 62 private $toolproxy; 63 /** @var stdClass LTI type object for the current service request. */ 64 private $type; 65 /** @var array LTI type config array for the current service request. */ 66 private $typeconfig; 67 /** @var array Instances of the resources associated with this service. */ 68 protected $resources; 69 70 71 /** 72 * Class constructor. 73 */ 74 public function __construct() { 75 76 $this->id = null; 77 $this->name = null; 78 $this->unsigned = false; 79 $this->toolproxy = null; 80 $this->type = null; 81 $this->typeconfig = null; 82 $this->resources = null; 83 84 } 85 86 /** 87 * Get the service ID. 88 * 89 * @return string 90 */ 91 public function get_id() { 92 93 return $this->id; 94 95 } 96 97 /** 98 * Get the service compoent ID. 99 * 100 * @return string 101 */ 102 public function get_component_id() { 103 104 return 'ltiservice_' . $this->id; 105 106 } 107 108 /** 109 * Get the service name. 110 * 111 * @return string 112 */ 113 public function get_name() { 114 115 return $this->name; 116 117 } 118 119 /** 120 * Get whether the service requests need to be signed. 121 * 122 * @return boolean 123 */ 124 public function is_unsigned() { 125 126 return $this->unsigned; 127 128 } 129 130 /** 131 * Get the tool proxy object. 132 * 133 * @return stdClass 134 */ 135 public function get_tool_proxy() { 136 137 return $this->toolproxy; 138 139 } 140 141 /** 142 * Set the tool proxy object. 143 * 144 * @param object $toolproxy The tool proxy for this service request 145 * 146 * @var stdClass 147 */ 148 public function set_tool_proxy($toolproxy) { 149 150 $this->toolproxy = $toolproxy; 151 152 } 153 154 /** 155 * Get the type object. 156 * 157 * @return stdClass 158 */ 159 public function get_type() { 160 161 return $this->type; 162 163 } 164 165 /** 166 * Set the LTI type object. 167 * 168 * @param object $type The LTI type for this service request 169 * 170 * @var stdClass 171 */ 172 public function set_type($type) { 173 174 $this->type = $type; 175 176 } 177 178 /** 179 * Get the type config array. 180 * 181 * @return array|null 182 */ 183 public function get_typeconfig() { 184 185 return $this->typeconfig; 186 187 } 188 189 /** 190 * Set the LTI type config object. 191 * 192 * @param array $typeconfig The LTI type config for this service request 193 * 194 * @var array 195 */ 196 public function set_typeconfig($typeconfig) { 197 198 $this->typeconfig = $typeconfig; 199 200 } 201 202 /** 203 * Get the resources for this service. 204 * 205 * @return resource_base[] 206 */ 207 abstract public function get_resources(); 208 209 /** 210 * Get the scope(s) permitted for this service in the context of a particular tool type. 211 * 212 * A null value indicates that no scopes are required to access the service. 213 * 214 * @return array|null 215 */ 216 public function get_permitted_scopes() { 217 return null; 218 } 219 220 /** 221 * Get the scope(s) permitted for this service. 222 * 223 * A null value indicates that no scopes are required to access the service. 224 * 225 * @return array|null 226 */ 227 public function get_scopes() { 228 return null; 229 } 230 231 /** 232 * Returns the configuration options for this service. 233 * 234 * @param \MoodleQuickForm $mform Moodle quickform object definition 235 */ 236 public function get_configuration_options(&$mform) { 237 238 } 239 240 /** 241 * Called when a new LTI Instance is added. 242 * 243 * @param object $lti LTI Instance. 244 */ 245 public function instance_added(object $lti): void { 246 247 } 248 249 /** 250 * Called when a new LTI Instance is updated. 251 * 252 * @param object $lti LTI Instance. 253 */ 254 public function instance_updated(object $lti): void { 255 256 } 257 258 /** 259 * Called when the launch data is created, offering a possibility to alter the 260 * target link URI. 261 * 262 * @param string $messagetype message type for this launch 263 * @param string $targetlinkuri current target link uri 264 * @param null|string $customstr concatenated list of custom parameters 265 * @param int $courseid 266 * @param null|object $lti LTI Instance. 267 * 268 * @return array containing the target link URL and the custom params string to use. 269 */ 270 public function override_endpoint(string $messagetype, string $targetlinkuri, 271 ?string $customstr, int $courseid, ?object $lti = null): array { 272 return [$targetlinkuri, $customstr]; 273 } 274 275 /** 276 * Called when a new LTI Instance is deleted. 277 * 278 * @param int $id LTI Instance. 279 */ 280 public function instance_deleted(int $id): void { 281 282 } 283 284 /** 285 * Set the form data when displaying the LTI Instance form. 286 * 287 * @param object $defaultvalues Default form values. 288 */ 289 public function set_instance_form_values(object $defaultvalues): void { 290 291 } 292 293 /** 294 * Return an array with the names of the parameters that the service will be saving in the configuration 295 * 296 * @return array Names list of the parameters that the service will be saving in the configuration 297 * @deprecated since Moodle 3.7 - please do not use this function any more. 298 */ 299 public function get_configuration_parameter_names() { 300 debugging('get_configuration_parameter_names() has been deprecated.', DEBUG_DEVELOPER); 301 return array(); 302 } 303 304 /** 305 * Default implementation will check for the existence of at least one mod_lti entry for that tool and context. 306 * 307 * It may be overridden if other inferences can be done. 308 * 309 * Ideally a Site Tool should be explicitly engaged with a course, the check on the presence of a link is a proxy 310 * to infer a Site Tool engagement until an explicit Site Tool - Course relationship exists. 311 * 312 * @param int $typeid The tool lti type id. 313 * @param int $courseid The course id. 314 * @return bool returns True if tool is used in context, false otherwise. 315 */ 316 public function is_used_in_context($typeid, $courseid) { 317 global $DB; 318 319 $ok = $DB->record_exists('lti', array('course' => $courseid, 'typeid' => $typeid)); 320 return $ok || $DB->record_exists('lti_types', array('course' => $courseid, 'id' => $typeid)); 321 } 322 323 /** 324 * Checks if there is a site tool or a course tool for this site. 325 * 326 * @param int $typeid The tool lti type id. 327 * @param int $courseid The course id. 328 * @return bool returns True if tool is allowed in context, false otherwise. 329 */ 330 public function is_allowed_in_context($typeid, $courseid) { 331 global $DB; 332 333 // Check if it is a Course tool for this course or a Site tool. 334 $type = $DB->get_record('lti_types', array('id' => $typeid)); 335 336 return $type && ($type->course == $courseid || $type->course == SITEID); 337 } 338 339 /** 340 * Return an array of key/values to add to the launch parameters. 341 * 342 * @param string $messagetype 'basic-lti-launch-request' or 'ContentItemSelectionRequest'. 343 * @param string $courseid The course id. 344 * @param string $userid The user id. 345 * @param string $typeid The tool lti type id. 346 * @param string $modlti The id of the lti activity. 347 * 348 * The type is passed to check the configuration and not return parameters for services not used. 349 * 350 * @return array Key/value pairs to add as launch parameters. 351 */ 352 public function get_launch_parameters($messagetype, $courseid, $userid, $typeid, $modlti = null) { 353 return array(); 354 } 355 356 /** 357 * Return an array of key/claim mapping allowing LTI 1.1 custom parameters 358 * to be transformed to LTI 1.3 claims. 359 * 360 * @return array Key/value pairs of params to claim mapping. 361 */ 362 public function get_jwt_claim_mappings(): array { 363 return []; 364 } 365 366 /** 367 * Get the path for service requests. 368 * 369 * @return string 370 */ 371 public static function get_service_path() { 372 373 $url = new \moodle_url('/mod/lti/services.php'); 374 375 return $url->out(false); 376 377 } 378 379 /** 380 * Parse a string for custom substitution parameter variables supported by this service's resources. 381 * 382 * @param string $value Value to be parsed 383 * 384 * @return string 385 */ 386 public function parse_value($value) { 387 388 if (empty($this->resources)) { 389 $this->resources = $this->get_resources(); 390 } 391 if (!empty($this->resources)) { 392 foreach ($this->resources as $resource) { 393 $value = $resource->parse_value($value); 394 } 395 } 396 397 return $value; 398 399 } 400 401 /** 402 * Check that the request has been properly signed and is permitted. 403 * 404 * @param string $typeid LTI type ID 405 * @param string $body Request body (null if none) 406 * @param string[] $scopes Array of required scope(s) for incoming request 407 * 408 * @return boolean 409 */ 410 public function check_tool($typeid, $body = null, $scopes = null) { 411 412 $ok = true; 413 $toolproxy = null; 414 $consumerkey = lti\get_oauth_key_from_headers($typeid, $scopes); 415 if ($consumerkey === false) { 416 $ok = $this->is_unsigned(); 417 } else { 418 if (empty($typeid) && is_int($consumerkey)) { 419 $typeid = $consumerkey; 420 } 421 if (!empty($typeid)) { 422 $this->type = lti_get_type($typeid); 423 $this->typeconfig = lti_get_type_config($typeid); 424 $ok = !empty($this->type->id); 425 if ($ok && !empty($this->type->toolproxyid)) { 426 $this->toolproxy = lti_get_tool_proxy($this->type->toolproxyid); 427 } 428 } else { 429 $toolproxy = lti_get_tool_proxy_from_guid($consumerkey); 430 if ($toolproxy !== false) { 431 $this->toolproxy = $toolproxy; 432 } 433 } 434 } 435 if ($ok && is_string($consumerkey)) { 436 if (!empty($this->toolproxy)) { 437 $key = $this->toolproxy->guid; 438 $secret = $this->toolproxy->secret; 439 } else { 440 $key = $this->typeconfig['resourcekey']; 441 $secret = $this->typeconfig['password']; 442 } 443 if (!$this->is_unsigned() && ($key == $consumerkey)) { 444 $ok = $this->check_signature($key, $secret, $body); 445 } else { 446 $ok = $this->is_unsigned(); 447 } 448 } 449 450 return $ok; 451 452 } 453 454 /** 455 * Check that the request has been properly signed. 456 * 457 * @param string $toolproxyguid Tool Proxy GUID 458 * @param string $body Request body (null if none) 459 * 460 * @return boolean 461 * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more. 462 * @see service_base::check_tool() 463 */ 464 public function check_tool_proxy($toolproxyguid, $body = null) { 465 466 debugging('check_tool_proxy() is deprecated to allow LTI 1 connections to support services. ' . 467 'Please use service_base::check_tool() instead.', DEBUG_DEVELOPER); 468 $ok = false; 469 $toolproxy = null; 470 $consumerkey = lti\get_oauth_key_from_headers(); 471 if (empty($toolproxyguid)) { 472 $toolproxyguid = $consumerkey; 473 } 474 475 if (!empty($toolproxyguid)) { 476 $toolproxy = lti_get_tool_proxy_from_guid($toolproxyguid); 477 if ($toolproxy !== false) { 478 if (!$this->is_unsigned() && ($toolproxy->guid == $consumerkey)) { 479 $ok = $this->check_signature($toolproxy->guid, $toolproxy->secret, $body); 480 } else { 481 $ok = $this->is_unsigned(); 482 } 483 } 484 } 485 if ($ok) { 486 $this->toolproxy = $toolproxy; 487 } 488 489 return $ok; 490 491 } 492 493 /** 494 * Check that the request has been properly signed. 495 * 496 * @param int $typeid The tool id 497 * @param int $courseid The course we are at 498 * @param string $body Request body (null if none) 499 * 500 * @return bool 501 * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more. 502 * @see service_base::check_tool() 503 */ 504 public function check_type($typeid, $courseid, $body = null) { 505 debugging('check_type() is deprecated to allow LTI 1 connections to support services. ' . 506 'Please use service_base::check_tool() instead.', DEBUG_DEVELOPER); 507 $ok = false; 508 $tool = null; 509 $consumerkey = lti\get_oauth_key_from_headers(); 510 if (empty($typeid)) { 511 return $ok; 512 } else if ($this->is_allowed_in_context($typeid, $courseid)) { 513 $tool = lti_get_type_type_config($typeid); 514 if ($tool !== false) { 515 if (!$this->is_unsigned() && ($tool->lti_resourcekey == $consumerkey)) { 516 $ok = $this->check_signature($tool->lti_resourcekey, $tool->lti_password, $body); 517 } else { 518 $ok = $this->is_unsigned(); 519 } 520 } 521 } 522 return $ok; 523 } 524 525 /** 526 * Check the request signature. 527 * 528 * @param string $consumerkey Consumer key 529 * @param string $secret Shared secret 530 * @param string $body Request body 531 * 532 * @return boolean 533 */ 534 private function check_signature($consumerkey, $secret, $body) { 535 536 $ok = true; 537 try { 538 // TODO: Switch to core oauthlib once implemented - MDL-30149. 539 lti\handle_oauth_body_post($consumerkey, $secret, $body); 540 } catch (\Exception $e) { 541 debugging($e->getMessage() . "\n"); 542 $ok = false; 543 } 544 545 return $ok; 546 547 } 548 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body