Skip to content

Commit

Permalink
MDL-71887 mod_lti: repost when no cookie due to crosssite request
Browse files Browse the repository at this point in the history
  • Loading branch information
claudevervoort authored and snake committed Jul 7, 2021
1 parent 1d8512b commit 7edc33a
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 4 deletions.
13 changes: 13 additions & 0 deletions mod/lti/auth.php
Expand Up @@ -24,6 +24,19 @@

require_once(__DIR__ . '/../../config.php');
require_once($CFG->dirroot . '/mod/lti/locallib.php');
global $_POST, $_SERVER;

if (!isloggedin() && empty($_POST['repost'])) {
header_remove("Set-Cookie");
$PAGE->set_pagelayout('popup');
$PAGE->set_context(context_system::instance());
$output = $PAGE->get_renderer('mod_lti');
$page = new \mod_lti\output\repost_crosssite_page($_SERVER['REQUEST_URI'], $_POST);
echo $output->header();
echo $output->render($page);
echo $output->footer();
return;
}

$scope = optional_param('scope', '', PARAM_TEXT);
$responsetype = optional_param('response_type', '', PARAM_TEXT);
Expand Down
13 changes: 13 additions & 0 deletions mod/lti/classes/output/renderer.php
Expand Up @@ -59,4 +59,17 @@ public function render_external_registration_return_page($page) {
$data = $page->export_for_template($this);
return parent::render_from_template('mod_lti/external_registration_return', $data);
}

/**
* Render the reposting of the cross site request.
*
* @param repost_crosssite_page $page the page renderable.
*
* @return string rendered html for the page.
*/
public function render_repost_crosssite_page(repost_crosssite_page $page): string {
$data = $page->export_for_template($this);
return parent::render_from_template('mod_lti/repost_crosssite', $data);
}

}
76 changes: 76 additions & 0 deletions mod/lti/classes/output/repost_crosssite_page.php
@@ -0,0 +1,76 @@
<?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/>.

/**
* Render a page containing a simple form which reposts to self via JS.
*
* The purpose of this form is to resend a cross-site request to self, which allows the browsers to include the Moodle
* session cookie alongside the original POST data, allowing LTI flows to function despite browsers blocking
* cross-site cookies.
*
* @copyright 2021 Cengage
* @package mod_lti
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lti\output;

defined('MOODLE_INTERNAL') || die;

require_once($CFG->dirroot.'/mod/lti/locallib.php');

use renderable;
use templatable;
use renderer_base;
use stdClass;

/**
* Render a page containing a simple form which reposts to self via JS.
*
* The purpose of this form is to resend a cross-site request to self, which allows the browsers to include the Moodle
* session cookie alongside the original POST data, allowing LTI flows to function despite browsers blocking
* cross-site cookies.
*
* @copyright 2021 Cengage
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class repost_crosssite_page implements renderable, templatable {

/**
* Constructor
*
* @param string $url moodle URL to repost to
* @param array $post the POST params to be re-posted
*/
public function __construct(string $url, array $post) {
$this->params = array_map(function($k) use ($post) {
return ["key" => $k, "value" => str_replace("\"", "&quot;", $post[$k])];
}, array_keys($post));
$this->url = $url;
}

/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output The renderer
* @return stdClass Data to be used by the template
*/
public function export_for_template(renderer_base $output) {
$renderdata = new stdClass();
$renderdata->url = $this->url;
$renderdata->params = $this->params;
return $renderdata;
}
}
26 changes: 22 additions & 4 deletions mod/lti/contentitem_return.php
Expand Up @@ -31,6 +31,28 @@

$jwt = optional_param('JWT', '', PARAM_RAW);

$context = context_course::instance($courseid);

$pageurl = new moodle_url('/mod/lti/contentitem_return.php');
$PAGE->set_url($pageurl);
$PAGE->set_pagelayout('popup');
$PAGE->set_context($context);

// Cross-Site causes the cookie to be lost if not POSTed from same site.
global $_POST;
if (!empty($_POST["repost"])) {
// Unset the param so that LTI 1.1 signature validation passes.
unset($_POST["repost"]);
} else if (!isloggedin()) {
header_remove("Set-Cookie");
$output = $PAGE->get_renderer('mod_lti');
$page = new \mod_lti\output\repost_crosssite_page($_SERVER['REQUEST_URI'], $_POST);
echo $output->header();
echo $output->render($page);
echo $output->footer();
return;
}

if (!empty($jwt)) {
$params = lti_convert_from_jwt($id, $jwt);
$consumerkey = $params['oauth_consumer_key'] ?? '';
Expand All @@ -52,7 +74,6 @@
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
require_login($course);
require_sesskey();
$context = context_course::instance($courseid);
require_capability('moodle/course:manageactivities', $context);
require_capability('mod/lti:addcoursetool', $context);

Expand All @@ -66,9 +87,6 @@
}
}

$pageurl = new moodle_url('/mod/lti/contentitem_return.php');
$PAGE->set_url($pageurl);
$PAGE->set_pagelayout('popup');
echo $OUTPUT->header();

// Call JS module to redirect the user to the course page or close the dialogue on error/cancel.
Expand Down
49 changes: 49 additions & 0 deletions mod/lti/templates/repost_crosssite.mustache
@@ -0,0 +1,49 @@
{{!
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/>.
}}
{{!
@template mod_lti/repost_crosssite
This template re-executes a cross site request but from the site domain, avoiding the cross site issue.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
*
Example context (json):
{
"url":"https://some.tool.example/mod/lti/auth.php",
"params": {
"response_type": "id_token"
}
}

}}
<form action="{{{url}}}" method="POST" id="autopostme">
{{#params}}
<input type="hidden" name="{{{key}}}" value="{{{value}}}">
{{/params}}
<input type="hidden" name="repost" value="true">
</form>

{{#js}}
document.getElementById("autopostme").submit();
{{/js}}

0 comments on commit 7edc33a

Please sign in to comment.