Esempio n. 1
0
 /**
  * pretty much a helper function to set up the request
  */
 public static function fromConsumerAndToken($consumer, $token, $http_method, $http_url, $parameters = null)
 {
     $parameters = $parameters ? $parameters : [];
     $defaults = ["oauth_version" => Request::$version, "oauth_nonce" => Request::generateNonce(), "oauth_timestamp" => Request::generateTimestamp(), "oauth_consumer_key" => $consumer->key];
     if ($token) {
         $defaults['oauth_token'] = $token->key;
     }
     $parameters = array_merge($defaults, $parameters);
     return new Request($http_method, $http_url, $parameters);
 }
Esempio n. 2
0
 /**
  * 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
         $query_params = [];
         $query_string = parse_url($url, PHP_URL_QUERY);
         if (!is_null($query_string)) {
             $query_items = explode('&', $query_string);
             foreach ($query_items as $item) {
                 if (strpos($item, '=') !== false) {
                     list($name, $value) = explode('=', $item);
                     $query_params[urldecode($name)] = urldecode($value);
                 } else {
                     $query_params[urldecode($item)] = '';
                 }
             }
         }
         $params = $params + $query_params;
         // Add standard parameters
         $params['lti_version'] = $version;
         $params['lti_message_type'] = $type;
         $params['oauth_callback'] = 'about:blank';
         // Add OAuth signature
         $hmac_method = new SignatureMethodHmacSha1();
         $consumer = new Consumer($this->getKey(), $this->secret, null);
         $req = Request::fromConsumerAndToken($consumer, null, 'POST', $url, $params);
         $req->signRequest($hmac_method, $consumer, null);
         $params = $req->getParameters();
         // Remove parameters being passed on the query string
         foreach (array_keys($query_params) as $name) {
             unset($params[$name]);
         }
     }
     return $params;
 }
Esempio n. 3
0
 /**
  * Check the authenticity of the LTI launch request.
  *
  * The consumer, resource link and user objects will be initialised if the request is valid.
  *
  * @param ServerRequestInterface $request
  * @return bool True if the request has been successfully validated.
  * @throws Exception
  * @internal param array $requestBody
  */
 private function authenticate(ServerRequestInterface $request)
 {
     $requestBody = (array) $request->getParsedBody();
     // Get the consumer
     $doSaveConsumer = false;
     // Check all required launch parameters
     $version = isset($requestBody['lti_version']) ? $requestBody['lti_version'] : '';
     $messageType = isset($requestBody['lti_message_type']) ? $requestBody['lti_message_type'] : '';
     if (!in_array($version, $this->LTI_VERSIONS)) {
         throw new Exception('Invalid or missing lti_version parameter');
     }
     switch ($messageType) {
         case 'basic-lti-launch-request':
         case 'DashboardRequest':
             if (!isset($requestBody['resource_link_id']) || strlen(trim($requestBody['resource_link_id'])) == 0) {
                 throw new Exception('Missing resource link ID');
             }
             break;
         case 'ContentItemSelectionRequest':
             $acceptMediaTypes = isset($requestBody['accept_media_types']) ? trim($requestBody['accept_media_types']) : '';
             if (strlen($acceptMediaTypes) == 0) {
                 throw new Exception('No accept_media_types found');
             }
             $mediaTypes = array_filter(explode(',', str_replace(' ', '', $acceptMediaTypes)), 'strlen');
             $mediaTypes = array_unique($mediaTypes);
             if (count($mediaTypes) == 0) {
                 throw new Exception('No valid accept_media_types found');
             }
             $this->mediaTypes = $mediaTypes;
             $acceptDocumentTargets = isset($requestBody['accept_presentation_document_targets']) ? trim($requestBody['accept_presentation_document_targets']) : '';
             if (strlen($acceptDocumentTargets) == 0) {
                 throw new Exception('No accept_presentation_document_targets found');
             }
             $documentTargets = array_filter(explode(',', str_replace(' ', '', $acceptDocumentTargets), 'strlen'));
             $documentTargets = array_unique($documentTargets);
             if (count($documentTargets) == 0) {
                 throw new Exception('No valid accept_presentation_document_targets found');
             }
             foreach ($documentTargets as $documentTarget) {
                 $this->checkValue($documentTarget, ['embed', 'frame', 'iframe', 'window', 'popup', 'overlay', 'none'], 'Invalid value in accept_presentation_document_targets parameter: %s.');
             }
             $this->documentTargets = $documentTargets;
             $returnUrl = isset($requestBody['content_item_return_url']) ? trim($requestBody['content_item_return_url']) : '';
             if (strlen($returnUrl) == 0) {
                 throw new Exception('Missing content_item_return_url parameter');
             }
             break;
         default:
             throw new Exception('Invalid or missing lti_message_type parameter');
     }
     // Check consumer key
     if (!isset($requestBody['oauth_consumer_key'])) {
         throw new Exception('Missing consumer key');
     }
     $this->consumer = new ToolConsumer($requestBody['oauth_consumer_key'], $this->storage);
     if (is_null($this->consumer->created)) {
         throw new Exception('Invalid consumer key');
     }
     $now = time();
     $today = date('Y-m-d', $now);
     if (is_null($this->consumer->lastAccess)) {
         $doSaveConsumer = true;
     } else {
         $last = date('Y-m-d', $this->consumer->lastAccess);
         $doSaveConsumer = $doSaveConsumer || $last != $today;
     }
     $this->consumer->lastAccess = $now;
     $store = new DataStore($this);
     $server = new Server($store);
     $method = new SignatureMethodHmacSha1();
     $server->addSignatureMethod($method);
     $request = Request::fromPsrRequest($request);
     $server->verifyRequest($request);
     if ($this->consumer->protected) {
         $consumerGuid = isset($requestBody['tool_consumer_instance_guid']) ? $requestBody['tool_consumer_instance_guid'] : '';
         if (empty($consumerGuid)) {
             throw new Exception('A tool consumer GUID must be included in the launch request');
         }
         if ($this->consumer->consumerGuid !== $consumerGuid) {
             throw new Exception('Request is from an invalid tool consumer');
         }
     }
     if (!$this->consumer->enabled) {
         throw new Exception('Tool consumer has not been enabled by the tool provider');
     }
     if (!is_null($this->consumer->enableFrom) && $this->consumer->enableFrom > $now) {
         throw new Exception('Tool consumer access is not yet available');
     }
     if (!is_null($this->consumer->enableUntil) && $this->consumer->enableUntil <= $now) {
         throw new Exception('Tool consumer access has expired');
     }
     // Validate other message parameter values
     if ($requestBody['lti_message_type'] != 'ContentItemSelectionRequest') {
         if (isset($requestBody['launch_presentation_document_target'])) {
             $this->checkValue($requestBody['launch_presentation_document_target'], ['embed', 'frame', 'iframe', 'window', 'popup', 'overlay'], 'Invalid value for launch_presentation_document_target parameter: %s.');
         }
     } else {
         if (isset($requestBody['accept_unsigned'])) {
             $this->checkValue($requestBody['accept_unsigned'], ['true', 'false'], 'Invalid value for accept_unsigned parameter: %s.');
         }
         if (isset($requestBody['accept_multiple'])) {
             $this->checkValue($requestBody['accept_multiple'], ['true', 'false'], 'Invalid value for accept_multiple parameter: %s.');
         }
         if (isset($requestBody['accept_copy_advice'])) {
             $this->checkValue($requestBody['accept_copy_advice'], ['true', 'false'], 'Invalid value for accept_copy_advice parameter: %s.');
         }
         if (isset($requestBody['auto_create'])) {
             $this->checkValue($requestBody['auto_create'], ['true', 'false'], 'Invalid value for auto_create parameter: %s.');
         }
         if (isset($requestBody['can_confirm'])) {
             $this->checkValue($requestBody['can_confirm'], ['true', 'false'], 'Invalid value for can_confirm parameter: %s.');
         }
     }
     // Validate message parameter constraints
     $invalid_parameters = [];
     foreach ($this->constraints as $name => $constraint) {
         if (empty($constraint['messages']) || in_array($messageType, $constraint['messages'])) {
             $ok = true;
             if ($constraint['required']) {
                 if (!isset($requestBody[$name]) || strlen(trim($requestBody[$name])) <= 0) {
                     $invalid_parameters[] = "{$name} (missing)";
                     $ok = false;
                 }
             }
             if ($ok && !is_null($constraint['max_length']) && isset($requestBody[$name])) {
                 if (strlen(trim($requestBody[$name])) > $constraint['max_length']) {
                     $invalid_parameters[] = "{$name} (too long)";
                 }
             }
         }
     }
     if (count($invalid_parameters) > 0) {
         throw new Exception('Invalid parameter(s): ' . implode(', ', $invalid_parameters));
     }
     // Set the request context/resource link
     if (isset($requestBody['resource_link_id'])) {
         $content_item_id = '';
         if (isset($requestBody['custom_content_item_id'])) {
             $content_item_id = $requestBody['custom_content_item_id'];
         }
         $this->resourceLink = new ResourceLink($this->consumer, trim($requestBody['resource_link_id']), $content_item_id);
         if (isset($requestBody['context_id'])) {
             $this->resourceLink->lti_context_id = trim($requestBody['context_id']);
         }
         $this->resourceLink->lti_resource_id = trim($requestBody['resource_link_id']);
         $title = '';
         if (isset($requestBody['context_title'])) {
             $title = trim($requestBody['context_title']);
         }
         if (isset($requestBody['resource_link_title']) && strlen(trim($requestBody['resource_link_title'])) > 0) {
             if (!empty($title)) {
                 $title .= ': ';
             }
             $title .= trim($requestBody['resource_link_title']);
         }
         if (empty($title)) {
             $title = "Course {$this->resourceLink->getId()}";
         }
         $this->resourceLink->title = $title;
         // Save LTI parameters
         foreach ($this->ltiSettingsNames as $name) {
             if (isset($requestBody[$name])) {
                 $this->resourceLink->setSetting($name, $requestBody[$name]);
             } else {
                 $this->resourceLink->setSetting($name, null);
             }
         }
         // Delete any existing custom parameters
         foreach ($this->resourceLink->getSettings() as $name => $value) {
             if (strpos($name, 'custom_') === 0) {
                 $this->resourceLink->setSetting($name);
             }
         }
         // Save custom parameters
         foreach ($requestBody as $name => $value) {
             if (strpos($name, 'custom_') === 0) {
                 $this->resourceLink->setSetting($name, $value);
             }
         }
     }
     // Set the user instance
     $user_id = '';
     if (isset($requestBody['user_id'])) {
         $user_id = trim($requestBody['user_id']);
     }
     $this->user = new User($this->resourceLink, $user_id);
     // Set the user name
     if (isset($requestBody['lis_person_name_given'])) {
         $firstname = $requestBody['lis_person_name_given'];
     } elseif (isset($requestBody['custom_lis_person_given_name'])) {
         $firstname = $requestBody['custom_lis_person_given_name'];
     } else {
         $firstname = '';
     }
     if (isset($requestBody['lis_person_name_family'])) {
         $lastname = $requestBody['lis_person_name_family'];
     } elseif (isset($requestBody['custom_lis_person_name_family'])) {
         $lastname = $requestBody['custom_lis_person_name_family'];
     } else {
         $lastname = '';
     }
     if (isset($requestBody['lis_person_name_full'])) {
         $fullname = $requestBody['lis_person_name_full'];
     } elseif (isset($requestBody['custom_lis_person_name_full'])) {
         $fullname = $requestBody['custom_lis_person_name_full'];
     } else {
         $fullname = '';
     }
     $this->user->setNames($firstname, $lastname, $fullname);
     // Set the user email
     if (isset($requestBody['lis_person_contact_email_primary'])) {
         $email = $requestBody['lis_person_contact_email_primary'];
     } elseif (isset($requestBody['custom_lis_person_contact_email_primary'])) {
         $email = $requestBody['custom_lis_person_contact_email_primary'];
     } else {
         $email = '';
     }
     $this->user->setEmail($email, $this->defaultEmail);
     // Set the user roles
     if (isset($requestBody['roles'])) {
         $this->user->roles = ToolProvider::parseRoles($requestBody['roles']);
     }
     // Save the user instance
     if (isset($requestBody['lis_result_sourcedid'])) {
         if ($this->user->ltiResultSourcedId != $requestBody['lis_result_sourcedid']) {
             $this->user->ltiResultSourcedId = $requestBody['lis_result_sourcedid'];
             $this->user->save();
         }
     } else {
         if (!empty($this->user->ltiResultSourcedId)) {
             $this->user->delete();
         }
     }
     // Initialise the consumer and check for changes
     $this->consumer->defaultEmail = $this->defaultEmail;
     if ($this->consumer->ltiVersion != $requestBody['lti_version']) {
         $this->consumer->ltiVersion = $requestBody['lti_version'];
         $doSaveConsumer = true;
     }
     if (isset($requestBody['tool_consumer_instance_name'])) {
         if ($this->consumer->consumerName != $requestBody['tool_consumer_instance_name']) {
             $this->consumer->consumerName = $requestBody['tool_consumer_instance_name'];
             $doSaveConsumer = true;
         }
     }
     if (isset($requestBody['tool_consumer_info_product_family_code'])) {
         $version = $requestBody['tool_consumer_info_product_family_code'];
         if (isset($requestBody['tool_consumer_info_version'])) {
             $version .= "-{$requestBody['tool_consumer_info_version']}";
         }
         // do not delete any existing consumer version if none is passed
         if ($this->consumer->consumerVersion != $version) {
             $this->consumer->consumerVersion = $version;
             $doSaveConsumer = true;
         }
     } else {
         if (isset($requestBody['ext_lms']) && $this->consumer->consumerName != $requestBody['ext_lms']) {
             $this->consumer->consumerVersion = $requestBody['ext_lms'];
             $doSaveConsumer = true;
         }
     }
     if (isset($requestBody['tool_consumer_instance_guid'])) {
         if (is_null($this->consumer->consumerGuid)) {
             $this->consumer->consumerGuid = $requestBody['tool_consumer_instance_guid'];
             $doSaveConsumer = true;
         } else {
             if (!$this->consumer->protected) {
                 $doSaveConsumer = $this->consumer->consumerGuid != $requestBody['tool_consumer_instance_guid'];
                 if ($doSaveConsumer) {
                     $this->consumer->consumerGuid = $requestBody['tool_consumer_instance_guid'];
                 }
             }
         }
     }
     if (isset($requestBody['launch_presentation_css_url'])) {
         if ($this->consumer->cssPath != $requestBody['launch_presentation_css_url']) {
             $this->consumer->cssPath = $requestBody['launch_presentation_css_url'];
             $doSaveConsumer = true;
         }
     } else {
         if (isset($requestBody['ext_launch_presentation_css_url']) && $this->consumer->cssPath != $requestBody['ext_launch_presentation_css_url']) {
             $this->consumer->cssPath = $requestBody['ext_launch_presentation_css_url'];
             $doSaveConsumer = true;
         } else {
             if (!empty($this->consumer->cssPath)) {
                 $this->consumer->cssPath = null;
                 $doSaveConsumer = true;
             }
         }
     }
     // Persist changes to consumer
     if ($doSaveConsumer) {
         $this->consumer->save();
     }
     if (isset($this->resourceLink)) {
         // Check if a share arrangement is in place for this resource link
         $this->checkForShare($requestBody);
         // Persist changes to resource link
         $this->resourceLink->save();
     }
     return true;
 }