V 1.003 Accès par MIRADOR sur deux environnements

This commit is contained in:
2026-08-30 15:53:07 +00:00
parent 64cc133b41
commit 00c58fb125
4 changed files with 150 additions and 79 deletions
+74 -37
View File
@@ -1,17 +1,24 @@
<?php
/**
* ASPIC — API d'authentification (aspic.php, anciennement login.php —
* renommé le 21 août 2026 à la demande de Jean-Marc, purement cosmétique,
* le contenu et le comportement ne changent pas)
* ASPIC — API d'authentification (aspic.php)
* ---------------------------------------------------------------------------
* Vérifie un couple identifiant/mot de passe contre ASP_Users et répond en
* JSON. Ne fait aucune gestion de compteur d'essais ni de verrouillage de
* compte : ce comportement (2 essais puis sortie, voir journal ASPIC section
* 8) est géré côté front (identification.php), application strictement
* interne, pas de mécanisme de bannissement d'IP prévu.
* ⚠️ Authentification centralisée via MIRADOR (30 août 2026, voir journal de
* développement). L'identifiant n'est plus USR_Code (3 caractères) mais
* MIR_Email. La vérification se fait contre MIR_Users, dans la base MIRADOR
* (voir getRouterConnection() dans db.php), qui indique en retour la base de
* travail propre à cet utilisateur (MIR_ServeurHost / MIR_ServeurPort /
* MIR_BaseNom) ainsi que son code court (MIR_Code, qui reprend le rôle
* jusqu'ici tenu par USR_Code — REA_CodeUser/OPE_CodeUser, jointure avec
* ASP_Users de la base cible pour les préférences/avatar).
*
* ASP_Users.USR_MDP n'est plus consulté ni comparé : le mot de passe est
* désormais vérifié une seule fois, ici, contre MIR_MDPHash (hash bcrypt,
* password_verify()) — la colonne USR_MDP peut être retirée de chaque base
* (ASPIC, MS-DEV, MS-TST...) quand Jean-Marc le souhaite, elle est devenue
* un vestige.
*
* Requête attendue : POST, corps JSON { "identifiant": "...", "motdepasse": "..." }
* (identifiant = USR_Code, 3 caractères — pas USR_Nom, voir journal section 2).
* (identifiant = adresse mail, MIR_Email).
*
* Réponses :
* 200 { "success": true, "user": { "USR_Code": "...", "USR_Nom": "..." } }
@@ -20,13 +27,17 @@
* 405 { "success": false, "error": "Méthode non autorisée" } (autre que POST)
* 500 { "success": false, "error": "..." } (connexion base impossible)
*
* ⚠️ Session utilisateur intégrée le 24 août 2026 (voir journal, sections 14
* et 15) : en cas d'identification réussie, une session PHP native est
* ouverte (`session_start()` + `$_SESSION['USR_Code']`), lue ensuite par
* contacts.php/professions.php/statuts.php/realisations.php pour autoriser
* leurs appels. `session_start()` doit rester la toute première instruction
* du fichier, avant tout `header()`/`echo` — sinon PHP refuse d'envoyer les
* cookies de session.
* Session utilisateur (voir journal, sections 14-15, mise à jour le 30 août
* 2026 pour MIRADOR) : en cas d'identification réussie, une session PHP
* native est ouverte avec $_SESSION['USR_Code'] (= MIR_Code) et les
* coordonnées de la base de travail cible ($_SESSION['ASPIC_DB_HOST'] /
* ['ASPIC_DB_PORT'] / ['ASPIC_DB_NAME']) — lues ensuite par getDbConnection()
* (db.php) à chaque appel de contacts.php / professions.php / statuts.php /
* realisations.php / preferences.php, sans qu'aucun de ces fichiers n'ait
* besoin d'être modifié : c'est getDbConnection() seule qui route désormais
* vers la bonne base selon la session en cours. `session_start()` doit
* rester la toute première instruction de ce fichier, avant tout
* `header()`/`echo` — sinon PHP refuse d'envoyer les cookies de session.
*/
session_start();
@@ -50,28 +61,32 @@ if ($identifiant === '' || $motdepasse === '') {
}
try {
$pdo = getDbConnection();
$pdoRouter = getRouterConnection();
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Connexion base de données impossible']);
echo json_encode(['success' => false, 'error' => 'Connexion à MIRADOR impossible']);
exit;
}
// USR_Code est un code court (3 caractères) conventionnellement saisi en
// majuscules ; comparaison insensible à la casse sur l'identifiant, comme
// dans le prototype front (voir ASPIC_ecran_identification.html,
// trouverUtilisateurDemo). Le mot de passe, lui, reste comparé strictement
// (sensible à la casse).
$stmt = $pdo->prepare('SELECT USR_Code, USR_Nom, USR_MDP FROM ASP_Users WHERE UPPER(USR_Code) = UPPER(?)');
// Comparaison directe sur l'email : la collation utf8mb4_0900_ai_ci déjà en
// place sur MIRADOR (comme sur les autres bases ASPIC) est nativement
// insensible à la casse et aux accents, pas besoin d'UPPER() ici.
$stmt = $pdoRouter->prepare(
'SELECT MIR_Code, MIR_MDPHash, MIR_ServeurHost, MIR_ServeurPort, MIR_BaseNom
FROM MIR_Users WHERE MIR_Email = ? AND MIR_Actif = 1'
);
$stmt->execute([$identifiant]);
$user = $stmt->fetch();
$routage = $stmt->fetch();
// hash_equals() plutôt que === : comparaison en temps constant, pour ne pas
// laisser une attaque par mesure de timing déduire des informations sur le
// mot de passe stocké. Mot de passe en clair (USR_MDP) assumé consciemment
// pour cette application interne (voir journal section 2) — cette précaution
// est un geste peu coûteux, pas une garantie de sécurité forte sur ce point.
if (!$user || !hash_equals($user['USR_MDP'], $motdepasse)) {
// Hash factice comparé même quand l'email est inconnu (password_verify()
// tourne alors quand même) : évite qu'une différence de temps de réponse
// laisse deviner qu'une adresse n'existe pas dans MIRADOR. Valeur sans
// signification, générée une fois pour toutes, jamais utilisée comme vrai
// mot de passe.
const HASH_FACTICE = '$2y$12$wni9cLq0BKttPzU0xAPM9uX/HMzu9UJhlOMj32H6/wLmkjlNfZ1cm';
$hashAVerifier = $routage['MIR_MDPHash'] ?? HASH_FACTICE;
if (!$routage || !password_verify($motdepasse, $hashAVerifier)) {
// Message volontairement générique : ne pas révéler si c'est l'identifiant
// ou le mot de passe qui est en cause. Le compteur d'essais / la sortie au
// 3e échec sont gérés côté front, pas ici (voir en-tête de ce fichier).
@@ -79,15 +94,37 @@ if (!$user || !hash_equals($user['USR_MDP'], $motdepasse)) {
exit;
}
// Session ouverte : les endpoints suivants (contacts.php, professions.php,
// statuts.php, realisations.php) exigent désormais $_SESSION['USR_Code']
// (voir journal, section 15).
$_SESSION['USR_Code'] = $user['USR_Code'];
// Coordonnées de la base de travail propre à cet utilisateur : posées en
// session pour que getDbConnection() (db.php) route tous les prochains
// appels de CETTE session vers la bonne base, sans qu'il faille les
// repasser en paramètre à chaque endpoint.
$_SESSION['USR_Code'] = $routage['MIR_Code'];
$_SESSION['ASPIC_DB_HOST'] = $routage['MIR_ServeurHost'];
$_SESSION['ASPIC_DB_PORT'] = (int) $routage['MIR_ServeurPort'];
$_SESSION['ASPIC_DB_NAME'] = $routage['MIR_BaseNom'];
// Nom affiché : toujours porté par ASP_Users de la base cible (MIRADOR ne
// connaît que l'email/code/mot de passe/routage, pas le nom complet). Un
// échec de connexion à cette base cible est remonté explicitement — plutôt
// que de laisser entrer un utilisateur dans une base injoignable, ce qui
// ferait échouer silencieusement tous les écrans suivants.
try {
$pdoCible = getDbConnection();
} catch (PDOException $e) {
session_unset();
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Connexion à la base de travail impossible']);
exit;
}
$stmtNom = $pdoCible->prepare('SELECT USR_Nom FROM ASP_Users WHERE USR_Code = ?');
$stmtNom->execute([$routage['MIR_Code']]);
$nom = $stmtNom->fetchColumn();
echo json_encode([
'success' => true,
'user' => [
'USR_Code' => $user['USR_Code'],
'USR_Nom' => $user['USR_Nom'],
'USR_Code' => $routage['MIR_Code'],
'USR_Nom' => $nom !== false ? $nom : null,
],
]);