diff --git a/config/definitions.php b/config/definitions.php index 495b03a..8c18f8e 100644 --- a/config/definitions.php +++ b/config/definitions.php @@ -44,12 +44,6 @@ define("NIVEAU_ACCES_MINI_EDITEUR", 50); define("NIVEAU_ACCES_MAXI", 99); -//===================================================================== -// Types d'utilisateurs : -define("TYPE_UTILISATEUR_INCONNU", 0); -define("TYPE_UTILISATEUR_PARTICULIER", 1); -define("TYPE_UTILISATEUR_PROFESSIONNEL", 2); - //===================================================================== // Informations générales : @@ -61,12 +55,6 @@ define("SOCIETE", "Koriolan"); define("ANNEE_CREATION", 2019); -//===================================================================== -// Paramètres de traitement des données, maps... -define ("DATE_SEUIL_1", 90); -define ("DATE_SEUIL_2", 180); -define ("CLE_PRIMAIRE_BATEAU_DEMO", 25); // Bateau "DEMO". Les données de ce bateau sont décalées dans le temps chaque matin à 00hxx. - //===================================================================== // Répertoires et fichiers : @@ -76,39 +64,16 @@ if (PRODUCTION) { define("REPERTOIRE_BATEAUX", REPERTOIRE_HOME."/bateaux"); // Pas de '/' de terminaison define("REPERTOIRE_BATEAUX_ARCHIVES", REPERTOIRE_HOME."/bateaux_archives"); // Pas de '/' de terminaison - define("REPERTOIRE_FICHIERS_CRON", REPERTOIRE_HOME."/cron/prod"); // Pas de '/' de terminaison - define("REPERTOIRE_LOGS", REPERTOIRE_HOME."/logs/prod"); // Pas de '/' de terminaison - define("NB_JOURS_AVANT_SUPPRESSION_FICHIERS_ARCHIVES", 90); - define("NB_JOURS_AVANT_SUPPRESSION_FICHIERS_ANOMALIES", 180); - define("NB_JOURS_AVANT_SUPPRESSION_FICHIERS_LOGS", 90); } else { define("REPERTOIRE_BATEAUX", REPERTOIRE_HOME."/bateaux_dev"); // Pas de '/' de terminaison define("REPERTOIRE_BATEAUX_ARCHIVES", REPERTOIRE_HOME."/bateaux_dev_archives"); // Pas de '/' de terminaison - define("REPERTOIRE_FICHIERS_CRON", REPERTOIRE_HOME."/cron/dev"); // Pas de '/' de terminaison - define("REPERTOIRE_LOGS", REPERTOIRE_HOME."/logs/dev"); // Pas de '/' de terminaison - define("NB_JOURS_AVANT_SUPPRESSION_FICHIERS_ARCHIVES", 30); - define("NB_JOURS_AVANT_SUPPRESSION_FICHIERS_ANOMALIES", 30); - define("NB_JOURS_AVANT_SUPPRESSION_FICHIERS_LOGS", 30); } define("REPERTOIRE_VERSIONS", REPERTOIRE_HOME."/versions"); // Pas de '/' de terminaison define("REPERTOIRE_VERSIONS_SHIPHEART", REPERTOIRE_VERSIONS.'/shipheart'); // Pas de '/' de terminaison -define("REPERTOIRE_LOGS_UPLOADS", REPERTOIRE_LOGS."/uploads"); // Pas de '/' de terminaison -define("REPERTOIRE_LOGS_SYNTHESE", REPERTOIRE_LOGS."/synthese"); // Pas de '/' de terminaison - -define("PREFIXE_REPERTOIRE_BATEAU_ARCHIVES", "bateau_"); // La clé primaire du bateau dans TB_Bateaux est ajouté pour former le nom du répertoire du bateau dans 'bateaux_archives' (ou 'bateaux_dev_archives' pour DEV). - -define("SOUS_REPERTOIRE_BATEAU_ANOMALIES", "uploads_anomalies"); // Pas de '/' de terminaison -define("SOUS_REPERTOIRE_BATEAU_ARCHIVES", "uploads_ok"); // Pas de '/' de terminaison -define("SOUS_REPERTOIRE_BATEAU_HISTORIQUE_BDD", "bdd_historique"); // Pas de '/' de terminaison -define("SOUS_REPERTOIRE_BATEAU_TEMP", "temp"); // Pas de '/' de terminaison - - -define("PREFIXE_FICHIER_SAUVEGARDE_BDD_BATEAU", "sauvegarde_bdd_"); // Le nom de la base de données est ensuite ajouté à la suite, avant le marqueur SQL. Par exemple 'sauvegarde_bdd_Bateau_22.sql' - //===================================================================== // Types de fichiers : @@ -159,20 +124,6 @@ define("ETAT_CARTE_EN_PANNE", 2); define("ETAT_CARTE_REBUT", 3); -//===================================================================== -// Types et Sous_types d'historique : -// A REVOIR COMPLETEMENT DEPUIS LE FICHIER 'gestion_bdd.h' de l'appli en C -define ("SYSTEME", 1000); -define ("SYSTEME_REINIT", 1001); // Réinitialisation de l'application -define ("SYSTEME_ERREUR_MYSQL", 1002); -define ("SYSTEME_CONNEXION_USER", 1003); -define ("SYSTEME_REINIT_MDP_USER", 1004); -define ("SYSTEME_REBOOT", 1005); // Reboot du système -define ("SYSTEME_ARRET", 1006); // Arrêt du système (Shutdown -h) -define ("SYSTEME_REINIT_BOX", 1007); -define ("SYSTEME_TIME_OUT_CONNEXION", 1008); -define ("SYSTEME_REINIT_TEIOT", 1009); -define ("SYSTEME_DECONNEXION_USER", 1010); diff --git a/config/production.php b/config/production.php new file mode 100644 index 0000000..9364849 --- /dev/null +++ b/config/production.php @@ -0,0 +1,7 @@ +.ship2net.fr/_auth?ticket=... (routeur Traefik dédié, SANS ForwardAuth), juste après le +// clic sur "Live" dans le portail. Consomme le ticket (60 s, usage unique, lié au bateau), crée une session et pose +// le cookie HttpOnly propre au sous-domaine du bateau, puis renvoie vers l'application. +//************************************************************************************************************ +require_once(__DIR__."/commun.php"); + +$id_bateau = Bateau_depuis_hote($_SERVER['HTTP_HOST'] ?? ''); +$ticket = $_GET['ticket'] ?? ''; +if ($id_bateau == 0 || !is_string($ticket) || preg_match('/^[0-9a-f]{64}$/', $ticket) !== 1) +{ + Page_refus(400, "Lien Live invalide", "Ce lien n'est pas valide. Ouvrez le Live depuis la page du bateau, sur le portail."); +} + +try +{ + $db = Connexion_live(); + + // Purge des tickets et sessions périmés depuis plus d'un jour (ici : appelé rarement, à chaque ouverture de session) : + $db->query("DELETE FROM TB_Tickets_live WHERE Expiration < NOW() - INTERVAL 1 DAY"); + $db->query("DELETE FROM TB_Sessions_live WHERE Expiration < NOW() - INTERVAL 1 DAY"); + + // Consommation du ticket : toutes les conditions DANS la mise à jour (usage unique, même en cas de requêtes simultanées) : + $empreinte_ticket = hash('sha256', $ticket); + $stmt = $db->prepare("UPDATE TB_Tickets_live SET Date_utilisation=NOW() + WHERE Empreinte=? AND ID_Bateau=? AND Date_utilisation IS NULL AND Expiration > NOW()"); + $stmt->bind_param('si', $empreinte_ticket, $id_bateau); + $stmt->execute(); + $consomme = ($stmt->affected_rows == 1); + $stmt->close(); + if (!$consomme) Page_refus(403, "Lien Live expiré", "Ce lien a expiré ou a déjà été utilisé. Ouvrez à nouveau le Live depuis la page du bateau, sur le portail."); + + $stmt = $db->prepare("SELECT ID_Shipmember FROM TB_Tickets_live WHERE Empreinte=?"); + $stmt->bind_param('s', $empreinte_ticket); + $stmt->execute(); + $id_shipmember = (int)$stmt->get_result()->fetch_row()[0]; + $stmt->close(); + + if (!Droit_live($db, $id_shipmember, $id_bateau)) Page_refus(403, "Accès refusé", "Vous n'avez pas (ou plus) accès à ce bateau."); + + // Durée de session (TB_Configuration, heures) : + $duree = DUREE_SESSION_LIVE_DEFAUT; + $resultat = $db->query("SELECT Valeur FROM TB_Configuration WHERE Nom='Duree_session_live' AND Validite=1"); + if ($ligne = $resultat->fetch_row()) $duree = max(1, (int)$ligne[0]); + + // Création de la session : seule l'empreinte de la valeur du cookie est enregistrée : + $valeur_cookie = bin2hex(random_bytes(32)); + $empreinte_session = hash('sha256', $valeur_cookie); + $stmt = $db->prepare("INSERT INTO TB_Sessions_live (Empreinte, ID_Shipmember, ID_Bateau, Expiration) VALUES (?, ?, ?, NOW() + INTERVAL ? HOUR)"); + $stmt->bind_param('siii', $empreinte_session, $id_shipmember, $id_bateau, $duree); + $stmt->execute(); + $stmt->close(); + $db->close(); +} +catch (Throwable $e) +{ + error_log("live/auth.php : ".$e->getMessage()); + Page_refus(503, "Service momentanément indisponible", "Le Live ne peut pas être ouvert pour l'instant. Réessayez dans quelques instants."); +} + +// Cookie propre à CE sous-domaine (pas d'attribut domain), inaccessible au code de la page (HttpOnly) : +setcookie(NOM_COOKIE_LIVE, $valeur_cookie, [ + 'expires' => time() + $duree * 3600, + 'path' => '/', + 'secure' => true, + 'httponly' => true, + 'samesite' => 'Lax', +]); +header('Cache-Control: no-store'); +header('Location: /', true, 303); // L'URL du ticket disparaît de la barre d'adresse +exit(); diff --git a/live/commun.php b/live/commun.php new file mode 100644 index 0000000..bc6754a --- /dev/null +++ b/live/commun.php @@ -0,0 +1,81 @@ +.ship2net.fr). +// Reçoit : Le nom d'hôte +// Retour : Le numéro (entier > 0), ou 0 si le nom ne correspond pas +//************************************************************************************************************ +function Bateau_depuis_hote($hote) +{ + $hote = strtolower(preg_replace('/:\d+$/', '', trim((string)$hote))); // sans port éventuel + if (preg_match('/^b([1-9][0-9]{0,9})\.'.preg_quote(URL_SITE, '/').'$/', $hote, $m) !== 1) return 0; + return (int)$m[1]; +} + + +//************************************************************************************************************ +// Fonction : Connexion à la base principale (mêmes paramètres que le site). +// Retour : L'objet mysqli (exception en cas d'échec) +//************************************************************************************************************ +function Connexion_live() +{ + return new mysqli(BDD_HOST, BDD_USER, BDD_PASSWORD, BDD_DATABASE); +} + + +//************************************************************************************************************ +// Fonction : Page d'explication renvoyée au navigateur (ticket invalide, session expirée...), avec un lien vers le +// portail. Le code HTTP est fourni par l'appelant. +//************************************************************************************************************ +function Page_refus($code_http, $titre, $texte) +{ + http_response_code($code_http); + header('Content-Type: text/html; charset=UTF-8'); + header('Cache-Control: no-store'); + echo '
' + .''.htmlspecialchars($texte).'
' + .''.htmlspecialchars("Retour au portail Ship2net").''; + exit(); +} + + +//************************************************************************************************************ +// Fonction : Le ShipMember a-t-il droit au Live de ce bateau EN CE MOMENT ? Compte valide, et : administrateur, ou +// accès 'actif' non expiré au bateau. Appelée à l'ouverture de la session ET à chaque requête : un accès +// suspendu ou révoqué depuis le portail coupe le Live immédiatement. +// Retour : true si autorisé +//************************************************************************************************************ +function Droit_live($db, $id_shipmember, $id_bateau) +{ + $stmt = $db->prepare("SELECT 1 FROM TB_Shipmembers M + WHERE M.ID=? AND M.Validite=1 + AND (M.Niveau_acces >= ? + OR EXISTS (SELECT 1 FROM TB_Lien_bateau_shipmember L + WHERE L.ID_Shipmember=M.ID AND L.ID_Bateau=? AND L.Statut='actif' AND L.Fin_validite >= NOW()))"); + $niveau_admin = NIVEAU_ACCES_MINI_ADMINISTRATEUR; + $stmt->bind_param('iii', $id_shipmember, $niveau_admin, $id_bateau); + $stmt->execute(); + $autorise = ($stmt->get_result()->num_rows == 1); + $stmt->close(); + return $autorise; +} diff --git a/live/verifier.php b/live/verifier.php new file mode 100644 index 0000000..d155cc4 --- /dev/null +++ b/live/verifier.php @@ -0,0 +1,52 @@ + 0 && is_string($valeur_cookie) && preg_match('/^[0-9a-f]{64}$/', $valeur_cookie) === 1) +{ + try + { + $db = Connexion_live(); + $empreinte = hash('sha256', $valeur_cookie); + $stmt = $db->prepare("SELECT ID_Shipmember FROM TB_Sessions_live WHERE Empreinte=? AND ID_Bateau=? AND Expiration > NOW()"); + $stmt->bind_param('si', $empreinte, $id_bateau); + $stmt->execute(); + $ligne = $stmt->get_result()->fetch_row(); + $stmt->close(); + if ($ligne) $autorise = Droit_live($db, (int)$ligne[0], $id_bateau); + $db->close(); + } + catch (Throwable $e) + { + error_log("live/verifier.php : ".$e->getMessage()); + $autorise = false; + } +} + +if ($autorise) +{ + http_response_code(200); + exit(); +} + +// Refus : page explicative pour une navigation (le navigateur affiche une page), réponse brève pour l'API (appels du wasm) : +$accept = $_SERVER['HTTP_ACCEPT'] ?? ''; +if (strpos($accept, 'text/html') !== false) +{ + Page_refus(401, "Session Live expirée", "Votre session Live a expiré, ou votre accès à ce bateau a changé. Ouvrez à nouveau le Live depuis la page du bateau, sur le portail."); +} +http_response_code(401); +header('Content-Type: text/plain; charset=UTF-8'); +header('Cache-Control: no-store'); +echo "Session Live absente ou expirée"; diff --git a/sql/live_tickets_sessions.sql b/sql/live_tickets_sessions.sql new file mode 100644 index 0000000..5b5d697 --- /dev/null +++ b/sql/live_tickets_sessions.sql @@ -0,0 +1,47 @@ +-- ============================================================================ +-- Live distant (lot 2, synthèse §49) : tickets et sessions d'accès au wasm d'un bateau via b