97 lines
3.9 KiB
PHP
97 lines
3.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Core\Access;
|
|
use App\Core\Controller;
|
|
use App\Core\Csrf;
|
|
use App\Core\Security;
|
|
use App\Models\AgentModel;
|
|
use App\Models\StructureModel;
|
|
use PDO;
|
|
|
|
/**
|
|
* Sélection du rôle et de l'identité de travail.
|
|
*
|
|
* ATTENTION : ce n'est PAS une authentification. L'utilisateur choisit
|
|
* librement qui il est, sans mot de passe ni vérification. C'est la faille
|
|
* ouverte la plus importante du projet, assumée pour cette version. Un passage
|
|
* en production doit remplacer cet écran par une authentification réelle — le
|
|
* reste de la mécanique de droits (Access, vues SQL) n'a pas à bouger, il
|
|
* suffit que la session soit renseignée par un compte vérifié.
|
|
*
|
|
* Contrairement aux autres pages, ce contrôleur n'est pas appelé par runPage() :
|
|
* il s'exécute AVANT tout contrôle d'accès, puisqu'il est ce qui permet d'en
|
|
* avoir un.
|
|
*/
|
|
final class RoleController extends Controller
|
|
{
|
|
public function __construct(private PDO $pdo)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Affiche le sélecteur et traite sa soumission.
|
|
*
|
|
* Trois chemins dans une seule méthode, faute de routeur :
|
|
* - GET ?action=reset : abandon du rôle courant, puis redirection ;
|
|
* - POST : tentative de sélection, puis redirection vers
|
|
* l'accueil du rôle ou réaffichage avec $error ;
|
|
* - GET simple : affichage du formulaire.
|
|
*
|
|
* Le CSRF est vérifié à la main plutôt que par Csrf::assertPayload() : les
|
|
* données arrivent ici en formulaire classique ($_POST / $_GET) et non en
|
|
* JSON, et un échec doit produire une page HTML, pas une réponse JSON.
|
|
* Un jeton invalide sur le reset est ignoré EN SILENCE : l'utilisateur est
|
|
* simplement renvoyé au sélecteur, sans message d'échec inutile.
|
|
*/
|
|
public function index(): void
|
|
{
|
|
$access = new Access($this->pdo);
|
|
|
|
// Le changement de rôle modifie l'état de la session : il est protégé
|
|
// par le jeton CSRF au même titre que les écritures de l'API.
|
|
if (($_GET['action'] ?? '') === 'reset') {
|
|
$provided = (string) ($_GET['csrf_token'] ?? '');
|
|
if ($provided !== '' && hash_equals(Csrf::ensureToken(), $provided)) {
|
|
$access->clear();
|
|
}
|
|
header('Location: role.php', true, 302);
|
|
exit;
|
|
}
|
|
|
|
$error = '';
|
|
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
|
|
$expected = Csrf::ensureToken();
|
|
$provided = (string) ($_POST['csrf_token'] ?? '');
|
|
if ($provided === '' || !hash_equals($expected, $provided)) {
|
|
$error = 'La session a expiré. Rechargez la page et recommencez.';
|
|
} else {
|
|
try {
|
|
$role = strtoupper(trim((string) ($_POST['role'] ?? '')));
|
|
$agentId = filter_var($_POST['agent_id'] ?? null, FILTER_VALIDATE_INT) ?: null;
|
|
$structureId = filter_var($_POST['structure_id'] ?? null, FILTER_VALIDATE_INT) ?: null;
|
|
$access->select($role, $agentId ? (int) $agentId : null, $structureId ? (int) $structureId : null);
|
|
header('Location: ' . $access->homeUrl(), true, 302);
|
|
exit;
|
|
} catch (\DomainException $e) {
|
|
$error = $e->getMessage();
|
|
}
|
|
}
|
|
}
|
|
|
|
Security::sendHtmlHeaders();
|
|
$this->render('pages/role', [
|
|
'pageTitle' => 'Choisir votre accès',
|
|
'cspNonce' => Security::nonce(),
|
|
'csrfToken' => Csrf::ensureToken(),
|
|
'agents' => (new AgentModel($this->pdo))->allActive(),
|
|
'structures' => (new StructureModel($this->pdo))->allActive(),
|
|
'error' => $error,
|
|
'currentProfile' => $access->isConfigured() ? $access->profile() : null,
|
|
]);
|
|
}
|
|
}
|