MDL-53832 enrol_lti: Map consumer to tool and handle instance deletion

This commit is contained in:
Jun Pataleta 2016-09-30 16:18:56 +08:00 committed by John Okely
parent c314d6ed8e
commit 6092bbc07a
7 changed files with 278 additions and 19 deletions

View File

@ -36,6 +36,7 @@ use IMSGlobal\LTI\Profile\Message;
use IMSGlobal\LTI\Profile\ResourceHandler;
use IMSGlobal\LTI\Profile\ServiceDefinition;
use IMSGlobal\LTI\ToolProvider\ToolProvider;
use moodle_exception;
use moodle_url;
use stdClass;
@ -78,13 +79,15 @@ class tool_provider extends ToolProvider {
$token = helper::generate_proxy_token($toolid);
$this->debugMode = $CFG->debugdeveloper;
$tool = helper::get_lti_tool($toolid);
$this->tool = $tool;
$dataconnector = new data_connector();
parent::__construct($dataconnector);
// Override debugMode and set to the configured value.
$this->debugMode = $CFG->debugdeveloper;
$this->baseUrl = $CFG->wwwroot;
$toolpath = helper::get_launch_url($toolid);
$toolpath = $this->strip_base_url($toolpath);
@ -164,14 +167,17 @@ class tool_provider extends ToolProvider {
* @return void
*/
protected function onError() {
global $OUTPUT;
$message = $this->message;
if ($this->debugMode && !empty($this->reason)) {
$message = $this->reason;
}
$this->errorOutput = '';
notification::error(get_string('failedrequest', 'enrol_lti', ['reason' => $message]));
// Display the error message from the provider's side if the consumer has not specified a URL to pass the error to.
if (empty($this->returnUrl)) {
$this->errorOutput = $OUTPUT->notification(get_string('failedrequest', 'enrol_lti', ['reason' => $message]), 'error');
}
}
/**
@ -389,6 +395,9 @@ class tool_provider extends ToolProvider {
}
if ($this->doToolProxyService()) {
// Map tool consumer and published tool, if necessary.
$this->map_tool_to_consumer();
// Indicate successful processing in message.
$this->message = get_string('successfulregistration', 'enrol_lti');
@ -411,4 +420,27 @@ class tool_provider extends ToolProvider {
$this->message = get_string('couldnotestablishproxy', 'enrol_lti');
}
}
/**
* Performs mapping of the tool consumer to a published tool.
*
* @throws moodle_exception
*/
public function map_tool_to_consumer() {
global $DB;
if (empty($this->consumer)) {
throw new moodle_exception('invalidtoolconsumer', 'enrol_lti');
}
// Map the consumer to the tool.
$mappingparams = [
'toolid' => $this->tool->id,
'consumer_pk' => $this->consumer->getRecordId()
];
$mappingexists = $DB->record_exists('enrol_lti_tool_consumer_map', $mappingparams);
if (!$mappingexists) {
$DB->insert_record('enrol_lti_tool_consumer_map', (object) $mappingparams);
}
}
}

View File

@ -120,7 +120,7 @@
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="consumer_pk" TYPE="foreign-unique" FIELDS="consumer_pk" REFTABLE="enrol_lti_lti2_consumer" REFFIELDS="id"/>
<KEY NAME="consumer_pk" TYPE="foreign" FIELDS="consumer_pk" REFTABLE="enrol_lti_lti2_consumer" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="enrol_lti_lti2_resource_link" COMMENT="Link from the consumer to the tool">
@ -170,5 +170,17 @@
<KEY NAME="resource_link_pk" TYPE="foreign" FIELDS="resource_link_pk" REFTABLE="enrol_lti_lti2_resource_link" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="enrol_lti_tool_consumer_map" COMMENT="Table that maps the published tool to tool consumers.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="toolid" TYPE="int" LENGTH="11" NOTNULL="true" SEQUENCE="false" COMMENT="The tool ID."/>
<FIELD NAME="consumer_pk" TYPE="int" LENGTH="11" NOTNULL="true" SEQUENCE="false" COMMENT="The consumer ID."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="toolid" TYPE="foreign" FIELDS="toolid" REFTABLE="enrol_lti_tools" REFFIELDS="id"/>
<KEY NAME="consumer_pk" TYPE="foreign" FIELDS="consumer_pk" REFTABLE="enrol_lti_lti2_consumer" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>

View File

@ -130,7 +130,7 @@ function xmldb_enrol_lti_upgrade($oldversion) {
// Adding keys to table enrol_lti_lti2_nonce.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('consumer_pk', XMLDB_KEY_FOREIGN_UNIQUE, array('consumer_pk'), 'enrol_lti_lti2_consumer', array('id'));
$table->add_key('consumer_pk', XMLDB_KEY_FOREIGN, array('consumer_pk'), 'enrol_lti_lti2_consumer', array('id'));
// Conditionally launch create table for enrol_lti_lti2_nonce.
if (!$dbman->table_exists($table)) {
@ -176,7 +176,8 @@ function xmldb_enrol_lti_upgrade($oldversion) {
// Adding keys to table enrol_lti_lti2_share_key.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('share_key_id', XMLDB_KEY_UNIQUE, array('share_key_id'));
$table->add_key('resource_link_pk', XMLDB_KEY_FOREIGN_UNIQUE, array('resource_link_pk'), 'enrol_lti_lti2_resource_link', array('id'));
$table->add_key('resource_link_pk', XMLDB_KEY_FOREIGN_UNIQUE, array('resource_link_pk'),
'enrol_lti_lti2_resource_link', array('id'));
// Conditionally launch create table for enrol_lti_lti2_share_key.
if (!$dbman->table_exists($table)) {
@ -204,6 +205,24 @@ function xmldb_enrol_lti_upgrade($oldversion) {
$dbman->create_table($table);
}
// Define table enrol_lti_tool_consumer_map to be created.
$table = new xmldb_table('enrol_lti_tool_consumer_map');
// Adding fields to table enrol_lti_tool_consumer_map.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('toolid', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, null);
$table->add_field('consumer_pk', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, null);
// Adding keys to table enrol_lti_tool_consumer_map.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('toolid', XMLDB_KEY_FOREIGN, array('toolid'), 'enrol_lti_tools', array('id'));
$table->add_key('consumer_pk', XMLDB_KEY_FOREIGN, array('consumer_pk'), 'enrol_lti_lti2_consumer', array('id'));
// Conditionally launch create table for enrol_lti_tool_consumer_map.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Lti savepoint reached.
upgrade_plugin_savepoint(true, 2016052303, 'enrol', 'lti');
}

View File

@ -22,6 +22,9 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use enrol_lti\data_connector;
use IMSGlobal\LTI\ToolProvider\ToolConsumer;
defined('MOODLE_INTERNAL') || die();
/**
@ -157,6 +160,21 @@ class enrol_lti_plugin extends enrol_plugin {
// Delete any users associated with this tool.
$DB->delete_records('enrol_lti_users', array('toolid' => $tool->id));
// Get tool and consumer mappings.
$rsmapping = $DB->get_recordset('enrol_lti_tool_consumer_map', array('toolid' => $tool->id));
// Delete consumers that are linked to this tool and their related data.
$dataconnector = new data_connector();
foreach ($rsmapping as $mapping) {
$consumer = new ToolConsumer(null, $dataconnector);
$consumer->setRecordId($mapping->consumer_pk);
$dataconnector->deleteToolConsumer($consumer);
}
$rsmapping->close();
// Delete mapping records.
$DB->delete_records('enrol_lti_tool_consumer_map', array('toolid' => $tool->id));
// Delete the lti tool record.
$DB->delete_records('enrol_lti_tools', array('id' => $tool->id));

View File

@ -0,0 +1,120 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tests for the enrol_lti_plugin class.
*
* @package enrol_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use enrol_lti\data_connector;
use enrol_lti\tool_provider;
use IMSGlobal\LTI\ToolProvider\ResourceLink;
use IMSGlobal\LTI\ToolProvider\ToolConsumer;
use IMSGlobal\LTI\ToolProvider\ToolProvider;
use IMSGlobal\LTI\ToolProvider\User;
defined('MOODLE_INTERNAL') || die();
/**
* Tests for the enrol_lti_plugin class.
*
* @package enrol_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_lti_testcase extends advanced_testcase {
/**
* Test set up.
*
* This is executed before running any tests in this file.
*/
public function setUp() {
$this->resetAfterTest();
$this->setAdminUser();
}
/**
* 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 = 'user1@example.com';
$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,
'consumer_pk' => $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 ]));
}
}

View File

@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Test the helper functionality.
* Tests for the tool_provider class.
*
* @package enrol_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
@ -35,7 +35,7 @@ use IMSGlobal\LTI\ToolProvider\User;
defined('MOODLE_INTERNAL') || die();
/**
* Test the helper functionality.
* Tests for the tool_provider class.
*
* @package enrol_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
@ -129,8 +129,12 @@ class tool_provider_testcase extends advanced_testcase {
// Tool provider object should have been created fine. OK flag should be fine for now.
$this->assertTrue($tp->ok);
// There's basically no request data submitted so OK flag should turn out false.
// Call handleRequest but suppress output.
ob_start();
$tp->handleRequest();
ob_end_clean();
// There's basically no request data submitted so OK flag should turn out false.
$this->assertFalse($tp->ok);
}
@ -138,19 +142,13 @@ class tool_provider_testcase extends advanced_testcase {
* Test for tool_provider::onError().
*/
public function test_on_error() {
global $SESSION;
$tool = $this->tool;
$tp = new dummy_tool_provider($tool->id);
$message = "THIS IS AN ERROR!";
$tp->message = $message;
$tp->onError();
// Assert that a notification has been added.
$this->assertCount(1, $SESSION->notifications);
$notification = $SESSION->notifications[0];
$errormessage = get_string('failedrequest', 'enrol_lti', ['reason' => $message]);
$this->assertEquals($errormessage, $notification->message);
$this->assertEquals('error', $notification->type);
$this->assertContains($errormessage, $tp->get_error_output());
}
/**
@ -229,7 +227,7 @@ class tool_provider_testcase extends advanced_testcase {
* Test for tool_provider::onRegister() when registration succeeds.
*/
public function test_on_register() {
global $CFG;
global $CFG, $DB;
$tool = $this->tool;
$dataconnector = new data_connector();
@ -266,6 +264,13 @@ class tool_provider_testcase extends advanced_testcase {
// Check tool provider message.
$this->assertEquals($successmessage, $tp->message);
// Check published tool and tool consumer mapping.
$mappingparams = [
'toolid' => $tool->id,
'consumer_pk' => $tp->consumer->getRecordId()
];
$this->assertTrue($DB->record_exists('enrol_lti_tool_consumer_map', $mappingparams));
}
/**
@ -456,12 +461,20 @@ class tool_provider_testcase extends advanced_testcase {
* Test for tool_provider::onLaunch() when the consumer object has not been set.
*/
public function test_on_launch_no_consumer() {
global $DB;
$tool = $this->tool;
$tp = new dummy_tool_provider($tool->id);
$tp->onLaunch();
$this->assertFalse($tp->ok);
$this->assertEquals(get_string('invalidtoolconsumer', 'enrol_lti'), $tp->message);
// Check published tool and tool consumer has not yet been mapped due to failure.
$mappingparams = [
'toolid' => $tool->id
];
$this->assertFalse($DB->record_exists('enrol_lti_tool_consumer_map', $mappingparams));
}
/**
@ -481,6 +494,32 @@ class tool_provider_testcase extends advanced_testcase {
$this->assertEquals(get_string('invalidtoolconsumer', 'enrol_lti'), $tp->message);
}
/**
* Test for tool_provider::map_tool_to_consumer().
*/
public function test_map_tool_to_consumer() {
global $DB;
$tp = $this->build_dummy_tp();
$tp->map_tool_to_consumer();
// Check published tool and tool consumer mapping.
$mappingparams = [
'toolid' => $this->tool->id,
'consumer_pk' => $tp->consumer->getRecordId()
];
$this->assertTrue($DB->record_exists('enrol_lti_tool_consumer_map', $mappingparams));
}
/**
* Test for tool_provider::map_tool_to_consumer().
*/
public function test_map_tool_to_consumer_no_consumer() {
$tp = new dummy_tool_provider($this->tool->id);
$this->expectException('moodle_exception');
$tp->map_tool_to_consumer();
}
/**
* Builds a dummy tool provider object.
*
@ -547,6 +586,7 @@ class tool_provider_testcase extends advanced_testcase {
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dummy_tool_provider extends tool_provider {
/**
* Exposes tool_provider::onError().
*/
@ -567,6 +607,15 @@ class dummy_tool_provider extends tool_provider {
public function onRegister() {
parent::onRegister();
}
/**
* Expose protected variable errorOutput.
*
* @return string
*/
public function get_error_output() {
return $this->errorOutput;
}
}
/**

View File

@ -63,6 +63,9 @@ if ($messagetype != "basic-lti-launch-request") {
exit();
}
// Initialise tool provider.
$toolprovider = new \enrol_lti\tool_provider($toolid);
// Special handling for LTIv1 launch requests.
if ($ltiversion === \IMSGlobal\LTI\ToolProvider\ToolProvider::LTI_VERSION1) {
$dataconnector = new \enrol_lti\data_connector();
@ -78,9 +81,15 @@ if ($ltiversion === \IMSGlobal\LTI\ToolProvider\ToolProvider::LTI_VERSION1) {
$consumer->enabled = true;
$consumer->protected = true;
$consumer->save();
// Set consumer to tool provider.
$toolprovider->consumer = $consumer;
// Map tool consumer and published tool, if necessary.
$toolprovider->map_tool_to_consumer();
}
$toolprovider = new \enrol_lti\tool_provider($toolid);
// Handle the request.
$toolprovider->handleRequest();
echo $OUTPUT->header();
echo $OUTPUT->footer();