See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 3 namespace IMSGlobal\LTI\ToolProvider; 4 5 use IMSGlobal\LTI\ToolProvider\DataConnector\DataConnector; 6 use IMSGlobal\LTI\ToolProvider\Service; 7 use IMSGlobal\LTI\HTTPMessage; 8 use IMSGlobal\LTI\OAuth; 9 use stdClass; 10 11 /** 12 * Class to represent a tool consumer 13 * 14 * @author Stephen P Vickers <svickers@imsglobal.org> 15 * @copyright IMS Global Learning Consortium Inc 16 * @date 2016 17 * @version 3.0.2 18 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 19 */ 20 class ToolConsumer 21 { 22 23 /** 24 * Local name of tool consumer. 25 * 26 * @var string $name 27 */ 28 public $name = null; 29 /** 30 * Shared secret. 31 * 32 * @var string $secret 33 */ 34 public $secret = null; 35 /** 36 * LTI version (as reported by last tool consumer connection). 37 * 38 * @var string $ltiVersion 39 */ 40 public $ltiVersion = null; 41 /** 42 * Name of tool consumer (as reported by last tool consumer connection). 43 * 44 * @var string $consumerName 45 */ 46 public $consumerName = null; 47 /** 48 * Tool consumer version (as reported by last tool consumer connection). 49 * 50 * @var string $consumerVersion 51 */ 52 public $consumerVersion = null; 53 /** 54 * Tool consumer GUID (as reported by first tool consumer connection). 55 * 56 * @var string $consumerGuid 57 */ 58 public $consumerGuid = null; 59 /** 60 * Optional CSS path (as reported by last tool consumer connection). 61 * 62 * @var string $cssPath 63 */ 64 public $cssPath = null; 65 /** 66 * Whether the tool consumer instance is protected by matching the consumer_guid value in incoming requests. 67 * 68 * @var boolean $protected 69 */ 70 public $protected = false; 71 /** 72 * Whether the tool consumer instance is enabled to accept incoming connection requests. 73 * 74 * @var boolean $enabled 75 */ 76 public $enabled = false; 77 /** 78 * Date/time from which the the tool consumer instance is enabled to accept incoming connection requests. 79 * 80 * @var int $enableFrom 81 */ 82 public $enableFrom = null; 83 /** 84 * Date/time until which the tool consumer instance is enabled to accept incoming connection requests. 85 * 86 * @var int $enableUntil 87 */ 88 public $enableUntil = null; 89 /** 90 * Date of last connection from this tool consumer. 91 * 92 * @var int $lastAccess 93 */ 94 public $lastAccess = null; 95 /** 96 * Default scope to use when generating an Id value for a user. 97 * 98 * @var int $idScope 99 */ 100 public $idScope = ToolProvider::ID_SCOPE_ID_ONLY; 101 /** 102 * Default email address (or email domain) to use when no email address is provided for a user. 103 * 104 * @var string $defaultEmail 105 */ 106 public $defaultEmail = ''; 107 /** 108 * Setting values (LTI parameters, custom parameters and local parameters). 109 * 110 * @var array $settings 111 */ 112 public $settings = null; 113 /** 114 * Date/time when the object was created. 115 * 116 * @var int $created 117 */ 118 public $created = null; 119 /** 120 * Date/time when the object was last updated. 121 * 122 * @var int $updated 123 */ 124 public $updated = null; 125 /** 126 * The consumer profile data. 127 * 128 * @var stdClass 129 */ 130 public $profile = null; 131 132 /** 133 * Consumer ID value. 134 * 135 * @var int $id 136 */ 137 private $id = null; 138 /** 139 * Consumer key value. 140 * 141 * @var string $key 142 */ 143 private $key = null; 144 /** 145 * Whether the settings value have changed since last saved. 146 * 147 * @var boolean $settingsChanged 148 */ 149 private $settingsChanged = false; 150 /** 151 * Data connector object or string. 152 * 153 * @var mixed $dataConnector 154 */ 155 private $dataConnector = null; 156 157 /** 158 * Class constructor. 159 * 160 * @param string $key Consumer key 161 * @param DataConnector $dataConnector A data connector object 162 * @param boolean $autoEnable true if the tool consumers is to be enabled automatically (optional, default is false) 163 */ 164 public function __construct($key = null, $dataConnector = null, $autoEnable = false) 165 { 166 167 $this->initialize(); 168 if (empty($dataConnector)) { 169 $dataConnector = DataConnector::getDataConnector(); 170 } 171 $this->dataConnector = $dataConnector; 172 if (!empty($key)) { 173 $this->load($key, $autoEnable); 174 } else { 175 $this->secret = DataConnector::getRandomString(32); 176 } 177 178 } 179 180 /** 181 * Initialise the tool consumer. 182 */ 183 public function initialize() 184 { 185 186 $this->id = null; 187 $this->key = null; 188 $this->name = null; 189 $this->secret = null; 190 $this->ltiVersion = null; 191 $this->consumerName = null; 192 $this->consumerVersion = null; 193 $this->consumerGuid = null; 194 $this->profile = null; 195 $this->toolProxy = null; 196 $this->settings = array(); 197 $this->protected = false; 198 $this->enabled = false; 199 $this->enableFrom = null; 200 $this->enableUntil = null; 201 $this->lastAccess = null; 202 $this->idScope = ToolProvider::ID_SCOPE_ID_ONLY; 203 $this->defaultEmail = ''; 204 $this->created = null; 205 $this->updated = null; 206 207 } 208 209 /** 210 * Initialise the tool consumer. 211 * 212 * Pseudonym for initialize(). 213 */ 214 public function initialise() 215 { 216 217 $this->initialize(); 218 219 } 220 221 /** 222 * Save the tool consumer to the database. 223 * 224 * @return boolean True if the object was successfully saved 225 */ 226 public function save() 227 { 228 229 $ok = $this->dataConnector->saveToolConsumer($this); 230 if ($ok) { 231 $this->settingsChanged = false; 232 } 233 234 return $ok; 235 236 } 237 238 /** 239 * Delete the tool consumer from the database. 240 * 241 * @return boolean True if the object was successfully deleted 242 */ 243 public function delete() 244 { 245 246 return $this->dataConnector->deleteToolConsumer($this); 247 248 } 249 250 /** 251 * Get the tool consumer record ID. 252 * 253 * @return int Consumer record ID value 254 */ 255 public function getRecordId() 256 { 257 258 return $this->id; 259 260 } 261 262 /** 263 * Sets the tool consumer record ID. 264 * 265 * @param int $id Consumer record ID value 266 */ 267 public function setRecordId($id) 268 { 269 270 $this->id = $id; 271 272 } 273 274 /** 275 * Get the tool consumer key. 276 * 277 * @return string Consumer key value 278 */ 279 public function getKey() 280 { 281 282 return $this->key; 283 284 } 285 286 /** 287 * Set the tool consumer key. 288 * 289 * @param string $key Consumer key value 290 */ 291 public function setKey($key) 292 { 293 294 $this->key = $key; 295 296 } 297 298 /** 299 * Get the data connector. 300 * 301 * @return mixed Data connector object or string 302 */ 303 public function getDataConnector() 304 { 305 306 return $this->dataConnector; 307 308 } 309 310 /** 311 * Is the consumer key available to accept launch requests? 312 * 313 * @return boolean True if the consumer key is enabled and within any date constraints 314 */ 315 public function getIsAvailable() 316 { 317 318 $ok = $this->enabled; 319 320 $now = time(); 321 if ($ok && !is_null($this->enableFrom)) { 322 $ok = $this->enableFrom <= $now; 323 } 324 if ($ok && !is_null($this->enableUntil)) { 325 $ok = $this->enableUntil > $now; 326 } 327 328 return $ok; 329 330 } 331 332 /** 333 * Get a setting value. 334 * 335 * @param string $name Name of setting 336 * @param string $default Value to return if the setting does not exist (optional, default is an empty string) 337 * 338 * @return string Setting value 339 */ 340 public function getSetting($name, $default = '') 341 { 342 343 if (array_key_exists($name, $this->settings)) { 344 $value = $this->settings[$name]; 345 } else { 346 $value = $default; 347 } 348 349 return $value; 350 351 } 352 353 /** 354 * Set a setting value. 355 * 356 * @param string $name Name of setting 357 * @param string $value Value to set, use an empty value to delete a setting (optional, default is null) 358 */ 359 public function setSetting($name, $value = null) 360 { 361 362 $old_value = $this->getSetting($name); 363 if ($value !== $old_value) { 364 if (!empty($value)) { 365 $this->settings[$name] = $value; 366 } else { 367 unset($this->settings[$name]); 368 } 369 $this->settingsChanged = true; 370 } 371 372 } 373 374 /** 375 * Get an array of all setting values. 376 * 377 * @return array Associative array of setting values 378 */ 379 public function getSettings() 380 { 381 382 return $this->settings; 383 384 } 385 386 /** 387 * Set an array of all setting values. 388 * 389 * @param array $settings Associative array of setting values 390 */ 391 public function setSettings($settings) 392 { 393 394 $this->settings = $settings; 395 396 } 397 398 /** 399 * Save setting values. 400 * 401 * @return boolean True if the settings were successfully saved 402 */ 403 public function saveSettings() 404 { 405 406 if ($this->settingsChanged) { 407 $ok = $this->save(); 408 } else { 409 $ok = true; 410 } 411 412 return $ok; 413 414 } 415 416 /** 417 * Check if the Tool Settings service is supported. 418 * 419 * @return boolean True if this tool consumer supports the Tool Settings service 420 */ 421 public function hasToolSettingsService() 422 { 423 424 $url = $this->getSetting('custom_system_setting_url'); 425 426 return !empty($url); 427 428 } 429 430 /** 431 * Get Tool Settings. 432 * 433 * @param boolean $simple True if all the simple media type is to be used (optional, default is true) 434 * 435 * @return mixed The array of settings if successful, otherwise false 436 */ 437 public function getToolSettings($simple = true) 438 { 439 440 $url = $this->getSetting('custom_system_setting_url'); 441 $service = new Service\ToolSettings($this, $url, $simple); 442 $response = $service->get(); 443 444 return $response; 445 446 } 447 448 /** 449 * Perform a Tool Settings service request. 450 * 451 * @param array $settings An associative array of settings (optional, default is none) 452 * 453 * @return boolean True if action was successful, otherwise false 454 */ 455 public function setToolSettings($settings = array()) 456 { 457 458 $url = $this->getSetting('custom_system_setting_url'); 459 $service = new Service\ToolSettings($this, $url); 460 $response = $service->set($settings); 461 462 return $response; 463 464 } 465 466 /** 467 * Add the OAuth signature to an LTI message. 468 * 469 * @param string $url URL for message request 470 * @param string $type LTI message type 471 * @param string $version LTI version 472 * @param array $params Message parameters 473 * 474 * @return array Array of signed message parameters 475 */ 476 public function signParameters($url, $type, $version, $params) 477 { 478 479 if (!empty($url)) { 480 // Check for query parameters which need to be included in the signature 481 $queryParams = array(); 482 $queryString = parse_url($url, PHP_URL_QUERY); 483 if (!is_null($queryString)) { 484 $queryItems = explode('&', $queryString); 485 foreach ($queryItems as $item) { 486 if (strpos($item, '=') !== false) { 487 list($name, $value) = explode('=', $item); 488 $queryParams[urldecode($name)] = urldecode($value); 489 } else { 490 $queryParams[urldecode($item)] = ''; 491 } 492 } 493 } 494 $params = $params + $queryParams; 495 // Add standard parameters 496 $params['lti_version'] = $version; 497 $params['lti_message_type'] = $type; 498 $params['oauth_callback'] = 'about:blank'; 499 // Add OAuth signature 500 $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1(); 501 $consumer = new OAuth\OAuthConsumer($this->getKey(), $this->secret, null); 502 $req = OAuth\OAuthRequest::from_consumer_and_token($consumer, null, 'POST', $url, $params); 503 $req->sign_request($hmacMethod, $consumer, null); 504 $params = $req->get_parameters(); 505 // Remove parameters being passed on the query string 506 foreach (array_keys($queryParams) as $name) { 507 unset($params[$name]); 508 } 509 } 510 511 return $params; 512 513 } 514 515 /** 516 * Add the OAuth signature to an array of message parameters or to a header string. 517 * 518 * @return mixed Array of signed message parameters or header string 519 */ 520 public static function addSignature($endpoint, $consumerKey, $consumerSecret, $data, $method = 'POST', $type = null) 521 { 522 523 $params = array(); 524 if (is_array($data)) { 525 $params = $data; 526 } 527 // Check for query parameters which need to be included in the signature 528 $queryParams = array(); 529 $queryString = parse_url($endpoint, PHP_URL_QUERY); 530 if (!is_null($queryString)) { 531 $queryItems = explode('&', $queryString); 532 foreach ($queryItems as $item) { 533 if (strpos($item, '=') !== false) { 534 list($name, $value) = explode('=', $item); 535 $queryParams[urldecode($name)] = urldecode($value); 536 } else { 537 $queryParams[urldecode($item)] = ''; 538 } 539 } 540 $params = $params + $queryParams; 541 } 542 543 if (!is_array($data)) { 544 // Calculate body hash 545 $hash = base64_encode(sha1($data, true)); 546 $params['oauth_body_hash'] = $hash; 547 } 548 549 // Add OAuth signature 550 $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1(); 551 $oauthConsumer = new OAuth\OAuthConsumer($consumerKey, $consumerSecret, null); 552 $oauthReq = OAuth\OAuthRequest::from_consumer_and_token($oauthConsumer, null, $method, $endpoint, $params); 553 $oauthReq->sign_request($hmacMethod, $oauthConsumer, null); 554 $params = $oauthReq->get_parameters(); 555 // Remove parameters being passed on the query string 556 foreach (array_keys($queryParams) as $name) { 557 unset($params[$name]); 558 } 559 560 if (!is_array($data)) { 561 $header = $oauthReq->to_header(); 562 if (empty($data)) { 563 if (!empty($type)) { 564 $header .= "\nAccept: {$type}"; 565 } 566 } else if (isset($type)) { 567 $header .= "\nContent-Type: {$type}"; 568 $header .= "\nContent-Length: " . strlen($data); 569 } 570 return $header; 571 } else { 572 return $params; 573 } 574 575 } 576 577 /** 578 * Perform a service request 579 * 580 * @param object $service Service object to be executed 581 * @param string $method HTTP action 582 * @param string $format Media type 583 * @param mixed $data Array of parameters or body string 584 * 585 * @return HTTPMessage HTTP object containing request and response details 586 */ 587 public function doServiceRequest($service, $method, $format, $data) 588 { 589 590 $header = ToolConsumer::addSignature($service->endpoint, $this->getKey(), $this->secret, $data, $method, $format); 591 592 // Connect to tool consumer 593 $http = new HTTPMessage($service->endpoint, $method, $data, $header); 594 // Parse JSON response 595 if ($http->send() && !empty($http->response)) { 596 $http->responseJson = json_decode($http->response); 597 $http->ok = !is_null($http->responseJson); 598 } 599 600 return $http; 601 602 } 603 604 /** 605 * Load the tool consumer from the database by its record ID. 606 * 607 * @param string $id The consumer key record ID 608 * @param DataConnector $dataConnector Database connection object 609 * 610 * @return object ToolConsumer The tool consumer object 611 */ 612 public static function fromRecordId($id, $dataConnector) 613 { 614 615 $toolConsumer = new ToolConsumer(null, $dataConnector); 616 617 $toolConsumer->initialize(); 618 $toolConsumer->setRecordId($id); 619 if (!$dataConnector->loadToolConsumer($toolConsumer)) { 620 $toolConsumer->initialize(); 621 } 622 623 return $toolConsumer; 624 625 } 626 627 628 ### 629 ### PRIVATE METHOD 630 ### 631 632 /** 633 * Load the tool consumer from the database. 634 * 635 * @param string $key The consumer key value 636 * @param boolean $autoEnable True if the consumer should be enabled (optional, default if false) 637 * 638 * @return boolean True if the consumer was successfully loaded 639 */ 640 private function load($key, $autoEnable = false) 641 { 642 643 $this->key = $key; 644 $ok = $this->dataConnector->loadToolConsumer($this); 645 if (!$ok) { 646 $this->enabled = $autoEnable; 647 } 648 649 return $ok; 650 651 } 652 653 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body