81 lines
3.7 KiB
PHP
81 lines
3.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/includes/app.php';
|
|
app_boot();
|
|
|
|
if (current_user()) {
|
|
redirect('index.php');
|
|
}
|
|
|
|
$error = null;
|
|
if (is_post()) {
|
|
verify_csrf();
|
|
$email = strtolower(trim((string) ($_POST['email'] ?? '')));
|
|
$password = (string) ($_POST['password'] ?? '');
|
|
|
|
$stmt = db()->prepare('SELECT * FROM users WHERE email = :email LIMIT 1');
|
|
$stmt->execute(['email' => $email]);
|
|
$user = $stmt->fetch();
|
|
|
|
if (!$user) {
|
|
$error = 'Identifiants invalides.';
|
|
} else {
|
|
$lockedUntil = $user['locked_until'] ?? null;
|
|
if ($lockedUntil && strtotime((string) $lockedUntil) > time()) {
|
|
$seconds = max(1, strtotime((string) $lockedUntil) - time());
|
|
$error = 'Compte temporairement bloqué. Réessayez dans ' . $seconds . ' secondes.';
|
|
} elseif (password_verify($password, (string) $user['password_hash'])) {
|
|
$reset = db()->prepare('UPDATE users SET failed_attempts = 0, locked_until = NULL WHERE id = :id');
|
|
$reset->execute(['id' => (int) $user['id']]);
|
|
login_user($user);
|
|
set_flash('success', 'Connexion réussie.');
|
|
redirect('index.php');
|
|
} else {
|
|
$attempts = (int) $user['failed_attempts'] + 1;
|
|
$locked = $attempts >= 5 ? date('Y-m-d H:i:s', time() + 30) : null;
|
|
$update = db()->prepare('UPDATE users SET failed_attempts = :failed_attempts, locked_until = :locked_until WHERE id = :id');
|
|
$update->bindValue(':failed_attempts', $attempts, PDO::PARAM_INT);
|
|
$update->bindValue(':locked_until', $locked);
|
|
$update->bindValue(':id', (int) $user['id'], PDO::PARAM_INT);
|
|
$update->execute();
|
|
$error = $attempts >= 5
|
|
? '5 tentatives atteintes. Compte bloqué pendant 30 secondes.'
|
|
: 'Identifiants invalides. Tentative ' . $attempts . '/5.';
|
|
}
|
|
}
|
|
}
|
|
|
|
render_header('Connexion', ['description' => 'Se connecter à RJLRESAKA pour accéder au registre des sportifs.']);
|
|
?>
|
|
<main class="container py-5 auth-wrap">
|
|
<div class="row justify-content-center">
|
|
<div class="col-lg-5 col-xl-4">
|
|
<div class="panel-card p-4 p-lg-5">
|
|
<p class="section-kicker mb-1">Authentification</p>
|
|
<h1 class="h3 mb-3">Connexion sécurisée</h1>
|
|
<p class="text-secondary mb-4">5 tentatives maximum, puis pause automatique de 30 secondes.</p>
|
|
<?php if ($error): ?>
|
|
<div class="alert alert-danger" role="alert"><?= e($error) ?></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="password">Mot de passe</label>
|
|
<input class="form-control" id="password" type="password" name="password" required>
|
|
</div>
|
|
<button class="btn btn-dark w-100" type="submit">Se connecter</button>
|
|
</form>
|
|
<div class="d-flex justify-content-between flex-wrap gap-2 mt-3 small">
|
|
<a href="forgot_password.php" class="link-dark">Mot de passe oublié ?</a>
|
|
<a href="register.php" class="link-dark">Créer un compte</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<?php render_footer(); ?>
|