From bd311da7115aba580d7a5620529c54f94e42fad4 Mon Sep 17 00:00:00 2001 From: Oleg Barykin Date: Wed, 11 Mar 2026 21:44:19 +0100 Subject: [PATCH] Connexion tocken, stripe 3D, tracking analytic --- connexion.php | 164 +-- connexion_back.php | 181 +++ includes/accepter_cookies.php | 55 +- index.php | 1 + js/tracking.js | 67 ++ plata/webhooks/stripe.php | 96 +- plata/webhooks/stripe_09_Mars_26.php | 1305 +++++++++++++++++++++ public/includes/header.php | 161 +-- public/lang/fr.php | 17 +- public/lang/ru.php | 15 + renvoyer_validat_mail.php | 292 +++++ traitement_inscription_client.php | 149 ++- traitement_inscription_client_back.php | 292 +++++ traitement_inscription_praticien.php | 206 ++-- traitement_inscription_praticien_back.php | 370 ++++++ validation.php | 137 +-- validation_back.php | 271 +++++ 17 files changed, 3324 insertions(+), 455 deletions(-) create mode 100644 connexion_back.php create mode 100644 js/tracking.js create mode 100644 plata/webhooks/stripe_09_Mars_26.php create mode 100644 renvoyer_validat_mail.php create mode 100644 traitement_inscription_client_back.php create mode 100644 traitement_inscription_praticien_back.php create mode 100644 validation_back.php diff --git a/connexion.php b/connexion.php index 17a8721..fe60525 100644 --- a/connexion.php +++ b/connexion.php @@ -5,62 +5,69 @@ $mot_de_passe = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $email = trim($_POST['email'] ?? ''); - $mot_de_passe = $_POST['mot_de_passe'] ?? ''; + $email = trim($_POST['email'] ?? ''); + $mot_de_passe = $_POST['mot_de_passe'] ?? ''; - if ($email === '' || $mot_de_passe === '') { - $erreur = $translations['erreur_champs_vides']; - } else { - // 1) On vérifie l’utilisateur - $stmt = $pdo->prepare("SELECT * FROM compte WHERE email = :email LIMIT 1"); - $stmt->execute(['email' => $email]); - $compte = $stmt->fetch(); + if ($email === '' || $mot_de_passe === '') { + $erreur = $translations['erreur_champs_vides']; + } else { + // 1) On vérifie l’utilisateur + $stmt = $pdo->prepare("SELECT * FROM compte WHERE email = :email LIMIT 1"); + $stmt->execute(['email' => $email]); + $compte = $stmt->fetch(); - if ($compte && password_verify($mot_de_passe, $compte['mot_de_passe'])) { - if (empty($compte['email_valide'])) { - $erreur = $translations['erreur_email_non_valide']; - } else { - session_regenerate_id(true); // ✅ anti session fixation - // 2) Connexion OK → on initialise la session - $_SESSION['compte_id'] = $compte['id']; - $_SESSION['role'] = strtolower(trim($compte['role'] ?? '')); - $_SESSION['reference_id'] = $compte['reference_id']; - $_SESSION['email'] = $compte['email']; - $_SESSION['last_activity'] = time(); + if ($compte && password_verify($mot_de_passe, $compte['mot_de_passe'])) { + if (empty($compte['email_valide'])) { + $_SESSION['pending_validation_resend'] = [ + 'compte_id' => (int) $compte['id'], + 'email' => (string) $compte['email'], + 'authorized' => true, + 'expires_at' => time() + 900, // 15 min + ]; - // 3) On gère la redirection prioritaire vers la page demandée - $next = $_POST['next'] ?? $_GET['next'] ?? ($_SESSION['redirect_to'] ?? null); - $next = next_safe($next); - if ($next) { - unset($_SESSION['redirect_to']); - header('Location: ' . $next); - exit; - } - - // 4) Sinon, fallback par rôle - if ($_SESSION['role'] === 'client') { - header('Location: /public/client/mes_rendez_vous.php');exit; - } elseif ($_SESSION['role'] === 'admin') { - header('Location: /admin/index.php');exit; - } elseif ($_SESSION['role'] === 'secretaire') { - header('Location: /public/cabinet/dashboard.php');exit; - } else { - header('Location: /public/praticien/agenda.php');exit; - } - } + $erreur = $translations['tvalidat_erreur_email_non_valide'] ?? $translations['erreur_email_non_valide'] ?? 'Veuillez valider votre adresse email avant de vous connecter.'; } else { - $erreur = $translations['erreur_identifiants']; + session_regenerate_id(true); // ✅ anti session fixation + // 2) Connexion OK → on initialise la session + $_SESSION['compte_id'] = $compte['id']; + $_SESSION['role'] = strtolower(trim($compte['role'] ?? '')); + $_SESSION['reference_id'] = $compte['reference_id']; + $_SESSION['email'] = $compte['email']; + $_SESSION['last_activity'] = time(); + + // 3) On gère la redirection prioritaire vers la page demandée + $next = $_POST['next'] ?? $_GET['next'] ?? ($_SESSION['redirect_to'] ?? null); + $next = next_safe($next); + if ($next) { + unset($_SESSION['redirect_to']); + header('Location: ' . $next); + exit; + } + + // 4) Sinon, fallback par rôle + if ($_SESSION['role'] === 'client') { + header('Location: /public/client/mes_rendez_vous.php');exit; + } elseif ($_SESSION['role'] === 'admin') { + header('Location: /admin/index.php');exit; + } elseif ($_SESSION['role'] === 'secretaire') { + header('Location: /public/cabinet/dashboard.php');exit; + } else { + header('Location: /public/praticien/agenda.php');exit; + } } + } else { + $erreur = $translations['erreur_identifiants']; } } + } ?> - <?php echo htmlspecialchars($translations['seo_connexion_title'], ENT_QUOTES, 'UTF-8')?> - - + <?php echo htmlspecialchars($translations['seo_connexion_title'], ENT_QUOTES, 'UTF-8') ?> + + @@ -68,10 +75,10 @@ - - - - + + + +
- +
-

+

-
+ = time() + && ! empty($email) + && ! empty($_SESSION['pending_validation_resend']['email']) + && hash_equals( + (string) $_SESSION['pending_validation_resend']['email'], + (string) $email + ); + ?> + +
+ placeholder="" + value="" required>
👁️ @@ -140,20 +172,20 @@ + value=""> - +
- | - + | +
- +
-

+

+ + + + diff --git a/includes/accepter_cookies.php b/includes/accepter_cookies.php index 0bf7cbd..b0b6147 100644 --- a/includes/accepter_cookies.php +++ b/includes/accepter_cookies.php @@ -1,13 +1,14 @@ - + @@ -16,15 +17,15 @@ function tt($k,$d=''){ global $t; return htmlspecialchars($t[$k]??$d, ENT_QUOTES (function(){ // Textes (prend ceux de tes fichiers de langue si présents) const TXT = { - title: , - desc: , - acceptAll: , - rejectAll: , - settings: , - necessary: , - analytics: , - personalization: , - save: , + title: , + desc: , + acceptAll: , + rejectAll: , + settings: , + necessary: , + analytics: , + personalization: , + save: , }; const SRCS = [ @@ -96,14 +97,8 @@ function tt($k,$d=''){ global $t; return htmlspecialchars($t[$k]??$d, ENT_QUOTES }, // Hooks - onConsent: ({cookie}) => { - if (CookieConsent.acceptedCategory('analytics')) enableAnalytics(); - }, - onChange: ({changedCategories}) => { - if (changedCategories.includes('analytics')) { - CookieConsent.acceptedCategory('analytics') ? enableAnalytics() : disableAnalytics(); - } - } + onConsent: ({cookie}) => {}, + onChange: ({changedCategories}) => {} }); // Lien pour rouvrir @@ -115,16 +110,6 @@ function tt($k,$d=''){ global $t; return htmlspecialchars($t[$k]??$d, ENT_QUOTES } - // Tes hooks perso (ex: activer GA seulement si analytics accepté) - window.enableAnalytics = function(){ - if (window.__gaLoaded) return; window.__gaLoaded = true; - // Exemple: - CookieConsent.loadScript('https://www.googletagmanager.com/gtag/js?id=G-XXXX').then(()=>{ - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments)}; gtag('js', new Date()); gtag('config','G-XXXX'); - }); - }; - window.disableAnalytics = function(){ /* éventuellement reset */ }; // Lance le chargement (local → CDN fallback) loadScriptSequential(0); diff --git a/index.php b/index.php index 8dd7fb8..72c9b1e 100644 --- a/index.php +++ b/index.php @@ -141,6 +141,7 @@ +
'; + + echo '
'; + if ($type === 'success') { + echo '
'; + } elseif ($type === 'info') { + echo '
+
+
'; + } else { + echo '
+
+
+
'; + } + echo '
'; + + echo '

' . $title . '

'; + echo '

' . $message . '

'; + + if ($cta_href) { + echo ''; + } + + echo '
'; + exit; +} + +if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { + ui_exit( + $translations['tvalidat_err_acces_titre'] ?? 'Accès incorrect', + $translations['tvalidat_err_acces_msg'] ?? 'Accès invalide.', + 405, + 'info', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); +} + +$email = trim((string) ($_POST['email'] ?? '')); + +if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['tvalidat_err_email_invalide'] ?? 'Email invalide.', + 400, + 'error', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); +} + +$pending = $_SESSION['pending_validation_resend'] ?? null; + +if ( + empty($pending['authorized']) || + empty($pending['compte_id']) || + empty($pending['email']) || + empty($pending['expires_at']) || + (int) $pending['expires_at'] < time() || + ! hash_equals((string) $pending['email'], $email) +) { + unset($_SESSION['pending_validation_resend']); + + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['tvalidat_err_action_non_autorisee'] ?? 'Action non autorisée. Merci de vous reconnecter.', + 403, + 'error', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); +} + +try { + $stmt = $pdo->prepare(" + SELECT id, email, role, reference_id, email_valide + FROM compte + WHERE id = :id AND email = :email + LIMIT 1 + "); + $stmt->execute([ + 'id' => (int) $pending['compte_id'], + 'email' => $email, + ]); + $compte = $stmt->fetch(PDO::FETCH_ASSOC); + + if (! $compte) { + unset($_SESSION['pending_validation_resend']); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['tvalidat_err_compte_introuvable'] ?? 'Compte introuvable.', + 404, + 'error', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); + } + + if (! empty($compte['email_valide'])) { + unset($_SESSION['pending_validation_resend']); + ui_exit( + $translations['tvalidat_info_titre'] ?? 'Information', + $translations['tvalidat_info_email_deja_valide'] ?? 'Votre adresse email est déjà validée.', + 200, + 'info', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); + } + + $prenom = ''; + $nom = ''; + + if (($compte['role'] ?? '') === 'praticien') { + $stmt = $pdo->prepare("SELECT prenom, nom FROM praticien WHERE id = :id LIMIT 1"); + $stmt->execute(['id' => (int) $compte['reference_id']]); + $profil = $stmt->fetch(PDO::FETCH_ASSOC); + } elseif (($compte['role'] ?? '') === 'client') { + $stmt = $pdo->prepare("SELECT prenom, nom FROM client WHERE id = :id LIMIT 1"); + $stmt->execute(['id' => (int) $compte['reference_id']]); + $profil = $stmt->fetch(PDO::FETCH_ASSOC); + } else { + $profil = false; + } + + if ($profil) { + $prenom = trim((string) ($profil['prenom'] ?? '')); + $nom = trim((string) ($profil['nom'] ?? '')); + } + + $token = bin2hex(random_bytes(32)); + $token_validation_expire = (new DateTime('+6 hours'))->format('Y-m-d H:i:s'); + $token_autologin = bin2hex(random_bytes(32)); + $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); + + $stmt = $pdo->prepare(" + UPDATE compte + SET token_validation = :token, + token_validation_expire = :token_expire, + token_autologin = :al, + token_autologin_expire = :al_exp + WHERE id = :id + LIMIT 1 + "); + $stmt->execute([ + 'token' => $token, + 'token_expire' => $token_validation_expire, + 'al' => $token_autologin, + 'al_exp' => $token_autologin_expire, + 'id' => (int) $compte['id'], + ]); + +} catch (Throwable $e) { + error_log('renvoyer_validat_mail.php - DB error: ' . $e->getMessage()); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_serveur'] ?? 'Erreur serveur.', + 500, + 'error', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); +} + +$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); +$validation_url_html = htmlspecialchars($validation_url, ENT_QUOTES, 'UTF-8'); + +$prenom_nom = trim($prenom . ' ' . $nom); +$to_name = $prenom_nom !== '' ? $prenom_nom : $email; + +$sujet = $translations['mail_valid_sujet'] ?? 'Validation de votre adresse email'; + +$html = " + + + + + + + + +
+
" . htmlspecialchars(sprintf($translations['mail_valid_bonjour_nom'] ?? 'Bonjour %s,', $to_name), ENT_QUOTES, 'UTF-8') . "
+

" . htmlspecialchars($translations['tvalidat_mail_intro'] ?? 'Voici un nouveau lien pour valider votre adresse email.', ENT_QUOTES, 'UTF-8') . "

+

" . htmlspecialchars($translations['mail_valid_action'] ?? 'Cliquez sur le bouton ci-dessous pour confirmer votre adresse.', ENT_QUOTES, 'UTF-8') . "

+ " . htmlspecialchars($translations['mail_valid_btn'] ?? 'Valider mon adresse email', ENT_QUOTES, 'UTF-8') . " + + +
+ +"; + +$text = strip_tags( + sprintf($translations['mail_valid_bonjour_nom'] ?? 'Bonjour %s,', $to_name) . "\n\n" . + ($translations['tvalidat_mail_intro'] ?? 'Voici un nouveau lien pour valider votre adresse email.') . "\n" . + ($translations['mail_valid_action'] ?? 'Cliquez sur le lien suivant pour confirmer votre adresse.') . "\n" . + $validation_url . "\n\n" . + ($translations['mail_valid_expire'] ?? 'Ce lien expire prochainement.') . "\n" . + ($translations['mail_valid_ignore'] ?? 'Si vous n\'êtes pas à l\'origine de cette demande, ignorez ce message.') . "\n\n" . + ($translations['mail_valid_signature'] ?? 'USLUGI.FR') +); + +list($sent, $err) = uslugi_mail( + $email, + $sujet, + $html, + $text, + ['to_name' => $to_name] +); + +unset($_SESSION['pending_validation_resend']); + +if (! $sent) { + error_log("Renvoi validation mail NOT sent to {$email}: {$err}"); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['tvalidat_err_envoi'] ?? 'Le mail n’a pas pu être envoyé. Merci de réessayer plus tard.', + 500, + 'error', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' + ); +} + +ui_exit( + $translations['tvalidat_success_titre'] ?? 'Email envoyé', + $translations['tvalidat_success_msg'] ?? 'Un nouveau lien de validation vient d’être envoyé à votre adresse email.', + 200, + 'success', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' +); diff --git a/traitement_inscription_client.php b/traitement_inscription_client.php index b565809..8c6d4ac 100644 --- a/traitement_inscription_client.php +++ b/traitement_inscription_client.php @@ -1,36 +1,37 @@ '; - echo ''.$title.''; + echo '' . $title . ''; echo '
'; // Icône - echo '
'; + echo '
'; if ($type === 'success') { - echo '
'; + echo '
'; } elseif ($type === 'info') { - echo '
-
+ echo '
+
'; } else { // error echo '
-
-
+
+
'; } echo '
'; - echo '

'.$title.'

'; - echo '

'.$message.'

'; + echo '

' . $title . '

'; + echo '

' . $message . '

'; if ($cta_href) { - echo ''; + echo ''; } echo '
'; @@ -76,7 +77,7 @@ function ui_exit(string $title, string $message, int $status = 400, string $type if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { ui_exit( $translations['err_acces_incorrect_titre'] ?? 'Accès incorrect', - $translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.", + $translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.", 405, 'info', 'inscription_client.php', @@ -105,7 +106,7 @@ if ($nom === '' || $prenom === '' || $email === '' || $mdp === '') { } // Email valide + pas d’injection CR/LF -if (!filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { +if (! filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_email_non_valide'] ?? 'Email invalide.', @@ -153,38 +154,60 @@ try { 'nom' => $nom, 'prenom' => $prenom, 'email' => $email, - 'accepted_cgu' => $accept_cgu + 'accepted_cgu' => $accept_cgu, ]); - $client_id = (int)$pdo->lastInsertId(); + $client_id = (int) $pdo->lastInsertId(); // 2) Créer compte - $mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT); - $token = bin2hex(random_bytes(32)); - $token_autologin = bin2hex(random_bytes(32)); - $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); - + $mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT); + $token = bin2hex(random_bytes(32)); + $token_validation_expire = (new DateTime('+6 hours'))->format('Y-m-d H:i:s'); + $token_autologin = bin2hex(random_bytes(32)); + $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); $stmt = $pdo->prepare(" - INSERT INTO compte (email, mot_de_passe, role, reference_id, token_validation, email_valide, token_autologin, token_autologin_expire) - VALUES (:email, :mdp, 'client', :ref_id, :token, 0, :al, :al_exp) + INSERT INTO compte ( + email, + mot_de_passe, + role, + reference_id, + token_validation, + token_validation_expire, + email_valide, + token_autologin, + token_autologin_expire + ) + VALUES ( + :email, + :mdp, + 'client', + :ref_id, + :token, + :token_expire, + 0, + :al, + :al_exp + ) "); -$stmt->execute([ - 'email' => $email, - 'mdp' => $mot_de_passe_hash, - 'ref_id' => $client_id, - 'token' => $token, - 'al' => $token_autologin, - 'al_exp' => $token_autologin_expire, -]); - - + $stmt->execute([ + 'email' => $email, + 'mdp' => $mot_de_passe_hash, + 'ref_id' => $client_id, + 'token' => $token, + 'token_expire' => $token_validation_expire, + 'al' => $token_autologin, + 'al_exp' => $token_autologin_expire, + ]); // Commit avant l’envoi du mail $pdo->commit(); } catch (Throwable $e) { - if ($pdo->inTransaction()) $pdo->rollBack(); - error_log('Inscription client - DB error: '.$e->getMessage()); + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + + error_log('Inscription client - DB error: ' . $e->getMessage()); ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_serveur'] ?? 'Erreur serveur.', @@ -197,7 +220,7 @@ $stmt->execute([ // ===================== // Email de validation // ===================== -$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); +$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); $validation_url_html = htmlspecialchars($validation_url, ENT_QUOTES, 'UTF-8'); $prenom_nom_safe = htmlspecialchars("$prenom $nom", ENT_QUOTES, 'UTF-8'); @@ -220,31 +243,31 @@ $html = "
-
".sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom_safe)."
-

".$translations['mail_valid_merci']."

-

".$translations['mail_valid_action']."

- ".$translations['mail_valid_btn']." +
" . sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom_safe) . "
+

" . $translations['mail_valid_merci'] . "

+

" . $translations['mail_valid_action'] . "

+ " . $translations['mail_valid_btn'] . "
"; $text = strip_tags( - sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom")."\n\n". - $translations['mail_valid_merci']."\n". - $translations['mail_valid_action']."\n". - ($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :')."\n". - $validation_url."\n\n". - $translations['mail_valid_expire']."\n". - $translations['mail_valid_ignore']."\n". + sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom") . "\n\n" . + $translations['mail_valid_merci'] . "\n" . + $translations['mail_valid_action'] . "\n" . + ($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :') . "\n" . + $validation_url . "\n\n" . + $translations['mail_valid_expire'] . "\n" . + $translations['mail_valid_ignore'] . "\n" . ($translations['mail_valid_signature'] ?? 'USLUGI.FR') ); @@ -256,7 +279,7 @@ list($sent, $err) = uslugi_mail( $text, ['to_name' => "$prenom $nom"] ); -if (!$sent) { +if (! $sent) { error_log("Validation mail NOT sent to $email : $err"); // On ne bloque pas l'utilisateur. } @@ -266,7 +289,7 @@ if (!$sent) { // ===================== echo ' -'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').' +' . htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8') . '
'; + + // Icône + echo '
'; + if ($type === 'success') { + echo '
'; + } elseif ($type === 'info') { + echo '
+
+
'; + } else { // error + echo '
+
+
+
'; + } + echo '
'; + + echo '

'.$title.'

'; + echo '

'.$message.'

'; + + if ($cta_href) { + echo ''; + } + + echo '
'; + exit; +} + +// ===================== +// Accès direct interdit +// ===================== +if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { + ui_exit( + $translations['err_acces_incorrect_titre'] ?? 'Accès incorrect', + $translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.", + 405, + 'info', + 'inscription_client.php', + $translations['retour_formulaire'] ?? 'Retour au formulaire' + ); +} + +// ===================== +// Données & vérifs +// ===================== +$nom = trim($_POST['nom'] ?? ''); +$prenom = trim($_POST['prenom'] ?? ''); +$email = trim($_POST['email'] ?? ''); +$mdp = $_POST['mot_de_passe'] ?? ''; + +// Champs obligatoires +if ($nom === '' || $prenom === '' || $email === '' || $mdp === '') { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_champs_obligatoires'] ?? 'Merci de remplir tous les champs obligatoires.', + 400, + 'error', + 'inscription_client.php', + $translations['retour_formulaire'] ?? 'Retour au formulaire' + ); +} + +// Email valide + pas d’injection CR/LF +if (!filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_email_non_valide'] ?? 'Email invalide.', + 400, + 'error', + 'inscription_client.php' + ); +} + +// Acceptation CGU +$accept_cgu = isset($_POST['accept_cgu']) ? 1 : 0; +if ($accept_cgu !== 1) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['cgu_obligatoire'] ?? 'Vous devez accepter les CGU.', + 400, + 'info', + 'inscription_client.php' + ); +} + +// ===================== +// Transaction (client + compte) +// ===================== +try { + $pdo->beginTransaction(); + + // Email déjà pris ? + $stmt = $pdo->prepare("SELECT id FROM compte WHERE email = :email LIMIT 1"); + $stmt->execute(['email' => $email]); + if ($stmt->fetch()) { + $pdo->rollBack(); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_email_deja_utilise'] ?? 'Cet email est déjà utilisé.', + 409, + 'error', + 'inscription_client.php' + ); + } + + // 1) Insert client + $stmt = $pdo->prepare("INSERT INTO client (nom, prenom, email, accepted_cgu) VALUES (:nom, :prenom, :email, :accepted_cgu)"); + $stmt->execute([ + 'nom' => $nom, + 'prenom' => $prenom, + 'email' => $email, + 'accepted_cgu' => $accept_cgu + ]); + $client_id = (int)$pdo->lastInsertId(); + + // 2) Créer compte + $mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT); + $token = bin2hex(random_bytes(32)); + $token_autologin = bin2hex(random_bytes(32)); + $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); + + + $stmt = $pdo->prepare(" + INSERT INTO compte (email, mot_de_passe, role, reference_id, token_validation, email_valide, token_autologin, token_autologin_expire) + VALUES (:email, :mdp, 'client', :ref_id, :token, 0, :al, :al_exp) +"); +$stmt->execute([ + 'email' => $email, + 'mdp' => $mot_de_passe_hash, + 'ref_id' => $client_id, + 'token' => $token, + 'al' => $token_autologin, + 'al_exp' => $token_autologin_expire, +]); + + + + // Commit avant l’envoi du mail + $pdo->commit(); + +} catch (Throwable $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + error_log('Inscription client - DB error: '.$e->getMessage()); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_serveur'] ?? 'Erreur serveur.', + 500, + 'error', + '/' + ); +} + +// ===================== +// Email de validation +// ===================== +$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); +$validation_url_html = htmlspecialchars($validation_url, ENT_QUOTES, 'UTF-8'); +$prenom_nom_safe = htmlspecialchars("$prenom $nom", ENT_QUOTES, 'UTF-8'); + +$sujet = $translations['mail_valid_sujet']; + +$html = " + + + + + + + + +
+
".sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom_safe)."
+

".$translations['mail_valid_merci']."

+

".$translations['mail_valid_action']."

+ ".$translations['mail_valid_btn']." + + +
+ +"; + +$text = strip_tags( + sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom")."\n\n". + $translations['mail_valid_merci']."\n". + $translations['mail_valid_action']."\n". + ($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :')."\n". + $validation_url."\n\n". + $translations['mail_valid_expire']."\n". + $translations['mail_valid_ignore']."\n". + ($translations['mail_valid_signature'] ?? 'USLUGI.FR') +); + +// Envoi du mail (non bloquant) +list($sent, $err) = uslugi_mail( + $email, + $sujet, + $html, + $text, + ['to_name' => "$prenom $nom"] +); +if (!$sent) { + error_log("Validation mail NOT sent to $email : $err"); + // On ne bloque pas l'utilisateur. +} + +// ===================== +// Écran final (succès) +// ===================== +echo ' + +'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').' + +
+
+
+
+

'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').'

+

'.htmlspecialchars($translations['insc_merci_ligne1'] ?? 'Votre compte a bien été créé.', ENT_QUOTES, 'UTF-8').'

+

'.htmlspecialchars($translations['insc_merci_ligne2'] ?? 'Vérifiez vos emails pour valider votre adresse.', ENT_QUOTES, 'UTF-8').'

+ +
+'; diff --git a/traitement_inscription_praticien.php b/traitement_inscription_praticien.php index a8de27f..511e38e 100644 --- a/traitement_inscription_praticien.php +++ b/traitement_inscription_praticien.php @@ -1,36 +1,37 @@ '; - echo ''.$title.''; + echo '' . $title . ''; echo '
'; // Icône - echo '
'; + echo '
'; if ($type === 'success') { - echo '
'; + echo '
'; } elseif ($type === 'info') { - echo '
-
+ echo '
+
'; } else { // error echo '
-
-
+
+
'; } echo '
'; - echo '

'.$title.'

'; - echo '

'.$message.'

'; + echo '

' . $title . '

'; + echo '

' . $message . '

'; if ($cta_href) { - echo ''; + echo ''; } echo '
'; @@ -76,7 +77,7 @@ function ui_exit(string $title, string $message, int $status = 400, string $type if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { ui_exit( $translations['err_acces_incorrect_titre'] ?? 'Accès incorrect', - $translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.", + $translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.", 405, 'info', 'inscription_praticien.php', @@ -106,7 +107,7 @@ if ($nom === '' || $prenom === '' || $email === '' || $mdp === '') { } // Email valide + pas d'injection d'entêtes -if (!filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { +if (! filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_email_non_valide'] ?? 'Email invalide.', @@ -128,20 +129,24 @@ if (strlen($siret) !== 14) { } // Luhn -function luhnCheck($num) { +function luhnCheck($num) +{ $arr = str_split(strrev($num)); $sum = 0; foreach ($arr as $i => $n) { - $n = (int)$n; + $n = (int) $n; if ($i % 2) { $n *= 2; - if ($n > 9) $n -= 9; + if ($n > 9) { + $n -= 9; + } + } $sum += $n; } return $sum % 10 === 0; } -if (!luhnCheck($siret)) { +if (! luhnCheck($siret)) { ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_siret_invalide'] ?? 'Numéro SIRET invalide (échec vérification).', @@ -199,66 +204,89 @@ try { } // 1) Préparer la date d'hier pour forcer l'expiration immédiate -$hier = date('Y-m-d', strtotime('-1 day')); + $hier = date('Y-m-d', strtotime('-1 day')); // 2) Créer le praticien avec les champs d'abonnement initialisés -$stmt = $pdo->prepare(" + $stmt = $pdo->prepare(" INSERT INTO praticien ( - nom, - prenom, - email, - siret, - accepted_cgu, - abonnement_id, + nom, + prenom, + email, + siret, + accepted_cgu, + abonnement_id, date_expiration ) VALUES ( - :nom, - :prenom, - :email, - :siret, - :accepted_cgu, - NULL, + :nom, + :prenom, + :email, + :siret, + :accepted_cgu, + NULL, :date_exp ) "); -$stmt->execute([ - 'nom' => $nom, - 'prenom' => $prenom, - 'email' => $email, - 'siret' => $siret, - 'accepted_cgu' => $accept_cgu, - 'date_exp' => $hier // On met hier pour que le script de vérification dise "C'est expiré !" -]); - $praticien_id = (int)$pdo->lastInsertId(); + $stmt->execute([ + 'nom' => $nom, + 'prenom' => $prenom, + 'email' => $email, + 'siret' => $siret, + 'accepted_cgu' => $accept_cgu, + 'date_exp' => $hier, // On met hier pour que le script de vérification dise "C'est expiré !" + ]); + $praticien_id = (int) $pdo->lastInsertId(); // 2) Créer le compte - $mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT); - $token = bin2hex(random_bytes(32)); - $token_autologin = bin2hex(random_bytes(32)); - $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); - + $mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT); + $token = bin2hex(random_bytes(32)); + $token_validation_expire = (new DateTime('+6 hours'))->format('Y-m-d H:i:s'); + $token_autologin = bin2hex(random_bytes(32)); + $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); $stmt = $pdo->prepare(" - INSERT INTO compte (email, mot_de_passe, role, reference_id, token_validation, email_valide, token_autologin, token_autologin_expire) - VALUES (:email, :mdp, 'praticien', :ref_id, :token, 0, :al, :al_exp) + INSERT INTO compte ( + email, + mot_de_passe, + role, + reference_id, + token_validation, + token_validation_expire, + email_valide, + token_autologin, + token_autologin_expire + ) + VALUES ( + :email, + :mdp, + 'praticien', + :ref_id, + :token, + :token_expire, + 0, + :al, + :al_exp + ) "); -$stmt->execute([ - 'email' => $email, - 'mdp' => $mot_de_passe_hash, - 'ref_id' => $praticien_id, - 'token' => $token, - 'al' => $token_autologin, - 'al_exp' => $token_autologin_expire, -]); - + $stmt->execute([ + 'email' => $email, + 'mdp' => $mot_de_passe_hash, + 'ref_id' => $praticien_id, + 'token' => $token, + 'token_expire' => $token_validation_expire, + 'al' => $token_autologin, + 'al_exp' => $token_autologin_expire, + ]); // Commit avant envoi mail $pdo->commit(); } catch (Throwable $e) { - if ($pdo->inTransaction()) $pdo->rollBack(); - error_log('Inscription praticien - DB error: '.$e->getMessage()); + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + + error_log('Inscription praticien - DB error: ' . $e->getMessage()); ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_serveur'] ?? 'Erreur serveur.', @@ -271,7 +299,7 @@ $stmt->execute([ // ===================== // Email de validation // ===================== -$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); +$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); $validation_url_html = htmlspecialchars($validation_url, ENT_QUOTES, 'UTF-8'); $prenom_nom = htmlspecialchars("$prenom $nom", ENT_QUOTES, 'UTF-8'); @@ -295,17 +323,17 @@ $html = "
-
".sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom)."
-

".$translations['mail_valid_merci_praticien']."

-

".$translations['mail_valid_action']."

- ".$translations['mail_valid_btn']." -
" . sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom) . "
+

" . $translations['mail_valid_merci_praticien'] . "

+

" . $translations['mail_valid_action'] . "

+ " . $translations['mail_valid_btn'] . " +
@@ -313,13 +341,13 @@ $html = " // Texte alternative $text = strip_tags( - sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom")."\n\n". - $translations['mail_valid_merci_praticien']."\n". - $translations['mail_valid_action']."\n". - ($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :')."\n". - $validation_url."\n\n". - $translations['mail_valid_expire']."\n". - $translations['mail_valid_ignore']."\n". + sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom") . "\n\n" . + $translations['mail_valid_merci_praticien'] . "\n" . + $translations['mail_valid_action'] . "\n" . + ($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :') . "\n" . + $validation_url . "\n\n" . + $translations['mail_valid_expire'] . "\n" . + $translations['mail_valid_ignore'] . "\n" . ($translations['mail_valid_signature'] ?? 'USLUGI.FR') ); @@ -334,7 +362,7 @@ list($sent, $err) = uslugi_mail( ] ); -if (!$sent) { +if (! $sent) { error_log("Validation mail NOT sent to $email : $err"); // On ne bloque pas l’utilisateur : il pourra redemander un mail. } @@ -344,7 +372,7 @@ if (!$sent) { // ===================== echo ' -'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').' +' . htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8') . '
'; + + // Icône + echo '
'; + if ($type === 'success') { + echo '
'; + } elseif ($type === 'info') { + echo '
+
+
'; + } else { // error + echo '
+
+
+
'; + } + echo '
'; + + echo '

'.$title.'

'; + echo '

'.$message.'

'; + + if ($cta_href) { + echo ''; + } + + echo '
'; + exit; +} + +// ===================== +// Sécurité de base +// ===================== +if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { + ui_exit( + $translations['err_acces_incorrect_titre'] ?? 'Accès incorrect', + $translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.", + 405, + 'info', + 'inscription_praticien.php', + $translations['retour_formulaire'] ?? 'Retour au formulaire' + ); +} + +// ===================== +// Récupération & checks +// ===================== +$nom = trim($_POST['nom'] ?? ''); +$prenom = trim($_POST['prenom'] ?? ''); +$email = trim($_POST['email'] ?? ''); +$mdp = $_POST['mot_de_passe'] ?? ''; +$siret = preg_replace('/\D/', '', $_POST['siret'] ?? ''); + +// Champs obligatoires +if ($nom === '' || $prenom === '' || $email === '' || $mdp === '') { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_champs_obligatoires'] ?? 'Merci de remplir tous les champs obligatoires.', + 400, + 'error', + 'inscription_praticien.php', + $translations['retour_formulaire'] ?? 'Retour au formulaire' + ); +} + +// Email valide + pas d'injection d'entêtes +if (!filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_email_non_valide'] ?? 'Email invalide.', + 400, + 'error', + 'inscription_praticien.php' + ); +} + +// SIRET longueur +if (strlen($siret) !== 14) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_siret_invalide'] ?? 'Numéro SIRET invalide (14 chiffres requis).', + 400, + 'error', + 'inscription_praticien.php' + ); +} + +// Luhn +function luhnCheck($num) { + $arr = str_split(strrev($num)); + $sum = 0; + foreach ($arr as $i => $n) { + $n = (int)$n; + if ($i % 2) { + $n *= 2; + if ($n > 9) $n -= 9; + } + $sum += $n; + } + return $sum % 10 === 0; +} +if (!luhnCheck($siret)) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_siret_invalide'] ?? 'Numéro SIRET invalide (échec vérification).', + 400, + 'error', + 'inscription_praticien.php' + ); +} + +// ===================== +// Transaction +// ===================== +try { + $pdo->beginTransaction(); + + // Email déjà utilisé ? + $stmt = $pdo->prepare("SELECT id FROM compte WHERE email = :email LIMIT 1"); + $stmt->execute(['email' => $email]); + if ($stmt->fetch()) { + $pdo->rollBack(); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_email_deja_utilise'] ?? 'Cet email est déjà utilisé.', + 409, + 'error', + 'inscription_praticien.php' + ); + } + + // SIRET déjà utilisé ? + $stmt = $pdo->prepare("SELECT id FROM praticien WHERE siret = :siret LIMIT 1"); + $stmt->execute(['siret' => $siret]); + if ($stmt->fetch()) { + $pdo->rollBack(); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_siret_deja_utilise'] ?? 'Ce numéro SIRET est déjà enregistré.', + 409, + 'error', + 'inscription_praticien.php' + ); + } + + // Acceptation CGU + $accept_cgu = isset($_POST['accept_cgu']) ? 1 : 0; + if ($accept_cgu !== 1) { + $pdo->rollBack(); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['cgu_obligatoire'] ?? 'Vous devez accepter les CGU.', + 400, + 'info', + 'inscription_praticien.php' + ); + } + + // 1) Préparer la date d'hier pour forcer l'expiration immédiate +$hier = date('Y-m-d', strtotime('-1 day')); + +// 2) Créer le praticien avec les champs d'abonnement initialisés +$stmt = $pdo->prepare(" + INSERT INTO praticien ( + nom, + prenom, + email, + siret, + accepted_cgu, + abonnement_id, + date_expiration + ) VALUES ( + :nom, + :prenom, + :email, + :siret, + :accepted_cgu, + NULL, + :date_exp + ) +"); + +$stmt->execute([ + 'nom' => $nom, + 'prenom' => $prenom, + 'email' => $email, + 'siret' => $siret, + 'accepted_cgu' => $accept_cgu, + 'date_exp' => $hier // On met hier pour que le script de vérification dise "C'est expiré !" +]); + $praticien_id = (int)$pdo->lastInsertId(); + + // 2) Créer le compte + $mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT); + $token = bin2hex(random_bytes(32)); + $token_autologin = bin2hex(random_bytes(32)); + $token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s'); + + + $stmt = $pdo->prepare(" + INSERT INTO compte (email, mot_de_passe, role, reference_id, token_validation, email_valide, token_autologin, token_autologin_expire) + VALUES (:email, :mdp, 'praticien', :ref_id, :token, 0, :al, :al_exp) +"); +$stmt->execute([ + 'email' => $email, + 'mdp' => $mot_de_passe_hash, + 'ref_id' => $praticien_id, + 'token' => $token, + 'al' => $token_autologin, + 'al_exp' => $token_autologin_expire, +]); + + + // Commit avant envoi mail + $pdo->commit(); + +} catch (Throwable $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + error_log('Inscription praticien - DB error: '.$e->getMessage()); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_serveur'] ?? 'Erreur serveur.', + 500, + 'error', + '/' + ); +} + +// ===================== +// Email de validation +// ===================== +$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin); +$validation_url_html = htmlspecialchars($validation_url, ENT_QUOTES, 'UTF-8'); +$prenom_nom = htmlspecialchars("$prenom $nom", ENT_QUOTES, 'UTF-8'); + +$sujet = $translations['mail_valid_sujet']; + +// Corps HTML mail +$html = " + + + + + + + + +
+
".sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom)."
+

".$translations['mail_valid_merci_praticien']."

+

".$translations['mail_valid_action']."

+ ".$translations['mail_valid_btn']." + + +
+ +"; + +// Texte alternative +$text = strip_tags( + sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom")."\n\n". + $translations['mail_valid_merci_praticien']."\n". + $translations['mail_valid_action']."\n". + ($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :')."\n". + $validation_url."\n\n". + $translations['mail_valid_expire']."\n". + $translations['mail_valid_ignore']."\n". + ($translations['mail_valid_signature'] ?? 'USLUGI.FR') +); + +// Envoi +list($sent, $err) = uslugi_mail( + $email, + $sujet, + $html, + $text, + [ + 'to_name' => "$prenom $nom", + ] +); + +if (!$sent) { + error_log("Validation mail NOT sent to $email : $err"); + // On ne bloque pas l’utilisateur : il pourra redemander un mail. +} + +// ===================== +// Écran final succès +// ===================== +echo ' + +'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').' + +
+
+
+
+

'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').'

+

'.htmlspecialchars($translations['insc_merci_ligne1'] ?? 'Votre compte a bien été créé.', ENT_QUOTES, 'UTF-8').'

+

'.htmlspecialchars($translations['insc_merci_ligne2'] ?? 'Vérifiez vos emails pour valider votre adresse.', ENT_QUOTES, 'UTF-8').'

+ +
+'; diff --git a/validation.php b/validation.php index f784603..8bb5d4c 100644 --- a/validation.php +++ b/validation.php @@ -1,36 +1,37 @@ '; - echo ''.$title.''; + echo '' . $title . ''; echo '
'; // Icône - echo '
'; + echo '
'; if ($type === 'success') { - echo '
'; + echo '
'; } elseif ($type === 'info') { - echo '
-
+ echo '
+
'; } else { // error echo '
-
-
+
+
'; } echo '
'; - echo '

'.$title.'

'; - echo '

'.$message.'

'; + echo '

' . $title . '

'; + echo '

' . $message . '

'; if ($cta_href) { - echo ''; + echo ''; } echo '
'; @@ -73,10 +74,10 @@ function ui_exit(string $title, string $message, int $status = 400, string $type // ===================== // 1) Token présent & valide // ===================== -$rawToken = trim((string)($_GET['token'] ?? '')); -$rawAL = trim((string)($_GET['al'] ?? '')); -if ($rawAL !== '' && !preg_match('/^[A-Fa-f0-9]{64}$/', $rawAL)) { - $rawAL = ''; // on ignore si format suspect +$rawToken = trim((string) ($_GET['token'] ?? '')); +$rawAL = trim((string) ($_GET['al'] ?? '')); +if ($rawAL !== '' && ! preg_match('/^[A-Fa-f0-9]{64}$/', $rawAL)) { + $rawAL = ''; // on ignore si format suspect } if ($rawToken === '') { @@ -90,7 +91,7 @@ if ($rawToken === '') { ); } // Optionnel : on vérifie le format attendu (bin2hex(32) => 64 hexdigits) -if (!preg_match('/^[A-Fa-f0-9]{64}$/', $rawToken)) { +if (! preg_match('/^[A-Fa-f0-9]{64}$/', $rawToken)) { ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_lien_invalide'] ?? 'Lien invalide.', @@ -108,7 +109,7 @@ $stmt = $pdo->prepare("SELECT * FROM compte WHERE token_validation = :token LIMI $stmt->execute(['token' => $rawToken]); $compte = $stmt->fetch(); -if (!$compte) { +if (! $compte) { ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_token_invalide'] ?? 'Token invalide.', @@ -123,56 +124,31 @@ if (!$compte) { // 3) Validité temporelle (6h) // ===================== try { - $created_at = new DateTime($compte['cree_le']); + if (empty($compte['token_validation_expire'])) { + throw new RuntimeException('token_validation_expire manquant'); + } + $expireAt = new DateTime((string) $compte['token_validation_expire']); } catch (Throwable $e) { - // Par prudence : si la date est illisible, on refuse ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', - $translations['err_lien_invalide'] ?? 'Lien invalide.', + $translations['tvalidat_err_lien_invalide'] ?? $translations['err_lien_invalide'] ?? 'Lien invalide.', 400, 'error', '/', $translations['retour_acceuil'] ?? 'Retour à l’accueil' ); } + $now = new DateTime(); -$diffH = ($now->getTimestamp() - $created_at->getTimestamp()) / 3600; - -if ($diffH > 6) { - // Supprimer les enregistrements expirés - try { - $pdo->beginTransaction(); - - $pdo->prepare("DELETE FROM compte WHERE id = :id")->execute(['id' => $compte['id']]); - - $ref_id = (int)$compte['reference_id']; - if (($compte['role'] ?? '') === 'praticien') { - $pdo->prepare("DELETE FROM praticien WHERE id = :id")->execute(['id' => $ref_id]); - } else { - $pdo->prepare("DELETE FROM client WHERE id = :id")->execute(['id' => $ref_id]); - } - - $pdo->commit(); - } catch (Throwable $e) { - if ($pdo->inTransaction()) $pdo->rollBack(); - error_log('Validation email - purge expirée échouée: '.$e->getMessage()); - ui_exit( - $translations['err_titre'] ?? 'Un problème est survenu', - $translations['err_serveur'] ?? 'Erreur serveur.', - 500, - 'error', - '/', - $translations['retour_acceuil'] ?? 'Retour à l’accueil' - ); - } +if ($now > $expireAt) { ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', - $translations['err_lien_expire'] ?? 'Le lien de validation a expiré.', + $translations['tvalidat_err_lien_expire'] ?? $translations['err_lien_expire'] ?? 'Le lien de validation a expiré.', 410, 'info', - '/', - $translations['retour_acceuil'] ?? 'Retour à l’accueil' + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' ); } @@ -180,10 +156,16 @@ if ($diffH > 6) { // 4) Valider l'email (maj compte) // ===================== try { - $stmt = $pdo->prepare("UPDATE compte SET email_valide = 1, token_validation = NULL WHERE id = :id"); + $stmt = $pdo->prepare(" + UPDATE compte + SET email_valide = 1, + token_validation = NULL, + token_validation_expire = NULL + WHERE id = :id +"); $stmt->execute(['id' => $compte['id']]); } catch (Throwable $e) { - error_log('Validation email - UPDATE échouée: '.$e->getMessage()); + error_log('Validation email - UPDATE échouée: ' . $e->getMessage()); ui_exit( $translations['err_titre'] ?? 'Un problème est survenu', $translations['err_serveur'] ?? 'Erreur serveur.', @@ -203,16 +185,16 @@ if ($rawAL !== '') { FROM compte WHERE id = :id LIMIT 1"); - $stmt->execute(['id' => (int)$compte['id']]); + $stmt->execute(['id' => (int) $compte['id']]); $c = $stmt->fetch(PDO::FETCH_ASSOC); - if ($c && !empty($c['token_autologin']) && hash_equals((string)$c['token_autologin'], $rawAL)) { + if ($c && ! empty($c['token_autologin']) && hash_equals((string) $c['token_autologin'], $rawAL)) { // Expiration (obligatoire) $expOk = false; - if (!empty($c['token_autologin_expire'])) { + if (! empty($c['token_autologin_expire'])) { try { - $exp = new DateTime((string)$c['token_autologin_expire']); + $exp = new DateTime((string) $c['token_autologin_expire']); $expOk = (new DateTime()) <= $exp; } catch (Throwable $e) { $expOk = false; @@ -224,23 +206,23 @@ if ($rawAL !== '') { $pdo->prepare("UPDATE compte SET token_autologin = NULL, token_autologin_expire = NULL WHERE id = :id") - ->execute(['id' => (int)$c['id']]); + ->execute(['id' => (int) $c['id']]); // Connexion session (même logique pour client/praticien) session_regenerate_id(true); - $_SESSION['compte_id'] = (int)$c['id']; // <-- IMPORTANT (secure.php l'exige) - $_SESSION['role'] = (string)$c['role']; - $_SESSION['reference_id'] = (int)$c['reference_id']; + $_SESSION['compte_id'] = (int) $c['id']; // <-- IMPORTANT (secure.php l'exige) + $_SESSION['role'] = (string) $c['role']; + $_SESSION['reference_id'] = (int) $c['reference_id']; if ($_SESSION['role'] === 'client') { - $_SESSION['client_id'] = (int)$c['reference_id']; - header('Location: /public/client/informations.php'); - exit; + $_SESSION['client_id'] = (int) $c['reference_id']; + header('Location: /public/client/informations.php'); + exit; } if ($_SESSION['role'] === 'praticien') { - $_SESSION['praticien_id'] = (int)$c['reference_id']; - header('Location: /public/praticien/informations.php'); - exit; + $_SESSION['praticien_id'] = (int) $c['reference_id']; + header('Location: /public/praticien/informations.php'); + exit; } // fallback si role inconnu @@ -250,13 +232,12 @@ if ($rawAL !== '') { } } - // ===================== // 5) Écran final (succès) // ===================== // Le texte de succès chez toi contient un lien HTML ; on affiche un message “texte” + un vrai bouton vers la connexion. $successMsg = isset($translations['success_validation_texte']) - ? strip_tags((string)$translations['success_validation_texte']) + ? strip_tags((string) $translations['success_validation_texte']) : 'Votre compte est confirmé. Vous pouvez maintenant vous connecter.'; ui_exit( @@ -267,5 +248,3 @@ ui_exit( '/connexion.php', $translations['btn_se_connecter'] ?? 'Se connecter' ); - - diff --git a/validation_back.php b/validation_back.php new file mode 100644 index 0000000..f784603 --- /dev/null +++ b/validation_back.php @@ -0,0 +1,271 @@ +'; + echo ''.$title.''; + echo '
'; + + // Icône + echo '
'; + if ($type === 'success') { + echo '
'; + } elseif ($type === 'info') { + echo '
+
+
'; + } else { // error + echo '
+
+
+
'; + } + echo '
'; + + echo '

'.$title.'

'; + echo '

'.$message.'

'; + + if ($cta_href) { + echo ''; + } + + echo '
'; + exit; +} + +// ===================== +// 1) Token présent & valide +// ===================== +$rawToken = trim((string)($_GET['token'] ?? '')); +$rawAL = trim((string)($_GET['al'] ?? '')); +if ($rawAL !== '' && !preg_match('/^[A-Fa-f0-9]{64}$/', $rawAL)) { + $rawAL = ''; // on ignore si format suspect +} + +if ($rawToken === '') { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_lien_invalide'] ?? 'Lien invalide.', + 400, + 'error', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); +} +// Optionnel : on vérifie le format attendu (bin2hex(32) => 64 hexdigits) +if (!preg_match('/^[A-Fa-f0-9]{64}$/', $rawToken)) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_lien_invalide'] ?? 'Lien invalide.', + 400, + 'error', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); +} + +// ===================== +// 2) Récupération du compte +// ===================== +$stmt = $pdo->prepare("SELECT * FROM compte WHERE token_validation = :token LIMIT 1"); +$stmt->execute(['token' => $rawToken]); +$compte = $stmt->fetch(); + +if (!$compte) { + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_token_invalide'] ?? 'Token invalide.', + 400, + 'error', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); +} + +// ===================== +// 3) Validité temporelle (6h) +// ===================== +try { + $created_at = new DateTime($compte['cree_le']); +} catch (Throwable $e) { + // Par prudence : si la date est illisible, on refuse + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_lien_invalide'] ?? 'Lien invalide.', + 400, + 'error', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); +} +$now = new DateTime(); +$diffH = ($now->getTimestamp() - $created_at->getTimestamp()) / 3600; + +if ($diffH > 6) { + // Supprimer les enregistrements expirés + try { + $pdo->beginTransaction(); + + $pdo->prepare("DELETE FROM compte WHERE id = :id")->execute(['id' => $compte['id']]); + + $ref_id = (int)$compte['reference_id']; + if (($compte['role'] ?? '') === 'praticien') { + $pdo->prepare("DELETE FROM praticien WHERE id = :id")->execute(['id' => $ref_id]); + } else { + $pdo->prepare("DELETE FROM client WHERE id = :id")->execute(['id' => $ref_id]); + } + + $pdo->commit(); + } catch (Throwable $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + error_log('Validation email - purge expirée échouée: '.$e->getMessage()); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_serveur'] ?? 'Erreur serveur.', + 500, + 'error', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); + } + + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_lien_expire'] ?? 'Le lien de validation a expiré.', + 410, + 'info', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); +} + +// ===================== +// 4) Valider l'email (maj compte) +// ===================== +try { + $stmt = $pdo->prepare("UPDATE compte SET email_valide = 1, token_validation = NULL WHERE id = :id"); + $stmt->execute(['id' => $compte['id']]); +} catch (Throwable $e) { + error_log('Validation email - UPDATE échouée: '.$e->getMessage()); + ui_exit( + $translations['err_titre'] ?? 'Un problème est survenu', + $translations['err_serveur'] ?? 'Erreur serveur.', + 500, + 'error', + '/', + $translations['retour_acceuil'] ?? 'Retour à l’accueil' + ); +} + +// ===================== +// 4bis) Auto-login (si al fourni et valide) +// ===================== +if ($rawAL !== '') { + // On recharge le compte pour lire token_autologin + expire (plus propre) + $stmt = $pdo->prepare("SELECT id, role, reference_id, token_autologin, token_autologin_expire + FROM compte + WHERE id = :id + LIMIT 1"); + $stmt->execute(['id' => (int)$compte['id']]); + $c = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($c && !empty($c['token_autologin']) && hash_equals((string)$c['token_autologin'], $rawAL)) { + + // Expiration (obligatoire) + $expOk = false; + if (!empty($c['token_autologin_expire'])) { + try { + $exp = new DateTime((string)$c['token_autologin_expire']); + $expOk = (new DateTime()) <= $exp; + } catch (Throwable $e) { + $expOk = false; + } + } + + if ($expOk) { + // One-time : on invalide le token autologin + $pdo->prepare("UPDATE compte + SET token_autologin = NULL, token_autologin_expire = NULL + WHERE id = :id") + ->execute(['id' => (int)$c['id']]); + + // Connexion session (même logique pour client/praticien) + session_regenerate_id(true); + $_SESSION['compte_id'] = (int)$c['id']; // <-- IMPORTANT (secure.php l'exige) + $_SESSION['role'] = (string)$c['role']; + $_SESSION['reference_id'] = (int)$c['reference_id']; + + if ($_SESSION['role'] === 'client') { + $_SESSION['client_id'] = (int)$c['reference_id']; + header('Location: /public/client/informations.php'); + exit; + } + if ($_SESSION['role'] === 'praticien') { + $_SESSION['praticien_id'] = (int)$c['reference_id']; + header('Location: /public/praticien/informations.php'); + exit; + } + + // fallback si role inconnu + header('Location: /connexion.php'); + exit; + } + } +} + + +// ===================== +// 5) Écran final (succès) +// ===================== +// Le texte de succès chez toi contient un lien HTML ; on affiche un message “texte” + un vrai bouton vers la connexion. +$successMsg = isset($translations['success_validation_texte']) + ? strip_tags((string)$translations['success_validation_texte']) + : 'Votre compte est confirmé. Vous pouvez maintenant vous connecter.'; + +ui_exit( + $translations['success_validation_titre'] ?? 'Compte confirmé !', + $successMsg, + 200, + 'success', + '/connexion.php', + $translations['btn_se_connecter'] ?? 'Se connecter' +); + +