MDL-71887 mod_lti: repost when no cookie due to crosssite request

This commit is contained in:
Claude Vervoort 2021-06-22 17:31:04 -04:00 committed by Jake Dallimore
parent 3610f1ee3b
commit c8c81a1357
5 changed files with 173 additions and 4 deletions

View File

@ -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);

View File

@ -71,4 +71,17 @@ class renderer extends plugin_renderer_base {
$data = $page->export_for_template($this);
return parent::render_from_template('mod_lti/registration_upgrade_choice_page', $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);
}
}

View File

@ -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;
}
}

View File

@ -31,6 +31,28 @@ $courseid = required_param('course', PARAM_INT);
$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'] ?? '';
@ -52,7 +74,6 @@ if (!empty($jwt)) {
$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);
@ -66,9 +87,6 @@ if (empty($errormsg) && !empty($items)) {
}
}
$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.

View File

@ -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}}