MDL-69542 enrol_lti: implement classes required by the lti13 library
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
use Packback\Lti1p3\Interfaces\IHttpClient;
|
||||
use Packback\Lti1p3\Interfaces\IHttpException;
|
||||
use Packback\Lti1p3\Interfaces\IHttpResponse;
|
||||
|
||||
/**
|
||||
* An implementation of IHTTPClient delegating to a curl object, for use with the lib/lti1p3 library code.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2022 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class http_client implements IHttpClient {
|
||||
|
||||
/** @var \curl a curl client instance. */
|
||||
private $curlclient;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \curl $curlclient a curl client instance.
|
||||
*/
|
||||
public function __construct(\curl $curlclient) {
|
||||
$this->curlclient = $curlclient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP request to the given URL.
|
||||
*
|
||||
* @param string $method the HTTP method to use.
|
||||
* @param string $url the URL to send the request to.
|
||||
* @param array $options an array of request options, mainly used to set headers and body.
|
||||
* @return IHttpResponse the response
|
||||
* @throws \Exception if the curl client encounters any errors making the request.
|
||||
* @throws IHttpException if the response contains a 400-level or 500-level status code.
|
||||
*/
|
||||
public function request(string $method, string $url, array $options): IHttpResponse {
|
||||
$this->curlclient->resetHeader();
|
||||
$this->curlclient->resetopt();
|
||||
if (isset($options['headers'])) {
|
||||
$headers = $options['headers'];
|
||||
array_walk(
|
||||
$headers,
|
||||
function(&$val, $key) {
|
||||
$val = "$key: $val";
|
||||
}
|
||||
);
|
||||
$this->curlclient->setHeader($headers);
|
||||
}
|
||||
if ($method == 'POST') {
|
||||
$res = $this->curlclient->post($url, $options['body'] ?? null, ['CURLOPT_HEADER' => 1]);
|
||||
} else if ($method == 'GET') {
|
||||
$res = $this->curlclient->get($url, [], ['CURLOPT_HEADER' => 1]);
|
||||
} else {
|
||||
throw new \Exception('Sorry, that HTTP method is not supported yet.');
|
||||
}
|
||||
|
||||
$info = $this->curlclient->get_info();
|
||||
if (!$this->curlclient->get_errno() && !$this->curlclient->error) {
|
||||
// No errors, so format the response.
|
||||
$headersize = $info['header_size'];
|
||||
$resheaders = substr($res, 0, $headersize);
|
||||
$resbody = substr($res, $headersize);
|
||||
$headerlines = array_filter(explode("\r\n", $resheaders));
|
||||
$parsedresponseheaders = [
|
||||
'httpstatus' => array_shift($headerlines)
|
||||
];
|
||||
foreach ($headerlines as $headerline) {
|
||||
$headerbits = explode(':', $headerline, 2);
|
||||
if (count($headerbits) == 2) {
|
||||
// Only parse headers having colon separation.
|
||||
$parsedresponseheaders[$headerbits[0]] = $headerbits[1];
|
||||
}
|
||||
}
|
||||
$response = new http_response(['headers' => $parsedresponseheaders, 'body' => $resbody], intval($info['http_code']));
|
||||
if ($response->getStatusCode() >= 400) {
|
||||
throw new http_exception($response, "An HTTP error status was received: '{$response->getHeaders()['httpstatus']}'");
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
// The curl client experienced errors, so report that.
|
||||
throw new \Exception("There was a cURL error when making the request: errno: {$this->curlclient->get_errno()},
|
||||
error: {$this->curlclient->error}.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
use Packback\Lti1p3\Interfaces\IHttpException;
|
||||
use Packback\Lti1p3\Interfaces\IHttpResponse;
|
||||
|
||||
/**
|
||||
* An implementation of IHTTPException, for use with the lib/lti1p3 library code.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2022 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class http_exception extends \Exception implements IHttpException {
|
||||
|
||||
/** @var IHttpResponse the response to which this exception relates.*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param IHttpResponse $response a response instance.
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param \Throwable|null $previous
|
||||
*/
|
||||
public function __construct(IHttpResponse $response, $message = "", $code = 0, \Throwable $previous = null) {
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the http response.
|
||||
*
|
||||
* @return IHttpResponse the response.
|
||||
*/
|
||||
public function getResponse(): IHttpResponse {
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
use Packback\Lti1p3\Interfaces\IHttpResponse;
|
||||
|
||||
/**
|
||||
* An implementation of IHTTPResponse, for use with the lib/lti1p3 library code.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2022 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class http_response implements IHttpResponse {
|
||||
|
||||
/** @var string HTTP response body */
|
||||
private $body;
|
||||
|
||||
/** @var array HTTP response header lines */
|
||||
private $headers;
|
||||
|
||||
/** @var int http status code */
|
||||
private $statuscode;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $payload the array containing the body and headers.
|
||||
* @param int $statuscode the HTTP status code.
|
||||
*/
|
||||
public function __construct(array $payload, int $statuscode) {
|
||||
$this->parse_payload($payload);
|
||||
$this->statuscode = $statuscode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the array containing headers and body into instance vars.
|
||||
*
|
||||
* @param array $payload
|
||||
*/
|
||||
private function parse_payload(array $payload): void {
|
||||
$this->headers = $payload['headers'];
|
||||
$this->body = $payload['body'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP response body string.
|
||||
*
|
||||
* @return string the HTTP response body.
|
||||
*/
|
||||
public function getBody() {
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP headers array.
|
||||
*
|
||||
* @return array the array containing the headers.
|
||||
*/
|
||||
public function getHeaders(): array {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP response status code.
|
||||
*
|
||||
* @return int the HTTP response status code.
|
||||
*/
|
||||
public function getStatusCode(): int {
|
||||
return $this->statuscode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
use enrol_lti\local\ltiadvantage\repository\application_registration_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\deployment_repository;
|
||||
use Packback\Lti1p3\Interfaces\IDatabase;
|
||||
use Packback\Lti1p3\LtiDeployment;
|
||||
use Packback\Lti1p3\LtiRegistration;
|
||||
|
||||
/**
|
||||
* The issuer_database class, providing a read-only store of issuer details.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2021 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class issuer_database implements IDatabase {
|
||||
|
||||
/** @var application_registration_repository an application registration repository instance used for lookups.*/
|
||||
private $appregrepo;
|
||||
|
||||
/** @var deployment_repository a deployment repository instance for lookups.*/
|
||||
private $deploymentrepo;
|
||||
|
||||
/**
|
||||
* The issuer_database constructor.
|
||||
* @param application_registration_repository $appregrepo an application registration repository instance.
|
||||
* @param deployment_repository $deploymentrepo a deployment repository instance.
|
||||
*/
|
||||
public function __construct(application_registration_repository $appregrepo,
|
||||
deployment_repository $deploymentrepo) {
|
||||
|
||||
$this->appregrepo = $appregrepo;
|
||||
$this->deploymentrepo = $deploymentrepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return an LTI registration based on its unique {issuer, client_id} tuple.
|
||||
*
|
||||
* @param string $iss the issuer id.
|
||||
* @param string $clientId the client_id of the registration.
|
||||
* @return LtiRegistration|null The registration object, or null if not found.
|
||||
*/
|
||||
public function findRegistrationByIssuer($iss, $clientId = null): ?LtiRegistration {
|
||||
if (is_null($clientId)) {
|
||||
throw new \coding_exception("Both issuer and client id are required to identify platform registrations ".
|
||||
"and must be sent by your LMS during both login requests (as 'client_id') and in the auth response ".
|
||||
"Tool JWT (as the 'aud' claim).");
|
||||
}
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/moodlelib.php'); // For get_config() usage.
|
||||
$reg = $this->appregrepo->find_by_platform($iss, $clientId);
|
||||
if (!$reg) {
|
||||
return null;
|
||||
}
|
||||
$privatekey = get_config('enrol_lti', 'lti_13_privatekey');
|
||||
$kid = get_config('enrol_lti', 'lti_13_kid');
|
||||
|
||||
return LtiRegistration::new()
|
||||
->setAuthLoginUrl($reg->get_authenticationrequesturl()->out(false))
|
||||
->setAuthTokenUrl($reg->get_accesstokenurl()->out(false))
|
||||
->setClientId($reg->get_clientid())
|
||||
->setKeySetUrl($reg->get_jwksurl()->out(false))
|
||||
->setKid($kid)
|
||||
->setIssuer($reg->get_platformid()->out(false))
|
||||
->setToolPrivateKey($privatekey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an LTI deployment based on the {issuer, client_id} tuple and a deployment id string.
|
||||
*
|
||||
* @param string $iss the issuer id.
|
||||
* @param string $deploymentId the deployment id.
|
||||
* @param string $clientId the client_id of the registration.
|
||||
* @return LtiDeployment|null The deployment object or null if not found.
|
||||
*/
|
||||
public function findDeployment($iss, $deploymentId, $clientId = null): ?LtiDeployment {
|
||||
if (is_null($clientId)) {
|
||||
throw new \coding_exception("Both issuer and client id are required to identify platform registrations ".
|
||||
"and must be sent by your LMS during both login requests (as 'client_id') and in the auth response ".
|
||||
"Tool JWT (as the 'aud' claim).");
|
||||
}
|
||||
|
||||
$appregistration = $this->appregrepo->find_by_platform($iss, $clientId);
|
||||
if (!$appregistration) {
|
||||
return null;
|
||||
}
|
||||
$deployment = $this->deploymentrepo->find_by_registration($appregistration->get_id(), $deploymentId);
|
||||
if (!$deployment) {
|
||||
return null;
|
||||
}
|
||||
return LtiDeployment::new()
|
||||
->setDeploymentId($deployment->get_deploymentid());
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
use Packback\Lti1p3\Interfaces\ICache;
|
||||
|
||||
/**
|
||||
* The launch_cache_session, providing a temporary session store for launch information.
|
||||
*
|
||||
* This is used to store the launch information while the user is transitioned through the Moodle authentication flows
|
||||
* and back to the deep linking launch handler (launch_deeplink.php).
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2021 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class launch_cache_session implements ICache {
|
||||
|
||||
/**
|
||||
* Get the launch data from the cache.
|
||||
*
|
||||
* @param string $key the launch id.
|
||||
* @return array|null the launch data.
|
||||
*/
|
||||
public function getLaunchData($key): ?array {
|
||||
global $SESSION;
|
||||
if (isset($SESSION->enrol_lti_launch[$key])) {
|
||||
return unserialize($SESSION->enrol_lti_launch[$key]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add launch data to the cache.
|
||||
*
|
||||
* @param string $key the launch id.
|
||||
* @param array $jwtBody the launch data.
|
||||
*/
|
||||
public function cacheLaunchData(string $key, array $jwtBody): void {
|
||||
global $SESSION;
|
||||
$SESSION->enrol_lti_launch[$key] = serialize($jwtBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the nonce.
|
||||
*
|
||||
* @param string $nonce the nonce.
|
||||
* @param string $state the state.
|
||||
*/
|
||||
public function cacheNonce(string $nonce, string $state): void {
|
||||
global $SESSION;
|
||||
$SESSION->enrol_lti_launch_nonce[$nonce] = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the cache contains the nonce.
|
||||
*
|
||||
* @param string $nonce the nonce
|
||||
* @param string $state the state
|
||||
* @return bool true if found, false otherwise.
|
||||
*/
|
||||
public function checkNonceIsValid(string $nonce, string $state): bool {
|
||||
global $SESSION;
|
||||
return isset($SESSION->enrol_lti_launch_nonce[$nonce]) && $SESSION->enrol_lti_launch_nonce[$nonce] == $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all data from the session cache.
|
||||
*/
|
||||
public function purge() {
|
||||
global $SESSION;
|
||||
unset($SESSION->enrol_lti_launch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the access token.
|
||||
*
|
||||
* @param string $key the key
|
||||
* @param string $accessToken the access token
|
||||
*/
|
||||
public function cacheAccessToken(string $key, string $accessToken): void {
|
||||
global $SESSION;
|
||||
$SESSION->enrol_lti_launch_token[$key] = $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cached access token.
|
||||
*
|
||||
* @param string $key the key to check.
|
||||
* @return string|null the token string, or null if not found.
|
||||
*/
|
||||
public function getAccessToken(string $key): ?string {
|
||||
global $SESSION;
|
||||
return $SESSION->enrol_lti_launch_token[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the access token from the cache.
|
||||
*
|
||||
* @param string $key the key to purge.
|
||||
*/
|
||||
public function clearAccessToken(string $key): void {
|
||||
global $SESSION;
|
||||
unset($SESSION->enrol_lti_launch_token[$key]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
/**
|
||||
* Tests for the http_client class.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2022 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \enrol_lti\local\ltiadvantage\lib\http_client
|
||||
*/
|
||||
class http_client_test extends \basic_testcase {
|
||||
|
||||
/**
|
||||
* Verify the http_client delegates to curl during a "GET" request.
|
||||
*
|
||||
* @covers ::request
|
||||
*/
|
||||
public function test_client_get_request() {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
$mockcurl = $this->createMock(\curl::class);
|
||||
$mockcurl->expects($this->once())
|
||||
->method('get')
|
||||
->with(
|
||||
$this->equalTo('https://example.com'),
|
||||
$this->equalTo([]),
|
||||
$this->equalTo(['CURLOPT_HEADER' => 1])
|
||||
);
|
||||
$mockcurl->expects($this->any())
|
||||
->method('get_info')
|
||||
->willReturnCallback(function() {
|
||||
return ['header_size' => 0, 'http_code' => 200];
|
||||
});
|
||||
$mockcurl->expects($this->once())
|
||||
->method('setHeader')
|
||||
->with($this->equalTo(['someheader' => 'someheader: headervalue']));
|
||||
|
||||
$client = new http_client($mockcurl);
|
||||
$client->request('GET', 'https://example.com', ['headers' => ['someheader' => 'headervalue']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the http_client delegates to curl during a "POST" request.
|
||||
*
|
||||
* @covers ::request
|
||||
*/
|
||||
public function test_client_post_request() {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
$mockcurl = $this->createMock(\curl::class);
|
||||
$mockcurl->expects($this->once())
|
||||
->method('post')
|
||||
->with(
|
||||
$this->equalTo('https://example.com'),
|
||||
$this->equalTo('examplebody'),
|
||||
$this->equalTo(['CURLOPT_HEADER' => 1])
|
||||
);
|
||||
$mockcurl->expects($this->any())
|
||||
->method('get_info')
|
||||
->willReturnCallback(function() {
|
||||
return ['header_size' => 0, 'http_code' => 200];
|
||||
});
|
||||
$mockcurl->expects($this->once())
|
||||
->method('setHeader')
|
||||
->with($this->equalTo(['someheader' => 'someheader: headervalue']));
|
||||
|
||||
$client = new http_client($mockcurl);
|
||||
$client->request('POST', 'https://example.com', ['headers' => ['someheader' => 'headervalue'], 'body' => 'examplebody']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a few of the unsupported HTTP methods.
|
||||
*
|
||||
* @dataProvider unsupported_methods_provider
|
||||
* @param string $httpmethod the http method.
|
||||
* @covers ::request
|
||||
*/
|
||||
public function test_request_unsupported_method(string $httpmethod) {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
$mockcurl = $this->createMock(\curl::class);
|
||||
|
||||
$client = new http_client($mockcurl);
|
||||
$this->expectException(\Exception::class);
|
||||
$client->request($httpmethod, 'https://example.com', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testing unsupported http methods.
|
||||
*
|
||||
* @return array the test case data.
|
||||
*/
|
||||
public function unsupported_methods_provider() {
|
||||
return [
|
||||
'head' => ['HEAD'],
|
||||
'put' => ['PUT'],
|
||||
'delete' => ['DELETE'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
use Packback\Lti1p3\Interfaces\IHttpResponse;
|
||||
|
||||
/**
|
||||
* Tests for the http_exception class.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2022 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \enrol_lti\local\ltiadvantage\lib\http_exception
|
||||
*/
|
||||
class http_exception_test extends \basic_testcase {
|
||||
|
||||
/**
|
||||
* Test constructor and getters for a range of inputs.
|
||||
*
|
||||
* @dataProvider exception_data_provider
|
||||
* @param array $args the arguments to the exception constructor.
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function test_exception(array $args) {
|
||||
|
||||
$exception = new http_exception(...array_values($args));
|
||||
$this->assertInstanceOf(IHttpResponse::class, $exception->getResponse());
|
||||
if (isset($args['message'])) {
|
||||
$this->assertEquals($args['message'], $exception->getMessage());
|
||||
}
|
||||
if (isset($args['code'])) {
|
||||
$this->assertEquals($args['code'], $exception->getCode());
|
||||
}
|
||||
if (isset($args['throwable'])) {
|
||||
$this->assertEquals($args['throwable'], $exception->getPrevious());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testing http_exception instances.
|
||||
*
|
||||
* @return array the test case data.
|
||||
*/
|
||||
public function exception_data_provider() {
|
||||
return [
|
||||
'With only the response object' => [
|
||||
'args' => [
|
||||
'response' => new http_response(
|
||||
['body' => '', 'headers' => ['Content-Type' => 'application/json']],
|
||||
401
|
||||
)
|
||||
]
|
||||
],
|
||||
'With response and message, code and throwable omitted' => [
|
||||
'args' => [
|
||||
'response' => new http_response(
|
||||
['body' => '', 'headers' => ['Content-Type' => 'application/json']],
|
||||
401
|
||||
),
|
||||
'message' => 'HTTP error: 401 Unauthorised'
|
||||
]
|
||||
],
|
||||
'With response, message and code, throwable omitted' => [
|
||||
'args' => [
|
||||
'response' => new http_response(
|
||||
['body' => '', 'headers' => ['Content-Type' => 'application/json']],
|
||||
401
|
||||
),
|
||||
'message' => 'HTTP error: 401 Unauthorised',
|
||||
'code' => 401
|
||||
]
|
||||
],
|
||||
'With response, message, code, throwable' => [
|
||||
'args' => [
|
||||
'response' => new http_response(
|
||||
['body' => '', 'headers' => ['Content-Type' => 'application/json']],
|
||||
401
|
||||
),
|
||||
'message' => 'HTTP error: 401 Unauthorised',
|
||||
'code' => 401,
|
||||
'throwable' => new \Exception('another exception')
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
/**
|
||||
* Tests for the http_response class.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2022 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \enrol_lti\local\ltiadvantage\lib\http_response
|
||||
*/
|
||||
class http_response_test extends \basic_testcase {
|
||||
|
||||
/**
|
||||
* Test constructor and getters for a range of inputs.
|
||||
*
|
||||
* @dataProvider response_data_provider
|
||||
* @param array $payload the array of header and body payload data.
|
||||
* @param int $status the int status of the http response.
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function test_response(array $payload, int $status) {
|
||||
$response = new http_response($payload, $status);
|
||||
$this->assertEquals($payload['headers'], $response->getHeaders());
|
||||
$this->assertEquals($payload['body'], $response->getBody());
|
||||
$this->assertEquals($status, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testing http_response instances.
|
||||
*
|
||||
* @return array the test case data.
|
||||
*/
|
||||
public function response_data_provider() {
|
||||
return [
|
||||
'valid headers and body' => [
|
||||
'payload' => [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'body' => '{"something": true}'
|
||||
],
|
||||
'httpstatus' => 200
|
||||
],
|
||||
'valid headers with empty body' => [
|
||||
'payload' => [
|
||||
'headers' => ['Content-Type' => 'application/json'],
|
||||
'body' => ''
|
||||
],
|
||||
'httpstatus' => 200
|
||||
],
|
||||
'valid, no headers or body' => [
|
||||
'payload' => [
|
||||
'headers' => [],
|
||||
'body' => ''
|
||||
],
|
||||
'httpstatus' => 200
|
||||
],
|
||||
'valid headers, empty body, non-200 response status' => [
|
||||
'payload' => [
|
||||
'headers' => ['httpstatus' => 'HTTP/1.1 401 Unauthorised: message ', 'Content-Type' => 'application/json'],
|
||||
'body' => ''
|
||||
],
|
||||
'httpstatus' => 401
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
use enrol_lti\local\ltiadvantage\entity\application_registration;
|
||||
use enrol_lti\local\ltiadvantage\repository\application_registration_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\deployment_repository;
|
||||
use Packback\Lti1p3\LtiDeployment;
|
||||
use Packback\Lti1p3\LtiRegistration;
|
||||
|
||||
/**
|
||||
* Tests for the issuer_database class.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2021 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \enrol_lti\local\ltiadvantage\lib\issuer_database
|
||||
*/
|
||||
class issuer_database_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the Moodle implementation of the library database method test_find_registration_by_issuer().
|
||||
*
|
||||
* @covers ::findRegistrationByIssuer
|
||||
*/
|
||||
public function test_find_registration_by_issuer() {
|
||||
$this->resetAfterTest();
|
||||
$appregrepo = new application_registration_repository();
|
||||
$appreg = application_registration::create(
|
||||
'My platform',
|
||||
new \moodle_url('https://lms.example.com'),
|
||||
'client-id-123',
|
||||
new \moodle_url('https://lms.example.com/lti/auth'),
|
||||
new \moodle_url('https://lms.example.com/lti/jwks'),
|
||||
new \moodle_url('https://lms.example.com/lti/token')
|
||||
);
|
||||
$appregrepo->save($appreg);
|
||||
|
||||
$issuerdb = new issuer_database($appregrepo, new deployment_repository());
|
||||
$registration = $issuerdb->findRegistrationByIssuer('https://lms.example.com', 'client-id-123');
|
||||
$this->assertInstanceOf(LtiRegistration::class, $registration);
|
||||
$this->assertEquals($appreg->get_authenticationrequesturl()->out(false), $registration->getAuthLoginUrl());
|
||||
$this->assertEquals($appreg->get_jwksurl()->out(false), $registration->getKeySetUrl());
|
||||
$this->assertEquals($appreg->get_accesstokenurl()->out(false), $registration->getAuthTokenUrl());
|
||||
$this->assertEquals($appreg->get_clientid(), $registration->getClientId());
|
||||
$this->assertEquals($appreg->get_platformid()->out(false), $registration->getIssuer());
|
||||
|
||||
$this->assertNull($issuerdb->findRegistrationByIssuer('https://lms.example.com', 'client-id-456'));
|
||||
|
||||
$this->expectException(\coding_exception::class);
|
||||
$this->expectExceptionMessageMatches('/Both issuer and client id are required to identify platform /');
|
||||
$issuerdb->findRegistrationByIssuer('https://lms.example.com');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Moodle implementation of the library database method test_find_deployment().
|
||||
*
|
||||
* @covers ::findDeployment
|
||||
*/
|
||||
public function test_find_deployment() {
|
||||
$this->resetAfterTest();
|
||||
$appregrepo = new application_registration_repository();
|
||||
$appreg = application_registration::create(
|
||||
'My platform',
|
||||
new \moodle_url('https://lms.example.com'),
|
||||
'client-id-123',
|
||||
new \moodle_url('https://lms.example.com/lti/auth'),
|
||||
new \moodle_url('https://lms.example.com/lti/jwks'),
|
||||
new \moodle_url('https://lms.example.com/lti/token')
|
||||
);
|
||||
$appreg = $appregrepo->save($appreg);
|
||||
$dep = $appreg->add_tool_deployment('Site wide tool deployment', 'deployment-id-1');
|
||||
$deploymentrepo = new deployment_repository();
|
||||
$deploymentrepo->save($dep);
|
||||
|
||||
$issuerdb = new issuer_database($appregrepo, new deployment_repository());
|
||||
$deployment = $issuerdb->findDeployment('https://lms.example.com', 'deployment-id-1', 'client-id-123');
|
||||
$this->assertInstanceOf(LtiDeployment::class, $deployment);
|
||||
$this->assertEquals($dep->get_deploymentid(), $deployment->getDeploymentId());
|
||||
|
||||
$this->assertNull($issuerdb->findDeployment('https://lms.example.com', 'deployment-id-1', 'client-id-456'));
|
||||
$this->assertNull($issuerdb->findDeployment('https://lms.example.com', 'deployment-id-2', 'client-id-123'));
|
||||
|
||||
$this->expectException(\coding_exception::class);
|
||||
$this->expectExceptionMessageMatches('/Both issuer and client id are required to identify platform /');
|
||||
$issuerdb->findDeployment('https://lms.example.com', 'deployment-id-2');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?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/>.
|
||||
|
||||
namespace enrol_lti\local\ltiadvantage\lib;
|
||||
|
||||
/**
|
||||
* Tests for the launch_cache_session class.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2021 Jake Dallimore <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \enrol_lti\local\ltiadvantage\lib\launch_cache_session
|
||||
*/
|
||||
class launch_cache_session_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test that the session cache, and in particular distinct object instances, can cache and retrieve launch data.
|
||||
*
|
||||
* Using different objects simulates the kind of usage we expect: uses across different requests.
|
||||
*
|
||||
* @covers ::cacheLaunchData
|
||||
*/
|
||||
public function test_cache_launch_data() {
|
||||
$lcs = new launch_cache_session();
|
||||
$lcs->cacheLaunchData('TestKey', ['JWT body' => 'xxx']);
|
||||
|
||||
$lcs2 = new launch_cache_session();
|
||||
$this->assertEquals(['JWT body' => 'xxx'], $lcs2->getLaunchData('TestKey'));
|
||||
$this->assertNull($lcs2->getLaunchData('TestKey123'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the session cache, and in particular distinct object instances, can cache and check the nonce data.
|
||||
*
|
||||
* Using different objects simulates the kind of usage we expect: uses across different requests.
|
||||
*
|
||||
* @covers ::cacheNonce
|
||||
*/
|
||||
public function test_cache_and_check_nonce() {
|
||||
$lcs = new launch_cache_session();
|
||||
$lcs->cacheNonce('my_nonce_123', 'my_state_234');
|
||||
|
||||
$lcs2 = new launch_cache_session();
|
||||
$this->assertTrue($lcs2->checkNonceIsValid('my_nonce_123', 'my_state_234'));
|
||||
$this->assertFalse($lcs2->checkNonceIsValid('different_nonce', 'my_state_234'));
|
||||
$this->assertFalse($lcs2->checkNonceIsValid('my_nonce_123', 'different_state'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the session cache, and in particular distinct object instances, can purge cached launch data.
|
||||
*
|
||||
* Using different objects simulates the kind of usage we expect: uses across different requests.
|
||||
*
|
||||
* @covers ::purge
|
||||
*/
|
||||
public function test_purge() {
|
||||
$lcs = new launch_cache_session();
|
||||
$lcs->cacheLaunchData('TestKey', ['JWT body' => 'xxx']);
|
||||
|
||||
$lcs2 = new launch_cache_session();
|
||||
$this->assertEquals(['JWT body' => 'xxx'], $lcs2->getLaunchData('TestKey'));
|
||||
$lcs2->purge();
|
||||
$this->assertNull($lcs2->getLaunchData('TestKey'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user