Initialisation du projet Bolsa
This commit is contained in:
Executable
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
// On envoie explicitement le code d'erreur 404 au navigateur
|
||||
http_response_code(404);
|
||||
|
||||
require_once('header.php');
|
||||
?>
|
||||
|
||||
<div style="text-align: center; padding: 50px; color: white;">
|
||||
<h1>Erreur 404</h1>
|
||||
<p>Désolé, la page que vous recherchez est introuvable.</p>
|
||||
<a href="/index.php" style="color: #007bff;">Retour à l'accueil</a>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once('footer.php');
|
||||
?>
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
|
||||
namespace App\Database;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class Database
|
||||
*
|
||||
* Gère la connexion PDO à la base de données MySQL.
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
private PDO $connection;
|
||||
|
||||
public const DB_HOST = '127.0.0.1';
|
||||
public const DB_NAME = 'bolsa';
|
||||
public const DB_USER = 'root';
|
||||
public const DB_PASS = 'sysadm-1963';
|
||||
public const DB_CHARSET = 'utf8mb4';
|
||||
|
||||
/**
|
||||
* Database constructor.
|
||||
*
|
||||
* Initialise la connexion PDO.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;dbname=%s;charset=%s',
|
||||
self::DB_HOST,
|
||||
self::DB_NAME,
|
||||
self::DB_CHARSET
|
||||
);
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
try {
|
||||
$this->connection = new PDO($dsn, self::DB_USER, self::DB_PASS, $options);
|
||||
} catch (PDOException $exception) {
|
||||
throw new RuntimeException('Connexion à la base de données impossible : ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'objet PDO.
|
||||
*
|
||||
* @return PDO
|
||||
*/
|
||||
public function getConnection(): PDO
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ferme la connexion PDO.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect(): void
|
||||
{
|
||||
$this->connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class LoginManager
|
||||
*
|
||||
* Fournit les opérations CRUD pour la table `login`.
|
||||
*/
|
||||
class LoginManager
|
||||
{
|
||||
private PDO $connection;
|
||||
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->connection = $database->getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un nouvel utilisateur dans la table login.
|
||||
*
|
||||
* @param array $data
|
||||
* @return int L'ID généré de l'utilisateur.
|
||||
*/
|
||||
public function create(array $data): int
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'INSERT INTO login (login, nom, prenom, mail, pass, actif, creer_le, modifie_le)
|
||||
VALUES (:login, :nom, :prenom, :mail, :pass, :actif, NOW(), NOW())'
|
||||
);
|
||||
|
||||
$hashedPassword = password_hash($data['pass'], PASSWORD_DEFAULT);
|
||||
$actif = array_key_exists('actif', $data) ? (int) $data['actif'] : 0;
|
||||
|
||||
$statement->bindValue(':login', $data['login'], PDO::PARAM_STR);
|
||||
$statement->bindValue(':nom', $data['nom'], PDO::PARAM_STR);
|
||||
$statement->bindValue(':prenom', $data['prenom'], PDO::PARAM_STR);
|
||||
$statement->bindValue(':mail', $data['mail'], PDO::PARAM_STR);
|
||||
$statement->bindValue(':pass', $hashedPassword, PDO::PARAM_STR);
|
||||
$statement->bindValue(':actif', $actif, PDO::PARAM_INT);
|
||||
|
||||
$statement->execute();
|
||||
|
||||
return (int) $this->connection->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lit un utilisateur par son ID.
|
||||
*
|
||||
* @param int $id
|
||||
* @return array|null
|
||||
*/
|
||||
public function read(int $id): ?array
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT id, login, nom, prenom, mail, creer_le, modifie_le
|
||||
FROM login
|
||||
WHERE id = :id'
|
||||
);
|
||||
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$statement->execute();
|
||||
|
||||
$result = $statement->fetch();
|
||||
|
||||
return $result === false ? null : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un utilisateur.
|
||||
*
|
||||
* @param int $id
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function update(int $id, array $data): bool
|
||||
{
|
||||
$fields = [];
|
||||
$params = [':id' => $id];
|
||||
|
||||
if (isset($data['login'])) {
|
||||
$fields[] = 'login = :login';
|
||||
$params[':login'] = $data['login'];
|
||||
}
|
||||
|
||||
if (isset($data['nom'])) {
|
||||
$fields[] = 'nom = :nom';
|
||||
$params[':nom'] = $data['nom'];
|
||||
}
|
||||
|
||||
if (isset($data['prenom'])) {
|
||||
$fields[] = 'prenom = :prenom';
|
||||
$params[':prenom'] = $data['prenom'];
|
||||
}
|
||||
|
||||
if (isset($data['mail'])) {
|
||||
$fields[] = 'mail = :mail';
|
||||
$params[':mail'] = $data['mail'];
|
||||
}
|
||||
|
||||
if (isset($data['pass'])) {
|
||||
$fields[] = 'pass = :pass';
|
||||
$params[':pass'] = password_hash($data['pass'], PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
if (empty($fields)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields[] = 'modifie_le = NOW()';
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
sprintf('UPDATE login SET %s WHERE id = :id', implode(', ', $fields))
|
||||
);
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
$statement->bindValue($param, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un utilisateur par son ID.
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(int $id): bool
|
||||
{
|
||||
$statement = $this->connection->prepare('DELETE FROM login WHERE id = :id');
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne tous les utilisateurs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function list(): array
|
||||
{
|
||||
$statement = $this->connection->query(
|
||||
'SELECT id, login, nom, prenom, mail, creer_le, modifie_le FROM login ORDER BY id ASC'
|
||||
);
|
||||
|
||||
return $statement->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un utilisateur par son login.
|
||||
*
|
||||
* @param string $login
|
||||
* @return array|null
|
||||
*/
|
||||
public function getByLogin(string $login): ?array
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT id, login, nom, prenom, mail, pass, actif, creer_le, modifie_le
|
||||
FROM login
|
||||
WHERE login = :login'
|
||||
);
|
||||
|
||||
$statement->bindValue(':login', $login, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
|
||||
$result = $statement->fetch();
|
||||
|
||||
return $result === false ? null : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le mot de passe correspond au login.
|
||||
*
|
||||
* @param string $login
|
||||
* @param string $password
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyPasswordByLogin(string $login, string $password): bool
|
||||
{
|
||||
$user = $this->getByLogin($login);
|
||||
|
||||
if ($user === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((int) ($user['actif'] ?? 0) === 1) && password_verify($password, $user['pass']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un login ou un email existe déjà.
|
||||
*
|
||||
* @param string $login
|
||||
* @param string $email
|
||||
* @return bool
|
||||
*/
|
||||
public function existsByLoginOrEmail(string $login, string $email): bool
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT COUNT(*) AS total
|
||||
FROM login
|
||||
WHERE login = :login OR mail = :mail'
|
||||
);
|
||||
|
||||
$statement->bindValue(':login', $login, PDO::PARAM_STR);
|
||||
$statement->bindValue(':mail', $email, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
|
||||
$result = $statement->fetch();
|
||||
|
||||
return isset($result['total']) && (int) $result['total'] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si une adresse e-mail existe.
|
||||
*
|
||||
* @param string $email
|
||||
* @return bool
|
||||
*/
|
||||
public function existsByEmail(string $email): bool
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT COUNT(*) AS total FROM login WHERE mail = :mail'
|
||||
);
|
||||
|
||||
$statement->bindValue(':mail', $email, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
|
||||
$result = $statement->fetch();
|
||||
|
||||
return isset($result['total']) && (int) $result['total'] > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit un token de réinitialisation pour un utilisateur identifié par son e-mail.
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function setResetTokenByEmail(string $email, string $token): bool
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'UPDATE login SET reset_token = :token, reset_token_created_at = NOW(), modifie_le = NOW() WHERE mail = :mail'
|
||||
);
|
||||
|
||||
$statement->bindValue(':token', $token, PDO::PARAM_STR);
|
||||
$statement->bindValue(':mail', $email, PDO::PARAM_STR);
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un utilisateur identifié par son token de réinitialisation.
|
||||
*
|
||||
* @param string $token
|
||||
* @return array|null
|
||||
*/
|
||||
public function getByResetToken(string $token): ?array
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT id, login, nom, prenom, mail, pass, actif, reset_token, reset_token_created_at, creer_le, modifie_le
|
||||
FROM login
|
||||
WHERE reset_token = :token
|
||||
AND reset_token_created_at >= NOW() - INTERVAL 30 MINUTE'
|
||||
);
|
||||
|
||||
$statement->bindValue(':token', $token, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
|
||||
$result = $statement->fetch();
|
||||
|
||||
return $result === false ? null : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le mot de passe d'un utilisateur et supprime le token de réinitialisation.
|
||||
*
|
||||
* @param int $id
|
||||
* @param string $password
|
||||
* @return bool
|
||||
*/
|
||||
public function updatePasswordById(int $id, string $password): bool
|
||||
{
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
'UPDATE login SET pass = :pass, reset_token = NULL, reset_token_created_at = NULL, modifie_le = NOW() WHERE id = :id'
|
||||
);
|
||||
|
||||
$statement->bindValue(':pass', $hashedPassword, PDO::PARAM_STR);
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
}
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<a class="logout-button footer-logout" href="/php/menu.php?action=logout">Se déconnecter</a>
|
||||
<?php endif; ?>
|
||||
<p>© 2026 - Jean-François GIRAUD - Positions en bourse - Tous droits réservés</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
use App\Database\Database;
|
||||
use App\Database\LoginManager;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
$errors = [];
|
||||
$successMessage = '';
|
||||
$email = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = trim((string) filter_input(INPUT_POST, 'mail', FILTER_SANITIZE_EMAIL));
|
||||
|
||||
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Veuillez saisir une adresse e-mail valide.';
|
||||
} else {
|
||||
try {
|
||||
$database = new Database();
|
||||
$loginManager = new LoginManager($database);
|
||||
|
||||
if (!$loginManager->existsByEmail($email)) {
|
||||
$errors[] = 'Aucun compte n’est associé à cette adresse e-mail.';
|
||||
} else {
|
||||
$resetToken = bin2hex(random_bytes(32));
|
||||
|
||||
if (!$loginManager->setResetTokenByEmail($email, $resetToken)) {
|
||||
$errors[] = 'Erreur interne : impossible de générer le token de réinitialisation.';
|
||||
} else {
|
||||
$resetLink = sprintf(
|
||||
'https://www.giraud-finance.com/php/reset_password.php?token=%s',
|
||||
urlencode($resetToken)
|
||||
);
|
||||
|
||||
try {
|
||||
$mailer = new PHPMailer(true);
|
||||
$mailer->isSMTP();
|
||||
$mailer->Host = 'mx.giraud-finance.com';
|
||||
$mailer->SMTPAuth = true;
|
||||
$mailer->Username = 'jfgiraud@giraud-finance.com';
|
||||
$mailer->Password = 'azertyQSDFGH123456+-';
|
||||
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||
$mailer->Port = 587;
|
||||
$mailer->CharSet = 'UTF-8';
|
||||
$mailer->Encoding = 'base64';
|
||||
|
||||
$mailer->setFrom('postmaster@giraud-finance.com', 'Giraud Finance');
|
||||
$mailer->addAddress($email);
|
||||
$mailer->Subject = 'Réinitialisation de votre mot de passe - Giraud Finance';
|
||||
$mailer->isHTML(true);
|
||||
$mailer->Body = sprintf(
|
||||
'<p>Bonjour,</p><p>Pour réinitialiser votre mot de passe, veuillez cliquer sur le lien suivant :</p><p><a href="%1$s">%1$s</a></p><p>Ce lien est valide pendant 30 minutes.</p><p>Si vous n\'avez pas demandé cette réinitialisation, ignorez ce message.</p>',
|
||||
htmlspecialchars($resetLink, ENT_QUOTES, 'UTF-8')
|
||||
);
|
||||
$mailer->AltBody = "Bonjour,\n\nPour réinitialiser votre mot de passe, veuillez utiliser ce lien : $resetLink\n\nCe lien est valide pendant 30 minutes.\n\nSi vous n\'avez pas demandé cette réinitialisation, ignorez ce message.";
|
||||
|
||||
$mailer->send();
|
||||
$successMessage = 'Nous avons bien pris en compte votre demande. Un lien de réinitialisation a été envoyé si votre adresse existe.';
|
||||
} catch (Exception $mailException) {
|
||||
$errors[] = 'Impossible d\'envoyer l\'e-mail de réinitialisation : ' . $mailException->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
$errors[] = 'Erreur interne : impossible de traiter la demande pour le moment.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php require_once __DIR__ . '/header.php'; ?>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<h2 class="login-title">Mot de passe oublié</h2>
|
||||
<p class="login-subtitle">Renseignez votre adresse e-mail pour réinitialiser votre mot de passe.</p>
|
||||
|
||||
<?php if (!empty($errors)): ?>
|
||||
<div class="alert alert-error">
|
||||
<ul>
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<li><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($successMessage): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8'); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="forgot_password.php" method="POST" class="login-form">
|
||||
<div class="form-group">
|
||||
<label for="mail" class="form-label">Adresse e-mail</label>
|
||||
<input type="email" id="mail" name="mail" class="form-input" placeholder="exemple@email.com" required autocomplete="email" value="<?php echo htmlspecialchars($email, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit">Envoyer la demande</button>
|
||||
</form>
|
||||
|
||||
<div class="login-footer">
|
||||
<span class="separator">•</span>
|
||||
<a href="../index.php" class="signup-link">Retour à la connexion</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/footer.php'; ?>
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Bourse - Page d'Accueil</title>
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link rel="shortcut icon" href="/assets/images/favicon.png" type="image/x-icon">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<img src="/assets/images/GFBlancLogo.png" alt="Logo GF Blanc" class="logo-img">
|
||||
</div>
|
||||
<h1 class="site-title">Positions en bourse</h1>
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<div class="user-info">
|
||||
<span>Nom : <?= htmlspecialchars((string) ($_SESSION['user_name'] ?? ''), ENT_QUOTES, 'UTF-8') ?></span>
|
||||
<span>IP : <?= htmlspecialchars((string) ($_SERVER['REMOTE_ADDR'] ?? 'N/A'), ENT_QUOTES, 'UTF-8') ?></span>
|
||||
<span class="info-date">Date : <?= date('d/m/Y') ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</header>
|
||||
<main class="main-content">
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/session.php';
|
||||
|
||||
use App\Database\Database;
|
||||
use App\Database\LoginManager;
|
||||
|
||||
if (!isset($_SESSION['login_attempts'])) {
|
||||
$_SESSION['login_attempts'] = 0;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$login = trim((string) filter_input(INPUT_POST, 'login', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
|
||||
$password = trim((string) ($_POST['password'] ?? ''));
|
||||
|
||||
if ($login === '' || $password === '') {
|
||||
$_SESSION['login_error'] = 'Veuillez renseigner votre login et votre mot de passe.';
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$handleFailedAttempt = function (string $message): void {
|
||||
$attempts = (int) ($_SESSION['login_attempts'] ?? 0) + 1;
|
||||
$_SESSION['login_attempts'] = $attempts;
|
||||
|
||||
if ($attempts >= 5) {
|
||||
$_SESSION['blocked'] = true;
|
||||
header('Location: https://home.giraud-finance.com');
|
||||
exit;
|
||||
}
|
||||
|
||||
$_SESSION['login_error'] = $message;
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
};
|
||||
|
||||
try {
|
||||
$database = new Database();
|
||||
$loginManager = new LoginManager($database);
|
||||
$user = $loginManager->getByLogin($login);
|
||||
} catch (Throwable $exception) {
|
||||
$_SESSION['login_error'] = 'Erreur interne : impossible de se connecter au service.';
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($user === null) {
|
||||
$handleFailedAttempt('Login ou mot de passe incorrect.');
|
||||
}
|
||||
|
||||
if ((int) ($user['actif'] ?? 0) !== 1) {
|
||||
$_SESSION['login_error'] = 'Votre compte n\'est pas encore activé. Veuillez contacter l\'administrateur.';
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!password_verify($password, $user['pass'])) {
|
||||
$handleFailedAttempt('Login ou mot de passe incorrect.');
|
||||
}
|
||||
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['login_attempts'] = 0;
|
||||
unset($_SESSION['blocked']);
|
||||
$_SESSION['user_id'] = (int) $user['id'];
|
||||
$_SESSION['user_login'] = $user['login'];
|
||||
$_SESSION['user_name'] = trim($user['prenom'] . ' ' . $user['nom']);
|
||||
|
||||
header('Location: menu.php');
|
||||
exit;
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/session.php';
|
||||
|
||||
// Logout action
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'logout') {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
setcookie(session_name(), '', time() - 42000, '/');
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Require login
|
||||
if (empty($_SESSION['user_id'])) {
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/header.php';
|
||||
?>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<h2 class="login-title">Espace utilisateur</h2>
|
||||
|
||||
<p>Session ID : <?php echo htmlspecialchars(session_id(), ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
<p>User ID : <?php echo htmlspecialchars((string)($_SESSION['user_id'] ?? ''), ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
<p>Login : <?php echo htmlspecialchars((string)($_SESSION['user_login'] ?? ''), ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
<p>Nom : <?php echo htmlspecialchars((string)($_SESSION['user_name'] ?? ''), ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
<p>Dernière activité (timestamp) : <?php echo htmlspecialchars((string)time(), ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
<p>Adresse IP : <?php echo htmlspecialchars($_SERVER['REMOTE_ADDR'] ?? '', ENT_QUOTES, 'UTF-8'); ?></p>
|
||||
|
||||
<div style="margin-top:16px;">
|
||||
<a class="btn-submit" href="menu.php?action=logout" style="display:inline-block;text-decoration:none;">Se déconnecter</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/footer.php'; ?>
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
use App\Database\Database;
|
||||
use App\Database\LoginManager;
|
||||
|
||||
$errors = [];
|
||||
$values = [
|
||||
'login' => '',
|
||||
'nom' => '',
|
||||
'prenom' => '',
|
||||
'mail' => '',
|
||||
];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$values['login'] = trim((string) filter_input(INPUT_POST, 'login', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
|
||||
$values['nom'] = trim((string) filter_input(INPUT_POST, 'nom', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
|
||||
$values['prenom'] = trim((string) filter_input(INPUT_POST, 'prenom', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
|
||||
$values['mail'] = trim((string) filter_input(INPUT_POST, 'mail', FILTER_SANITIZE_EMAIL));
|
||||
$password = trim((string) ($_POST['password'] ?? ''));
|
||||
$passwordConfirm = trim((string) ($_POST['password_confirm'] ?? ''));
|
||||
|
||||
if ($values['login'] === '') {
|
||||
$errors[] = 'Le login est requis.';
|
||||
}
|
||||
|
||||
if ($values['nom'] === '') {
|
||||
$errors[] = 'Le nom est requis.';
|
||||
}
|
||||
|
||||
if ($values['prenom'] === '') {
|
||||
$errors[] = 'Le prénom est requis.';
|
||||
}
|
||||
|
||||
if ($values['mail'] === '' || !filter_var($values['mail'], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = 'Une adresse e-mail valide est requise.';
|
||||
}
|
||||
|
||||
if ($password === '' || $passwordConfirm === '') {
|
||||
$errors[] = 'Le mot de passe et la confirmation sont requis.';
|
||||
} elseif ($password !== $passwordConfirm) {
|
||||
$errors[] = 'Les mots de passe ne correspondent pas.';
|
||||
} elseif (strlen($password) < 8) {
|
||||
$errors[] = 'Le mot de passe doit contenir au moins 8 caractères.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
try {
|
||||
$database = new Database();
|
||||
$loginManager = new LoginManager($database);
|
||||
|
||||
if ($loginManager->existsByLoginOrEmail($values['login'], $values['mail'])) {
|
||||
$errors[] = 'Le login ou l’adresse e-mail existe déjà.';
|
||||
} else {
|
||||
$loginManager->create([
|
||||
'login' => $values['login'],
|
||||
'nom' => $values['nom'],
|
||||
'prenom' => $values['prenom'],
|
||||
'mail' => $values['mail'],
|
||||
'pass' => $password,
|
||||
]);
|
||||
|
||||
header('Location: ../index.php?registered=1');
|
||||
exit;
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
$errors[] = 'Erreur interne : impossible de créer le compte.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php require_once __DIR__ . '/header.php'; ?>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<h2 class="login-title">Créer un compte</h2>
|
||||
<p class="login-subtitle">Inscrivez-vous pour accéder à Giraud Finance.</p>
|
||||
|
||||
<?php if (!empty($errors)): ?>
|
||||
<div class="alert alert-error">
|
||||
<ul>
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<li><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="register.php" method="POST" class="login-form">
|
||||
<div class="form-group">
|
||||
<label for="login" class="form-label">Login</label>
|
||||
<input type="text" id="login" name="login" class="form-input" placeholder="Votre login" required autocomplete="username" value="<?php echo htmlspecialchars($values['login'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="nom" class="form-label">Nom</label>
|
||||
<input type="text" id="nom" name="nom" class="form-input" placeholder="Votre nom" required value="<?php echo htmlspecialchars($values['nom'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="prenom" class="form-label">Prénom</label>
|
||||
<input type="text" id="prenom" name="prenom" class="form-input" placeholder="Votre prénom" required value="<?php echo htmlspecialchars($values['prenom'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mail" class="form-label">Adresse e-mail</label>
|
||||
<input type="email" id="mail" name="mail" class="form-input" placeholder="exemple@email.com" required autocomplete="email" value="<?php echo htmlspecialchars($values['mail'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="form-label">Mot de passe</label>
|
||||
<input type="password" id="password" name="password" class="form-input" placeholder="••••••••" required autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password_confirm" class="form-label">Confirmer le mot de passe</label>
|
||||
<input type="password" id="password_confirm" name="password_confirm" class="form-input" placeholder="••••••••" required autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit">S’inscrire</button>
|
||||
</form>
|
||||
|
||||
<div class="login-footer">
|
||||
<span class="separator">•</span>
|
||||
<a href="../index.php" class="signup-link">Retour à la connexion</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/footer.php'; ?>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
use App\Database\Database;
|
||||
use App\Database\LoginManager;
|
||||
|
||||
$errors = [];
|
||||
$successMessage = '';
|
||||
$token = trim((string) ($_GET['token'] ?? ''));
|
||||
$user = null;
|
||||
|
||||
try {
|
||||
$database = new Database();
|
||||
$loginManager = new LoginManager($database);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$token = trim((string) ($_POST['token'] ?? ''));
|
||||
$password = trim((string) ($_POST['password'] ?? ''));
|
||||
$passwordConfirm = trim((string) ($_POST['password_confirm'] ?? ''));
|
||||
|
||||
if ($token === '') {
|
||||
$errors[] = 'Lien invalide.';
|
||||
} else {
|
||||
$user = $loginManager->getByResetToken($token);
|
||||
|
||||
if ($user === null) {
|
||||
$errors[] = 'Lien invalide.';
|
||||
} else {
|
||||
if ($password === '' || $passwordConfirm === '') {
|
||||
$errors[] = 'Veuillez renseigner les deux champs de mot de passe.';
|
||||
} elseif ($password !== $passwordConfirm) {
|
||||
$errors[] = 'Les mots de passe ne correspondent pas.';
|
||||
} elseif (strlen($password) < 8) {
|
||||
$errors[] = 'Le mot de passe doit contenir au moins 8 caractères.';
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
if ($loginManager->updatePasswordById((int) $user['id'], $password)) {
|
||||
$successMessage = 'Votre mot de passe a été réinitialisé avec succès. Vous pouvez maintenant vous connecter.';
|
||||
$user = null;
|
||||
} else {
|
||||
$errors[] = 'Impossible de mettre à jour le mot de passe. Veuillez réessayer.';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($token !== '') {
|
||||
$user = $loginManager->getByResetToken($token);
|
||||
}
|
||||
} catch (Throwable $exception) {
|
||||
$errors[] = 'Erreur interne : impossible de traiter votre demande.';
|
||||
}
|
||||
?>
|
||||
|
||||
<?php require_once __DIR__ . '/header.php'; ?>
|
||||
|
||||
<div class="login-container">
|
||||
<div class="login-box">
|
||||
<h2 class="login-title">Réinitialisation du mot de passe</h2>
|
||||
<p class="login-subtitle">Entrez un nouveau mot de passe pour votre compte.</p>
|
||||
|
||||
<?php if (!empty($errors)): ?>
|
||||
<div class="alert alert-error">
|
||||
<ul>
|
||||
<?php foreach ($errors as $error): ?>
|
||||
<li><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($successMessage): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8'); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($user !== null): ?>
|
||||
<form action="reset_password.php" method="POST" class="login-form">
|
||||
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="form-label">Nouveau mot de passe</label>
|
||||
<input type="password" id="password" name="password" class="form-input" placeholder="••••••••" required autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password_confirm" class="form-label">Confirmer le mot de passe</label>
|
||||
<input type="password" id="password_confirm" name="password_confirm" class="form-input" placeholder="••••••••" required autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-submit">Réinitialiser le mot de passe</button>
|
||||
</form>
|
||||
<?php elseif ($successMessage === ''): ?>
|
||||
<div class="alert alert-error">Lien invalide.</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="login-footer">
|
||||
<span class="separator">•</span>
|
||||
<a href="../index.php" class="signup-link">Retour à la connexion</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once __DIR__ . '/footer.php'; ?>
|
||||
Executable
+185
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
use App\Database\Database;
|
||||
|
||||
class MySqlSessionHandler implements SessionHandlerInterface
|
||||
{
|
||||
private ?PDO $connection = null;
|
||||
private bool $ipMismatch = false;
|
||||
|
||||
public function open(string $savePath, string $sessionName): bool
|
||||
{
|
||||
try {
|
||||
$database = new Database();
|
||||
$this->connection = $database->getConnection();
|
||||
|
||||
return true;
|
||||
} catch (Throwable $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function close(): bool
|
||||
{
|
||||
$this->connection = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function read(string $id): string
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
if (!$this->open('', '')) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT data, ip_address
|
||||
FROM sessions
|
||||
WHERE id = :id'
|
||||
);
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
|
||||
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($row === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$currentIp = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
|
||||
if (!empty($row['ip_address']) && $row['ip_address'] !== $currentIp) {
|
||||
$this->ipMismatch = true;
|
||||
return '';
|
||||
}
|
||||
|
||||
$this->updateAccessAndIp($id, $currentIp);
|
||||
|
||||
return (string) $row['data'];
|
||||
}
|
||||
|
||||
public function write(string $id, string $data): bool
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
if (!$this->open('', '')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$currentIp = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$access = time();
|
||||
$userId = isset($_SESSION['user_id']) ? (int) $_SESSION['user_id'] : null;
|
||||
$createdAt = date('Y-m-d H:i:s');
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
'INSERT INTO sessions (id, access, data, user_id, ip_address, created_at)
|
||||
VALUES (:id, :access, :data, :user_id, :ip_address, :created_at)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
access = VALUES(access),
|
||||
data = VALUES(data),
|
||||
user_id = VALUES(user_id),
|
||||
ip_address = VALUES(ip_address)'
|
||||
);
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_STR);
|
||||
$statement->bindValue(':access', $access, PDO::PARAM_INT);
|
||||
$statement->bindValue(':data', $data, PDO::PARAM_STR);
|
||||
|
||||
if ($userId === null) {
|
||||
$statement->bindValue(':user_id', null, PDO::PARAM_NULL);
|
||||
} else {
|
||||
$statement->bindValue(':user_id', $userId, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$statement->bindValue(':ip_address', $currentIp, PDO::PARAM_STR);
|
||||
$statement->bindValue(':created_at', $createdAt, PDO::PARAM_STR);
|
||||
|
||||
try {
|
||||
return $statement->execute();
|
||||
} catch (Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(string $id): bool
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
'DELETE FROM sessions WHERE id = :id'
|
||||
);
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_STR);
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
public function gc(int $maxLifetime): int|false
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
'DELETE FROM sessions WHERE access < :time'
|
||||
);
|
||||
$statement->bindValue(':time', time() - $maxLifetime, PDO::PARAM_INT);
|
||||
|
||||
return $statement->execute();
|
||||
}
|
||||
|
||||
public function validateIp(string $id): bool
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$statement = $this->connection->prepare(
|
||||
'SELECT ip_address FROM sessions WHERE id = :id'
|
||||
);
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
|
||||
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
||||
$currentIp = $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
|
||||
return empty($row['ip_address']) || $row['ip_address'] === $currentIp;
|
||||
}
|
||||
|
||||
public function hasIpMismatch(): bool
|
||||
{
|
||||
return $this->ipMismatch;
|
||||
}
|
||||
|
||||
private function updateAccessAndIp(string $id, string $ip): void
|
||||
{
|
||||
$statement = $this->connection->prepare(
|
||||
'UPDATE sessions
|
||||
SET access = :access,
|
||||
ip_address = :ip_address
|
||||
WHERE id = :id'
|
||||
);
|
||||
$statement->bindValue(':access', time(), PDO::PARAM_INT);
|
||||
$statement->bindValue(':ip_address', $ip, PDO::PARAM_STR);
|
||||
$statement->bindValue(':id', $id, PDO::PARAM_STR);
|
||||
$statement->execute();
|
||||
}
|
||||
}
|
||||
|
||||
$handler = new MySqlSessionHandler();
|
||||
session_set_save_handler($handler, true);
|
||||
session_start();
|
||||
|
||||
// Only enforce IP mismatch for authenticated sessions (user_id present)
|
||||
if (!empty($_SESSION['user_id']) && $handler->hasIpMismatch()) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
setcookie(session_name(), '', time() - 42000, '/');
|
||||
header('Location: /index.php');
|
||||
exit;
|
||||
}
|
||||
Reference in New Issue
Block a user