96 lines
4.3 KiB
PHP
96 lines
4.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/includes/app.php';
|
|
app_boot();
|
|
|
|
$errors = [];
|
|
if (is_post()) {
|
|
verify_csrf();
|
|
$email = strtolower(trim((string) ($_POST['email'] ?? '')));
|
|
$token = strtoupper(trim((string) ($_POST['token'] ?? '')));
|
|
$password = (string) ($_POST['password'] ?? '');
|
|
$confirm = (string) ($_POST['password_confirm'] ?? '');
|
|
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
$errors[] = 'Adresse email invalide.';
|
|
}
|
|
if ($token === '') {
|
|
$errors[] = 'Le token est obligatoire.';
|
|
}
|
|
if (!password_rules_ok($password)) {
|
|
$errors[] = 'Le nouveau mot de passe doit contenir au moins 8 caractères.';
|
|
}
|
|
if ($password !== $confirm) {
|
|
$errors[] = 'La confirmation du mot de passe est incorrecte.';
|
|
}
|
|
|
|
if (!$errors) {
|
|
$stmt = db()->prepare('SELECT id, reset_token_hash, reset_expires_at FROM users WHERE email = :email LIMIT 1');
|
|
$stmt->execute(['email' => $email]);
|
|
$user = $stmt->fetch();
|
|
$hash = hash('sha256', $token);
|
|
|
|
$valid = $user
|
|
&& !empty($user['reset_token_hash'])
|
|
&& hash_equals((string) $user['reset_token_hash'], $hash)
|
|
&& !empty($user['reset_expires_at'])
|
|
&& strtotime((string) $user['reset_expires_at']) >= time();
|
|
|
|
if (!$valid) {
|
|
$errors[] = 'Token invalide ou expiré.';
|
|
} else {
|
|
$update = db()->prepare('UPDATE users SET password_hash = :password_hash, reset_token_hash = NULL, reset_expires_at = NULL, failed_attempts = 0, locked_until = NULL WHERE id = :id');
|
|
$update->execute([
|
|
'password_hash' => password_hash($password, PASSWORD_BCRYPT),
|
|
'id' => (int) $user['id'],
|
|
]);
|
|
set_flash('success', 'Mot de passe réinitialisé. Vous pouvez maintenant vous connecter.');
|
|
redirect('login.php');
|
|
}
|
|
}
|
|
}
|
|
|
|
render_header('Reset mot de passe', ['description' => 'Valider un token et définir un nouveau mot de passe sur RJLRESAKA.']);
|
|
?>
|
|
<main class="container py-5 auth-wrap">
|
|
<div class="row justify-content-center">
|
|
<div class="col-lg-6 col-xl-5">
|
|
<div class="panel-card p-4 p-lg-5">
|
|
<p class="section-kicker mb-1">Validation du token</p>
|
|
<h1 class="h3 mb-3">Définir un nouveau mot de passe</h1>
|
|
<p class="text-secondary mb-4">Copiez le token reçu puis confirmez un nouveau mot de passe sécurisé.</p>
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-danger" role="alert">
|
|
<ul class="mb-0 ps-3">
|
|
<?php foreach ($errors as $error): ?>
|
|
<li><?= e($error) ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
<?php endif; ?>
|
|
<form method="post" class="vstack gap-3">
|
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
|
<div>
|
|
<label class="form-label" for="email">Email</label>
|
|
<input class="form-control" id="email" type="email" name="email" value="<?= old('email') ?>" required>
|
|
</div>
|
|
<div>
|
|
<label class="form-label" for="token">Token</label>
|
|
<input class="form-control text-uppercase" id="token" name="token" value="<?= old('token', (string) ($_GET['token'] ?? '')) ?>" required>
|
|
</div>
|
|
<div>
|
|
<label class="form-label" for="password">Nouveau mot de passe</label>
|
|
<input class="form-control" id="password" type="password" name="password" minlength="8" required>
|
|
</div>
|
|
<div>
|
|
<label class="form-label" for="password_confirm">Confirmer</label>
|
|
<input class="form-control" id="password_confirm" type="password" name="password_confirm" minlength="8" required>
|
|
</div>
|
|
<button class="btn btn-dark" type="submit">Mettre à jour le mot de passe</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<?php render_footer(); ?>
|