Skip to content

Commit

Permalink
MDL-79675 libraries: upgrade lti1p3 to v5.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
snake committed Jan 17, 2024
1 parent 580c009 commit 2362077
Show file tree
Hide file tree
Showing 24 changed files with 469 additions and 101 deletions.
4 changes: 1 addition & 3 deletions lib/lti1p3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ A library used for building IMS-certified LTI 1.3 tool providers in PHP.

This library is a fork of the [packbackbooks/lti-1-3-php-library](https://github.com/packbackbooks/lti-1-3-php-library), patched specifically for use in [Moodle](https://github.com/moodle/moodle).

It is currently based on version [5.4.1 of the packbackbooks/lti-1-3-php-library](https://github.com/packbackbooks/lti-1-3-php-library/releases/tag/v5.4.1) library.
It is currently based on version [5.7.0 of the packbackbooks/lti-1-3-php-library](https://github.com/packbackbooks/lti-1-3-php-library/releases/tag/v5.7.0) library.

The following changes are included so that the library may be used with Moodle:

* Replace the phpseclib dependency with openssl equivalent call in public key generation code.

Please see the original [README](https://github.com/packbackbooks/lti-1-3-php-library/blob/master/README.md) for more information about the upstream library.


15 changes: 15 additions & 0 deletions lib/lti1p3/src/Helpers/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,19 @@ public static function checkIfNullValue($value): bool
{
return !is_null($value);
}

public static function buildUrlWithQueryParams(string $url, array $params = []): string
{
if (empty($params)) {
return $url;
}

if (parse_url($url, PHP_URL_QUERY)) {
$separator = '&';
} else {
$separator = '?';
}

return $url.$separator.http_build_query($params, '');
}
}
3 changes: 3 additions & 0 deletions lib/lti1p3/src/ImsStorage/ImsCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Packback\Lti1p3\Interfaces\ICache;

/**
* @deprecated
*/
class ImsCache implements ICache
{
private $cache;
Expand Down
3 changes: 3 additions & 0 deletions lib/lti1p3/src/ImsStorage/ImsCookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Packback\Lti1p3\Interfaces\ICookie;

/**
* @deprecated
*/
class ImsCookie implements ICookie
{
public function getCookie(string $name): ?string
Expand Down
35 changes: 35 additions & 0 deletions lib/lti1p3/src/Interfaces/IMigrationDatabase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Packback\Lti1p3\Interfaces;

use Packback\Lti1p3\LtiDeployment;
use Packback\Lti1p3\LtiMessageLaunch;

/**
* This is an optional interface if an LTI 1.3 tool supports migrations
* from LTI 1.1 compatible installations.
*
* To use this, just have whatever class you create that implements IDatabase
* also implement this interface.
*/
interface IMigrationDatabase extends IDatabase
{
/**
* Using the LtiMessageLaunch return an array of matching LTI 1.1 keys
*
* @return array<\Packback\Lti1p3\Lti1p1Key>
*/
public function findLti1p1Keys(LtiMessageLaunch $launch): array;

/**
* Given an LtiMessageLaunch, return true if this tool should migrate from 1.1 to 1.3
*/
public function shouldMigrate(LtiMessageLaunch $launch): bool;

/**
* This method should create a 1.3 deployment in your DB based on the LtiMessageLaunch.
* Previous to this, we validated the oauth_consumer_key_sign to ensure this migration
* can safely occur.
*/
public function migrateFromLti1p1(LtiMessageLaunch $launch): ?LtiDeployment;
}
8 changes: 8 additions & 0 deletions lib/lti1p3/src/JwksEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
use Packback\Lti1p3\Interfaces\IDatabase;
use Packback\Lti1p3\Interfaces\ILtiRegistration;

/**
* @todo Pin versions to v6.6 and php 8
*/
class JwksEndpoint
{
private $keys;
Expand Down Expand Up @@ -52,8 +55,13 @@ public function getPublicJwks()
return ['keys' => $jwks];
}

/**
* @deprecated
*/
public function outputJwks()
{
trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED);

echo json_encode($this->getPublicJwks());
}
}
68 changes: 68 additions & 0 deletions lib/lti1p3/src/Lti1p1Key.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Packback\Lti1p3;

/**
* Used for migrations from LTI 1.1 to LTI 1.3
*
* @see IMigrationDatabase
*/
class Lti1p1Key
{
private $key;
private $secret;

public function __construct(?array $key = null)
{
$this->key = $key['key'] ?? null;
$this->secret = $key['secret'] ?? null;
}

public function getKey()
{
return $this->key;
}

public function setKey(array $key)
{
$this->key = $key;

return $this;
}

public function getSecret()
{
return $this->secret;
}

public function setSecret(array $secret)
{
$this->secret = $secret;

return $this;
}

/**
* Create a signature using the key and secret
*
* @see https://www.imsglobal.org/spec/lti/v1p3/migr#oauth_consumer_key_sign
*/
public function sign(string $deploymentId, string $iss, string $clientId, string $exp, string $nonce): string
{
$signatureComponents = [
$this->getKey(),
$deploymentId,
$iss,
$clientId,
$exp,
$nonce,
];

$baseString = implode('&', $signatureComponents);
$utf8String = mb_convert_encoding($baseString, 'utf8', mb_detect_encoding($baseString));
$hash = hash_hmac('sha256', $utf8String, $this->getSecret(), true);

return base64_encode($hash);
}

}
2 changes: 1 addition & 1 deletion lib/lti1p3/src/LtiAbstractService.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ protected function makeServiceRequest(IServiceRequest $request): array
);
}

protected function getAll(IServiceRequest $request, string $key = null): array
protected function getAll(IServiceRequest $request, ?string $key = null): array
{
return $this->serviceConnector->getAll(
$this->registration,
Expand Down
33 changes: 19 additions & 14 deletions lib/lti1p3/src/LtiAssignmentsGradesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,12 @@ public function getResourceLaunchLineItem(): ?LtiLineitem
return LtiLineitem::new()->setId($serviceData['lineitem']);
}

public function putGrade(LtiGrade $grade, LtiLineitem $lineitem = null)
public function putGrade(LtiGrade $grade, ?LtiLineitem $lineitem = null)
{
$this->validateScopes([LtiConstants::AGS_SCOPE_SCORE]);

$lineitem = $this->ensureLineItemExists($lineitem);

$scoreUrl = $lineitem->getId();

// Place '/scores' before url params
$pos = strpos($scoreUrl, '?');
$scoreUrl = $pos === false ? $scoreUrl.'/scores' : substr_replace($scoreUrl, '/scores', $pos, 0);
$scoreUrl = $this->appendLineItemPath($lineitem, '/scores');

$request = new ServiceRequest(
ServiceRequest::METHOD_POST,
Expand Down Expand Up @@ -112,14 +107,10 @@ public function findOrCreateLineitem(LtiLineitem $newLineItem): LtiLineitem
return $this->findLineItem($newLineItem) ?? $this->createLineitem($newLineItem);
}

public function getGrades(LtiLineitem $lineitem = null)
public function getGrades(?LtiLineitem $lineitem = null)
{
$lineitem = $this->ensureLineItemExists($lineitem);
$resultsUrl = $lineitem->getId();

// Place '/results' before url params
$pos = strpos($resultsUrl, '?');
$resultsUrl = $pos === false ? $resultsUrl.'/results' : substr_replace($resultsUrl, '/results', $pos, 0);
$resultsUrl = $this->appendLineItemPath($lineitem, '/results');

$request = new ServiceRequest(
ServiceRequest::METHOD_GET,
Expand Down Expand Up @@ -168,7 +159,7 @@ public function getLineItem(string $url): LtiLineitem
return new LtiLineitem($response);
}

private function ensureLineItemExists(LtiLineitem $lineitem = null): LtiLineitem
private function ensureLineItemExists(?LtiLineitem $lineitem = null): LtiLineitem
{
// If no line item is passed in, attempt to use the one associated with
// this launch.
Expand Down Expand Up @@ -198,4 +189,18 @@ private function isMatchingLineitem(array $lineitem, LtiLineitem $newLineItem):
$newLineItem->getResourceId() == ($lineitem['resourceId'] ?? null) &&
$newLineItem->getResourceLinkId() == ($lineitem['resourceLinkId'] ?? null);
}

private function appendLineItemPath(LtiLineitem $lineItem, string $suffix): string
{
$url = $lineItem->getId();
$pos = strpos($url, '?');

if ($pos === false) {
$url = $url.$suffix;
} else {
$url = substr_replace($url, $suffix, $pos, 0);
}

return $url;
}
}
7 changes: 4 additions & 3 deletions lib/lti1p3/src/LtiConstants.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ class LtiConstants

// Optional message claims
public const CONTEXT = 'https://purl.imsglobal.org/spec/lti/claim/context';
public const TOOL_PLATFORM = 'https://purl.imsglobal.org/spec/lti/claim/tool_platform';
public const ROLE_SCOPE_MENTOR = 'https://purlimsglobal.org/spec/lti/claim/role_scope_mentor';
public const CUSTOM = 'https://purl.imsglobal.org/spec/lti/claim/custom';
public const LAUNCH_PRESENTATION = 'https://purl.imsglobal.org/spec/lti/claim/launch_presentation';
public const LIS = 'https://purl.imsglobal.org/spec/lti/claim/lis';
public const CUSTOM = 'https://purl.imsglobal.org/spec/lti/claim/custom';
public const LTI1P1 = 'https://purl.imsglobal.org/spec/lti/claim/lti1p1';
public const ROLE_SCOPE_MENTOR = 'https://purlimsglobal.org/spec/lti/claim/role_scope_mentor';
public const TOOL_PLATFORM = 'https://purl.imsglobal.org/spec/lti/claim/tool_platform';

// LTI DL
public const DL_CONTENT_ITEMS = 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items';
Expand Down
11 changes: 3 additions & 8 deletions lib/lti1p3/src/LtiDeepLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,12 @@ public function getResponseJwt($resources)
}

/**
* This method builds an auto-submitting HTML form to post the deep linking response message
* back to platform, as per LTI-DL 2.0 specification. The resulting HTML is then written to standard output,
* so calling this method will automatically send an HTTP response to conclude the content selection flow.
*
* @param LtiDeepLinkResource[] $resources The list of selected resources to be sent to the platform
*
* @todo Consider wrapping the content inside a well-formed HTML document,
* and returning it instead of directly writing to standard output
* @deprecated
*/
public function outputResponseForm($resources)
{
trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED);

$jwt = $this->getResponseJwt($resources);
$formActionUrl = $this->deep_link_settings['deep_link_return_url'];

Expand Down
2 changes: 1 addition & 1 deletion lib/lti1p3/src/LtiDeepLinkDateTimeInterval.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class LtiDeepLinkDateTimeInterval
private ?DateTime $start;
private ?DateTime $end;

public function __construct(DateTime $start = null, DateTime $end = null)
public function __construct(?DateTime $start = null, ?DateTime $end = null)
{
if ($start !== null && $end !== null && $end < $start) {
throw new LtiException('Interval start time cannot be greater than end time');
Expand Down
4 changes: 4 additions & 0 deletions lib/lti1p3/src/LtiDeepLinkResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ public function setCustomParams(array $value): LtiDeepLinkResource
*/
public function getTarget(): string
{
trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED);

return $this->target;
}

Expand All @@ -134,6 +136,8 @@ public function getTarget(): string
*/
public function setTarget(string $value): LtiDeepLinkResource
{
trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED);

$this->target = $value;

return $this;
Expand Down
19 changes: 18 additions & 1 deletion lib/lti1p3/src/LtiDeepLinkResourceIframe.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ class LtiDeepLinkResourceIframe
{
private ?int $width;
private ?int $height;
private ?string $src;

public function __construct(int $width = null, int $height = null)
public function __construct(?int $width = null, ?int $height = null, ?string $src = null)
{
$this->width = $width ?? null;
$this->height = $height ?? null;
$this->src = $src ?? null;
}

public static function new(): LtiDeepLinkResourceIframe
Expand Down Expand Up @@ -42,6 +44,18 @@ public function getHeight(): ?int
return $this->height;
}

public function setSrc(?string $src): LtiDeepLinkResourceIframe
{
$this->src = $src;

return $this;
}

public function getSrc(): ?string
{
return $this->src;
}

public function toArray(): array
{
$iframe = [];
Expand All @@ -52,6 +66,9 @@ public function toArray(): array
if (isset($this->height)) {
$iframe['height'] = $this->height;
}
if (isset($this->src)) {
$iframe['src'] = $this->src;
}

return $iframe;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/lti1p3/src/LtiDeepLinkResourceWindow.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class LtiDeepLinkResourceWindow
private ?int $height;
private ?string $window_features;

public function __construct(string $targetName = null, int $width = null, int $height = null, string $windowFeatures = null)
public function __construct(?string $targetName = null, ?int $width = null, ?int $height = null, ?string $windowFeatures = null)
{
$this->target_name = $targetName ?? null;
$this->width = $width ?? null;
Expand Down
Loading

0 comments on commit 2362077

Please sign in to comment.