Ejemplo n.º 1
1
 /**
  * Check the authenticity of the LTI launch request.
  *
  * The consumer, resource link and user objects will be initialised if the request is valid.
  *
  * @return boolean True if the request has been successfully validated.
  */
 private function authenticate()
 {
     // Get the consumer
     $doSaveConsumer = false;
     // Check all required launch parameters
     $this->ok = isset($_POST['lti_message_type']) && array_key_exists($_POST['lti_message_type'], self::$MESSAGE_TYPES);
     if (!$this->ok) {
         $this->reason = 'Invalid or missing lti_message_type parameter.';
     }
     if ($this->ok) {
         $this->ok = isset($_POST['lti_version']) && in_array($_POST['lti_version'], self::$LTI_VERSIONS);
         if (!$this->ok) {
             $this->reason = 'Invalid or missing lti_version parameter.';
         }
     }
     if ($this->ok) {
         if ($_POST['lti_message_type'] === 'basic-lti-launch-request') {
             $this->ok = isset($_POST['resource_link_id']) && strlen(trim($_POST['resource_link_id'])) > 0;
             if (!$this->ok) {
                 $this->reason = 'Missing resource link ID.';
             }
         } else {
             if ($_POST['lti_message_type'] === 'ContentItemSelectionRequest') {
                 if (isset($_POST['accept_media_types']) && strlen(trim($_POST['accept_media_types'])) > 0) {
                     $mediaTypes = array_filter(explode(',', str_replace(' ', '', $_POST['accept_media_types'])), 'strlen');
                     $mediaTypes = array_unique($mediaTypes);
                     $this->ok = count($mediaTypes) > 0;
                     if (!$this->ok) {
                         $this->reason = 'No accept_media_types found.';
                     } else {
                         $this->mediaTypes = $mediaTypes;
                     }
                 } else {
                     $this->ok = false;
                 }
                 if ($this->ok && isset($_POST['accept_presentation_document_targets']) && strlen(trim($_POST['accept_presentation_document_targets'])) > 0) {
                     $documentTargets = array_filter(explode(',', str_replace(' ', '', $_POST['accept_presentation_document_targets'])), 'strlen');
                     $documentTargets = array_unique($documentTargets);
                     $this->ok = count($documentTargets) > 0;
                     if (!$this->ok) {
                         $this->reason = 'Missing or empty accept_presentation_document_targets parameter.';
                     } else {
                         foreach ($documentTargets as $documentTarget) {
                             $this->ok = $this->checkValue($documentTarget, array('embed', 'frame', 'iframe', 'window', 'popup', 'overlay', 'none'), 'Invalid value in accept_presentation_document_targets parameter: %s.');
                             if (!$this->ok) {
                                 break;
                             }
                         }
                         if ($this->ok) {
                             $this->documentTargets = $documentTargets;
                         }
                     }
                 } else {
                     $this->ok = false;
                 }
                 if ($this->ok) {
                     $this->ok = isset($_POST['content_item_return_url']) && strlen(trim($_POST['content_item_return_url'])) > 0;
                     if (!$this->ok) {
                         $this->reason = 'Missing content_item_return_url parameter.';
                     }
                 }
             } else {
                 if ($_POST['lti_message_type'] == 'ToolProxyRegistrationRequest') {
                     $this->ok = isset($_POST['reg_key']) && strlen(trim($_POST['reg_key'])) > 0 && (isset($_POST['reg_password']) && strlen(trim($_POST['reg_password'])) > 0) && (isset($_POST['tc_profile_url']) && strlen(trim($_POST['tc_profile_url'])) > 0) && (isset($_POST['launch_presentation_return_url']) && strlen(trim($_POST['launch_presentation_return_url'])) > 0);
                     if ($this->debugMode && !$this->ok) {
                         $this->reason = 'Missing message parameters.';
                     }
                 }
             }
         }
     }
     $now = time();
     // Check consumer key
     if ($this->ok && $_POST['lti_message_type'] != 'ToolProxyRegistrationRequest') {
         $this->ok = isset($_POST['oauth_consumer_key']);
         if (!$this->ok) {
             $this->reason = 'Missing consumer key.';
         }
         if ($this->ok) {
             $this->consumer = new ToolConsumer($_POST['oauth_consumer_key'], $this->dataConnector);
             $this->ok = !is_null($this->consumer->created);
             if (!$this->ok) {
                 $this->reason = 'Invalid consumer key.';
             }
         }
         if ($this->ok) {
             $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->last_access = $now;
             try {
                 $store = new OAuthDataStore($this);
                 $server = new OAuth\OAuthServer($store);
                 $method = new OAuth\OAuthSignatureMethod_HMAC_SHA1();
                 $server->add_signature_method($method);
                 $request = OAuth\OAuthRequest::from_request();
                 $res = $server->verify_request($request);
             } catch (\Exception $e) {
                 $this->ok = false;
                 if (empty($this->reason)) {
                     if ($this->debugMode) {
                         $consumer = new OAuth\OAuthConsumer($this->consumer->getKey(), $this->consumer->secret);
                         $signature = $request->build_signature($method, $consumer, false);
                         $this->reason = $e->getMessage();
                         if (empty($this->reason)) {
                             $this->reason = 'OAuth exception';
                         }
                         $this->details[] = 'Timestamp: ' . time();
                         $this->details[] = "Signature: {$signature}";
                         $this->details[] = "Base string: {$request->base_string}]";
                     } else {
                         $this->reason = 'OAuth signature check failed - perhaps an incorrect secret or timestamp.';
                     }
                 }
             }
         }
         if ($this->ok) {
             $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->last_access = $now;
             if ($this->consumer->protected) {
                 if (!is_null($this->consumer->consumerGuid)) {
                     $this->ok = empty($_POST['tool_consumer_instance_guid']) || $this->consumer->consumerGuid === $_POST['tool_consumer_instance_guid'];
                     if (!$this->ok) {
                         $this->reason = 'Request is from an invalid tool consumer.';
                     }
                 } else {
                     $this->ok = isset($_POST['tool_consumer_instance_guid']);
                     if (!$this->ok) {
                         $this->reason = 'A tool consumer GUID must be included in the launch request.';
                     }
                 }
             }
             if ($this->ok) {
                 $this->ok = $this->consumer->enabled;
                 if (!$this->ok) {
                     $this->reason = 'Tool consumer has not been enabled by the tool provider.';
                 }
             }
             if ($this->ok) {
                 $this->ok = is_null($this->consumer->enableFrom) || $this->consumer->enableFrom <= $now;
                 if ($this->ok) {
                     $this->ok = is_null($this->consumer->enableUntil) || $this->consumer->enableUntil > $now;
                     if (!$this->ok) {
                         $this->reason = 'Tool consumer access has expired.';
                     }
                 } else {
                     $this->reason = 'Tool consumer access is not yet available.';
                 }
             }
         }
         // Validate other message parameter values
         if ($this->ok) {
             if ($_POST['lti_message_type'] === 'ContentItemSelectionRequest') {
                 if (isset($_POST['accept_unsigned'])) {
                     $this->ok = $this->checkValue($_POST['accept_unsigned'], array('true', 'false'), 'Invalid value for accept_unsigned parameter: %s.');
                 }
                 if ($this->ok && isset($_POST['accept_multiple'])) {
                     $this->ok = $this->checkValue($_POST['accept_multiple'], array('true', 'false'), 'Invalid value for accept_multiple parameter: %s.');
                 }
                 if ($this->ok && isset($_POST['accept_copy_advice'])) {
                     $this->ok = $this->checkValue($_POST['accept_copy_advice'], array('true', 'false'), 'Invalid value for accept_copy_advice parameter: %s.');
                 }
                 if ($this->ok && isset($_POST['auto_create'])) {
                     $this->ok = $this->checkValue($_POST['auto_create'], array('true', 'false'), 'Invalid value for auto_create parameter: %s.');
                 }
                 if ($this->ok && isset($_POST['can_confirm'])) {
                     $this->ok = $this->checkValue($_POST['can_confirm'], array('true', 'false'), 'Invalid value for can_confirm parameter: %s.');
                 }
             } else {
                 if (isset($_POST['launch_presentation_document_target'])) {
                     $this->ok = $this->checkValue($_POST['launch_presentation_document_target'], array('embed', 'frame', 'iframe', 'window', 'popup', 'overlay'), 'Invalid value for launch_presentation_document_target parameter: %s.');
                 }
             }
         }
     }
     if ($this->ok && $_POST['lti_message_type'] === 'ToolProxyRegistrationRequest') {
         $this->ok = $_POST['lti_version'] == self::LTI_VERSION2;
         if (!$this->ok) {
             $this->reason = 'Invalid lti_version parameter';
         }
         if ($this->ok) {
             $http = new HTTPMessage($_POST['tc_profile_url'], 'GET', null, 'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
             $this->ok = $http->send();
             if (!$this->ok) {
                 $this->reason = 'Tool consumer profile not accessible.';
             } else {
                 $tcProfile = json_decode($http->response);
                 $this->ok = !is_null($tcProfile);
                 if (!$this->ok) {
                     $this->reason = 'Invalid JSON in tool consumer profile.';
                 }
             }
         }
         // Check for required capabilities
         if ($this->ok) {
             $this->consumer = new ToolConsumer($_POST['reg_key'], $this->dataConnector);
             $this->consumer->profile = $tcProfile;
             $capabilities = $this->consumer->profile->capability_offered;
             $missing = array();
             foreach ($this->resourceHandlers as $resourceHandler) {
                 foreach ($resourceHandler->requiredMessages as $message) {
                     if (!in_array($message->type, $capabilities)) {
                         $missing[$message->type] = true;
                     }
                 }
             }
             foreach ($this->constraints as $name => $constraint) {
                 if ($constraint['required']) {
                     if (!in_array($name, $capabilities) && !in_array($name, array_flip($capabilities))) {
                         $missing[$name] = true;
                     }
                 }
             }
             if (!empty($missing)) {
                 ksort($missing);
                 $this->reason = 'Required capability not offered - \'' . implode('\', \'', array_keys($missing)) . '\'';
                 $this->ok = false;
             }
         }
         // Check for required services
         if ($this->ok) {
             foreach ($this->requiredServices as $service) {
                 foreach ($service->formats as $format) {
                     if (!$this->findService($format, $service->actions)) {
                         if ($this->ok) {
                             $this->reason = 'Required service(s) not offered - ';
                             $this->ok = false;
                         } else {
                             $this->reason .= ', ';
                         }
                         $this->reason .= "'{$format}' [" . implode(', ', $service->actions) . ']';
                     }
                 }
             }
         }
         if ($this->ok) {
             if ($_POST['lti_message_type'] === 'ToolProxyRegistrationRequest') {
                 $this->consumer->profile = $tcProfile;
                 $this->consumer->secret = $_POST['reg_password'];
                 $this->consumer->ltiVersion = $_POST['lti_version'];
                 $this->consumer->name = $tcProfile->product_instance->service_owner->service_owner_name->default_value;
                 $this->consumer->consumerName = $this->consumer->name;
                 $this->consumer->consumerVersion = "{$tcProfile->product_instance->product_info->product_family->code}-{$tcProfile->product_instance->product_info->product_version}";
                 $this->consumer->consumerGuid = $tcProfile->product_instance->guid;
                 $this->consumer->enabled = true;
                 $this->consumer->protected = true;
                 $doSaveConsumer = true;
             }
         }
     } else {
         if ($this->ok && !empty($_POST['custom_tc_profile_url']) && empty($this->consumer->profile)) {
             $http = new HTTPMessage($_POST['custom_tc_profile_url'], 'GET', null, 'Accept: application/vnd.ims.lti.v2.toolconsumerprofile+json');
             if ($http->send()) {
                 $tcProfile = json_decode($http->response);
                 if (!is_null($tcProfile)) {
                     $this->consumer->profile = $tcProfile;
                     $doSaveConsumer = true;
                 }
             }
         }
     }
     // Validate message parameter constraints
     if ($this->ok) {
         $invalidParameters = array();
         foreach ($this->constraints as $name => $constraint) {
             if (empty($constraint['messages']) || in_array($_POST['lti_message_type'], $constraint['messages'])) {
                 $ok = true;
                 if ($constraint['required']) {
                     if (!isset($_POST[$name]) || strlen(trim($_POST[$name])) <= 0) {
                         $invalidParameters[] = "{$name} (missing)";
                         $ok = false;
                     }
                 }
                 if ($ok && !is_null($constraint['max_length']) && isset($_POST[$name])) {
                     if (strlen(trim($_POST[$name])) > $constraint['max_length']) {
                         $invalidParameters[] = "{$name} (too long)";
                     }
                 }
             }
         }
         if (count($invalidParameters) > 0) {
             $this->ok = false;
             if (empty($this->reason)) {
                 $this->reason = 'Invalid parameter(s): ' . implode(', ', $invalidParameters) . '.';
             }
         }
     }
     if ($this->ok) {
         // Set the request context
         if (isset($_POST['context_id'])) {
             $this->context = Context::fromConsumer($this->consumer, trim($_POST['context_id']));
             $title = '';
             if (isset($_POST['context_title'])) {
                 $title = trim($_POST['context_title']);
             }
             if (empty($title)) {
                 $title = "Course {$this->context->getId()}";
             }
             if (isset($_POST['context_type'])) {
                 $this->context->type = trim($_POST['context_type']);
             }
             $this->context->title = $title;
         }
         // Set the request resource link
         if (isset($_POST['resource_link_id'])) {
             $contentItemId = '';
             if (isset($_POST['custom_content_item_id'])) {
                 $contentItemId = $_POST['custom_content_item_id'];
             }
             $this->resourceLink = ResourceLink::fromConsumer($this->consumer, trim($_POST['resource_link_id']), $contentItemId);
             if (!empty($this->context)) {
                 $this->resourceLink->setContextId($this->context->getRecordId());
             }
             $title = '';
             if (isset($_POST['resource_link_title'])) {
                 $title = trim($_POST['resource_link_title']);
             }
             if (empty($title)) {
                 $title = "Resource {$this->resourceLink->getId()}";
             }
             $this->resourceLink->title = $title;
             // Delete any existing custom parameters
             foreach ($this->consumer->getSettings() as $name => $value) {
                 if (strpos($name, 'custom_') === 0) {
                     $this->consumer->setSetting($name);
                     $doSaveConsumer = true;
                 }
             }
             if (!empty($this->context)) {
                 foreach ($this->context->getSettings() as $name => $value) {
                     if (strpos($name, 'custom_') === 0) {
                         $this->context->setSetting($name);
                     }
                 }
             }
             foreach ($this->resourceLink->getSettings() as $name => $value) {
                 if (strpos($name, 'custom_') === 0) {
                     $this->resourceLink->setSetting($name);
                 }
             }
             // Save LTI parameters
             foreach (self::$LTI_CONSUMER_SETTING_NAMES as $name) {
                 if (isset($_POST[$name])) {
                     $this->consumer->setSetting($name, $_POST[$name]);
                 } else {
                     $this->consumer->setSetting($name);
                 }
             }
             if (!empty($this->context)) {
                 foreach (self::$LTI_CONTEXT_SETTING_NAMES as $name) {
                     if (isset($_POST[$name])) {
                         $this->context->setSetting($name, $_POST[$name]);
                     } else {
                         $this->context->setSetting($name);
                     }
                 }
             }
             foreach (self::$LTI_RESOURCE_LINK_SETTING_NAMES as $name) {
                 if (isset($_POST[$name])) {
                     $this->resourceLink->setSetting($name, $_POST[$name]);
                 } else {
                     $this->resourceLink->setSetting($name);
                 }
             }
             // Save other custom parameters
             foreach ($_POST as $name => $value) {
                 if (strpos($name, 'custom_') === 0 && !in_array($name, array_merge(self::$LTI_CONSUMER_SETTING_NAMES, self::$LTI_CONTEXT_SETTING_NAMES, self::$LTI_RESOURCE_LINK_SETTING_NAMES))) {
                     $this->resourceLink->setSetting($name, $value);
                 }
             }
         }
         // Set the user instance
         $userId = '';
         if (isset($_POST['user_id'])) {
             $userId = trim($_POST['user_id']);
         }
         $this->user = User::fromResourceLink($this->resourceLink, $userId);
         // Set the user name
         $firstname = isset($_POST['lis_person_name_given']) ? $_POST['lis_person_name_given'] : '';
         $lastname = isset($_POST['lis_person_name_family']) ? $_POST['lis_person_name_family'] : '';
         $fullname = isset($_POST['lis_person_name_full']) ? $_POST['lis_person_name_full'] : '';
         $this->user->setNames($firstname, $lastname, $fullname);
         // Set the user email
         $email = isset($_POST['lis_person_contact_email_primary']) ? $_POST['lis_person_contact_email_primary'] : '';
         $this->user->setEmail($email, $this->defaultEmail);
         // Set the user image URI
         if (isset($_POST['user_image'])) {
             $this->user->image = $_POST['user_image'];
         }
         // Set the user roles
         if (isset($_POST['roles'])) {
             $this->user->roles = self::parseRoles($_POST['roles']);
         }
         // Initialise the consumer and check for changes
         $this->consumer->defaultEmail = $this->defaultEmail;
         if ($this->consumer->ltiVersion !== $_POST['lti_version']) {
             $this->consumer->ltiVersion = $_POST['lti_version'];
             $doSaveConsumer = true;
         }
         if (isset($_POST['tool_consumer_instance_name'])) {
             if ($this->consumer->consumerName !== $_POST['tool_consumer_instance_name']) {
                 $this->consumer->consumerName = $_POST['tool_consumer_instance_name'];
                 $doSaveConsumer = true;
             }
         }
         if (isset($_POST['tool_consumer_info_product_family_code'])) {
             $version = $_POST['tool_consumer_info_product_family_code'];
             if (isset($_POST['tool_consumer_info_version'])) {
                 $version .= "-{$_POST['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($_POST['ext_lms']) && $this->consumer->consumerName !== $_POST['ext_lms']) {
                 $this->consumer->consumerVersion = $_POST['ext_lms'];
                 $doSaveConsumer = true;
             }
         }
         if (isset($_POST['tool_consumer_instance_guid'])) {
             if (is_null($this->consumer->consumerGuid)) {
                 $this->consumer->consumerGuid = $_POST['tool_consumer_instance_guid'];
                 $doSaveConsumer = true;
             } else {
                 if (!$this->consumer->protected) {
                     $doSaveConsumer = $this->consumer->consumerGuid !== $_POST['tool_consumer_instance_guid'];
                     if ($doSaveConsumer) {
                         $this->consumer->consumerGuid = $_POST['tool_consumer_instance_guid'];
                     }
                 }
             }
         }
         if (isset($_POST['launch_presentation_css_url'])) {
             if ($this->consumer->cssPath !== $_POST['launch_presentation_css_url']) {
                 $this->consumer->cssPath = $_POST['launch_presentation_css_url'];
                 $doSaveConsumer = true;
             }
         } else {
             if (isset($_POST['ext_launch_presentation_css_url']) && $this->consumer->cssPath !== $_POST['ext_launch_presentation_css_url']) {
                 $this->consumer->cssPath = $_POST['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 ($this->ok && isset($this->context)) {
         $this->context->save();
     }
     if ($this->ok && isset($this->resourceLink)) {
         // Check if a share arrangement is in place for this resource link
         $this->ok = $this->checkForShare();
         // Persist changes to resource link
         $this->resourceLink->save();
         // Save the user instance
         if (isset($_POST['lis_result_sourcedid'])) {
             if ($this->user->ltiResultSourcedId !== $_POST['lis_result_sourcedid']) {
                 $this->user->ltiResultSourcedId = $_POST['lis_result_sourcedid'];
                 $this->user->save();
             }
         } else {
             if (!empty($this->user->ltiResultSourcedId)) {
                 $this->user->ltiResultSourcedId = '';
                 $this->user->save();
             }
         }
     }
     return $this->ok;
 }
Ejemplo n.º 2
0
 public function setUp()
 {
     $this->resetAfterTest();
     // Set this user as the admin.
     $this->setAdminUser();
     $this->task = new dummy_sync_members_task();
     $generator = $this->getDataGenerator();
     $course = $generator->create_course();
     $tooldata = ['courseid' => $course->id, 'membersyncmode' => helper::MEMBER_SYNC_ENROL_AND_UNENROL, 'membersync' => 1];
     $tool = $generator->create_lti_tool((object) $tooldata);
     $this->tool = helper::get_lti_tool($tool->id);
     $dataconnector = $this->task->get_dataconnector();
     $this->consumer = new ToolConsumer('Consumer1Key', $dataconnector);
     $this->consumer->name = 'Consumer1';
     $this->consumer->secret = 'Consumer1Secret';
     $this->consumer->save();
     $toolprovider = new tool_provider($this->tool->id);
     $toolprovider->consumer = $this->consumer;
     $toolprovider->map_tool_to_consumer();
     $imageurl = $this->getExternalTestFileUrl('test.jpg');
     $count = 10;
     $this->members = [];
     for ($i = 1; $i <= $count; $i++) {
         $user = new User();
         $user->firstname = 'Firstname' . $i;
         $user->lastname = 'Lastname' . $i;
         $user->ltiUserId = 'user' . $i;
         // Set user image values for some users.
         if ($i % 3 == 0) {
             $user->image = $imageurl;
         }
         $this->members[] = $user;
     }
     $this->context = Context::fromConsumer($this->consumer, 'testlticontextid');
     $this->context->save();
     $this->resourcelink = ResourceLink::fromContext($this->context, 'testresourcelinkid');
     $this->resourcelink->save();
 }
Ejemplo n.º 3
0
 /**
  * Class constructor from consumer.
  *
  * @param ToolConsumer $consumer Consumer instance
  * @param string $ltiContextId LTI Context ID value
  * @return Context
  */
 public static function fromConsumer($consumer, $ltiContextId)
 {
     $context = new Context();
     $context->consumer = $consumer;
     $context->dataConnector = $consumer->getDataConnector();
     $context->ltiContextId = $ltiContextId;
     if (!empty($ltiContextId)) {
         $context->load();
     }
     return $context;
 }
Ejemplo n.º 4
0
 /**
  * Class constructor from context.
  *
  * @param Context $context Context object
  * @param string $ltiResourceLinkId Resource link ID value
  * @param string $tempId Temporary Resource link ID value (optional, default is null)
  * @return ResourceLink
  */
 public static function fromContext($context, $ltiResourceLinkId, $tempId = null)
 {
     $resourceLink = new ResourceLink();
     $resourceLink->setContextId($context->getRecordId());
     $resourceLink->context = $context;
     $resourceLink->dataConnector = $context->getDataConnector();
     $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
     if (!empty($ltiResourceLinkId)) {
         $resourceLink->load();
         if (is_null($resourceLink->id) && !empty($tempId)) {
             $resourceLink->ltiResourceLinkId = $tempId;
             $resourceLink->load();
             $resourceLink->ltiResourceLinkId = $ltiResourceLinkId;
         }
     }
     return $resourceLink;
 }
Ejemplo n.º 5
0
 /**
  * Performs membership service request using an LTI Context object.
  *
  * If the context has a 'custom_context_memberships_url' setting, we use this to perform the membership service request.
  * Otherwise, if a context is associated with resource link, we try first to get the members using the
  * ResourceLink::doMembershipsService() method.
  * If we're still unable to fetch members from the resource link, we try to build a memberships URL from the memberships URL
  * endpoint template that is defined in the ToolConsumer profile and substitute the parameters accordingly.
  *
  * @param Context $context The context object.
  * @param ResourceLink $resourcelink The resource link object.
  * @param string $membershipsurltemplate The memberships endpoint URL template.
  * @return bool|User[] Array of User objects upon successful membership service request. False, otherwise.
  */
 protected function do_context_membership_request(Context $context, ResourceLink $resourcelink = null, $membershipsurltemplate = '')
 {
     $dataconnector = $this->dataconnector;
     // Flag to indicate whether to save the context later.
     $contextupdated = false;
     // If membership URL is not set, try to generate using the default membership URL from the consumer profile.
     if (!$context->hasMembershipService()) {
         if (empty($membershipsurltemplate)) {
             mtrace("Skipping - No membership service available.\n");
             return false;
         }
         if ($resourcelink === null) {
             $resourcelink = $dataconnector->get_resourcelink_from_context($context);
         }
         if ($resourcelink !== null) {
             // Try to perform a membership service request using this resource link.
             $resourcelinkmembers = $this->do_resourcelink_membership_request($resourcelink);
             if ($resourcelinkmembers) {
                 // If we're able to fetch members using this resource link, return these.
                 return $resourcelinkmembers;
             }
         }
         // If fetching memberships through resource link failed and we don't have a memberships URL, build one from template.
         mtrace("'custom_context_memberships_url' not set. Fetching default template: {$membershipsurltemplate}");
         $membershipsurl = $membershipsurltemplate;
         // Check if we need to fetch tool code.
         $needstoolcode = strpos($membershipsurl, '{tool_code}') !== false;
         if ($needstoolcode) {
             $toolcode = false;
             // Fetch tool code from the resource link data.
             $lisresultsourcedidjson = $resourcelink->getSetting('lis_result_sourcedid');
             if ($lisresultsourcedidjson) {
                 $lisresultsourcedid = json_decode($lisresultsourcedidjson);
                 if (isset($lisresultsourcedid->data->typeid)) {
                     $toolcode = $lisresultsourcedid->data->typeid;
                 }
             }
             if ($toolcode) {
                 // Substitute fetched tool code value.
                 $membershipsurl = str_replace('{tool_code}', $toolcode, $membershipsurl);
             } else {
                 // We're unable to determine the tool code. End this processing.
                 return false;
             }
         }
         // Get context_id parameter and substitute, if applicable.
         $membershipsurl = str_replace('{context_id}', $context->getId(), $membershipsurl);
         // Get context_type and substitute, if applicable.
         if (strpos($membershipsurl, '{context_type}') !== false) {
             $contexttype = $context->type !== null ? $context->type : 'CourseSection';
             $membershipsurl = str_replace('{context_type}', $contexttype, $membershipsurl);
         }
         // Save this URL for the context's custom_context_memberships_url setting.
         $context->setSetting('custom_context_memberships_url', $membershipsurl);
         $contextupdated = true;
     }
     // Perform membership service request.
     $url = $context->getSetting('custom_context_memberships_url');
     mtrace("Performing membership service request from context with URL {$url}.");
     $members = $context->getMembership();
     // Save the context if membership request succeeded and if it has been updated.
     if ($members && $contextupdated) {
         $context->save();
     }
     return $members;
 }
Ejemplo n.º 6
0
 /**
  * Delete context object.
  *
  * @param Context $context Context object
  *
  * @return boolean True if the Context object was successfully deleted
  */
 public function deleteContext($context)
 {
     // Delete any outstanding share keys for resource links for this context
     $sql = sprintf('DELETE sk ' . "FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . ' sk ' . "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ON sk.resource_link_pk = rl.resource_link_pk ' . 'WHERE rl.context_pk = %d', $context->getRecordId());
     mysql_query($sql);
     // Delete any users in resource links for this context
     $sql = sprintf('DELETE u ' . "FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' u ' . "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ON u.resource_link_pk = rl.resource_link_pk ' . 'WHERE rl.context_pk = %d', $context->getRecordId());
     mysql_query($sql);
     // Update any resource links for which this consumer is acting as a primary resource link
     $sql = sprintf("UPDATE {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' prl ' . "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ON prl.primary_resource_link_pk = rl.resource_link_pk ' . 'SET prl.primary_resource_link_pk = null, prl.share_approved = null ' . 'WHERE rl.context_pk = %d', $context->getRecordId());
     $ok = mysql_query($sql);
     // Delete any resource links for this consumer
     $sql = sprintf('DELETE rl ' . "FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' . 'WHERE rl.context_pk = %d', $context->getRecordId());
     mysql_query($sql);
     // Delete context
     $sql = sprintf('DELETE c ' . "FROM {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' c ', 'WHERE c.context_pk = %d', $context->getRecordId());
     $ok = mysql_query($sql);
     if ($ok) {
         $context->initialize();
     }
     return $ok;
 }
Ejemplo n.º 7
0
 /**
  * Test for data_connector::getSharesResourceLink().
  */
 public function test_get_shares_resource_link()
 {
     $dc = new data_connector();
     $consumer = new ToolConsumer(null, $dc);
     $consumer->name = 'testconsumername';
     $consumer->setKey('TestKey');
     $consumer->secret = 'testsecret';
     $consumer->idScope = ToolProvider::ID_SCOPE_GLOBAL;
     $consumer->save();
     $title = 'testcontexttitle';
     $settings = ['a', 'b', 'c'];
     $lticontextid = 'testlticontextid';
     $context = Context::fromConsumer($consumer, $lticontextid);
     $context->title = $title;
     $context->settings = $settings;
     $context->save();
     $resourcelink = ResourceLink::fromConsumer($consumer, 'testresourcelinkid');
     $resourcelink->setContextId($context->getRecordId());
     $resourcelink->save();
     $shares = $dc->getSharesResourceLink($resourcelink);
     $this->assertEmpty($shares);
     $resourcelinkchild = ResourceLink::fromConsumer($consumer, 'testresourcelinkchildid');
     $resourcelinkchild->primaryResourceLinkId = $resourcelink->getRecordId();
     $resourcelinkchild->shareApproved = true;
     $resourcelinkchild->save();
     $resourcelinkchild2 = ResourceLink::fromConsumer($consumer, 'testresourcelinkchildid2');
     $resourcelinkchild2->primaryResourceLinkId = $resourcelink->getRecordId();
     $resourcelinkchild2->shareApproved = false;
     $resourcelinkchild2->save();
     $shares = $dc->getSharesResourceLink($resourcelink);
     $this->assertCount(2, $shares);
     $shareids = [$resourcelinkchild->getRecordId(), $resourcelinkchild2->getRecordId()];
     foreach ($shares as $share) {
         $this->assertTrue($share instanceof ResourceLinkShare);
         $this->assertTrue(in_array($share->resourceLinkId, $shareids));
         if ($share->resourceLinkId == $shareids[0]) {
             $this->assertTrue($share->approved);
         } else {
             $this->assertFalse($share->approved);
         }
     }
 }
Ejemplo n.º 8
0
 /**
  * Delete context object.
  *
  * @param Context $context Context object
  *
  * @return boolean True if the Context object was successfully deleted
  */
 public function deleteContext($context)
 {
     $id = $context->getRecordId();
     // Delete any outstanding share keys for resource links for this context
     $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . ' ' . "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' . "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_SHARE_KEY_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (rl.context_pk = :id))';
     $query = $this->db->prepare($sql);
     $query->bindValue('id', $id, PDO::PARAM_INT);
     $query->execute();
     // Delete any users in resource links for this context
     $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' ' . "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' . "WHERE ({$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . '.resource_link_pk = rl.resource_link_pk) AND (rl.context_pk = :id))';
     $query = $this->db->prepare($sql);
     $query->bindValue('id', $id, PDO::PARAM_INT);
     $query->execute();
     // Update any resource links for which this consumer is acting as a primary resource link
     $sql = "UPDATE {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' . 'SET primary_resource_link_pk = null, share_approved = null ' . "WHERE EXISTS (SELECT * FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' rl ' . "WHERE ({$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . '.primary_resource_link_pk = rl.resource_link_pk) AND (rl.context_pk = :id))';
     $query = $this->db->prepare($sql);
     $query->bindValue('id', $id, PDO::PARAM_INT);
     $query->execute();
     // Delete any resource links for this consumer
     $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' ' . 'WHERE context_pk = :id';
     $query = $this->db->prepare($sql);
     $query->bindValue('id', $id, PDO::PARAM_INT);
     $query->execute();
     // Delete context
     $sql = "DELETE FROM {$this->dbTableNamePrefix}" . DataConnector::CONTEXT_TABLE_NAME . ' ' . 'WHERE context_pk = :id';
     $query = $this->db->prepare($sql);
     $query->bindValue('id', $id, PDO::PARAM_INT);
     $ok = $query->execute();
     if ($ok) {
         $context->initialize();
     }
     return $ok;
 }
Ejemplo n.º 9
0
 /**
  * Delete context object.
  *
  * @param Context $context Context object
  * @return boolean True if the Context object was successfully deleted
  */
 public function deleteContext($context)
 {
     global $DB;
     $contextid = $context->getRecordId();
     $params = ['id' => $contextid];
     // Delete any outstanding share keys for resource links for this context.
     $where = "resourcelinkid IN (\n                    SELECT rl.id\n                      FROM {{$this->resourcelinktable}} rl\n                     WHERE rl.contextid = :id\n               )";
     $DB->delete_records_select($this->sharekeytable, $where, $params);
     // Delete any users in resource links for this context.
     $DB->delete_records_select($this->userresulttable, $where, $params);
     // Update any resource links for which this consumer is acting as a primary resource link.
     $where = "primaryresourcelinkid IN (\n                    SELECT rl.id\n                      FROM {{$this->resourcelinktable}} rl\n                     WHERE rl.contextid = :id\n               )";
     $updaterecords = $DB->get_records_select($this->resourcelinktable, $where, $params);
     foreach ($updaterecords as $record) {
         $record->primaryresourcelinkid = null;
         $record->shareapproved = null;
         $DB->update_record($this->resourcelinktable, $record);
     }
     // Delete any resource links for this context.
     $DB->delete_records($this->resourcelinktable, ['contextid' => $contextid]);
     // Delete context.
     $DB->delete_records($this->contexttable, $params);
     $context->initialize();
     return true;
 }
Ejemplo n.º 10
0
 /**
  * Delete context object.
  *
  * @param Context $context Context object
  *
  * @return boolean True if the Context object was successfully deleted
  */
 public function deleteContext($context)
 {
     $context->initialize();
     return true;
 }
Ejemplo n.º 11
0
 /**
  * Test for data_connector::get_resourcelink_from_context()
  */
 public function test_get_resourcelink_from_context()
 {
     $dc = new data_connector();
     $consumer = new ToolConsumer(null, $dc);
     $consumer->name = 'TestName';
     $consumer->setKey('TestKey');
     $consumer->secret = 'TestSecret';
     $consumer->save();
     $settings = ['a', 'b', 'c'];
     $lticontextid = 'testlticontextid';
     $context = Context::fromConsumer($consumer, $lticontextid);
     $context->settings = $settings;
     $context->save();
     // No ResourceLink associated with the Context yet.
     $this->assertNull($dc->get_resourcelink_from_context($context));
     // Create and save ResourceLink from the Context.
     $resourcelink = ResourceLink::fromContext($context, 'testresourcelinkid');
     $resourcelink->save();
     $dc->loadResourceLink($resourcelink);
     // Assert that the resource link and the one fetched by get_resourcelink_from_context() are the same.
     $this->assertEquals($resourcelink, $dc->get_resourcelink_from_context($context));
 }
Ejemplo n.º 12
0
 /**
  * Fetches a resource link record that is associated with a Context object.
  *
  * @param Context $context
  * @return ResourceLink
  */
 public function get_resourcelink_from_context(Context $context)
 {
     global $DB;
     $resourcelink = null;
     if ($resourcelinkrecord = $DB->get_record($this->resourcelinktable, ['contextid' => $context->getRecordId()], 'ltiresourcelinkkey')) {
         $resourcelink = ResourceLink::fromContext($context, $resourcelinkrecord->ltiresourcelinkkey);
     }
     return $resourcelink;
 }