display attempts to admin

This commit is contained in:
2025-03-26 20:48:39 +01:00
parent db2079e0b3
commit 45b9cc7dfa
9 changed files with 443 additions and 26 deletions

22
css/dataTables.css Normal file

File diff suppressed because one or more lines are too long

73
database.php Normal file
View File

@@ -0,0 +1,73 @@
<?php
function GetDbConnection(): ?PDO
{
$host = 'localhost'; // ou l'adresse IP du serveur MariaDB
$dbname = 'ldap'; // nom de votre base de donn<6E>es
$username = 'root'; // nom d'utilisateur MariaDB
$password = '4321'; // mot de passe pour l'utilisateur
try {
// Cr<43>ation d'une instance PDO pour la connexion <20> la base de donn<6E>es
$pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
// Configuration du mode d'erreur de PDO pour les exceptions
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
echo "Échec de la connexion : " . $e->getMessage();
}
return null;
}
class AuthAttempt
{
public string $username;
public string $status;
public string $timestamp;
public string $ip_address;
public function __construct(
string $username,
string $status,
string $ip_address,
string $timestamp = "",
) {
$this->username = $username;
$this->status = $status;
$this->ip_address = $ip_address;
$this->timestamp = $timestamp;
}
}
function InsertLine(AuthAttempt $attempt)
{
$table_name = "authentification_attempts";
$pdo = GetDbConnection();
$query = $pdo->prepare("INSERT INTO $table_name(`username`, `status`, `ip_address`) VALUES (:user, :status, :ip);");
$query->bindValue(":user", $attempt->username, PDO::PARAM_STR);
$query->bindValue(":status", $attempt->status, PDO::PARAM_STR);
$query->bindValue(":ip", $attempt->ip_address, PDO::PARAM_STR);
$query->execute();
}
function GetLines() : array {
$table_name = "authentification_attempts";
$pdo = GetDbConnection();
$query = $pdo->prepare("SELECT * FROM $table_name;");
$query->execute();
$lines = $query->fetchAll(\PDO::FETCH_ASSOC);
$result = [];
foreach($lines as $line) {
array_push($result, new AuthAttempt($line["username"], $line["status"], $line["ip_address"], $line["timestamp"]));
}
return $result;
}

View File

@@ -1,15 +0,0 @@
<?php
$host = 'localhost'; // ou l'adresse IP du serveur MariaDB
$dbname = 'mysql'; // nom de votre base de donn<6E>es
$username = 'root'; // nom d'utilisateur MariaDB
$password = '4321'; // mot de passe pour l'utilisateur
try {
// Cr<43>ation d'une instance PDO pour la connexion <20> la base de donn<6E>es
$pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
// Configuration du mode d'erreur de PDO pour les exceptions
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connexion réussie <20> MariaDB avec PDO!";
} catch (PDOException $e) {
echo "<EFBFBD>chec de la connexion : " . $e->getMessage();
}
?>

36
js/datatables.min.js vendored Normal file

File diff suppressed because one or more lines are too long

245
lang/fr.json Normal file
View File

@@ -0,0 +1,245 @@
{
"emptyTable": "Aucune donnée disponible dans le tableau",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"select": {
"rows": {
"_": "%d lignes sélectionnées",
"1": "1 ligne sélectionnée"
},
"cells": {
"1": "1 cellule sélectionnée",
"_": "%d cellules sélectionnées"
},
"columns": {
"1": "1 colonne sélectionnée",
"_": "%d colonnes sélectionnées"
}
},
"autoFill": {
"cancel": "Annuler",
"fill": "Remplir toutes les cellules avec <i>%d<\/i>",
"fillHorizontal": "Remplir les cellules horizontalement",
"fillVertical": "Remplir les cellules verticalement"
},
"searchBuilder": {
"conditions": {
"date": {
"after": "Après le",
"before": "Avant le",
"between": "Entre",
"empty": "Vide",
"not": "Différent de",
"notBetween": "Pas entre",
"notEmpty": "Non vide",
"equals": "Égal à"
},
"number": {
"between": "Entre",
"empty": "Vide",
"gt": "Supérieur à",
"gte": "Supérieur ou égal à",
"lt": "Inférieur à",
"lte": "Inférieur ou égal à",
"not": "Différent de",
"notBetween": "Pas entre",
"notEmpty": "Non vide",
"equals": "Égal à"
},
"string": {
"contains": "Contient",
"empty": "Vide",
"endsWith": "Se termine par",
"not": "Différent de",
"notEmpty": "Non vide",
"startsWith": "Commence par",
"equals": "Égal à",
"notContains": "Ne contient pas",
"notEndsWith": "Ne termine pas par",
"notStartsWith": "Ne commence pas par"
},
"array": {
"empty": "Vide",
"contains": "Contient",
"not": "Différent de",
"notEmpty": "Non vide",
"without": "Sans",
"equals": "Égal à"
}
},
"add": "Ajouter une condition",
"button": {
"0": "Recherche avancée",
"_": "Recherche avancée (%d)"
},
"clearAll": "Effacer tout",
"condition": "Condition",
"data": "Donnée",
"deleteTitle": "Supprimer la règle de filtrage",
"logicAnd": "Et",
"logicOr": "Ou",
"title": {
"0": "Recherche avancée",
"_": "Recherche avancée (%d)"
},
"value": "Valeur",
"leftTitle": "Désindenter le critère",
"rightTitle": "Indenter le critère"
},
"searchPanes": {
"clearMessage": "Effacer tout",
"count": "{total}",
"title": "Filtres actifs - %d",
"collapse": {
"0": "Volet de recherche",
"_": "Volet de recherche (%d)"
},
"countFiltered": "{shown} ({total})",
"emptyPanes": "Pas de volet de recherche",
"loadMessage": "Chargement du volet de recherche...",
"collapseMessage": "Réduire tout",
"showMessage": "Montrer tout"
},
"buttons": {
"collection": "Collection",
"colvis": "Visibilité colonnes",
"colvisRestore": "Rétablir visibilité",
"copy": "Copier",
"copySuccess": {
"1": "1 ligne copiée dans le presse-papier",
"_": "%d lignes copiées dans le presse-papier"
},
"copyTitle": "Copier dans le presse-papier",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Afficher toutes les lignes",
"_": "Afficher %d lignes",
"1": "Afficher 1 ligne"
},
"pdf": "PDF",
"print": "Imprimer",
"copyKeys": "Appuyez sur ctrl ou u2318 + C pour copier les données du tableau dans votre presse-papier.",
"createState": "Créer un état",
"removeAllStates": "Supprimer tous les états",
"removeState": "Supprimer",
"renameState": "Renommer",
"savedStates": "États sauvegardés",
"stateRestore": "État %d",
"updateState": "Mettre à jour"
},
"decimal": ",",
"datetime": {
"previous": "Précédent",
"next": "Suivant",
"hours": "Heures",
"minutes": "Minutes",
"seconds": "Secondes",
"unknown": "-",
"amPm": [
"am",
"pm"
],
"months": {
"0": "Janvier",
"2": "Mars",
"3": "Avril",
"4": "Mai",
"5": "Juin",
"6": "Juillet",
"8": "Septembre",
"9": "Octobre",
"10": "Novembre",
"1": "Février",
"11": "Décembre",
"7": "Août"
},
"weekdays": [
"Dim",
"Lun",
"Mar",
"Mer",
"Jeu",
"Ven",
"Sam"
]
},
"editor": {
"close": "Fermer",
"create": {
"title": "Créer une nouvelle entrée",
"button": "Nouveau",
"submit": "Créer"
},
"edit": {
"button": "Editer",
"title": "Editer Entrée",
"submit": "Mettre à jour"
},
"remove": {
"button": "Supprimer",
"title": "Supprimer",
"submit": "Supprimer",
"confirm": {
"_": "Êtes-vous sûr de vouloir supprimer %d lignes ?",
"1": "Êtes-vous sûr de vouloir supprimer 1 ligne ?"
}
},
"multi": {
"title": "Valeurs multiples",
"info": "Les éléments sélectionnés contiennent différentes valeurs pour cette entrée. Pour modifier et définir tous les éléments de cette entrée à la même valeur, cliquez ou tapez ici, sinon ils conserveront leurs valeurs individuelles.",
"restore": "Annuler les modifications",
"noMulti": "Ce champ peut être modifié individuellement, mais ne fait pas partie d'un groupe. "
},
"error": {
"system": "Une erreur système s'est produite (<a target=\"\\\" rel=\"nofollow\" href=\"\\\">Plus d'information<\/a>)."
}
},
"stateRestore": {
"removeSubmit": "Supprimer",
"creationModal": {
"button": "Créer",
"order": "Tri",
"paging": "Pagination",
"scroller": "Position du défilement",
"search": "Recherche",
"select": "Sélection",
"columns": {
"search": "Recherche par colonne",
"visible": "Visibilité des colonnes"
},
"name": "Nom :",
"searchBuilder": "Recherche avancée",
"title": "Créer un nouvel état",
"toggleLabel": "Inclus :"
},
"renameButton": "Renommer",
"duplicateError": "Il existe déjà un état avec ce nom.",
"emptyError": "Le nom ne peut pas être vide.",
"emptyStates": "Aucun état sauvegardé",
"removeConfirm": "Voulez vous vraiment supprimer %s ?",
"removeError": "Échec de la suppression de l'état.",
"removeJoiner": "et",
"removeTitle": "Supprimer l'état",
"renameLabel": "Nouveau nom pour %s :",
"renameTitle": "Renommer l'état"
},
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"paginate": {
"first": "Première",
"last": "Dernière",
"next": "Suivante",
"previous": "Précédente"
},
"zeroRecords": "Aucune entrée correspondante trouvée",
"aria": {
"sortAscending": " : activer pour trier la colonne par ordre croissant",
"sortDescending": " : activer pour trier la colonne par ordre décroissant"
},
"infoThousands": " ",
"search": "Rechercher :",
"thousands": " "
}

View File

@@ -1,5 +1,7 @@
<?php <?php
require_once "database.php";
$ldap_domain_name = "woodywood"; $ldap_domain_name = "woodywood";
$handle = ldap_connect("ldap://$ldap_domain_name.local"); $handle = ldap_connect("ldap://$ldap_domain_name.local");
@@ -18,15 +20,25 @@ class UserInfo
} }
} }
function LdapConnect(string $domain, string $username, string $password, ?array $controls): LDAP\Result|false function LdapConnect(string $domain, string $username, string $password): LDAP\Result|false
{ {
global $handle; global $handle;
$bind = ldap_bind_ext($handle, $username . '@' . $domain, $password, $controls); $bind = ldap_bind_ext($handle, $username . '@' . $domain, $password);
LogConnection();
return $bind; return $bind;
} }
function LogConnection() {} function LdapIsConnected(string $domain, string $username, string $password) {
global $handle;
$result = LdapConnect($domain, $username, $password);
ldap_parse_result($handle, $result, $error_code, $matched_dn, $error_message, $referrals, $controls);
$success = $error_code == 0;
LogConnection($username, $success);
return $success;
}
function LogConnection(string $username, bool $success) {
InsertLine(new AuthAttempt($username, $success ? "success" : "failure", $_SERVER['REMOTE_ADDR']));
}
function LdapConnectAndBind() function LdapConnectAndBind()
{ {

View File

@@ -11,12 +11,9 @@ $password = rtrim($_POST["password"]);
require_once "ldap.php"; require_once "ldap.php";
$result = LdapConnect($domain, $user, $password, []); $result = LdapIsConnected($domain, $user, $password, []);
ldap_parse_result($handle, $result, $error_code, $matched_dn, $error_message, $referrals, $controls); if (!$result) {
if ($error_code != 0) {
require_once "templates/login_failed.html"; require_once "templates/login_failed.html";
exit; exit;
} }
@@ -29,5 +26,4 @@ $body = PrintLoginInfo($info);
require_once "templates/login_success.html.php"; require_once "templates/login_success.html.php";
// TODO: Mettre les tentatives dans la db
// TODO: ldaps // TODO: ldaps

View File

@@ -6,6 +6,8 @@
<!-- Bootstrap CSS --> <!-- Bootstrap CSS -->
<link rel="stylesheet" href="../css/index.css"> <link rel="stylesheet" href="../css/index.css">
<link rel="stylesheet" href="../css/main.css"> <link rel="stylesheet" href="../css/main.css">
<!-- Datatables CSS -->
<link rel="stylesheet" href="../css/dataTables.css">
</head> </head>
<body> <body>
<?php <?php

View File

@@ -1,5 +1,7 @@
<?php <?php
$admin_account = "Administrateur";
function PrintListFirsts(string $title, array $liste): string function PrintListFirsts(string $title, array $liste): string
{ {
$result = '<div class="list-group-item"><h5>' . $title . '</h5>'; $result = '<div class="list-group-item"><h5>' . $title . '</h5>';
@@ -13,7 +15,11 @@ function PrintListFirsts(string $title, array $liste): string
function PrintLoginInfo($info) function PrintLoginInfo($info)
{ {
global $admin_account;
$body = '<h2 class="text-center pt-3">Bienvenue ' . $info->fullName . " !</h2>"; $body = '<h2 class="text-center pt-3">Bienvenue ' . $info->fullName . " !</h2>";
if ($info->fullName == $admin_account) {
return $body .= PrintAdminInterface();
}
$body .= '<div class="list-group ">'; $body .= '<div class="list-group ">';
foreach ($info->ous as $ou) { foreach ($info->ous as $ou) {
$body .= '<div class="list-group-item"><h3>' . $ou . "</h3>"; $body .= '<div class="list-group-item"><h3>' . $ou . "</h3>";
@@ -22,8 +28,48 @@ function PrintLoginInfo($info)
$body .= PrintListFirsts("Groupes", LdapGetGroupsInOU($ou)); $body .= PrintListFirsts("Groupes", LdapGetGroupsInOU($ou));
$body .= "</div>"; $body .= "</div>";
$body .= "</div>"; $body .= "</div>";
} }
$body .= "</div>"; $body .= "</div>";
return $body; return $body;
} }
function translateSuccess(string $success) {
return $success == "success" ? "Succès" : "Échec";
}
function PrintAdminInterface(): string
{
$auth_attempts = GetLines();
$body = '<h5 class="text-center">Historique des connexions</h5>';
$body .= '<table id="attempts" class="table table-striped table-bordered">
<thead class="thead-light">
<tr>
<th scope="col">Utilisateur</th>
<th scope="col">Status</th>
<th scope="col">Date</th>
<th scope="col">Adresse IP</th>
</tr>
</thead>';
foreach ($auth_attempts as $attempt) {
$body .= '<tr>';
$body .= '<td>' . $attempt->username . '</td>';
$body .= '<td>' . translateSuccess($attempt->status) . '</td>';
$body .= '<td>' . $attempt->timestamp . '</td>';
$body .= '<td>' . $attempt->ip_address . '</td>';
$body .= '</tr>';
}
$body .= "</table>";
$body .= '
<script src="/js/datatables.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
let table = new DataTable("#attempts", {
language: {
url: "/lang/fr.json",
},
});
});
</script>
';
return $body;
}