238 lines
9.2 KiB
PHP
238 lines
9.2 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap_global.php';
|
||
|
||
// Helpers
|
||
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||
function dt_to_ts(?string $s): int { return $s ? strtotime($s) ?: 0 : 0; }
|
||
|
||
// ------------------------------------------------------------
|
||
// 1) Arrivée par lien GET ?token=...&email=...
|
||
// ------------------------------------------------------------
|
||
$erreur = '';
|
||
$success = '';
|
||
|
||
// Si on arrive via le lien d'activation
|
||
if (isset($_GET['token'], $_GET['email'])) {
|
||
$token = trim((string)$_GET['token']);
|
||
$email = strtolower(trim((string)$_GET['email']));
|
||
|
||
if ($token === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||
$erreur = $translations['err_lien_invalide'] ?? 'Lien invalide.';
|
||
} else {
|
||
// Vérifier que le token/email existent et sont dans la fenêtre 24h
|
||
$sql = "SELECT id, cree_le, modifie_le, token_validation
|
||
FROM compte
|
||
WHERE email = :email AND token_validation = :token
|
||
LIMIT 1";
|
||
$stmt = $pdo->prepare($sql);
|
||
$stmt->execute([':email' => $email, ':token' => $token]);
|
||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$row) {
|
||
$erreur = $translations['err_lien_invalide'] ?? 'Lien invalide.';
|
||
} else {
|
||
// Fenêtre de validité 24h
|
||
$t0 = max(dt_to_ts($row['modifie_le'] ?? null), dt_to_ts($row['cree_le'] ?? null));
|
||
$ageH = (time() - $t0) / 3600;
|
||
|
||
if ($ageH > 24) {
|
||
$erreur = $translations['err_lien_expire'] ?? 'Le lien a expiré. Demandez un nouvel envoi.';
|
||
} else {
|
||
// On autorise la page de changement de mot de passe via session
|
||
$_SESSION['reset_email'] = $email;
|
||
$_SESSION['code_valide'] = true;
|
||
|
||
// Redirection propre (on retire token/email de l'URL)
|
||
$lang = $_SESSION['lang'] ?? 'ru';
|
||
header('Location: valider_email_cabinet.php');
|
||
exit;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// 2) Sécurité d’accès à la page sans token
|
||
// ------------------------------------------------------------
|
||
if (!isset($_SESSION['reset_email'], $_SESSION['code_valide']) || !$_SESSION['code_valide']) {
|
||
// Si pas de session valide ET pas d’erreur déjà définie → rediriger
|
||
if ($erreur === '') {
|
||
header("Location: mdp_oublie.php");
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// 3) Soumission du nouveau mot de passe
|
||
// ------------------------------------------------------------
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($success) && empty($erreur)) {
|
||
$mdp1 = $_POST['mot_de_passe'] ?? '';
|
||
$mdp2 = $_POST['confirm_mot_de_passe'] ?? '';
|
||
|
||
if (strlen($mdp1) < 8 || $mdp1 !== $mdp2) {
|
||
$erreur = $translations['erreur_mdp_invalides'] ?? 'Mot de passe invalide.';
|
||
} else {
|
||
try {
|
||
$hash = password_hash($mdp1, PASSWORD_DEFAULT);
|
||
$stmt = $pdo->prepare("
|
||
UPDATE compte
|
||
SET mot_de_passe = :hash,
|
||
email_valide = 1,
|
||
token_validation = NULL,
|
||
modifie_le = NOW()
|
||
WHERE email = :email
|
||
LIMIT 1
|
||
");
|
||
$stmt->execute([
|
||
':hash' => $hash,
|
||
':email' => $_SESSION['reset_email'],
|
||
]);
|
||
|
||
// Nettoyage session (garder la langue)
|
||
$lang = $_SESSION['lang'] ?? 'ru';
|
||
session_unset();
|
||
session_destroy();
|
||
session_start();
|
||
$_SESSION['lang'] = $lang;
|
||
|
||
$success = $translations['success_mdp_mis_a_jour'] ?? 'Mot de passe mis à jour. Votre email est confirmé.';
|
||
} catch (Throwable $e) {
|
||
error_log('valider_mail.php update pwd: '.$e->getMessage());
|
||
$erreur = $translations['erreur_generique'] ?? 'Erreur, veuillez réessayer.';
|
||
}
|
||
}
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title><?= h($translations['nouveau_mdp_title'] ?? 'Nouveau mot de passe') ?></title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<link href="/css/bootstrap.min.css" rel="stylesheet">
|
||
<link rel="stylesheet" href="<?= asset('/includes/site_style.css') ?>">
|
||
<link rel="stylesheet" href="<?= asset('/includes/mobil_style.css') ?>">
|
||
|
||
<style>
|
||
.strength-bar { height: 8px; display: flex; margin-bottom: 10px; border-radius: 5px; overflow: hidden; }
|
||
.strength-fill { flex: 1; background: #ccc; transition: background-color 0.3s ease; }
|
||
.toggle-password { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); cursor: pointer; font-size: 18px; }
|
||
.field-wrapper { position: relative; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header class="site-header">
|
||
<div class="container">
|
||
<div class="brand"><a href="index.php"><img src="public/uploads/img/logo.png" width="130" alt="USLUGI.FR"></a></div>
|
||
<nav class="header-actions">
|
||
<div class="lang-switch">
|
||
<?php require_once __DIR__ . '/includes/menu_langue.php';?>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="main">
|
||
<div class="card shadow-sm p-4" style="min-width: 380px; max-width: 480px; width: 100%;">
|
||
<h3 class="mb-4 text-center"><?= h($translations['nouveau_mdp_h3'] ?? 'Définir un nouveau mot de passe') ?></h3>
|
||
|
||
<?php if (!empty($erreur)): ?>
|
||
<div class="alert alert-danger py-2"><?= h($erreur) ?></div>
|
||
<div class="text-center">
|
||
<a class="btn btn-danger" href="mdp_oublie.php"><?= h($translations['renvoyer_lien'] ?? 'Renvoyer un lien') ?></a>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($success)): ?>
|
||
<div class="alert alert-primary py-2 text-center">
|
||
<div><?= h($success) ?></div>
|
||
</div>
|
||
<a href="connexion.php" class="btn btn-danger"><?= h($translations['btn_se_connecter'] ?? 'Se connecter') ?></a>
|
||
<?php endif; ?>
|
||
|
||
<?php if (empty($success) && empty($erreur)): ?>
|
||
<form method="POST">
|
||
<!-- Nouveau mot de passe -->
|
||
<div class="mb-3 field-wrapper">
|
||
<input type="password" name="mot_de_passe" id="mot_de_passe" class="form-control" placeholder="<?= h($translations['nouveau_mdp_placeholder'] ?? 'Nouveau mot de passe') ?>" required>
|
||
<span class="toggle-password" onclick="toggle('mot_de_passe')">👁️</span>
|
||
</div>
|
||
|
||
<div id="niveau-securite" class="mb-2 text-muted"><?= h($translations['mdp_regle_min'] ?? 'Au moins 8 caractères, lettres et chiffres.') ?></div>
|
||
|
||
<div class="strength-bar mb-3">
|
||
<div class="strength-fill"></div>
|
||
<div class="strength-fill"></div>
|
||
<div class="strength-fill"></div>
|
||
</div>
|
||
|
||
<!-- Confirmation mot de passe -->
|
||
<div class="mb-4 field-wrapper">
|
||
<input type="password" name="confirm_mot_de_passe" id="confirm_mot_de_passe" class="form-control" placeholder="<?= h($translations['confirm_mdp_placeholder'] ?? 'Confirmer le mot de passe') ?>" required>
|
||
<span class="toggle-password" onclick="toggle('confirm_mot_de_passe')">👁️</span>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary w-100"><?= h($translations['btn_valider'] ?? 'Valider') ?></button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<script>
|
||
const T = {
|
||
rule: '<?= addslashes($translations['mdp_regle_min'] ?? 'Au moins 8 caractères, lettres et chiffres.') ?>',
|
||
weak: '<?= addslashes($translations['securite_faible'] ?? 'Sécurité faible') ?>',
|
||
medium: '<?= addslashes($translations['securite_moyen'] ?? 'Sécurité moyenne') ?>',
|
||
strong: '<?= addslashes($translations['securite_fort'] ?? 'Sécurité forte') ?>'
|
||
};
|
||
|
||
function toggle(id) {
|
||
const el = document.getElementById(id);
|
||
el.type = el.type === 'password' ? 'text' : 'password';
|
||
}
|
||
|
||
document.getElementById('mot_de_passe')?.addEventListener('input', function () {
|
||
const val = this.value;
|
||
const segments = document.querySelectorAll('.strength-fill');
|
||
const niveauText = document.getElementById('niveau-securite');
|
||
|
||
segments.forEach(seg => seg.style.backgroundColor = '#ccc');
|
||
niveauText.textContent = T.rule;
|
||
niveauText.style.color = '#666';
|
||
|
||
const longOk = val.length >= 8;
|
||
const lettre = /[A-Za-z]/.test(val);
|
||
const chiffre = /\d/.test(val);
|
||
const maj = /[A-Z]/.test(val);
|
||
const special = /[^A-Za-z0-9]/.test(val);
|
||
|
||
let force = 0;
|
||
if (longOk && lettre && chiffre) force++;
|
||
if (maj) force++;
|
||
if (special) force++;
|
||
|
||
if (force === 1) {
|
||
segments[0].style.backgroundColor = 'red';
|
||
niveauText.textContent = T.weak;
|
||
niveauText.style.color = 'red';
|
||
} else if (force === 2) {
|
||
segments[0].style.backgroundColor = 'orange';
|
||
segments[1].style.backgroundColor = 'orange';
|
||
niveauText.textContent = T.medium;
|
||
niveauText.style.color = 'orange';
|
||
} else if (force >= 3) {
|
||
segments.forEach(seg => seg.style.backgroundColor = 'green');
|
||
niveauText.textContent = T.strong;
|
||
niveauText.style.color = 'green';
|
||
}
|
||
});
|
||
</script>
|
||
</div>
|
||
|
||
<footer class="site-footer">
|
||
<div class="container">
|
||
<p><?php echo $translations['copyr']?></p>
|
||
</div>
|
||
</footer>
|
||
</body>
|
||
</html>
|