99 lines
3.2 KiB
PHP
99 lines
3.2 KiB
PHP
|
|
<?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'];
|
||
|
|
}
|
||
|
|
}
|