ajoute Moodle LTI 1.3 (V2), une 6e façon de rejoindre une classe
LTI authentifie l'élève et le fait rejoindre sa classe (même code que le code/QR existant), pas lancer un lab précis - relecture du CLAUDE.md qui liste LTI avec les autres façons d'ajouter des élèves. packbackbooks/lti-1p3-tool (Apache-2.0, recommandé par le CLAUDE.md), trois ponts vers Redis/cookies/MariaDB, élève retrouvé par identité Moodle stable pour ne jamais créer de doublon. Enregistrement d'une plateforme/d'un déploiement par commande Artisan (opération rare, faite à la main par Adam), jamais une page d'administration. Corrige au passage un vrai bug préexistant trouvé en vérifiant en réel sur le serveur : withoutMiddleware(ValidateCsrfToken::class) sur une route ne retire jamais le vrai middleware CSRF (ValidateCsrfToken est un alias déprécié de PreventRequestForgery, pas la classe réellement enregistrée) - /csp-rapport avait le même défaut depuis l'étape 14, invisible faute de surveillance. Corrigé pour les deux par la bonne méthode (validateCsrfTokens except, dans bootstrap/app.php). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
777dd1cbc8
commit
91d7cde4c1
27 changed files with 1293 additions and 9 deletions
38
portail/app/Console/Commands/EnregistrerDeploiementLti.php
Normal file
38
portail/app/Console/Commands/EnregistrerDeploiementLti.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\LtiDeploiement;
|
||||
use App\Models\LtiPlateforme;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Enregistre un déploiement (V2, LTI 1.3) : Moodle en crée un chaque fois
|
||||
* que l'outil est ajouté à un cours, affiché dans la configuration de
|
||||
* l'activité. Un lancement dont le déploiement n'est pas enregistré est
|
||||
* refusé.
|
||||
*/
|
||||
class EnregistrerDeploiementLti extends Command
|
||||
{
|
||||
protected $signature = 'lti:enregistrer-deploiement {plateforme_id} {deployment_id}';
|
||||
|
||||
protected $description = 'Enregistre un déploiement Moodle (ajout de l’outil à un cours)';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$plateforme = LtiPlateforme::find($this->argument('plateforme_id'));
|
||||
|
||||
if ($plateforme === null) {
|
||||
$this->error('Plateforme introuvable.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LtiDeploiement::create([
|
||||
'lti_plateforme_id' => $plateforme->id,
|
||||
'deployment_id' => $this->argument('deployment_id'),
|
||||
]);
|
||||
|
||||
$this->info('Déploiement enregistré.');
|
||||
}
|
||||
}
|
||||
59
portail/app/Console/Commands/EnregistrerPlateformeLti.php
Normal file
59
portail/app/Console/Commands/EnregistrerPlateformeLti.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\LtiPlateforme;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
// phpseclib/phpseclib ^4.0 est installé (composer.lock) : son espace de
|
||||
// noms est phpseclib4, pas phpseclib3 (contrairement à la bibliothèque LTI
|
||||
// elle-même, qui gère les deux — voir Packback\Lti1p3\JwksEndpoint).
|
||||
use phpseclib4\Crypt\RSA;
|
||||
|
||||
/**
|
||||
* Enregistre un établissement Moodle (V2, CLAUDE.md section 6) : à lancer
|
||||
* une fois par Moodle, avec les informations que Moodle affiche après
|
||||
* qu'Adam y a ajouté « Syllabus » comme outil externe LTI 1.3 (menu
|
||||
* Administration du site > Plugins > Outils externes > Gérer les outils).
|
||||
* Jamais les identifiants d'un élève ou d'un prof ; toujours ceux
|
||||
* qu'Adam lit lui-même dans l'administration Moodle.
|
||||
*/
|
||||
class EnregistrerPlateformeLti extends Command
|
||||
{
|
||||
protected $signature = 'lti:enregistrer-plateforme
|
||||
{emetteur : « Platform ID » affiché par Moodle}
|
||||
{client_id : « Client ID » affiché par Moodle}
|
||||
{url_jeu_cles : « Public keyset URL » affichée par Moodle}
|
||||
{url_jeton_auth : « Access token URL » affichée par Moodle}
|
||||
{url_connexion_auth : « Authentication request URL » affichée par Moodle}
|
||||
{serveur_auth? : « Authentication server » — facultatif, souvent identique à l’émetteur}';
|
||||
|
||||
protected $description = 'Enregistre un établissement Moodle (plateforme LTI 1.3)';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$clePrivee = RSA::createKey(2048)->toString('PKCS8');
|
||||
$kid = Str::random(16);
|
||||
|
||||
$plateforme = LtiPlateforme::create([
|
||||
'emetteur' => $this->argument('emetteur'),
|
||||
'client_id' => $this->argument('client_id'),
|
||||
'url_jeu_cles' => $this->argument('url_jeu_cles'),
|
||||
'url_jeton_auth' => $this->argument('url_jeton_auth'),
|
||||
'url_connexion_auth' => $this->argument('url_connexion_auth'),
|
||||
'serveur_auth' => $this->argument('serveur_auth'),
|
||||
'cle_privee_outil' => $clePrivee,
|
||||
'identifiant_cle' => $kid,
|
||||
]);
|
||||
|
||||
$this->info("Plateforme #{$plateforme->id} enregistrée.");
|
||||
$this->newLine();
|
||||
$this->line('À saisir côté Moodle, dans la configuration de l’outil externe :');
|
||||
$this->line(' Initiate login URL : '.route('lti.connexion'));
|
||||
$this->line(' Public keyset URL (JWKS) : '.route('lti.cles'));
|
||||
$this->newLine();
|
||||
$this->line('Ensuite, pour chaque cours où l’outil est ajouté, Moodle affiche un');
|
||||
$this->line('« Deployment ID » à enregistrer avec :');
|
||||
$this->line(" php artisan lti:enregistrer-deploiement {$plateforme->id} <deployment_id>");
|
||||
}
|
||||
}
|
||||
26
portail/app/Http/Controllers/LtiClesController.php
Normal file
26
portail/app/Http/Controllers/LtiClesController.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\LtiPlateforme;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Packback\Lti1p3\JwksEndpoint;
|
||||
|
||||
/**
|
||||
* Clés publiques du portail (V2, LTI 1.3) : chaque plateforme Moodle
|
||||
* enregistrée a sa propre paire de clés (une ligne de lti_plateformes),
|
||||
* toutes publiées ici — Moodle n'en retient que celle dont le « kid »
|
||||
* correspond à ses propres jetons signés.
|
||||
*/
|
||||
class LtiClesController extends Controller
|
||||
{
|
||||
public function cles(): JsonResponse
|
||||
{
|
||||
$cles = LtiPlateforme::query()
|
||||
->whereNotNull('identifiant_cle')
|
||||
->pluck('cle_privee_outil', 'identifiant_cle')
|
||||
->all();
|
||||
|
||||
return response()->json(JwksEndpoint::new($cles)->getPublicJwks());
|
||||
}
|
||||
}
|
||||
37
portail/app/Http/Controllers/LtiConnexionController.php
Normal file
37
portail/app/Http/Controllers/LtiConnexionController.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Packback\Lti1p3\Interfaces\ICache;
|
||||
use Packback\Lti1p3\Interfaces\ICookie;
|
||||
use Packback\Lti1p3\Interfaces\IDatabase;
|
||||
use Packback\Lti1p3\LtiOidcLogin;
|
||||
use Packback\Lti1p3\OidcException;
|
||||
|
||||
/**
|
||||
* Première étape du lancement LTI 1.3 (V2) : Moodle appelle cette URL
|
||||
* (« initiate_login_url », enregistrée une fois par plateforme) pour
|
||||
* démarrer la connexion OIDC, avant de rediriger l'utilisateur vers la
|
||||
* vraie page de lancement — voir LtiLancementController.
|
||||
*/
|
||||
class LtiConnexionController extends Controller
|
||||
{
|
||||
public function connecter(Request $request, IDatabase $db, ICache $cache, ICookie $cookie): RedirectResponse
|
||||
{
|
||||
$donnees = $request->all();
|
||||
|
||||
if (! isset($donnees['target_link_uri'])) {
|
||||
abort(400, 'target_link_uri manquant.');
|
||||
}
|
||||
|
||||
try {
|
||||
$url = LtiOidcLogin::new($db, $cache, $cookie)->getRedirectUrl($donnees['target_link_uri'], $donnees);
|
||||
} catch (OidcException $e) {
|
||||
abort(400, $e->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->away($url);
|
||||
}
|
||||
}
|
||||
98
portail/app/Http/Controllers/LtiLancementController.php
Normal file
98
portail/app/Http/Controllers/LtiLancementController.php
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Classe;
|
||||
use App\Models\Eleve;
|
||||
use App\Services\GenerateurCompteEleve;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\View\View;
|
||||
use Packback\Lti1p3\Factories\MessageFactory;
|
||||
use Packback\Lti1p3\LtiException;
|
||||
use Packback\Lti1p3\OidcException;
|
||||
|
||||
/**
|
||||
* Deuxième étape du lancement LTI 1.3 (V2, CLAUDE.md section 6) : Moodle
|
||||
* poste ici le jeton signé après la connexion OIDC
|
||||
* (LtiConnexionController). L'URL (une par classe, affichée dans « Faire
|
||||
* rejoindre tes élèves ») est à coller comme adresse de l'outil externe
|
||||
* dans l'activité Moodle du prof.
|
||||
*/
|
||||
class LtiLancementController extends Controller
|
||||
{
|
||||
public function lancer(Request $request, string $codeClasse, MessageFactory $factory): View|RedirectResponse
|
||||
{
|
||||
$classe = Classe::where('code_classe', $codeClasse)->first();
|
||||
|
||||
if ($classe === null || $classe->estArchivee()) {
|
||||
return view('lti.erreur', ['message' => 'Classe introuvable ou archivée.']);
|
||||
}
|
||||
|
||||
try {
|
||||
$lancement = $factory->create($request->all());
|
||||
} catch (LtiException|OidcException $e) {
|
||||
return view('lti.erreur', ['message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
$donnees = $lancement->getBody();
|
||||
$eleve = $this->trouverOuCreerEleve($classe, (string) $donnees['iss'], (string) $donnees['sub'], $donnees);
|
||||
|
||||
Auth::guard('eleve')->login($eleve);
|
||||
$request->session()->regenerate();
|
||||
$eleve->forceFill(['derniere_connexion_a' => now()])->save();
|
||||
$request->session()->put('jeton_session_eleve', $eleve->demarreNouvelleSession());
|
||||
|
||||
return redirect()->route('eleve');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $claims
|
||||
*/
|
||||
private function trouverOuCreerEleve(Classe $classe, string $emetteur, string $sub, array $claims): Eleve
|
||||
{
|
||||
$eleve = Eleve::where('classe_id', $classe->id)
|
||||
->where('lti_emetteur', $emetteur)
|
||||
->where('lti_sub', $sub)
|
||||
->first();
|
||||
|
||||
if ($eleve !== null) {
|
||||
return $eleve;
|
||||
}
|
||||
|
||||
[$prenom, $nom] = $this->nomDepuisClaims($claims);
|
||||
|
||||
$generateur = app(GenerateurCompteEleve::class);
|
||||
|
||||
return $classe->eleves()->create([
|
||||
'prenom' => $prenom,
|
||||
'nom' => $nom,
|
||||
'identifiant' => $generateur->genererIdentifiant($prenom, $nom),
|
||||
// Jamais utilisé : cet élève se connecte toujours via Moodle.
|
||||
'code_secret' => Hash::make($generateur->genererCodeSecret()),
|
||||
'lti_emetteur' => $emetteur,
|
||||
'lti_sub' => $sub,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $claims
|
||||
* @return array{0: string, 1: ?string}
|
||||
*/
|
||||
private function nomDepuisClaims(array $claims): array
|
||||
{
|
||||
if (filled($claims['given_name'] ?? null)) {
|
||||
return [$claims['given_name'], $claims['family_name'] ?? null];
|
||||
}
|
||||
|
||||
if (filled($claims['name'] ?? null)) {
|
||||
$parties = explode(' ', trim($claims['name']), 2);
|
||||
|
||||
return [$parties[0], $parties[1] ?? null];
|
||||
}
|
||||
|
||||
return ['Élève', 'Moodle'];
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ class Eleve extends Model implements AuthenticatableContract
|
|||
/** @use HasFactory<EleveFactory> */
|
||||
use Authenticatable, GerePreferences, GereSessionUnique, HasFactory;
|
||||
|
||||
protected $fillable = ['classe_id', 'prenom', 'nom', 'identifiant', 'code_secret', 'jeton_qr'];
|
||||
protected $fillable = ['classe_id', 'prenom', 'nom', 'identifiant', 'code_secret', 'jeton_qr', 'lti_emetteur', 'lti_sub'];
|
||||
|
||||
protected $hidden = ['code_secret', 'jeton_session_actif', 'jeton_qr'];
|
||||
|
||||
|
|
|
|||
17
portail/app/Models/LtiDeploiement.php
Normal file
17
portail/app/Models/LtiDeploiement.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class LtiDeploiement extends Model
|
||||
{
|
||||
protected $fillable = ['lti_plateforme_id', 'deployment_id'];
|
||||
|
||||
/** @return BelongsTo<LtiPlateforme, $this> */
|
||||
public function plateforme(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(LtiPlateforme::class, 'lti_plateforme_id');
|
||||
}
|
||||
}
|
||||
22
portail/app/Models/LtiPlateforme.php
Normal file
22
portail/app/Models/LtiPlateforme.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class LtiPlateforme extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'emetteur', 'client_id', 'url_jeu_cles', 'url_jeton_auth',
|
||||
'url_connexion_auth', 'serveur_auth', 'cle_privee_outil', 'identifiant_cle',
|
||||
];
|
||||
|
||||
protected $hidden = ['cle_privee_outil'];
|
||||
|
||||
/** @return HasMany<LtiDeploiement, $this> */
|
||||
public function deploiements(): HasMany
|
||||
{
|
||||
return $this->hasMany(LtiDeploiement::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,22 @@ namespace App\Providers;
|
|||
use App\Models\Eleve;
|
||||
use App\Models\User;
|
||||
use App\Services\CompteurDevoirsEnAttente;
|
||||
use App\Services\Lti\CacheLti;
|
||||
use App\Services\Lti\CookieLti;
|
||||
use App\Services\Lti\DatabaseLti;
|
||||
use Firebase\JWT\JWT;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Packback\Lti1p3\Interfaces\ICache;
|
||||
use Packback\Lti1p3\Interfaces\ICookie;
|
||||
use Packback\Lti1p3\Interfaces\IDatabase;
|
||||
use Packback\Lti1p3\Interfaces\ILtiServiceConnector;
|
||||
use Packback\Lti1p3\LtiServiceConnector;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -19,7 +29,12 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
// Moodle LTI 1.3 (V2, CLAUDE.md section 6).
|
||||
$this->app->bind(ICache::class, CacheLti::class);
|
||||
$this->app->bind(ICookie::class, CookieLti::class);
|
||||
$this->app->bind(IDatabase::class, DatabaseLti::class);
|
||||
$this->app->bind(ILtiServiceConnector::class, fn ($app) => (new LtiServiceConnector($app->make(ICache::class), new Client))
|
||||
->setDebuggingMode(config('app.debug')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -27,6 +42,9 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Tolérance d'horloge pour les jetons signés par Moodle (LTI 1.3).
|
||||
JWT::$leeway = 5;
|
||||
|
||||
// Coquille de l'application (layouts/app) : variables communes à
|
||||
// la barre du haut et à la barre latérale, pour qu'aucun contrôleur
|
||||
// n'ait à les repasser (séparation vitrine/application, 2026-10-13).
|
||||
|
|
|
|||
69
portail/app/Services/Lti/CacheLti.php
Normal file
69
portail/app/Services/Lti/CacheLti.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Lti;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Packback\Lti1p3\Interfaces\ICache;
|
||||
|
||||
/**
|
||||
* Pont vers le cache Laravel (Redis en production) pour la bibliothèque
|
||||
* LTI 1.3 : nonce OIDC, données d'un lancement en cours, jetons d'accès
|
||||
* aux services LTI (jamais utilisés aujourd'hui, voir ADR).
|
||||
*/
|
||||
class CacheLti implements ICache
|
||||
{
|
||||
private const EXPIRATION_NONCE = 3600;
|
||||
|
||||
private const EXPIRATION_DONNEES_LANCEMENT = 86400;
|
||||
|
||||
private const EXPIRATION_JETON_ACCES = 3600;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function getLaunchData(string $key): ?array
|
||||
{
|
||||
return Cache::get("lti1p3-lancement-{$key}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $jwtBody
|
||||
*/
|
||||
public function cacheLaunchData(string $key, array $jwtBody): void
|
||||
{
|
||||
Cache::put("lti1p3-lancement-{$key}", $jwtBody, self::EXPIRATION_DONNEES_LANCEMENT);
|
||||
}
|
||||
|
||||
public function cacheNonce(string $nonce, string $state): void
|
||||
{
|
||||
Cache::put("lti1p3-nonce-{$nonce}", $state, self::EXPIRATION_NONCE);
|
||||
}
|
||||
|
||||
public function checkNonceIsValid(string $nonce, string $state): bool
|
||||
{
|
||||
$etatEnCache = Cache::get("lti1p3-nonce-{$nonce}");
|
||||
|
||||
if ($etatEnCache === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cache::forget("lti1p3-nonce-{$nonce}");
|
||||
|
||||
return $etatEnCache === $state;
|
||||
}
|
||||
|
||||
public function cacheAccessToken(string $key, string $accessToken): void
|
||||
{
|
||||
Cache::put("lti1p3-jeton-{$key}", $accessToken, self::EXPIRATION_JETON_ACCES);
|
||||
}
|
||||
|
||||
public function getAccessToken(string $key): ?string
|
||||
{
|
||||
return Cache::get("lti1p3-jeton-{$key}");
|
||||
}
|
||||
|
||||
public function clearAccessToken(string $key): void
|
||||
{
|
||||
Cache::forget("lti1p3-jeton-{$key}");
|
||||
}
|
||||
}
|
||||
28
portail/app/Services/Lti/CookieLti.php
Normal file
28
portail/app/Services/Lti/CookieLti.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Lti;
|
||||
|
||||
use Illuminate\Support\Facades\Cookie;
|
||||
use Packback\Lti1p3\Interfaces\ICookie;
|
||||
|
||||
/**
|
||||
* Pont vers les cookies Laravel, pour le jeton d'état OIDC du lancement
|
||||
* LTI 1.3. SameSite=None obligatoire : la plateforme (Moodle) renvoie ce
|
||||
* cookie via un POST depuis SON origine, pas la nôtre — un cookie "Lax" ou
|
||||
* "Strict" ne partirait jamais, et chaque lancement échouerait en silence.
|
||||
*/
|
||||
class CookieLti implements ICookie
|
||||
{
|
||||
public function getCookie(string $name): ?string
|
||||
{
|
||||
return Cookie::get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function setCookie(string $name, string $value, int $exp = 3600, array $options = []): void
|
||||
{
|
||||
Cookie::queue($name, $value, (int) ceil($exp / 60), '/', null, true, true, false, 'none');
|
||||
}
|
||||
}
|
||||
61
portail/app/Services/Lti/DatabaseLti.php
Normal file
61
portail/app/Services/Lti/DatabaseLti.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Lti;
|
||||
|
||||
use App\Models\LtiDeploiement;
|
||||
use App\Models\LtiPlateforme;
|
||||
use Packback\Lti1p3\Interfaces\IDatabase;
|
||||
use Packback\Lti1p3\Interfaces\ILtiDeployment;
|
||||
use Packback\Lti1p3\Interfaces\ILtiRegistration;
|
||||
use Packback\Lti1p3\LtiDeployment;
|
||||
use Packback\Lti1p3\LtiRegistration;
|
||||
|
||||
/**
|
||||
* Pont vers les tables lti_plateformes/lti_deploiements : enregistrées à la
|
||||
* main par Adam (`lti:enregistrer-plateforme`, `lti:enregistrer-deploiement`),
|
||||
* jamais devinées ni créées par un lancement lui-même — un lancement dont
|
||||
* la plateforme ou le déploiement est inconnu est refusé.
|
||||
*/
|
||||
class DatabaseLti implements IDatabase
|
||||
{
|
||||
public function findRegistrationByIssuer(string $iss, ?string $clientId = null): ?ILtiRegistration
|
||||
{
|
||||
$requete = LtiPlateforme::where('emetteur', $iss);
|
||||
|
||||
if ($clientId !== null) {
|
||||
$requete->where('client_id', $clientId);
|
||||
}
|
||||
|
||||
$plateforme = $requete->first();
|
||||
|
||||
if ($plateforme === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LtiRegistration::new([
|
||||
'issuer' => $plateforme->emetteur,
|
||||
'clientId' => $plateforme->client_id,
|
||||
'keySetUrl' => $plateforme->url_jeu_cles,
|
||||
'authTokenUrl' => $plateforme->url_jeton_auth,
|
||||
'authLoginUrl' => $plateforme->url_connexion_auth,
|
||||
'authServer' => $plateforme->serveur_auth,
|
||||
'toolPrivateKey' => $plateforme->cle_privee_outil,
|
||||
'kid' => $plateforme->identifiant_cle,
|
||||
]);
|
||||
}
|
||||
|
||||
public function findDeployment(string $iss, string $deploymentId, ?string $clientId = null): ?ILtiDeployment
|
||||
{
|
||||
$requete = LtiDeploiement::where('deployment_id', $deploymentId)
|
||||
->whereHas('plateforme', function ($q) use ($iss, $clientId) {
|
||||
$q->where('emetteur', $iss);
|
||||
if ($clientId !== null) {
|
||||
$q->where('client_id', $clientId);
|
||||
}
|
||||
});
|
||||
|
||||
$deploiement = $requete->first();
|
||||
|
||||
return $deploiement === null ? null : LtiDeployment::new($deploiement->deployment_id);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue