/** * 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; }
/** * Test for enrol_lti_plugin::delete_instance(). */ public function test_delete_instance() { global $DB; // Create tool enrolment instance. $data = new stdClass(); $data->enrolstartdate = time(); $data->secret = 'secret'; $tool = $this->getDataGenerator()->create_lti_tool($data); // Create consumer and related data. $dataconnector = new data_connector(); $consumer = new ToolConsumer('testkey', $dataconnector); $consumer->secret = $tool->secret; $consumer->ltiVersion = ToolProvider::LTI_VERSION1; $consumer->name = 'TEST CONSUMER NAME'; $consumer->consumerName = 'TEST CONSUMER INSTANCE NAME'; $consumer->consumerGuid = 'TEST CONSUMER INSTANCE GUID'; $consumer->consumerVersion = 'TEST CONSUMER INFO VERSION'; $consumer->enabled = true; $consumer->protected = true; $consumer->save(); $resourcelink = ResourceLink::fromConsumer($consumer, 'testresourcelinkid'); $resourcelink->save(); $ltiuser = User::fromResourceLink($resourcelink, ''); $ltiuser->ltiResultSourcedId = 'testLtiResultSourcedId'; $ltiuser->ltiUserId = 'testuserid'; $ltiuser->email = '*****@*****.**'; $ltiuser->save(); $tp = new tool_provider($tool->id); $tp->user = $ltiuser; $tp->resourceLink = $resourcelink; $tp->consumer = $consumer; $tp->map_tool_to_consumer(); $mappingparams = ['toolid' => $tool->id, 'consumerid' => $tp->consumer->getRecordId()]; // Check first that the related records exist. $this->assertTrue($DB->record_exists('enrol_lti_tool_consumer_map', $mappingparams)); $this->assertTrue($DB->record_exists('enrol_lti_lti2_consumer', ['id' => $consumer->getRecordId()])); $this->assertTrue($DB->record_exists('enrol_lti_lti2_resource_link', ['id' => $resourcelink->getRecordId()])); $this->assertTrue($DB->record_exists('enrol_lti_lti2_user_result', ['id' => $ltiuser->getRecordId()])); // Perform deletion. $enrollti = new enrol_lti_plugin(); $instance = $DB->get_record('enrol', ['id' => $tool->enrolid]); $enrollti->delete_instance($instance); // Check that the related records have been deleted. $this->assertFalse($DB->record_exists('enrol_lti_tool_consumer_map', $mappingparams)); $this->assertFalse($DB->record_exists('enrol_lti_lti2_consumer', ['id' => $consumer->getRecordId()])); $this->assertFalse($DB->record_exists('enrol_lti_lti2_resource_link', ['id' => $resourcelink->getRecordId()])); $this->assertFalse($DB->record_exists('enrol_lti_lti2_user_result', ['id' => $ltiuser->getRecordId()])); // Check that the enrolled users and the tool instance has been deleted. $this->assertFalse($DB->record_exists('enrol_lti_users', ['toolid' => $tool->id])); $this->assertFalse($DB->record_exists('enrol_lti_tools', ['id' => $tool->id])); $this->assertFalse($DB->record_exists('enrol', ['id' => $instance->id])); }
/** * Perform a Memberships service request. * * The user table is updated with the new list of user objects. * * @param boolean $withGroups True is group information is to be requested as well * * @return mixed Array of User objects or False if the request was not successful */ public function doMembershipsService($withGroups = false) { $users = array(); $oldUsers = $this->getUserResultSourcedIDs(true, ToolProvider::ID_SCOPE_RESOURCE); $this->extResponse = null; $url = $this->getSetting('ext_ims_lis_memberships_url'); $params = array(); $params['id'] = $this->getSetting('ext_ims_lis_memberships_id'); $ok = false; if ($withGroups) { $ok = $this->doService('basic-lis-readmembershipsforcontextwithgroups', $url, $params); } if ($ok) { $this->groupSets = array(); $this->groups = array(); } else { $ok = $this->doService('basic-lis-readmembershipsforcontext', $url, $params); } if ($ok) { if (!isset($this->extNodes['memberships']['member'])) { $members = array(); } else { if (!isset($this->extNodes['memberships']['member'][0])) { $members = array(); $members[0] = $this->extNodes['memberships']['member']; } else { $members = $this->extNodes['memberships']['member']; } } for ($i = 0; $i < count($members); $i++) { $user = User::fromResourceLink($this, $members[$i]['user_id']); // Set the user name $firstname = isset($members[$i]['person_name_given']) ? $members[$i]['person_name_given'] : ''; $lastname = isset($members[$i]['person_name_family']) ? $members[$i]['person_name_family'] : ''; $fullname = isset($members[$i]['person_name_full']) ? $members[$i]['person_name_full'] : ''; $user->setNames($firstname, $lastname, $fullname); // Set the user email $email = isset($members[$i]['person_contact_email_primary']) ? $members[$i]['person_contact_email_primary'] : ''; $user->setEmail($email, $this->getConsumer()->defaultEmail); /// Set the user roles if (isset($members[$i]['roles'])) { $user->roles = ToolProvider::parseRoles($members[$i]['roles']); } // Set the user groups if (!isset($members[$i]['groups']['group'])) { $groups = array(); } else { if (!isset($members[$i]['groups']['group'][0])) { $groups = array(); $groups[0] = $members[$i]['groups']['group']; } else { $groups = $members[$i]['groups']['group']; } } for ($j = 0; $j < count($groups); $j++) { $group = $groups[$j]; if (isset($group['set'])) { $set_id = $group['set']['id']; if (!isset($this->groupSets[$set_id])) { $this->groupSets[$set_id] = array('title' => $group['set']['title'], 'groups' => array(), 'num_members' => 0, 'num_staff' => 0, 'num_learners' => 0); } $this->groupSets[$set_id]['num_members']++; if ($user->isStaff()) { $this->groupSets[$set_id]['num_staff']++; } if ($user->isLearner()) { $this->groupSets[$set_id]['num_learners']++; } if (!in_array($group['id'], $this->groupSets[$set_id]['groups'])) { $this->groupSets[$set_id]['groups'][] = $group['id']; } $this->groups[$group['id']] = array('title' => $group['title'], 'set' => $set_id); } else { $this->groups[$group['id']] = array('title' => $group['title']); } $user->groups[] = $group['id']; } // If a result sourcedid is provided save the user if (isset($members[$i]['lis_result_sourcedid'])) { $user->ltiResultSourcedId = $members[$i]['lis_result_sourcedid']; $user->save(); } $users[] = $user; // Remove old user (if it exists) unset($oldUsers[$user->getId(ToolProvider::ID_SCOPE_RESOURCE)]); } // Delete any old users which were not in the latest list from the tool consumer foreach ($oldUsers as $id => $user) { $user->delete(); } } else { $users = false; } return $users; }
/** * Get the memberships. * * @param string $role Role for which memberships are to be requested (optional, default is all roles) * @param int $limit Limit on the number of memberships to be returned (optional, default is all) * * @return mixed The array of User objects if successful, otherwise false */ public function get($role = null, $limit = 0) { $isLink = is_a($this->source, 'IMSGlobal\\LTI\\ToolProvider\\ResourceLink'); $parameters = array(); if (!empty($role)) { $parameters['role'] = $role; } if ($limit > 0) { $parameters['limit'] = strval($limit); } if ($isLink) { $parameters['rlid'] = $this->source->getId(); } $http = $this->send('GET', $parameters); if (!$http->ok) { $users = false; } else { $users = array(); if ($isLink) { $oldUsers = $this->source->getUserResultSourcedIDs(true, ToolProvider\ToolProvider::ID_SCOPE_RESOURCE); } foreach ($http->responseJson->pageOf->membershipSubject->membership as $membership) { $member = $membership->member; if ($isLink) { $user = ToolProvider\User::fromResourceLink($this->source, $member->userId); } else { $user = new ToolProvider\User(); $user->ltiUserId = $member->userId; } // Set the user name $firstname = isset($member->givenName) ? $member->givenName : ''; $lastname = isset($member->familyName) ? $member->familyName : ''; $fullname = isset($member->name) ? $member->name : ''; $user->setNames($firstname, $lastname, $fullname); // Set the user email $email = isset($member->email) ? $member->email : ''; $user->setEmail($email, $this->source->getConsumer()->defaultEmail); // Set the user roles if (isset($membership->role)) { $user->roles = ToolProvider\ToolProvider::parseRoles($membership->role); } // If a result sourcedid is provided save the user if ($isLink) { if (isset($member->message)) { foreach ($member->message as $message) { if (isset($message->message_type) && $message->message_type === 'basic-lti-launch-request') { if (isset($message->lis_result_sourcedid)) { $user->ltiResultSourcedId = $message->lis_result_sourcedid; $user->save(); } break; } } } } $users[] = $user; // Remove old user (if it exists) if ($isLink) { unset($oldUsers[$user->getId(ToolProvider\ToolProvider::ID_SCOPE_RESOURCE)]); } } // Delete any old users which were not in the latest list from the tool consumer if ($isLink) { foreach ($oldUsers as $id => $user) { $user->delete(); } } } return $users; }
/** * Get array of user objects. * * Obtain an array of User objects for users with a result sourcedId. The array may include users from other * resource links which are sharing this resource link. It may also be optionally indexed by the user ID of a specified scope. * * @param ResourceLink $resourceLink Resource link object * @param boolean $localOnly True if only users within the resource link are to be returned (excluding users sharing this resource link) * @param int $idScope Scope value to use for user IDs * * @return array Array of User objects */ public function getUserResultSourcedIDsResourceLink($resourceLink, $localOnly, $idScope) { $users = array(); if ($localOnly) { $sql = sprintf('SELECT u.user_pk, u.lti_result_sourcedid, u.lti_user_id, u.created, u.updated ' . "FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' AS u ' . "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' AS rl ' . 'ON u.resource_link_pk = rl.resource_link_pk ' . "WHERE (rl.resource_link_pk = %d) AND (rl.primary_resource_link_pk IS NULL)", $resourceLink->getRecordId()); } else { $sql = sprintf('SELECT u.user_pk, u.lti_result_sourcedid, u.lti_user_id, u.created, u.updated ' . "FROM {$this->dbTableNamePrefix}" . DataConnector::USER_RESULT_TABLE_NAME . ' AS u ' . "INNER JOIN {$this->dbTableNamePrefix}" . DataConnector::RESOURCE_LINK_TABLE_NAME . ' AS rl ' . 'ON u.resource_link_pk = rl.resource_link_pk ' . 'WHERE ((rl.resource_link_pk = %d) AND (rl.primary_resource_link_pk IS NULL)) OR ' . '((rl.primary_resource_link_pk = %d) AND (share_approved = 1))', $resourceLink->getRecordId(), $resourceLink->getRecordId()); } $rsUser = mysql_query($sql); if ($rsUser) { while ($row = mysql_fetch_object($rsUser)) { $user = ToolProvider\User::fromResourceLink($resourceLink, $row->lti_user_id); $user->setRecordId(intval($row->user_pk)); $user->ltiResultSourcedId = $row->lti_result_sourcedid; $user->created = strtotime($row->created); $user->updated = strtotime($row->updated); if (is_null($idScope)) { $users[] = $user; } else { $users[$user->getId($idScope)] = $user; } } } return $users; }
/** * Test for data_connector::deleteUser(). */ public function test_delete_user() { $dc = new data_connector(); $consumer = new ToolConsumer(null, $dc); $consumer->name = 'TestName'; $consumer->setKey('TestKey'); $consumer->secret = 'TestSecret'; $consumer->save(); $resourcelink = ResourceLink::fromConsumer($consumer, 'testresourcelinkid'); $resourcelink->save(); $user = User::fromResourceLink($resourcelink, ''); $user->ltiResultSourcedId = 'testLtiResultSourcedId'; $user->firstname = 'First'; $user->lastname = 'Last'; $user->fullname = 'Full name'; $user->email = '*****@*****.**'; $user->roles = ['a', 'b']; $user->groups = ['1', '2']; $user->save(); // Delete user. $this->assertTrue($dc->deleteUser($user)); // User record should have been deleted from the DB. $this->assertFalse($dc->loadUser($user)); // User object should have been initialised(). $this->assertEmpty($user->firstname); $this->assertEmpty($user->lastname); $this->assertEmpty($user->fullname); $this->assertEmpty($user->email); $this->assertEmpty($user->roles); $this->assertEmpty($user->groups); $this->assertNull($user->ltiResultSourcedId); $this->assertNull($user->created); $this->assertNull($user->updated); }
/** * Get array of user objects. * * Obtain an array of User objects for users with a result sourcedId. The array may include users from other * resource links which are sharing this resource link. It may also be optionally indexed by the user ID of a specified scope. * * @param ResourceLink $resourcelink Resource link object * @param boolean $localonly True if only users within the resource link are to be returned * (excluding users sharing this resource link) * @param int $idscope Scope value to use for user IDs * @return array Array of User objects */ public function getUserResultSourcedIDsResourceLink($resourcelink, $localonly, $idscope) { global $DB; $users = []; $params = ['resourcelinkid' => $resourcelink->getRecordId()]; // Where clause for the subquery. $subwhere = "(id = :resourcelinkid AND primaryresourcelinkid IS NULL)"; if (!$localonly) { $subwhere .= " OR (primaryresourcelinkid = :resourcelinkid2 AND shareapproved = 1)"; $params['resourcelinkid2'] = $resourcelink->getRecordId(); } // The subquery. $subsql = "SELECT id\n FROM {{$this->resourcelinktable}}\n WHERE {$subwhere}"; // Our main where clause. $where = "resourcelinkid IN ({$subsql})"; // Fields to be queried. $fields = 'id, ltiresultsourcedid, ltiuserkey, created, updated'; // Fetch records. $rs = $DB->get_recordset_select($this->userresulttable, $where, $params, '', $fields); foreach ($rs as $row) { $user = User::fromResourceLink($resourcelink, $row->ltiuserkey); $user->setRecordId($row->id); $user->ltiResultSourcedId = $row->ltiresultsourcedid; $user->created = $row->created; $user->updated = $row->updated; if (is_null($idscope)) { $users[] = $user; } else { $users[$user->getId($idscope)] = $user; } } $rs->close(); return $users; }
/** * Builds a dummy tool provider object. * * @param string $secret Consumer secret. * @param array|stdClass $proxy Tool proxy data. * @param null $resourcelinksettings Key-value array for resource link settings. * @return dummy_tool_provider */ protected function build_dummy_tp($secret = null, $proxy = null, $resourcelinksettings = null) { $tool = $this->tool; $dataconnector = new data_connector(); $consumer = new ToolConsumer('testkey', $dataconnector); $ltiversion = ToolProvider::LTI_VERSION2; if ($secret === null && $proxy === null) { $consumer->secret = $tool->secret; $ltiversion = ToolProvider::LTI_VERSION1; } else { $consumer->secret = $secret; } $consumer->ltiVersion = $ltiversion; $consumer->name = 'TEST CONSUMER NAME'; $consumer->consumerName = 'TEST CONSUMER INSTANCE NAME'; $consumer->consumerGuid = 'TEST CONSUMER INSTANCE GUID'; $consumer->consumerVersion = 'TEST CONSUMER INFO VERSION'; $consumer->enabled = true; $consumer->protected = true; if ($proxy !== null) { $consumer->toolProxy = json_encode($proxy); } $consumer->save(); $resourcelink = ResourceLink::fromConsumer($consumer, 'testresourcelinkid'); if (!empty($resourcelinksettings)) { foreach ($resourcelinksettings as $setting => $value) { $resourcelink->setSetting($setting, $value); } } $resourcelink->save(); $ltiuser = User::fromResourceLink($resourcelink, ''); $ltiuser->ltiResultSourcedId = 'testLtiResultSourcedId'; $ltiuser->ltiUserId = 'testuserid'; $ltiuser->email = '*****@*****.**'; $ltiuser->save(); $tp = new dummy_tool_provider($tool->id); $tp->user = $ltiuser; $tp->resourceLink = $resourcelink; $tp->consumer = $consumer; return $tp; }