Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
<?php

namespace IMSGlobal\LTI\ToolProvider;

use IMSGlobal\LTI\ToolProvider\DataConnector\DataConnector;
use IMSGlobal\LTI\ToolProvider\Service;
use IMSGlobal\LTI\HTTPMessage;
use IMSGlobal\LTI\OAuth;
use stdClass;

/**
 * Class to represent a tool consumer
 *
 * @author  Stephen P Vickers <svickers@imsglobal.org>
 * @copyright  IMS Global Learning Consortium Inc
 * @date  2016
 * @version 3.0.2
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */
> #[\AllowDynamicProperties]
class ToolConsumer { /** * Local name of tool consumer. * * @var string $name */ public $name = null; /** * Shared secret. * * @var string $secret */ public $secret = null; /** * LTI version (as reported by last tool consumer connection). * * @var string $ltiVersion */ public $ltiVersion = null; /** * Name of tool consumer (as reported by last tool consumer connection). * * @var string $consumerName */ public $consumerName = null; /** * Tool consumer version (as reported by last tool consumer connection). * * @var string $consumerVersion */ public $consumerVersion = null; /** * Tool consumer GUID (as reported by first tool consumer connection). * * @var string $consumerGuid */ public $consumerGuid = null; /** * Optional CSS path (as reported by last tool consumer connection). * * @var string $cssPath */ public $cssPath = null; /** * Whether the tool consumer instance is protected by matching the consumer_guid value in incoming requests. * * @var boolean $protected */ public $protected = false; /** * Whether the tool consumer instance is enabled to accept incoming connection requests. * * @var boolean $enabled */ public $enabled = false; /** * Date/time from which the the tool consumer instance is enabled to accept incoming connection requests. * * @var int $enableFrom */ public $enableFrom = null; /** * Date/time until which the tool consumer instance is enabled to accept incoming connection requests. * * @var int $enableUntil */ public $enableUntil = null; /** * Date of last connection from this tool consumer. * * @var int $lastAccess */ public $lastAccess = null; /** * Default scope to use when generating an Id value for a user. * * @var int $idScope */ public $idScope = ToolProvider::ID_SCOPE_ID_ONLY; /** * Default email address (or email domain) to use when no email address is provided for a user. * * @var string $defaultEmail */ public $defaultEmail = ''; /** * Setting values (LTI parameters, custom parameters and local parameters). * * @var array $settings */ public $settings = null; /** * Date/time when the object was created. * * @var int $created */ public $created = null; /** * Date/time when the object was last updated. * * @var int $updated */ public $updated = null; /** * The consumer profile data. * * @var stdClass */ public $profile = null; /** * Consumer ID value. * * @var int $id */ private $id = null; /** * Consumer key value. * * @var string $key */ private $key = null; /** * Whether the settings value have changed since last saved. * * @var boolean $settingsChanged */ private $settingsChanged = false; /** * Data connector object or string. * * @var mixed $dataConnector */ private $dataConnector = null; /** * Class constructor. * * @param string $key Consumer key * @param DataConnector $dataConnector A data connector object * @param boolean $autoEnable true if the tool consumers is to be enabled automatically (optional, default is false) */ public function __construct($key = null, $dataConnector = null, $autoEnable = false) { $this->initialize(); if (empty($dataConnector)) { $dataConnector = DataConnector::getDataConnector(); } $this->dataConnector = $dataConnector; if (!empty($key)) { $this->load($key, $autoEnable); } else { $this->secret = DataConnector::getRandomString(32); } } /** * Initialise the tool consumer. */ public function initialize() { $this->id = null; $this->key = null; $this->name = null; $this->secret = null; $this->ltiVersion = null; $this->consumerName = null; $this->consumerVersion = null; $this->consumerGuid = null; $this->profile = null; $this->toolProxy = null; $this->settings = array(); $this->protected = false; $this->enabled = false; $this->enableFrom = null; $this->enableUntil = null; $this->lastAccess = null; $this->idScope = ToolProvider::ID_SCOPE_ID_ONLY; $this->defaultEmail = ''; $this->created = null; $this->updated = null; } /** * Initialise the tool consumer. * * Pseudonym for initialize(). */ public function initialise() { $this->initialize(); } /** * Save the tool consumer to the database. * * @return boolean True if the object was successfully saved */ public function save() { $ok = $this->dataConnector->saveToolConsumer($this); if ($ok) { $this->settingsChanged = false; } return $ok; } /** * Delete the tool consumer from the database. * * @return boolean True if the object was successfully deleted */ public function delete() { return $this->dataConnector->deleteToolConsumer($this); } /** * Get the tool consumer record ID. * * @return int Consumer record ID value */ public function getRecordId() { return $this->id; } /** * Sets the tool consumer record ID. * * @param int $id Consumer record ID value */ public function setRecordId($id) { $this->id = $id; } /** * Get the tool consumer key. * * @return string Consumer key value */ public function getKey() { return $this->key; } /** * Set the tool consumer key. * * @param string $key Consumer key value */ public function setKey($key) { $this->key = $key; } /** * Get the data connector. * * @return mixed Data connector object or string */ public function getDataConnector() { return $this->dataConnector; } /** * Is the consumer key available to accept launch requests? * * @return boolean True if the consumer key is enabled and within any date constraints */ public function getIsAvailable() { $ok = $this->enabled; $now = time(); if ($ok && !is_null($this->enableFrom)) { $ok = $this->enableFrom <= $now; } if ($ok && !is_null($this->enableUntil)) { $ok = $this->enableUntil > $now; } return $ok; } /** * Get a setting value. * * @param string $name Name of setting * @param string $default Value to return if the setting does not exist (optional, default is an empty string) * * @return string Setting value */ public function getSetting($name, $default = '') { if (array_key_exists($name, $this->settings)) { $value = $this->settings[$name]; } else { $value = $default; } return $value; } /** * Set a setting value. * * @param string $name Name of setting * @param string $value Value to set, use an empty value to delete a setting (optional, default is null) */ public function setSetting($name, $value = null) { $old_value = $this->getSetting($name); if ($value !== $old_value) { if (!empty($value)) { $this->settings[$name] = $value; } else { unset($this->settings[$name]); } $this->settingsChanged = true; } } /** * Get an array of all setting values. * * @return array Associative array of setting values */ public function getSettings() { return $this->settings; } /** * Set an array of all setting values. * * @param array $settings Associative array of setting values */ public function setSettings($settings) { $this->settings = $settings; } /** * Save setting values. * * @return boolean True if the settings were successfully saved */ public function saveSettings() { if ($this->settingsChanged) { $ok = $this->save(); } else { $ok = true; } return $ok; } /** * Check if the Tool Settings service is supported. * * @return boolean True if this tool consumer supports the Tool Settings service */ public function hasToolSettingsService() { $url = $this->getSetting('custom_system_setting_url'); return !empty($url); } /** * Get Tool Settings. * * @param boolean $simple True if all the simple media type is to be used (optional, default is true) * * @return mixed The array of settings if successful, otherwise false */ public function getToolSettings($simple = true) { $url = $this->getSetting('custom_system_setting_url'); $service = new Service\ToolSettings($this, $url, $simple); $response = $service->get(); return $response; } /** * Perform a Tool Settings service request. * * @param array $settings An associative array of settings (optional, default is none) * * @return boolean True if action was successful, otherwise false */ public function setToolSettings($settings = array()) { $url = $this->getSetting('custom_system_setting_url'); $service = new Service\ToolSettings($this, $url); $response = $service->set($settings); return $response; } /** * Add the OAuth signature to an LTI message. * * @param string $url URL for message request * @param string $type LTI message type * @param string $version LTI version * @param array $params Message parameters * * @return array Array of signed message parameters */ public function signParameters($url, $type, $version, $params) { if (!empty($url)) { // Check for query parameters which need to be included in the signature $queryParams = array(); $queryString = parse_url($url, PHP_URL_QUERY); if (!is_null($queryString)) { $queryItems = explode('&', $queryString); foreach ($queryItems as $item) { if (strpos($item, '=') !== false) { list($name, $value) = explode('=', $item); $queryParams[urldecode($name)] = urldecode($value); } else { $queryParams[urldecode($item)] = ''; } } } $params = $params + $queryParams; // Add standard parameters $params['lti_version'] = $version; $params['lti_message_type'] = $type; $params['oauth_callback'] = 'about:blank'; // Add OAuth signature $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1(); $consumer = new OAuth\OAuthConsumer($this->getKey(), $this->secret, null); $req = OAuth\OAuthRequest::from_consumer_and_token($consumer, null, 'POST', $url, $params); $req->sign_request($hmacMethod, $consumer, null); $params = $req->get_parameters(); // Remove parameters being passed on the query string foreach (array_keys($queryParams) as $name) { unset($params[$name]); } } return $params; } /** * Add the OAuth signature to an array of message parameters or to a header string. * * @return mixed Array of signed message parameters or header string */ public static function addSignature($endpoint, $consumerKey, $consumerSecret, $data, $method = 'POST', $type = null) { $params = array(); if (is_array($data)) { $params = $data; } // Check for query parameters which need to be included in the signature $queryParams = array(); $queryString = parse_url($endpoint, PHP_URL_QUERY); if (!is_null($queryString)) { $queryItems = explode('&', $queryString); foreach ($queryItems as $item) { if (strpos($item, '=') !== false) { list($name, $value) = explode('=', $item); $queryParams[urldecode($name)] = urldecode($value); } else { $queryParams[urldecode($item)] = ''; } } $params = $params + $queryParams; } if (!is_array($data)) { // Calculate body hash $hash = base64_encode(sha1($data, true)); $params['oauth_body_hash'] = $hash; } // Add OAuth signature $hmacMethod = new OAuth\OAuthSignatureMethod_HMAC_SHA1(); $oauthConsumer = new OAuth\OAuthConsumer($consumerKey, $consumerSecret, null); $oauthReq = OAuth\OAuthRequest::from_consumer_and_token($oauthConsumer, null, $method, $endpoint, $params); $oauthReq->sign_request($hmacMethod, $oauthConsumer, null); $params = $oauthReq->get_parameters(); // Remove parameters being passed on the query string foreach (array_keys($queryParams) as $name) { unset($params[$name]); } if (!is_array($data)) { $header = $oauthReq->to_header(); if (empty($data)) { if (!empty($type)) { $header .= "\nAccept: {$type}"; } } else if (isset($type)) { $header .= "\nContent-Type: {$type}"; $header .= "\nContent-Length: " . strlen($data); } return $header; } else { return $params; } } /** * Perform a service request * * @param object $service Service object to be executed * @param string $method HTTP action * @param string $format Media type * @param mixed $data Array of parameters or body string * * @return HTTPMessage HTTP object containing request and response details */ public function doServiceRequest($service, $method, $format, $data) { $header = ToolConsumer::addSignature($service->endpoint, $this->getKey(), $this->secret, $data, $method, $format); // Connect to tool consumer $http = new HTTPMessage($service->endpoint, $method, $data, $header); // Parse JSON response if ($http->send() && !empty($http->response)) { $http->responseJson = json_decode($http->response); $http->ok = !is_null($http->responseJson); } return $http; } /** * Load the tool consumer from the database by its record ID. * * @param string $id The consumer key record ID * @param DataConnector $dataConnector Database connection object * * @return object ToolConsumer The tool consumer object */ public static function fromRecordId($id, $dataConnector) { $toolConsumer = new ToolConsumer(null, $dataConnector); $toolConsumer->initialize(); $toolConsumer->setRecordId($id); if (!$dataConnector->loadToolConsumer($toolConsumer)) { $toolConsumer->initialize(); } return $toolConsumer; } ### ### PRIVATE METHOD ### /** * Load the tool consumer from the database. * * @param string $key The consumer key value * @param boolean $autoEnable True if the consumer should be enabled (optional, default if false) * * @return boolean True if the consumer was successfully loaded */ private function load($key, $autoEnable = false) { $this->key = $key; $ok = $this->dataConnector->loadToolConsumer($this); if (!$ok) { $this->enabled = $autoEnable; } return $ok; } }