mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 16:32:18 +02:00
MDL-69542 enrol_lti: add application registration service
This commit is contained in:
parent
948746eae7
commit
0f6239a054
@ -0,0 +1,176 @@
|
||||
<?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\service;
|
||||
|
||||
use enrol_lti\local\ltiadvantage\entity\application_registration;
|
||||
use enrol_lti\local\ltiadvantage\entity\registration_url;
|
||||
use enrol_lti\local\ltiadvantage\repository\application_registration_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\context_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\deployment_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\registration_url_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\resource_link_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\user_repository;
|
||||
|
||||
/**
|
||||
* Class application_registration_service.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class application_registration_service {
|
||||
/** @var application_registration_repository repository to work with application_registration instances. */
|
||||
private $appregistrationrepo;
|
||||
|
||||
/** @var deployment_repository repository to work with deployment instances. */
|
||||
private $deploymentrepo;
|
||||
|
||||
/** @var resource_link_repository repository to work with resource link instances. */
|
||||
private $resourcelinkrepo;
|
||||
|
||||
/** @var context_repository repository to work with context instances. */
|
||||
private $contextrepo;
|
||||
|
||||
/** @var user_repository repository to work with user instances. */
|
||||
private $userrepo;
|
||||
|
||||
/**
|
||||
* The application_registration_service constructor.
|
||||
*
|
||||
* @param application_registration_repository $appregistrationrepo an application registration repository instance.
|
||||
* @param deployment_repository $deploymentrepo a deployment repository instance.
|
||||
* @param resource_link_repository $resourcelinkrepo a resource_link_repository instance.
|
||||
* @param context_repository $contextrepo a context_repository instance.
|
||||
* @param user_repository $userrepo a user_repository instance.
|
||||
*/
|
||||
public function __construct(application_registration_repository $appregistrationrepo,
|
||||
deployment_repository $deploymentrepo, resource_link_repository $resourcelinkrepo,
|
||||
context_repository $contextrepo, user_repository $userrepo) {
|
||||
|
||||
$this->appregistrationrepo = $appregistrationrepo;
|
||||
$this->deploymentrepo = $deploymentrepo;
|
||||
$this->resourcelinkrepo = $resourcelinkrepo;
|
||||
$this->contextrepo = $contextrepo;
|
||||
$this->userrepo = $userrepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a DTO into a new application_registration domain object.
|
||||
*
|
||||
* @param \stdClass $dto the object containing information needed to register an application.
|
||||
* @return application_registration the application_registration object
|
||||
*/
|
||||
private function registration_from_dto(\stdClass $dto): application_registration {
|
||||
return application_registration::create(
|
||||
$dto->name,
|
||||
new \moodle_url($dto->platformid),
|
||||
$dto->clientid,
|
||||
new \moodle_url($dto->authenticationrequesturl),
|
||||
new \moodle_url($dto->jwksurl),
|
||||
new \moodle_url($dto->accesstokenurl),
|
||||
$dto->id ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Application service handling the use case "As an admin I can register an application as an LTI platform".
|
||||
*
|
||||
* @param \stdClass $appregdto details of the application to register.
|
||||
* @return application_registration the application_registration domain object.
|
||||
*/
|
||||
public function create_application_registration(\stdClass $appregdto): application_registration {
|
||||
if ($this->appregistrationrepo->find_by_platform($appregdto->platformid, $appregdto->clientid)) {
|
||||
throw new \moodle_exception("A registration for issuer '$appregdto->platformid', "
|
||||
."clientid '$appregdto->clientid' already exists.");
|
||||
}
|
||||
return $this->appregistrationrepo->save($this->registration_from_dto($appregdto));
|
||||
}
|
||||
|
||||
/**
|
||||
* Application service handling the use case "As an admin I can update the registration of an LTI platform".
|
||||
*
|
||||
* @param \stdClass $appregdto details of the registration to update.
|
||||
* @return application_registration the application_registration domain object.
|
||||
*/
|
||||
public function update_application_registration(\stdClass $appregdto): application_registration {
|
||||
if (empty($appregdto->id)) {
|
||||
throw new \coding_exception('Cannot update registration. Id is missing.');
|
||||
}
|
||||
return $this->appregistrationrepo->save($this->registration_from_dto($appregdto));
|
||||
}
|
||||
|
||||
/**
|
||||
* Application service handling the use case "As an admin I can delete a registration of an LTI platform".
|
||||
*
|
||||
* @param int $registrationid id of the registration to delete.
|
||||
*/
|
||||
public function delete_application_registration(int $registrationid): void {
|
||||
|
||||
$deployments = $this->deploymentrepo->find_all_by_registration($registrationid);
|
||||
if ($deployments) {
|
||||
$deploymentservice = new tool_deployment_service(
|
||||
$this->appregistrationrepo,
|
||||
$this->deploymentrepo,
|
||||
$this->resourcelinkrepo,
|
||||
$this->contextrepo,
|
||||
$this->userrepo
|
||||
);
|
||||
foreach ($deployments as $deployment) {
|
||||
$deploymentservice->delete_tool_deployment($deployment->get_id());
|
||||
}
|
||||
}
|
||||
|
||||
$this->appregistrationrepo->delete($registrationid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a one-time-use dynamic registration URL which is valid only for the specified duration.
|
||||
*
|
||||
* @param int $durationsecs how long, in seconds, the registration URL will be valid for.
|
||||
* @return registration_url the registration_url instnace.
|
||||
*/
|
||||
public function create_registration_url(int $durationsecs = 86400): registration_url {
|
||||
if ($durationsecs <= 0) {
|
||||
throw new \coding_exception('Invalid registration URL duration. Must be greater than 0.');
|
||||
}
|
||||
|
||||
$regurl = new registration_url(time() + $durationsecs);
|
||||
$regurlrepo = new registration_url_repository();
|
||||
return $regurlrepo->save($regurl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current dynamic registration URL.
|
||||
*
|
||||
* Will return null if the URL doesn't exist, or if the URL has expired.
|
||||
*
|
||||
* @param string $token Used to get the registration url by token.
|
||||
* @return registration_url|null the registration_url instance if valid, otherwise null.
|
||||
*/
|
||||
public function get_registration_url(string $token = ''): ?registration_url {
|
||||
$regurlrepo = new registration_url_repository();
|
||||
return $token ? $regurlrepo->find_by_token($token) : $regurlrepo->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current dynamic registration URL.
|
||||
*/
|
||||
public function delete_registration_url(): void {
|
||||
$regurlrepo = new registration_url_repository();
|
||||
$regurlrepo->delete();
|
||||
}
|
||||
}
|
@ -0,0 +1,324 @@
|
||||
<?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\service;
|
||||
|
||||
use enrol_lti\helper;
|
||||
use enrol_lti\local\ltiadvantage\entity\registration_url;
|
||||
use enrol_lti\local\ltiadvantage\repository\application_registration_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\context_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\deployment_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\resource_link_repository;
|
||||
use enrol_lti\local\ltiadvantage\repository\user_repository;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__ . '/../lti_advantage_testcase.php');
|
||||
|
||||
/**
|
||||
* Tests for the application_registration_service.
|
||||
*
|
||||
* @package enrol_lti
|
||||
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \enrol_lti\local\ltiadvantage\service\application_registration_service
|
||||
*/
|
||||
class application_registration_service_test extends \lti_advantage_testcase {
|
||||
/**
|
||||
* Helper to get an application_registration_service instance.
|
||||
* @return application_registration_service
|
||||
*/
|
||||
protected function get_application_registration_service(): application_registration_service {
|
||||
return new application_registration_service(
|
||||
new application_registration_repository(),
|
||||
new deployment_repository(),
|
||||
new resource_link_repository(),
|
||||
new context_repository(),
|
||||
new user_repository()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the use case "As an admin, I can register an application as an LTI consumer (platform)".
|
||||
*
|
||||
* @covers ::create_application_registration
|
||||
*/
|
||||
public function test_register_application() {
|
||||
$this->resetAfterTest();
|
||||
$reg = (object) [
|
||||
'name' => 'Example LMS application',
|
||||
'platformid' => 'https://lms.example.org',
|
||||
'clientid' => '123',
|
||||
'authenticationrequesturl' => new \moodle_url('https://example.org/authrequesturl'),
|
||||
'jwksurl' => new \moodle_url('https://example.org/jwksurl'),
|
||||
'accesstokenurl' => new \moodle_url('https://example.org/accesstokenurl')
|
||||
];
|
||||
|
||||
$service = $this->get_application_registration_service();
|
||||
$createdreg = $service->create_application_registration($reg);
|
||||
|
||||
$regrepo = new application_registration_repository();
|
||||
$this->assertTrue($regrepo->exists($createdreg->get_id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test verifying that the service cannot save two identical (same issuer and clientid) application registrations.
|
||||
*
|
||||
* @covers ::create_application_registration
|
||||
*/
|
||||
public function test_register_application_unique_constraints() {
|
||||
$this->resetAfterTest();
|
||||
$reg = (object) [
|
||||
'name' => 'Example LMS application',
|
||||
'platformid' => 'https://lms.example.org',
|
||||
'clientid' => '123',
|
||||
'authenticationrequesturl' => new \moodle_url('https://example.org/authrequesturl'),
|
||||
'jwksurl' => new \moodle_url('https://example.org/jwksurl'),
|
||||
'accesstokenurl' => new \moodle_url('https://example.org/accesstokenurl')
|
||||
];
|
||||
|
||||
$service = $this->get_application_registration_service();
|
||||
$service->create_application_registration($reg);
|
||||
|
||||
$this->expectException(\moodle_exception::class);
|
||||
$service->create_application_registration($reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the use case "As an admin, I can update an application registered as an LTI consumer (platform)".
|
||||
*
|
||||
* @covers ::update_application_registration
|
||||
*/
|
||||
public function test_update_application_registration() {
|
||||
$this->resetAfterTest();
|
||||
$reg = (object) [
|
||||
'name' => 'Example LMS application',
|
||||
'platformid' => 'https://lms.example.org',
|
||||
'clientid' => '123',
|
||||
'authenticationrequesturl' => new \moodle_url('https://example.org/authrequesturl'),
|
||||
'jwksurl' => new \moodle_url('https://example.org/jwksurl'),
|
||||
'accesstokenurl' => new \moodle_url('https://example.org/accesstokenurl')
|
||||
];
|
||||
|
||||
$service = $this->get_application_registration_service();
|
||||
$createdreg = $service->create_application_registration($reg);
|
||||
|
||||
$reg->id = $createdreg->get_id();
|
||||
$reg->jwksurl = new \moodle_url('https://example.org/updated_jwksurl');
|
||||
|
||||
$updatedreg = $service->update_application_registration($reg);
|
||||
$this->assertEquals($reg->name, $updatedreg->get_name());
|
||||
$this->assertEquals($reg->jwksurl, $updatedreg->get_jwksurl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test verifying that the service requires an object id.
|
||||
*
|
||||
* @covers ::update_application_registration
|
||||
*/
|
||||
public function test_update_application_registration_missing_id() {
|
||||
$this->resetAfterTest();
|
||||
$reg = (object) [
|
||||
'name' => 'Example LMS application',
|
||||
'platformid' => 'https://lms.example.org',
|
||||
'clientid' => '123',
|
||||
'authenticationrequesturl' => new \moodle_url('https://example.org/authrequesturl'),
|
||||
'jwksurl' => new \moodle_url('https://example.org/jwksurl'),
|
||||
'accesstokenurl' => new \moodle_url('https://example.org/accesstokenurl')
|
||||
];
|
||||
|
||||
$service = $this->get_application_registration_service();
|
||||
$service->create_application_registration($reg);
|
||||
|
||||
$reg->jwksurl = new \moodle_url('https://example.org/updated_jwksurl');
|
||||
|
||||
$this->expectException(\coding_exception::class);
|
||||
$service->update_application_registration($reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that removing an application registration also removes all associated data.
|
||||
*
|
||||
* @covers ::delete_application_registration
|
||||
*/
|
||||
public function test_delete_application_registration() {
|
||||
$this->resetAfterTest();
|
||||
// Setup.
|
||||
$registrationrepo = new application_registration_repository();
|
||||
$deploymentrepo = new deployment_repository();
|
||||
$contextrepo = new context_repository();
|
||||
$resourcelinkrepo = new resource_link_repository();
|
||||
$userrepo = new user_repository();
|
||||
[$course, $resource] = $this->create_test_environment();
|
||||
|
||||
// Launch the tool for a user.
|
||||
$mocklaunch = $this->get_mock_launch($resource, $this->get_mock_launch_users_with_ids(['1'])[0]);
|
||||
$instructoruser = $this->getDataGenerator()->create_user();
|
||||
$launchservice = $this->get_tool_launch_service();
|
||||
$launchservice->user_launches_tool($instructoruser, $mocklaunch);
|
||||
|
||||
// Check all the expected data exists for the deployment after setup.
|
||||
$registrations = $registrationrepo->find_all();
|
||||
$this->assertCount(1, $registrations);
|
||||
$registration = array_pop($registrations);
|
||||
|
||||
$deployments = $deploymentrepo->find_all_by_registration($registration->get_id());
|
||||
$this->assertCount(1, $deployments);
|
||||
$deployment = array_pop($deployments);
|
||||
|
||||
$resourcelinks = $resourcelinkrepo->find_by_resource($resource->id);
|
||||
$this->assertCount(1, $resourcelinks);
|
||||
$resourcelink = array_pop($resourcelinks);
|
||||
|
||||
$context = $contextrepo->find($resourcelink->get_contextid());
|
||||
$this->assertNotNull($context);
|
||||
|
||||
$users = $userrepo->find_by_resource($resource->id);
|
||||
$this->assertCount(1, $users);
|
||||
$user = array_pop($users);
|
||||
|
||||
$enrolledusers = get_enrolled_users(\context_course::instance($course->id));
|
||||
$this->assertCount(1, $enrolledusers);
|
||||
|
||||
// Now delete the application_registration using the service.
|
||||
$service = $this->get_application_registration_service();
|
||||
$service->delete_application_registration($registration->get_id());
|
||||
|
||||
// Verify that the context, resourcelink, user, deployment and registration instances are all deleted.
|
||||
$this->assertFalse($registrationrepo->exists($registration->get_id()));
|
||||
$this->assertFalse($deploymentrepo->exists($deployment->get_id()));
|
||||
$this->assertFalse($contextrepo->exists($context->get_id()));
|
||||
$this->assertFalse($resourcelinkrepo->exists($resourcelink->get_id()));
|
||||
$this->assertFalse($userrepo->exists($user->get_id()));
|
||||
|
||||
// Verify that all users are unenrolled.
|
||||
$enrolledusers = get_enrolled_users(\context_course::instance($course->id));
|
||||
$this->assertCount(0, $enrolledusers);
|
||||
|
||||
// Verify the tool record stays in place (I.e. the published resource is still available).
|
||||
$this->assertNotEmpty(helper::get_lti_tool($resource->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creation of a dynamic registration url.
|
||||
* @dataProvider registration_url_data_provider
|
||||
* @param int|null $duration how long the URL is valid for, in seconds.
|
||||
* @param array $expected the array of expected values/results.
|
||||
* @covers ::create_registration_url
|
||||
*/
|
||||
public function test_create_registration_url(?int $duration, array $expected) {
|
||||
$this->resetAfterTest();
|
||||
$appregservice = $this->get_application_registration_service();
|
||||
if ($expected['exception']) {
|
||||
$this->expectException($expected['exception']);
|
||||
}
|
||||
if (!is_null($duration)) {
|
||||
$regurl = $appregservice->create_registration_url($duration);
|
||||
} else {
|
||||
$regurl = $appregservice->create_registration_url();
|
||||
}
|
||||
$this->assertInstanceOf(registration_url::class, $regurl);
|
||||
$this->assertGreaterThanOrEqual($expected['expirytime'], $regurl->get_expiry_time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testing registration url creation.
|
||||
*
|
||||
* @return array the test data.
|
||||
*/
|
||||
public function registration_url_data_provider() {
|
||||
return [
|
||||
'no params' => [
|
||||
'duration' => null,
|
||||
'expected' => [
|
||||
'expirytime' => time() + 86400,
|
||||
'exception' => false,
|
||||
]
|
||||
],
|
||||
'expiry specified' => [
|
||||
'duration' => 3600,
|
||||
'expected' => [
|
||||
'expirytime' => time() + 3600,
|
||||
'exception' => false,
|
||||
]
|
||||
],
|
||||
'invalid expiry specified' => [
|
||||
'duration' => -5,
|
||||
'expected' => [
|
||||
'expirytime' => null,
|
||||
'exception' => \coding_exception::class,
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting the current registration url.
|
||||
*
|
||||
* @covers ::get_registration_url
|
||||
*/
|
||||
public function test_get_registration_url() {
|
||||
$this->resetAfterTest();
|
||||
$appregservice = $this->get_application_registration_service();
|
||||
|
||||
// Check when not existing.
|
||||
$this->assertNull($appregservice->get_registration_url());
|
||||
|
||||
// Check after creation.
|
||||
$appregservice->create_registration_url();
|
||||
$regurl = $appregservice->get_registration_url();
|
||||
$this->assertInstanceOf(registration_url::class, $regurl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting a registration URL by its token.
|
||||
*
|
||||
* @covers ::get_registration_url
|
||||
*/
|
||||
public function test_get_registration_url_using_token() {
|
||||
$this->resetAfterTest();
|
||||
$appregservice = $this->get_application_registration_service();
|
||||
$createdregurl = $appregservice->create_registration_url();
|
||||
|
||||
// Check valid token.
|
||||
$token = $createdregurl->param('token');
|
||||
$regurl = $appregservice->get_registration_url($token);
|
||||
$this->assertInstanceOf(registration_url::class, $regurl);
|
||||
|
||||
// Check invalid token.
|
||||
$this->assertNull($appregservice->get_registration_url('invalid_token'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deletion of the current registration URL.
|
||||
*
|
||||
* @covers ::delete_registration_url
|
||||
*/
|
||||
public function test_delete_registration_url() {
|
||||
$this->resetAfterTest();
|
||||
$appregservice = $this->get_application_registration_service();
|
||||
|
||||
// Deletion when no URL exists.
|
||||
$this->assertNull($appregservice->delete_registration_url());
|
||||
|
||||
// Deletion of a URL.
|
||||
$appregservice->create_registration_url();
|
||||
$regurl = $appregservice->get_registration_url();
|
||||
$this->assertInstanceOf(registration_url::class, $regurl);
|
||||
$appregservice->delete_registration_url();
|
||||
$this->assertNull($appregservice->get_registration_url());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user