From 55c86d87db696f81c67310110897bd94feaba0e3 Mon Sep 17 00:00:00 2001 From: Moritz Strohm Date: Wed, 3 Jul 2024 11:46:52 +0200 Subject: renamed LTI class file names for new autoloader, fixed NegotiatesWithPsr7 requirement in course/lti controller --- app/controllers/course/lti.php | 2 +- lib/classes/lti1.3a/KeyChainFactory.class.php | 30 ------ lib/classes/lti1.3a/KeyChainFactory.php | 30 ++++++ lib/classes/lti1.3a/KeyManager.class.php | 31 ------ lib/classes/lti1.3a/KeyManager.php | 31 ++++++ lib/classes/lti1.3a/NonceGenerator.class.php | 35 ------- lib/classes/lti1.3a/NonceGenerator.php | 35 +++++++ lib/classes/lti1.3a/PlatformManager.class.php | 106 -------------------- lib/classes/lti1.3a/PlatformManager.php | 106 ++++++++++++++++++++ lib/classes/lti1.3a/Registration.class.php | 112 ---------------------- lib/classes/lti1.3a/Registration.php | 112 ++++++++++++++++++++++ lib/classes/lti1.3a/RegistrationManager.class.php | 63 ------------ lib/classes/lti1.3a/RegistrationManager.php | 63 ++++++++++++ lib/classes/lti1.3a/ToolManager.class.php | 55 ----------- lib/classes/lti1.3a/ToolManager.php | 55 +++++++++++ lib/classes/lti1.3a/UserAuthenticator.class.php | 41 -------- lib/classes/lti1.3a/UserAuthenticator.php | 41 ++++++++ lib/models/LtiDeploymentPrivacySettings.class.php | 13 --- lib/models/LtiDeploymentPrivacySettings.php | 13 +++ 19 files changed, 487 insertions(+), 487 deletions(-) delete mode 100644 lib/classes/lti1.3a/KeyChainFactory.class.php create mode 100644 lib/classes/lti1.3a/KeyChainFactory.php delete mode 100644 lib/classes/lti1.3a/KeyManager.class.php create mode 100644 lib/classes/lti1.3a/KeyManager.php delete mode 100644 lib/classes/lti1.3a/NonceGenerator.class.php create mode 100644 lib/classes/lti1.3a/NonceGenerator.php delete mode 100644 lib/classes/lti1.3a/PlatformManager.class.php create mode 100644 lib/classes/lti1.3a/PlatformManager.php delete mode 100644 lib/classes/lti1.3a/Registration.class.php create mode 100644 lib/classes/lti1.3a/Registration.php delete mode 100644 lib/classes/lti1.3a/RegistrationManager.class.php create mode 100644 lib/classes/lti1.3a/RegistrationManager.php delete mode 100644 lib/classes/lti1.3a/ToolManager.class.php create mode 100644 lib/classes/lti1.3a/ToolManager.php delete mode 100644 lib/classes/lti1.3a/UserAuthenticator.class.php create mode 100644 lib/classes/lti1.3a/UserAuthenticator.php delete mode 100644 lib/models/LtiDeploymentPrivacySettings.class.php create mode 100644 lib/models/LtiDeploymentPrivacySettings.php diff --git a/app/controllers/course/lti.php b/app/controllers/course/lti.php index 50b142e..55dc799 100644 --- a/app/controllers/course/lti.php +++ b/app/controllers/course/lti.php @@ -16,7 +16,7 @@ use OAT\Library\Lti1p3Core\Security\Jwks\Fetcher\JwksFetcher; class Course_LtiController extends StudipController { - use NegotiatesWithPsr7; + use Studip\OAuth2\NegotiatesWithPsr7; /** * Callback function being called before an action is executed. diff --git a/lib/classes/lti1.3a/KeyChainFactory.class.php b/lib/classes/lti1.3a/KeyChainFactory.class.php deleted file mode 100644 index da13fb9..0000000 --- a/lib/classes/lti1.3a/KeyChainFactory.class.php +++ /dev/null @@ -1,30 +0,0 @@ - $identifier]); - if ($keyring) { - return $keyring->toKeyChain(); - } - } - throw new \StudipException('Unable to create a keyring.'); - } -} diff --git a/lib/classes/lti1.3a/KeyChainFactory.php b/lib/classes/lti1.3a/KeyChainFactory.php new file mode 100644 index 0000000..da13fb9 --- /dev/null +++ b/lib/classes/lti1.3a/KeyChainFactory.php @@ -0,0 +1,30 @@ + $identifier]); + if ($keyring) { + return $keyring->toKeyChain(); + } + } + throw new \StudipException('Unable to create a keyring.'); + } +} diff --git a/lib/classes/lti1.3a/KeyManager.class.php b/lib/classes/lti1.3a/KeyManager.class.php deleted file mode 100644 index f27002a..0000000 --- a/lib/classes/lti1.3a/KeyManager.class.php +++ /dev/null @@ -1,31 +0,0 @@ -toKeyChain(); - } - return null; - } - - /** - * @inheritDoc - */ - #[\Override] public function findByKeySetName(string $keySetName): array - { - $keyring = \Keyring::findOneByRange_id($keySetName); - if ($keyring) { - return [$keyring->toKeyChain()]; - } - return []; - } -} diff --git a/lib/classes/lti1.3a/KeyManager.php b/lib/classes/lti1.3a/KeyManager.php new file mode 100644 index 0000000..f27002a --- /dev/null +++ b/lib/classes/lti1.3a/KeyManager.php @@ -0,0 +1,31 @@ +toKeyChain(); + } + return null; + } + + /** + * @inheritDoc + */ + #[\Override] public function findByKeySetName(string $keySetName): array + { + $keyring = \Keyring::findOneByRange_id($keySetName); + if ($keyring) { + return [$keyring->toKeyChain()]; + } + return []; + } +} diff --git a/lib/classes/lti1.3a/NonceGenerator.class.php b/lib/classes/lti1.3a/NonceGenerator.class.php deleted file mode 100644 index 349c969..0000000 --- a/lib/classes/lti1.3a/NonceGenerator.class.php +++ /dev/null @@ -1,35 +0,0 @@ -pass_nonce_from_request = $pass_nonce_from_request; - } - - #[\Override] public function generate(?int $ttl = null): NonceInterface - { - $expiration = new \DateTime(); - $expiration = $expiration->add(new \DateInterval('PT5M')); - if ($this->pass_nonce_from_request) { - return new Nonce( - \Request::get('nonce'), - $expiration - ); - } else { - $nonce = md5(random_bytes(16) . 'lti13a_nonce'); - //TODO: save nonce. - return new Nonce( - $nonce, - $expiration - ); - } - } -} diff --git a/lib/classes/lti1.3a/NonceGenerator.php b/lib/classes/lti1.3a/NonceGenerator.php new file mode 100644 index 0000000..349c969 --- /dev/null +++ b/lib/classes/lti1.3a/NonceGenerator.php @@ -0,0 +1,35 @@ +pass_nonce_from_request = $pass_nonce_from_request; + } + + #[\Override] public function generate(?int $ttl = null): NonceInterface + { + $expiration = new \DateTime(); + $expiration = $expiration->add(new \DateInterval('PT5M')); + if ($this->pass_nonce_from_request) { + return new Nonce( + \Request::get('nonce'), + $expiration + ); + } else { + $nonce = md5(random_bytes(16) . 'lti13a_nonce'); + //TODO: save nonce. + return new Nonce( + $nonce, + $expiration + ); + } + } +} diff --git a/lib/classes/lti1.3a/PlatformManager.class.php b/lib/classes/lti1.3a/PlatformManager.class.php deleted file mode 100644 index 23b46c9..0000000 --- a/lib/classes/lti1.3a/PlatformManager.class.php +++ /dev/null @@ -1,106 +0,0 @@ -STUDIP_INSTALLATION_ID, - $c->UNI_NAME_CLEAN, - $GLOBALS['ABSOLUTE_URI_STUDIP'], - \URLHelper::getURL('dispatch.php/lti/auth/oidc_init', null, true), - \URLHelper::getURL('dispatch.php/lti/auth/oauth2_token', null, true) - ); - } - - /** - * Generates an object containing the settings for using this Stud.IP - * as a platform that connects to an LTI tool via Deep Linking. - * - * @param string $tool_id An optional LTI tool ID that is used to construct - * the platform return URL. - * - * @return DeepLinkingSettings The settings for deep linking. - */ - public static function getDeepLinkingConfiguration(string $tool_id = '') : DeepLinkingSettings - { - $c = \Config::get(); - - return new DeepLinkingSettings( - self::getDeepLinkingReturnUrl($tool_id), - [ - LtiResourceLinkInterface::TYPE - ], - ['window', 'iframe'], - 'text/html', - true, - false, - $c->UNI_NAME_CLEAN, - '' - ); - } - - /** - * Returns the keyring for the platform. - * - * @return \Keyring|null The keyring for the platform or null if no such keyring exists. - */ - public static function getPlatformKeyring() : ?\Keyring - { - return \Keyring::findOneBySQL("`range_type` = 'global' AND `range_id` = 'lti13a_platform'"); - } - - public static function generatePlatformKeyring() : \Keyring - { - return \Keyring::generate('lti13a_platform', 'global'); - } - - public static function getLtiRoleClaimForStudipRole(string $role) : string - { - if (in_array($role, ['tutor', 'dozent', 'admin', 'root'])) { - //Lecturer/admin - return 'http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor'; - } elseif (in_array($role, ['user', 'autor'])) { - //Learner - return 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'; - } - //Invalid role: - return ''; - } - - /** - * Generates the URL for returning from the tool in an LTI deep linking process. - * - * @param string $tool_id The optional LTI Tool-ID to append to the URL. - * - * @return string The URL for returning from an LTI deep linking process. - */ - public static function getDeepLinkingReturnUrl(string $tool_id = '') : string - { - return \URLHelper::getURL('dispatch.php/course/lti/save_link/' . $tool_id, null, true); - } - - /** - * Returns the URL from which the JSON web key set (JWKS) can be retrieved. - * - * @return string The JWKS URL. - */ - public static function getJwksUrl() : string - { - return \URLHelper::getURL('lti/auth/jwks'); - } -} diff --git a/lib/classes/lti1.3a/PlatformManager.php b/lib/classes/lti1.3a/PlatformManager.php new file mode 100644 index 0000000..23b46c9 --- /dev/null +++ b/lib/classes/lti1.3a/PlatformManager.php @@ -0,0 +1,106 @@ +STUDIP_INSTALLATION_ID, + $c->UNI_NAME_CLEAN, + $GLOBALS['ABSOLUTE_URI_STUDIP'], + \URLHelper::getURL('dispatch.php/lti/auth/oidc_init', null, true), + \URLHelper::getURL('dispatch.php/lti/auth/oauth2_token', null, true) + ); + } + + /** + * Generates an object containing the settings for using this Stud.IP + * as a platform that connects to an LTI tool via Deep Linking. + * + * @param string $tool_id An optional LTI tool ID that is used to construct + * the platform return URL. + * + * @return DeepLinkingSettings The settings for deep linking. + */ + public static function getDeepLinkingConfiguration(string $tool_id = '') : DeepLinkingSettings + { + $c = \Config::get(); + + return new DeepLinkingSettings( + self::getDeepLinkingReturnUrl($tool_id), + [ + LtiResourceLinkInterface::TYPE + ], + ['window', 'iframe'], + 'text/html', + true, + false, + $c->UNI_NAME_CLEAN, + '' + ); + } + + /** + * Returns the keyring for the platform. + * + * @return \Keyring|null The keyring for the platform or null if no such keyring exists. + */ + public static function getPlatformKeyring() : ?\Keyring + { + return \Keyring::findOneBySQL("`range_type` = 'global' AND `range_id` = 'lti13a_platform'"); + } + + public static function generatePlatformKeyring() : \Keyring + { + return \Keyring::generate('lti13a_platform', 'global'); + } + + public static function getLtiRoleClaimForStudipRole(string $role) : string + { + if (in_array($role, ['tutor', 'dozent', 'admin', 'root'])) { + //Lecturer/admin + return 'http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor'; + } elseif (in_array($role, ['user', 'autor'])) { + //Learner + return 'http://purl.imsglobal.org/vocab/lis/v2/membership#Learner'; + } + //Invalid role: + return ''; + } + + /** + * Generates the URL for returning from the tool in an LTI deep linking process. + * + * @param string $tool_id The optional LTI Tool-ID to append to the URL. + * + * @return string The URL for returning from an LTI deep linking process. + */ + public static function getDeepLinkingReturnUrl(string $tool_id = '') : string + { + return \URLHelper::getURL('dispatch.php/course/lti/save_link/' . $tool_id, null, true); + } + + /** + * Returns the URL from which the JSON web key set (JWKS) can be retrieved. + * + * @return string The JWKS URL. + */ + public static function getJwksUrl() : string + { + return \URLHelper::getURL('lti/auth/jwks'); + } +} diff --git a/lib/classes/lti1.3a/Registration.class.php b/lib/classes/lti1.3a/Registration.class.php deleted file mode 100644 index df0a9b9..0000000 --- a/lib/classes/lti1.3a/Registration.class.php +++ /dev/null @@ -1,112 +0,0 @@ -lti_link = $lti_link; - } - - public function setLtiDeployment(\LtiDeployment $lti_link) - { - $this->lti_link = $lti_link; - } - - public function getLtiDeployment() : ?\LtiDeployment - { - return $this->lti_link; - } - - #[\Override] public function getIdentifier(): string - { - if (!$this->lti_link) { - return ''; - } - return $this->lti_link->id; - } - - #[\Override] public function getClientId(): string - { - return \Config::get()->STUDIP_INSTALLATION_ID; - } - - #[\Override] public function getPlatform(): PlatformInterface - { - return \Studip\LTI13a\PlatformManager::getPlatformConfiguration(); - } - - #[\Override] public function getTool(): ToolInterface - { - if (!$this->lti_link || !$this->lti_link->tool) { - throw new \StudipException('No LTI tool link present.'); - } - return $this->lti_link->tool->getToolData(); - } - - #[\Override] public function getDeploymentIds(): array - { - if (!$this->lti_link) { - return []; - } - return [$this->lti_link->id]; - } - - #[\Override] public function hasDeploymentId(string $deploymentId): bool - { - if (!$this->lti_link) { - return false; - } - return $this->lti_link->id == $deploymentId; - } - - #[\Override] public function getDefaultDeploymentId(): ?string - { - if (!$this->lti_link) { - return null; - }; - if ($this->lti_link->isNew() && !$this->lti_link->id) { - $this->lti_link->getNewId(); - } - return $this->lti_link->id; - } - - #[\Override] public function getPlatformKeyChain(): ?KeyChainInterface - { - $platform_keyring = \Studip\LTI13a\PlatformManager::getPlatformKeyring(); - if (!$platform_keyring) { - $platform_keyring = \Studip\LTI13a\PlatformManager::generatePlatformKeyring(); - } - return $platform_keyring->toKeyChain(); - } - - #[\Override] public function getToolKeyChain(): ?KeyChainInterface - { - if (!$this->lti_link || !$this->lti_link->tool) { - return null; - } - $keyring = $this->lti_link->tool->getKeyring(); - if (!$keyring) { - $keyring = $this->lti_link->tool->getKeyring(true); - } - return $keyring->toKeyChain(); - } - - #[\Override] public function getPlatformJwksUrl(): ?string - { - return PlatformManager::getJwksUrl(); - } - - #[\Override] public function getToolJwksUrl(): ?string - { - return $this->lti_link->tool->jwks_url ?? null; - } -} diff --git a/lib/classes/lti1.3a/Registration.php b/lib/classes/lti1.3a/Registration.php new file mode 100644 index 0000000..df0a9b9 --- /dev/null +++ b/lib/classes/lti1.3a/Registration.php @@ -0,0 +1,112 @@ +lti_link = $lti_link; + } + + public function setLtiDeployment(\LtiDeployment $lti_link) + { + $this->lti_link = $lti_link; + } + + public function getLtiDeployment() : ?\LtiDeployment + { + return $this->lti_link; + } + + #[\Override] public function getIdentifier(): string + { + if (!$this->lti_link) { + return ''; + } + return $this->lti_link->id; + } + + #[\Override] public function getClientId(): string + { + return \Config::get()->STUDIP_INSTALLATION_ID; + } + + #[\Override] public function getPlatform(): PlatformInterface + { + return \Studip\LTI13a\PlatformManager::getPlatformConfiguration(); + } + + #[\Override] public function getTool(): ToolInterface + { + if (!$this->lti_link || !$this->lti_link->tool) { + throw new \StudipException('No LTI tool link present.'); + } + return $this->lti_link->tool->getToolData(); + } + + #[\Override] public function getDeploymentIds(): array + { + if (!$this->lti_link) { + return []; + } + return [$this->lti_link->id]; + } + + #[\Override] public function hasDeploymentId(string $deploymentId): bool + { + if (!$this->lti_link) { + return false; + } + return $this->lti_link->id == $deploymentId; + } + + #[\Override] public function getDefaultDeploymentId(): ?string + { + if (!$this->lti_link) { + return null; + }; + if ($this->lti_link->isNew() && !$this->lti_link->id) { + $this->lti_link->getNewId(); + } + return $this->lti_link->id; + } + + #[\Override] public function getPlatformKeyChain(): ?KeyChainInterface + { + $platform_keyring = \Studip\LTI13a\PlatformManager::getPlatformKeyring(); + if (!$platform_keyring) { + $platform_keyring = \Studip\LTI13a\PlatformManager::generatePlatformKeyring(); + } + return $platform_keyring->toKeyChain(); + } + + #[\Override] public function getToolKeyChain(): ?KeyChainInterface + { + if (!$this->lti_link || !$this->lti_link->tool) { + return null; + } + $keyring = $this->lti_link->tool->getKeyring(); + if (!$keyring) { + $keyring = $this->lti_link->tool->getKeyring(true); + } + return $keyring->toKeyChain(); + } + + #[\Override] public function getPlatformJwksUrl(): ?string + { + return PlatformManager::getJwksUrl(); + } + + #[\Override] public function getToolJwksUrl(): ?string + { + return $this->lti_link->tool->jwks_url ?? null; + } +} diff --git a/lib/classes/lti1.3a/RegistrationManager.class.php b/lib/classes/lti1.3a/RegistrationManager.class.php deleted file mode 100644 index 5e75275..0000000 --- a/lib/classes/lti1.3a/RegistrationManager.class.php +++ /dev/null @@ -1,63 +0,0 @@ -getIdentifier(), - $tool->getName(), - $tool->getAudience(), - $tool->getOidcInitiationUrl(), - $tool->getLaunchUrl(), - $tool->getDeepLinkingUrl() - ); - } - - public function getToolRegistration(string $tool_id) :?Registration - { - $platform_config = PlatformManager::getPlatformConfiguration(); - $tool_config = self::getToolConfiguration($tool_id); - if (!$platform_config || !$tool_config) { - return null; - } - - $platform_keyring = \Keyring::findOneBySQL("`range_type` = 'global' AND `range_id` = 'lti13a_platform'"); - $tool_keyring = \Keyring::findOneBySQL( - "`range_type` = 'lti-tool' AND 'range_id = :tool_id", - ['tool_id' => $tool_id] - ); - - return new Registration( - sprintf( - '%s_%s', - $platform_config->getIdentifier(), - $tool_config->getIdentifier() - ), - $GLOBALS['user']->id, - $platform_config, - $tool_config, - [], //TODO - $platform_keyring ? $platform_keyring->toKeyChain() : null, - $tool_keyring ? $tool_keyring->toKeyChain() : null - ); - } -} diff --git a/lib/classes/lti1.3a/ToolManager.php b/lib/classes/lti1.3a/ToolManager.php new file mode 100644 index 0000000..bf5529d --- /dev/null +++ b/lib/classes/lti1.3a/ToolManager.php @@ -0,0 +1,55 @@ +getIdentifier(), + $tool->getName(), + $tool->getAudience(), + $tool->getOidcInitiationUrl(), + $tool->getLaunchUrl(), + $tool->getDeepLinkingUrl() + ); + } + + public function getToolRegistration(string $tool_id) :?Registration + { + $platform_config = PlatformManager::getPlatformConfiguration(); + $tool_config = self::getToolConfiguration($tool_id); + if (!$platform_config || !$tool_config) { + return null; + } + + $platform_keyring = \Keyring::findOneBySQL("`range_type` = 'global' AND `range_id` = 'lti13a_platform'"); + $tool_keyring = \Keyring::findOneBySQL( + "`range_type` = 'lti-tool' AND 'range_id = :tool_id", + ['tool_id' => $tool_id] + ); + + return new Registration( + sprintf( + '%s_%s', + $platform_config->getIdentifier(), + $tool_config->getIdentifier() + ), + $GLOBALS['user']->id, + $platform_config, + $tool_config, + [], //TODO + $platform_keyring ? $platform_keyring->toKeyChain() : null, + $tool_keyring ? $tool_keyring->toKeyChain() : null + ); + } +} diff --git a/lib/classes/lti1.3a/UserAuthenticator.class.php b/lib/classes/lti1.3a/UserAuthenticator.class.php deleted file mode 100644 index 6c06bbd..0000000 --- a/lib/classes/lti1.3a/UserAuthenticator.class.php +++ /dev/null @@ -1,41 +0,0 @@ -logger = $logger; - } - - #[\Override] public function authenticate(RegistrationInterface $registration, string $loginHint): UserAuthenticationResultInterface - { - $user = \User::find($loginHint); - - $identity = null; - $deployment = null; - if ($registration instanceof \Studip\LTI13a\Registration) { - $deployment = $registration->getLtiDeployment(); - } else { - $deployment = \LtiDeployment::find($registration->getIdentifier()); - } - if ($user instanceof \User && $deployment instanceof \LtiDeployment) { - $identity = new Identity($user, $deployment); - } - if ($this->logger) { - //$this->logger->debug($user instanceof \User); - //$this->logger->debug($loginHint); - $this->logger->debug(var_export($identity->normalize(), true)); - } - - return new UserAuthenticationResult($user instanceof \User, $identity); - } -} diff --git a/lib/classes/lti1.3a/UserAuthenticator.php b/lib/classes/lti1.3a/UserAuthenticator.php new file mode 100644 index 0000000..6c06bbd --- /dev/null +++ b/lib/classes/lti1.3a/UserAuthenticator.php @@ -0,0 +1,41 @@ +logger = $logger; + } + + #[\Override] public function authenticate(RegistrationInterface $registration, string $loginHint): UserAuthenticationResultInterface + { + $user = \User::find($loginHint); + + $identity = null; + $deployment = null; + if ($registration instanceof \Studip\LTI13a\Registration) { + $deployment = $registration->getLtiDeployment(); + } else { + $deployment = \LtiDeployment::find($registration->getIdentifier()); + } + if ($user instanceof \User && $deployment instanceof \LtiDeployment) { + $identity = new Identity($user, $deployment); + } + if ($this->logger) { + //$this->logger->debug($user instanceof \User); + //$this->logger->debug($loginHint); + $this->logger->debug(var_export($identity->normalize(), true)); + } + + return new UserAuthenticationResult($user instanceof \User, $identity); + } +} diff --git a/lib/models/LtiDeploymentPrivacySettings.class.php b/lib/models/LtiDeploymentPrivacySettings.class.php deleted file mode 100644 index d9a2335..0000000 --- a/lib/models/LtiDeploymentPrivacySettings.class.php +++ /dev/null @@ -1,13 +0,0 @@ -