mirror of
https://github.com/tips-of-mine/gestion-certificats2.git
synced 2025-07-01 21:38:42 +02:00
Add files via upload
This commit is contained in:
98
app/src/Controllers/AuthController.php
Normal file
98
app/src/Controllers/AuthController.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
use App\Services\LogService; // Pour les logs de connexion
|
||||
|
||||
/**
|
||||
* Contrôleur pour la gestion de l'authentification (connexion, déconnexion).
|
||||
*/
|
||||
class AuthController
|
||||
{
|
||||
private $authService;
|
||||
private $langService;
|
||||
private $logService;
|
||||
|
||||
/**
|
||||
* Constructeur du AuthController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$db = Database::getInstance();
|
||||
$this->authService = new AuthService($db);
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
$this->logService = new LogService(APP_LOG_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le formulaire de connexion.
|
||||
* Redirige vers le tableau de bord si l'utilisateur est déjà connecté.
|
||||
*/
|
||||
public function showLoginForm()
|
||||
{
|
||||
if ($this->authService->isLoggedIn()) {
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Charge les traductions et la classe pour le mode sombre pour la vue
|
||||
global $translations; // Utilise la variable globale chargée dans index.php
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
|
||||
// Récupère les messages d'erreur/succès de la session
|
||||
$error = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']); // Supprime le message après l'avoir affiché
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/auth/login.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la soumission du formulaire de connexion.
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
// Vérifie si la requête est bien un POST
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
// Validation simple des entrées
|
||||
if (empty($username) || empty($password)) {
|
||||
$_SESSION['error'] = $this->langService->__('login_error_empty_fields');
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($this->authService->login($username, $password, $ipAddress)) {
|
||||
// Connexion réussie
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
} else {
|
||||
// Connexion échouée
|
||||
$_SESSION['error'] = $this->langService->__('login_error_credentials');
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Déconnecte l'utilisateur.
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$this->authService->logout($ipAddress);
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
}
|
251
app/src/Controllers/CertificateController.php
Normal file
251
app/src/Controllers/CertificateController.php
Normal file
@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LogService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la gestion des certificats.
|
||||
* (Création, révocation, affichage).
|
||||
*/
|
||||
class CertificateController
|
||||
{
|
||||
private $db;
|
||||
private $authService;
|
||||
private $logService;
|
||||
private $langService;
|
||||
|
||||
/**
|
||||
* Constructeur du CertificateController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = Database::getInstance();
|
||||
$this->authService = new AuthService($this->db);
|
||||
$this->logService = new LogService(APP_LOG_PATH);
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la liste des certificats, regroupés par périmètre fonctionnel.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn()) {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
$userRole = $this->authService->getUserRole();
|
||||
|
||||
// Récupérer les périmètres et les certificats
|
||||
// Joindre pour obtenir le nom du périmètre
|
||||
$certificates = $this->db->query("
|
||||
SELECT
|
||||
c.id, c.name, c.type, c.expiration_date, c.is_revoked, c.revoked_at,
|
||||
fp.name as perimeter_name
|
||||
FROM
|
||||
certificates c
|
||||
LEFT JOIN
|
||||
functional_perimeters fp ON c.functional_perimeter_id = fp.id
|
||||
ORDER BY
|
||||
fp.name IS NULL DESC, fp.name ASC, c.type DESC, c.expiration_date DESC
|
||||
")->fetchAll();
|
||||
|
||||
// Regrouper les certificats par périmètre fonctionnel
|
||||
$groupedCertificates = [];
|
||||
foreach ($certificates as $cert) {
|
||||
$perimeterName = $cert['perimeter_name'] ?? 'Certificats Root'; // Nom pour le groupe Root
|
||||
if (!isset($groupedCertificates[$perimeterName])) {
|
||||
$groupedCertificates[$perimeterName] = [];
|
||||
}
|
||||
$groupedCertificates[$perimeterName][] = $cert;
|
||||
}
|
||||
|
||||
$successMessage = $_SESSION['success'] ?? null;
|
||||
unset($_SESSION['success']);
|
||||
$errorMessage = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/certificates/index.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le formulaire de création d'un nouveau certificat.
|
||||
*/
|
||||
public function showCreateForm()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn()) {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
|
||||
// Récupérer la liste des périmètres fonctionnels pour le sélecteur
|
||||
$perimeters = $this->db->query("SELECT id, name FROM functional_perimeters ORDER BY name ASC")->fetchAll();
|
||||
|
||||
$errorMessage = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/certificates/create.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la soumission du formulaire de création de certificat.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
$subdomainName = trim($_POST['subdomain_name'] ?? '');
|
||||
$functionalPerimeterId = $_POST['functional_perimeter_id'] ?? null;
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$userId = $this->authService->getUserId();
|
||||
|
||||
if (empty($subdomainName) || empty($functionalPerimeterId)) {
|
||||
$_SESSION['error'] = $this->langService->__('cert_create_error_empty_fields');
|
||||
header('Location: /certificates/create');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Récupérer le nom du périmètre fonctionnel pour le script shell
|
||||
$stmt = $this->db->prepare("SELECT name FROM functional_perimeters WHERE id = ?");
|
||||
$stmt->execute([$functionalPerimeterId]);
|
||||
$perimeter = $stmt->fetch();
|
||||
|
||||
if (!$perimeter) {
|
||||
$_SESSION['error'] = $this->langService->__('cert_create_error_perimeter_not_found');
|
||||
header('Location: /certificates/create');
|
||||
exit();
|
||||
}
|
||||
$functionalPerimeterName = $perimeter['name'];
|
||||
|
||||
// Préparer la commande du script shell
|
||||
// Important: utiliser escapeshellarg pour protéger les arguments
|
||||
$command = escapeshellcmd(SCRIPTS_PATH . '/create_cert.sh') . ' ' .
|
||||
escapeshellarg($subdomainName) . ' ' .
|
||||
escapeshellarg($functionalPerimeterName);
|
||||
|
||||
$this->logService->log('info', "Tentative de création du certificat '$subdomainName' pour le périmètre '$functionalPerimeterName'. Commande: '$command'", $userId, $ipAddress);
|
||||
|
||||
// Exécuter le script shell
|
||||
$output = shell_exec($command . ' 2>&1'); // Redirige stderr vers stdout
|
||||
|
||||
// Vérifier le résultat du script (simple vérification de chaîne, une meilleure parsage serait utile)
|
||||
if (strpos($output, "Certificat '${subdomainName}.${functionalPerimeterName}.cert' créé avec succès") !== false) {
|
||||
// Extraire la date d'expiration du certificat créé (en lisant le fichier cert ou en estimant 1 an)
|
||||
$certFileName = "{$subdomainName}.{$functionalPerimeterName}.cert.pem";
|
||||
$fullCertPath = INTERMEDIATE_CA_PATH_BASE . "/{$functionalPerimeterName}/certs/{$certFileName}";
|
||||
|
||||
$expirationDate = (new \DateTime('+1 year'))->format('Y-m-d H:i:s'); // Valeur par défaut
|
||||
if (file_exists($fullCertPath)) {
|
||||
$certInfo = shell_exec("openssl x509 -in " . escapeshellarg($fullCertPath) . " -noout -enddate 2>/dev/null | cut -d= -f2");
|
||||
$expirationTimestamp = strtotime($certInfo);
|
||||
if ($expirationTimestamp) {
|
||||
$expirationDate = date('Y-m-d H:i:s', $expirationTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Enregistrer le certificat dans la base de données
|
||||
$stmt = $this->db->prepare("INSERT INTO certificates (name, type, functional_perimeter_id, expiration_date) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$certFileName, 'simple', $functionalPerimeterId, $expirationDate]);
|
||||
|
||||
$this->logService->log('info', "Certificat '{$certFileName}' créé et enregistré pour le périmètre '{$functionalPerimeterName}'.", $userId, $ipAddress);
|
||||
$_SESSION['success'] = $this->langService->__('cert_create_success');
|
||||
} else {
|
||||
$_SESSION['error'] = $this->langService->__('cert_create_error', ['output' => htmlspecialchars($output)]);
|
||||
$this->logService->log('error', "Échec création certificat '$subdomainName' pour périmètre '$functionalPerimeterName'. Output: $output", $userId, $ipAddress);
|
||||
}
|
||||
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la révocation d'un certificat.
|
||||
*/
|
||||
public function revoke()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
$certificateId = $_POST['certificate_id'] ?? null;
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$userId = $this->authService->getUserId();
|
||||
|
||||
if (empty($certificateId)) {
|
||||
$_SESSION['error'] = $this->langService->__('cert_revoke_error_id_missing');
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Récupérer les informations du certificat depuis la DB
|
||||
$stmt = $this->db->prepare("SELECT c.name, c.type, fp.name as perimeter_name FROM certificates c LEFT JOIN functional_perimeters fp ON c.functional_perimeter_id = fp.id WHERE c.id = ?");
|
||||
$stmt->execute([$certificateId]);
|
||||
$cert = $stmt->fetch();
|
||||
|
||||
if (!$cert) {
|
||||
$_SESSION['error'] = $this->langService->__('cert_revoke_error_not_found');
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Empêcher la révocation des certificats Root ou Intermédiaires via l'interface
|
||||
if ($cert['type'] === 'root' || $cert['type'] === 'intermediate') {
|
||||
$_SESSION['error'] = $this->langService->__('cert_revoke_error_ca_revocation');
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Préparer le nom de base du certificat pour le script (sans l'extension .pem)
|
||||
$certBaseName = str_replace('.cert.pem', '.cert', $cert['name']);
|
||||
$functionalPerimeterName = $cert['perimeter_name'];
|
||||
|
||||
// Vérifier si le certificat n'est pas déjà révoqué dans la DB
|
||||
if ($cert['is_revoked']) {
|
||||
$_SESSION['error'] = $this->langService->__('cert_revoke_error_already_revoked');
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Appeler le script shell de révocation
|
||||
$command = escapeshellcmd(SCRIPTS_PATH . '/revoke_cert.sh') . ' ' .
|
||||
escapeshellarg($certBaseName) . ' ' .
|
||||
escapeshellarg($functionalPerimeterName);
|
||||
|
||||
$this->logService->log('info', "Tentative de révocation du certificat '{$cert['name']}' pour le périmètre '$functionalPerimeterName'. Commande: '$command'", $userId, $ipAddress);
|
||||
|
||||
$output = shell_exec($command . ' 2>&1');
|
||||
|
||||
if (strpos($output, "Certificat '$certBaseName' révoqué avec succès.") !== false) {
|
||||
// Mettre à jour le statut du certificat dans la base de données
|
||||
$stmt = $this->db->prepare("UPDATE certificates SET is_revoked = TRUE, revoked_at = NOW() WHERE id = ?");
|
||||
$stmt->execute([$certificateId]);
|
||||
|
||||
$this->logService->log('info', "Certificat '{$cert['name']}' révoqué et enregistré en DB.", $userId, $ipAddress);
|
||||
$_SESSION['success'] = $this->langService->__('cert_revoke_success');
|
||||
} else {
|
||||
$_SESSION['error'] = $this->langService->__('cert_revoke_error', ['output' => htmlspecialchars($output)]);
|
||||
$this->logService->log('error', "Échec révocation certificat '{$cert['name']}': $output", $userId, $ipAddress);
|
||||
}
|
||||
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
}
|
47
app/src/Controllers/DashboardController.php
Normal file
47
app/src/Controllers/DashboardController.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la page du tableau de bord.
|
||||
*/
|
||||
class DashboardController
|
||||
{
|
||||
private $authService;
|
||||
private $langService;
|
||||
|
||||
/**
|
||||
* Constructeur du DashboardController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->authService = new AuthService(Database::getInstance());
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le tableau de bord.
|
||||
* Redirige vers la page de connexion si l'utilisateur n'est pas connecté.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn()) {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Récupère les traductions et les informations pour la vue
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$username = $this->authService->getUsername();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
$userRole = $this->authService->getUserRole(); // Pour afficher/masquer certains éléments
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/dashboard/index.php';
|
||||
}
|
||||
}
|
45
app/src/Controllers/HomeController.php
Normal file
45
app/src/Controllers/HomeController.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la page d'accueil.
|
||||
* Redirige vers le tableau de bord si l'utilisateur est déjà connecté.
|
||||
*/
|
||||
class HomeController
|
||||
{
|
||||
private $authService;
|
||||
private $langService;
|
||||
|
||||
/**
|
||||
* Constructeur du HomeController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->authService = new AuthService(Database::getInstance());
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la page d'accueil ou redirige.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Si l'utilisateur est déjà connecté, le rediriger vers le tableau de bord
|
||||
if ($this->authService->isLoggedIn()) {
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Sinon, afficher la page de connexion
|
||||
// On réutilise la logique de showLoginForm de AuthController pour éviter la duplication.
|
||||
// On pourrait aussi faire un require de la vue directement.
|
||||
$authController = new AuthController();
|
||||
$authController->showLoginForm();
|
||||
}
|
||||
}
|
76
app/src/Controllers/OcspController.php
Normal file
76
app/src/Controllers/OcspController.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\LogService;
|
||||
|
||||
/**
|
||||
* Contrôleur simple pour simuler un répondeur OCSP.
|
||||
* ATTENTION: Ce n'est pas une implémentation robuste d'un répondeur OCSP de production.
|
||||
* Un répondeur OCSP réel écouterait les requêtes binaires et répondrait en conséquence.
|
||||
* Ce contrôleur est juste pour illustrer le point d'entrée.
|
||||
*/
|
||||
class OcspController
|
||||
{
|
||||
private $db;
|
||||
private $logService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = Database::getInstance();
|
||||
$this->logService = new LogService(APP_LOG_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère les requêtes OCSP.
|
||||
* En production, cette méthode devrait lire la requête OCSP binaire du corps de la requête HTTP,
|
||||
* puis utiliser une bibliothèque ou un outil OpenSSL pour générer une réponse OCSP valide.
|
||||
* Pour ce POC, nous allons juste logguer et retourner un message simple.
|
||||
*/
|
||||
public function handleRequest()
|
||||
{
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$requestMethod = $_SERVER['REQUEST_METHOD'];
|
||||
$requestBody = file_get_contents('php://input'); // Récupère le corps de la requête (pour les requêtes POST OCSP)
|
||||
|
||||
$this->logService->log('info', "Requête OCSP reçue. Méthode: {$requestMethod}, Taille du corps: " . strlen($requestBody) . " octets.", null, $ipAddress);
|
||||
|
||||
// En-tête pour une réponse OCSP (Content-Type standard)
|
||||
header('Content-Type: application/ocsp-response');
|
||||
http_response_code(200); // OK
|
||||
|
||||
// --- Logique OCSP simplifiée pour POC ---
|
||||
// En réalité, vous devriez:
|
||||
// 1. Parser la requête OCSP ($requestBody) pour identifier le certificat à vérifier et son émetteur.
|
||||
// 2. Chercher le statut de ce certificat dans votre base de données (table `certificates`).
|
||||
// 3. Utiliser OpenSSL ou une bibliothèque PKI pour générer une réponse OCSP signée.
|
||||
// Ceci impliquerait d'avoir le certificat et la clé du répondeur OCSP (souvent le CA intermédiaire lui-même)
|
||||
// et l'index.txt et crl.pem du CA émetteur.
|
||||
// 4. Envoyer la réponse binaire.
|
||||
|
||||
// Pour l'exemple, nous allons retourner une réponse factice ou une erreur.
|
||||
// Une vraie réponse OCSP est un format binaire ASN.1.
|
||||
// Retourner du texte est INCORRECT pour un client OCSP.
|
||||
// Ceci est une SIMULATION pour le POC.
|
||||
|
||||
// Si la requête est un GET (pour les petites requêtes), le "cert" et l'"issuer" pourraient être dans les paramètres
|
||||
// if ($requestMethod === 'GET' && isset($_GET['cert']) && isset($_GET['issuer'])) {
|
||||
// $certHash = $_GET['cert'];
|
||||
// $issuerHash = $_GET['issuer'];
|
||||
// $this->logService->log('info', "Requête OCSP GET pour Cert: $certHash, Issuer: $issuerHash", null, $ipAddress);
|
||||
// // Simuler une réponse "bon" ou "révoqué"
|
||||
// echo "OCSP Response: Good (Simulated)";
|
||||
// } else {
|
||||
// echo "OCSP Responder (POC): Expects binary POST request or specific GET parameters.";
|
||||
// }
|
||||
|
||||
// Retourner une réponse OCSP vide ou d'erreur (pour les clients qui s'attendent à du binaire)
|
||||
// Un client OCSP s'attend à une réponse binaire, pas du texte.
|
||||
// Pour éviter les erreurs chez le client OCSP, il vaut mieux renvoyer une réponse binaire valide
|
||||
// ou au moins une réponse HTTP 500 pour indiquer un problème.
|
||||
// Pour un POC minimaliste sans générer de binaire:
|
||||
// C'est juste un marqueur de place. La vraie réponse serait générée par OpenSSL.
|
||||
echo ""; // Réponse vide ou générer une vraie réponse binaire
|
||||
}
|
||||
}
|
147
app/src/Controllers/PerimeterController.php
Normal file
147
app/src/Controllers/PerimeterController.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LogService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la gestion des périmètres fonctionnels.
|
||||
* (Création, affichage).
|
||||
*/
|
||||
class PerimeterController
|
||||
{
|
||||
private $db;
|
||||
private $authService;
|
||||
private $logService;
|
||||
private $langService;
|
||||
|
||||
/**
|
||||
* Constructeur du PerimeterController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = Database::getInstance();
|
||||
$this->authService = new AuthService($this->db);
|
||||
$this->logService = new LogService(APP_LOG_PATH);
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la liste des périmètres fonctionnels.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn()) {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
|
||||
$perimeters = $this->db->query("SELECT * FROM functional_perimeters ORDER BY name ASC")->fetchAll();
|
||||
|
||||
$successMessage = $_SESSION['success'] ?? null;
|
||||
unset($_SESSION['success']);
|
||||
$errorMessage = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/perimeters/index.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le formulaire de création d'un nouveau périmètre fonctionnel.
|
||||
*/
|
||||
public function showCreateForm()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn() || $this->authService->getUserRole() !== 'admin') {
|
||||
$_SESSION['error'] = $this->langService->__('permission_denied');
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
|
||||
$errorMessage = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/perimeters/create.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la soumission du formulaire de création de périmètre fonctionnel.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn() || $this->authService->getUserRole() !== 'admin' || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
$_SESSION['error'] = $this->langService->__('permission_denied');
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
$perimeterName = trim($_POST['name'] ?? '');
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$userId = $this->authService->getUserId();
|
||||
|
||||
if (empty($perimeterName)) {
|
||||
$_SESSION['error'] = $this->langService->__('perimeter_create_error_empty_name');
|
||||
header('Location: /perimeters/create');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Vérifier si le périmètre existe déjà
|
||||
$stmt = $this->db->prepare("SELECT COUNT(*) FROM functional_perimeters WHERE name = ?");
|
||||
$stmt->execute([$perimeterName]);
|
||||
if ($stmt->fetchColumn() > 0) {
|
||||
$_SESSION['error'] = $this->langService->__('perimeter_create_error_exists');
|
||||
header('Location: /perimeters/create');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Appeler le script shell pour créer le certificat intermédiaire
|
||||
$command = escapeshellcmd(SCRIPTS_PATH . '/create_intermediate_cert.sh') . ' ' . escapeshellarg($perimeterName);
|
||||
|
||||
$this->logService->log('info', "Tentative de création du périmètre '$perimeterName' et de son certificat intermédiaire. Commande: '$command'", $userId, $ipAddress);
|
||||
|
||||
$output = shell_exec($command . ' 2>&1');
|
||||
|
||||
if (strpos($output, "Certificat Intermédiaire CA pour '$perimeterName' créé avec succès") !== false) {
|
||||
// Enregistrer le périmètre dans la base de données
|
||||
$stmt = $this->db->prepare("INSERT INTO functional_perimeters (name, intermediate_cert_name) VALUES (?, ?)");
|
||||
$intermediateCertFileName = "intermediate.cert.pem"; // Nom générique du fichier pour l'intermédiaire
|
||||
$stmt->execute([$perimeterName, $intermediateCertFileName]);
|
||||
$perimeterId = $this->db->lastInsertId();
|
||||
|
||||
// Enregistrer le certificat intermédiaire dans la table des certificats
|
||||
$fullCertPath = INTERMEDIATE_CA_PATH_BASE . "/{$perimeterName}/certs/intermediate.cert.pem";
|
||||
$expirationDate = (new \DateTime('+5 years'))->format('Y-m-d H:i:s'); // Valeur par défaut
|
||||
if (file_exists($fullCertPath)) {
|
||||
$certInfo = shell_exec("openssl x509 -in " . escapeshellarg($fullCertPath) . " -noout -enddate 2>/dev/null | cut -d= -f2");
|
||||
$expirationTimestamp = strtotime($certInfo);
|
||||
if ($expirationTimestamp) {
|
||||
$expirationDate = date('Y-m-d H:i:s', $expirationTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->db->prepare("INSERT INTO certificates (name, type, functional_perimeter_id, expiration_date) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$intermediateCertFileName, 'intermediate', $perimeterId, $expirationDate]);
|
||||
|
||||
|
||||
$this->logService->log('info', "Périmètre fonctionnel '$perimeterName' créé avec succès et certificat intermédiaire généré.", $userId, $ipAddress);
|
||||
$_SESSION['success'] = $this->langService->__('perimeter_create_success');
|
||||
} else {
|
||||
$_SESSION['error'] = $this->langService->__('perimeter_create_error', ['output' => htmlspecialchars($output)]);
|
||||
$this->logService->log('error', "Échec création périmètre '$perimeterName': $output", $userId, $ipAddress);
|
||||
}
|
||||
|
||||
header('Location: /perimeters');
|
||||
exit();
|
||||
}
|
||||
}
|
212
app/src/Controllers/UserController.php
Normal file
212
app/src/Controllers/UserController.php
Normal file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LogService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la gestion des utilisateurs.
|
||||
* (Création, suppression, affichage).
|
||||
* Nécessite un rôle 'admin'.
|
||||
*/
|
||||
class UserController
|
||||
{
|
||||
private $db;
|
||||
private $authService;
|
||||
private $logService;
|
||||
private $langService;
|
||||
|
||||
/**
|
||||
* Constructeur du UserController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = Database::getInstance();
|
||||
$this->authService = new AuthService($this->db);
|
||||
$this->logService = new LogService(APP_LOG_PATH);
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si l'utilisateur est un administrateur.
|
||||
* Si non, redirige et affiche un message d'erreur.
|
||||
*/
|
||||
private function requireAdmin()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn() || $this->authService->getUserRole() !== 'admin') {
|
||||
$_SESSION['error'] = $this->langService->__('permission_denied');
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche la liste des utilisateurs.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
|
||||
$users = $this->db->query("SELECT id, username, role, created_at FROM users ORDER BY username ASC")->fetchAll();
|
||||
|
||||
$successMessage = $_SESSION['success'] ?? null;
|
||||
unset($_SESSION['success']);
|
||||
$errorMessage = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/users/index.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Affiche le formulaire de création d'un nouvel utilisateur.
|
||||
*/
|
||||
public function showCreateForm()
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
global $translations;
|
||||
$currentLang = $this->langService->getLanguage();
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
|
||||
$errorMessage = $_SESSION['error'] ?? null;
|
||||
unset($_SESSION['error']);
|
||||
|
||||
require_once APP_ROOT_DIR . '/src/Views/users/create.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Traite la soumission du formulaire de création d'utilisateur.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: /users/create');
|
||||
exit();
|
||||
}
|
||||
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$role = $_POST['role'] ?? 'user';
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$adminUserId = $this->authService->getUserId();
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
$_SESSION['error'] = $this->langService->__('user_create_error_empty_fields');
|
||||
header('Location: /users/create');
|
||||
exit();
|
||||
}
|
||||
if (!in_array($role, ['admin', 'user'])) {
|
||||
$_SESSION['error'] = $this->langService->__('user_create_error_invalid_role');
|
||||
header('Location: /users/create');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Vérifier si l'utilisateur existe déjà
|
||||
$stmt = $this->db->prepare("SELECT COUNT(*) FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
if ($stmt->fetchColumn() > 0) {
|
||||
$_SESSION['error'] = $this->langService->__('user_create_error_exists', ['username' => htmlspecialchars($username)]);
|
||||
header('Location: /users/create');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Hacher le mot de passe
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
try {
|
||||
$stmt = $this->db->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$username, $hashedPassword, $role]);
|
||||
$newUserId = $this->db->lastInsertId();
|
||||
|
||||
$this->logService->log('info', "Nouvel utilisateur '{$username}' ({$role}) créé par l'administrateur.", $adminUserId, $ipAddress);
|
||||
$_SESSION['success'] = $this->langService->__('user_create_success', ['username' => htmlspecialchars($username)]);
|
||||
} catch (\PDOException $e) {
|
||||
error_log("Erreur lors de la création de l'utilisateur: " . $e->getMessage());
|
||||
$_SESSION['error'] = $this->langService->__('user_create_error_db');
|
||||
$this->logService->log('error', "Échec création utilisateur '{$username}': " . $e->getMessage(), $adminUserId, $ipAddress);
|
||||
}
|
||||
|
||||
header('Location: /users');
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un utilisateur.
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->requireAdmin();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: /users');
|
||||
exit();
|
||||
}
|
||||
|
||||
$userIdToDelete = $_POST['user_id'] ?? null;
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$adminUserId = $this->authService->getUserId();
|
||||
|
||||
if (empty($userIdToDelete)) {
|
||||
$_SESSION['error'] = $this->langService->__('user_delete_error_id_missing');
|
||||
header('Location: /users');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Empêcher un admin de se supprimer lui-même (ou le dernier admin)
|
||||
if ($userIdToDelete == $adminUserId) {
|
||||
$_SESSION['error'] = $this->langService->__('user_delete_error_self_delete');
|
||||
header('Location: /users');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Vérifier s'il reste au moins un administrateur après la suppression
|
||||
$stmt = $this->db->prepare("SELECT role FROM users WHERE id = ?");
|
||||
$stmt->execute([$userIdToDelete]);
|
||||
$userRoleToDelete = $stmt->fetchColumn();
|
||||
|
||||
if ($userRoleToDelete === 'admin') {
|
||||
$stmt = $this->db->query("SELECT COUNT(*) FROM users WHERE role = 'admin'");
|
||||
$adminCount = $stmt->fetchColumn();
|
||||
if ($adminCount <= 1) {
|
||||
$_SESSION['error'] = $this->langService->__('user_delete_error_last_admin');
|
||||
header('Location: /users');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Récupérer le nom d'utilisateur avant suppression pour le log
|
||||
$stmt = $this->db->prepare("SELECT username FROM users WHERE id = ?");
|
||||
$stmt->execute([$userIdToDelete]);
|
||||
$usernameToDelete = $stmt->fetchColumn();
|
||||
|
||||
$stmt = $this->db->prepare("DELETE FROM users WHERE id = ?");
|
||||
$stmt->execute([$userIdToDelete]);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$this->logService->log('info', "Utilisateur '{$usernameToDelete}' (ID: {$userIdToDelete}) supprimé par l'administrateur.", $adminUserId, $ipAddress);
|
||||
$_SESSION['success'] = $this->langService->__('user_delete_success', ['username' => htmlspecialchars($usernameToDelete)]);
|
||||
} else {
|
||||
$_SESSION['error'] = $this->langService->__('user_delete_error_not_found');
|
||||
}
|
||||
} catch (\PDOException $e) {
|
||||
error_log("Erreur lors de la suppression de l'utilisateur: " . $e->getMessage());
|
||||
$_SESSION['error'] = $this->langService->__('user_delete_error_db');
|
||||
$this->logService->log('error', "Échec suppression utilisateur ID: {$userIdToDelete}: " . $e->getMessage(), $adminUserId, $ipAddress);
|
||||
}
|
||||
|
||||
header('Location: /users');
|
||||
exit();
|
||||
}
|
||||
}
|
42
app/src/Core/Autoloader.php
Normal file
42
app/src/Core/Autoloader.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
/**
|
||||
* Autoloader PSR-4 pour les classes de l'application.
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
/**
|
||||
* Enregistre l'autoloader dans la pile de chargement de PHP.
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
spl_autoload_register(function ($class) {
|
||||
// Préfixe du namespace de l'application
|
||||
$prefix = 'App\\';
|
||||
|
||||
// Répertoire de base où se trouvent les fichiers de l'application (src/)
|
||||
$baseDir = __DIR__ . '/../'; // Cela pointe vers /app/src/
|
||||
|
||||
// Vérifie si la classe utilise le préfixe du namespace
|
||||
$len = strlen($prefix);
|
||||
if (strncmp($prefix, $class, $len) !== 0) {
|
||||
// Si la classe n'utilise pas notre préfixe, passe au prochain autoloader enregistré
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupère le nom de la classe relatif au namespace de base
|
||||
$relativeClass = substr($class, $len);
|
||||
|
||||
// Convertit le nom de la classe relatif en chemin de fichier
|
||||
// Remplace les séparateurs de namespace par des séparateurs de répertoire et ajoute l'extension .php
|
||||
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
|
||||
|
||||
// Si le fichier existe, l'inclut
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
71
app/src/Core/Database.php
Normal file
71
app/src/Core/Database.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Classe singleton pour gérer la connexion à la base de données MySQL.
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
private static $instance = null; // Instance unique de la connexion PDO
|
||||
private $conn; // L'objet de connexion PDO
|
||||
|
||||
/**
|
||||
* Constructeur privé pour empêcher l'instanciation directe (Singleton).
|
||||
*
|
||||
* @param string $host Nom d'hôte de la base de données
|
||||
* @param string $dbName Nom de la base de données
|
||||
* @param string $user Nom d'utilisateur de la base de données
|
||||
* @param string $password Mot de passe de la base de données
|
||||
* @throws PDOException Si la connexion échoue
|
||||
*/
|
||||
private function __construct($host, $dbName, $user, $password)
|
||||
{
|
||||
$dsn = "mysql:host=$host;dbname=$dbName;charset=utf8mb4";
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Rapporte les erreurs SQL sous forme d'exceptions
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Récupère les résultats sous forme de tableaux associatifs
|
||||
PDO::ATTR_EMULATE_PREPARES => false, // Désactive l'émulation des requêtes préparées pour une meilleure sécurité
|
||||
];
|
||||
try {
|
||||
$this->conn = new PDO($dsn, $user, $password, $options);
|
||||
} catch (PDOException $e) {
|
||||
// Log l'erreur plutôt que de l'afficher directement en production
|
||||
error_log("Erreur de connexion à la base de données: " . $e->getMessage());
|
||||
// Relance l'exception après l'avoir logguée
|
||||
throw new PDOException("Impossible de se connecter à la base de données.", (int)$e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connecte à la base de données ou retourne l'instance existante.
|
||||
*
|
||||
* @param string $host Nom d'hôte de la base de données
|
||||
* @param string $dbName Nom de la base de données
|
||||
* @param string $user Nom d'utilisateur de la base de données
|
||||
* @param string $password Mot de passe de la base de données
|
||||
*/
|
||||
public static function connect($host, $dbName, $user, $password)
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Database($host, $dbName, $user, $password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne l'instance PDO de la connexion à la base de données.
|
||||
*
|
||||
* @return PDO L'objet de connexion PDO
|
||||
* @throws \Exception Si la connexion n'a pas été établie au préalable
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
throw new \Exception("La base de données n'est pas connectée. Appelez Database::connect() d'abord.");
|
||||
}
|
||||
return self::$instance->conn;
|
||||
}
|
||||
}
|
91
app/src/Core/Router.php
Normal file
91
app/src/Core/Router.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use App\Services\AuthService;
|
||||
use App\Utils\DarkMode; // Assurez-vous d'importer la classe DarkMode
|
||||
|
||||
/**
|
||||
* Simple routeur pour diriger les requêtes HTTP vers les contrôleurs appropriés.
|
||||
*/
|
||||
class Router
|
||||
{
|
||||
private $routes = []; // Tableau pour stocker toutes les routes définies
|
||||
private $authService; // Service d'authentification pour vérifier l'accès aux routes protégées
|
||||
|
||||
/**
|
||||
* Constructeur du routeur.
|
||||
* Initialise le service d'authentification.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->authService = new AuthService(Database::getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute une nouvelle route au routeur.
|
||||
*
|
||||
* @param string $method Méthode HTTP (GET, POST, etc.)
|
||||
* @param string $path Chemin de l'URL (ex: '/', '/dashboard')
|
||||
* @param string $controllerAction Action du contrôleur (ex: 'HomeController@index')
|
||||
* @param bool $requiresAuth Indique si la route nécessite une authentification (true par défaut)
|
||||
*/
|
||||
public function addRoute($method, $path, $controllerAction, $requiresAuth = false)
|
||||
{
|
||||
$this->routes[] = [
|
||||
'method' => $method,
|
||||
'path' => $path,
|
||||
'controllerAction' => $controllerAction,
|
||||
'requiresAuth' => $requiresAuth
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatche la requête entrante vers le contrôleur et l'action correspondants.
|
||||
* Gère également les redirections pour l'authentification et les erreurs 404.
|
||||
*/
|
||||
public function dispatch()
|
||||
{
|
||||
// Récupère le chemin de l'URL demandé (sans les paramètres GET)
|
||||
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
// Récupère la méthode HTTP de la requête (GET, POST, etc.)
|
||||
$requestMethod = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
foreach ($this->routes as $route) {
|
||||
// Vérifie si la méthode et le chemin correspondent à une route définie
|
||||
if ($route['method'] === $requestMethod && $route['path'] === $requestUri) {
|
||||
// Si la route nécessite une authentification et que l'utilisateur n'est pas connecté
|
||||
if ($route['requiresAuth'] && !$this->authService->isLoggedIn()) {
|
||||
// Redirige vers la page de connexion
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Sépare le nom du contrôleur et de l'action
|
||||
list($controllerName, $actionName) = explode('@', $route['controllerAction']);
|
||||
// Construit le nom complet de la classe du contrôleur avec son namespace
|
||||
$controllerClass = "App\\Controllers\\" . $controllerName;
|
||||
|
||||
// Vérifie si la classe du contrôleur existe
|
||||
if (class_exists($controllerClass)) {
|
||||
// Instancie le contrôleur
|
||||
$controller = new $controllerClass();
|
||||
// Vérifie si la méthode de l'action existe dans le contrôleur
|
||||
if (method_exists($controller, $actionName)) {
|
||||
// Appelle la méthode de l'action
|
||||
$controller->$actionName();
|
||||
return; // Termine l'exécution après avoir traité la route
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucune route correspondante n'est trouvée, retourne une erreur 404
|
||||
http_response_code(404);
|
||||
// Utilisation de DarkMode::getBodyClass() pour éviter l'erreur de syntaxe
|
||||
echo "<!DOCTYPE html><html lang=\"fr\"><head><meta charset=\"UTF-8\"><title>404 Non Trouvé</title><link rel=\"stylesheet\" href=\"/css/style.css\"></head><body class=\"" . DarkMode::getBodyClass() . "\">";
|
||||
echo "<div class=\"container\"><h1>404 Non Trouvé</h1>";
|
||||
echo "<p>La page que vous avez demandée n'a pas pu être trouvée.</p>";
|
||||
echo "<p><a href=\"/\">Retour à l'accueil</a></p></div></body></html>";
|
||||
}
|
||||
}
|
84
app/src/Lang/de.json
Normal file
84
app/src/Lang/de.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"app_name": "Zertifikatsverwaltung",
|
||||
"login_title": "Login - Zertifikatsverwaltung",
|
||||
"login_heading": "Anmeldung zur Anwendung",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"login_button": "Anmelden",
|
||||
"dark_mode": "Dunkler Modus",
|
||||
"light_mode": "Heller Modus",
|
||||
"dashboard_title": "Dashboard",
|
||||
"welcome": "Willkommen, {username}!",
|
||||
"logout": "Abmelden",
|
||||
"certificates": "Zertifikate",
|
||||
"functional_perimeters": "Funktionale Perimeter",
|
||||
"users": "Benutzer",
|
||||
"quick_actions": "Schnellaktionen",
|
||||
"create_new_certificate": "Neues Zertifikat erstellen",
|
||||
"create_new_perimeter": "Neuen Perimeter erstellen",
|
||||
"new_user": "Neuer Benutzer",
|
||||
"certificate_name": "Zertifikatsname",
|
||||
"type": "Typ",
|
||||
"expiration_date": "Ablaufdatum",
|
||||
"status": "Status",
|
||||
"revoked": "Widerrufen",
|
||||
"active": "Aktiv",
|
||||
"actions": "Aktionen",
|
||||
"revoke_certificate": "Widerrufen",
|
||||
"confirm_revoke": "Sind Sie sicher, dass Sie dieses Zertifikat widerrufen möchten? Diese Aktion ist irreversibel und macht das Zertifikat ungültig.",
|
||||
"perimeter_name": "Perimetername",
|
||||
"intermediate_cert_file": "Zwischenzertifikat-Datei",
|
||||
"created_at": "Erstellt am",
|
||||
"create_perimeter_button": "Perimeter erstellen",
|
||||
"create_new_user": "Neuen Benutzer erstellen",
|
||||
"user_role": "Rolle",
|
||||
"admin": "Administrator",
|
||||
"user": "Benutzer",
|
||||
"create_user_button": "Benutzer erstellen",
|
||||
"delete_user": "Löschen",
|
||||
"confirm_delete_user": "Sind Sie sicher, dass Sie diesen Benutzer löschen möchten? Diese Aktion ist irreversibel.",
|
||||
"new_certificate_heading": "Neues Zertifikat erstellen",
|
||||
"subdomain_name": "Subdomain / CN-Name",
|
||||
"select_perimeter": "Funktionalen Perimeter auswählen",
|
||||
"select_perimeter_placeholder": "Wählen Sie einen Perimeter",
|
||||
"create_certificate": "Zertifikat erstellen",
|
||||
"root": "Root",
|
||||
"intermediate": "Zwischen",
|
||||
"simple": "Einfach",
|
||||
"back_to_dashboard": "Zurück zum Dashboard",
|
||||
"back_to_cert_list": "Zurück zur Zertifikatsliste",
|
||||
"back_to_perimeter_list": "Zurück zur Perimeterliste",
|
||||
"back_to_user_list": "Zurück zur Benutzerliste",
|
||||
"no_certificates_yet": "Es wurden noch keine Zertifikate erstellt.",
|
||||
"no_perimeters_yet": "Es wurden noch keine funktionalen Perimeter erstellt.",
|
||||
"no_users_yet": "Es wurden noch keine Benutzer erstellt.",
|
||||
"login_error_empty_fields": "Bitte geben Sie Ihren Benutzernamen und Ihr Passwort ein.",
|
||||
"login_error_credentials": "Falscher Benutzername oder Passwort.",
|
||||
"permission_denied": "Sie haben nicht die notwendigen Berechtigungen, um auf diese Seite zuzugreifen.",
|
||||
"cert_create_error_empty_fields": "Subdomain-Name und funktionaler Perimeter sind erforderlich.",
|
||||
"cert_create_error_perimeter_not_found": "Ausgewählter funktionaler Perimeter nicht gefunden.",
|
||||
"cert_create_success": "Zertifikat erfolgreich erstellt.",
|
||||
"cert_create_error": "Fehler beim Erstellen des Zertifikats: {output}",
|
||||
"cert_revoke_error_id_missing": "Zertifikats-ID für den Widerruf fehlt.",
|
||||
"cert_revoke_error_not_found": "Zertifikat für den Widerruf nicht gefunden.",
|
||||
"cert_revoke_error_ca_revocation": "ROOT- und INTERMEDIATE-Zertifikate können aus PKI-Sicherheitsgründen nicht über die Schnittstelle widerrufen werden.",
|
||||
"cert_revoke_error_already_revoked": "Dieses Zertifikat ist bereits widerrufen.",
|
||||
"cert_revoke_success": "Zertifikat erfolgreich widerrufen.",
|
||||
"cert_revoke_error": "Fehler beim Widerrufen des Zertifikats: {output}",
|
||||
"perimeter_create_error_empty_name": "Der Name des funktionalen Perimeters ist erforderlich.",
|
||||
"perimeter_create_error_exists": "Ein funktionaler Perimeter mit diesem Namen existiert bereits.",
|
||||
"perimeter_create_success": "Funktionaler Perimeter und sein Zwischenzertifikat erfolgreich erstellt.",
|
||||
"perimeter_create_error": "Fehler beim Erstellen des funktionalen Perimeters: {output}",
|
||||
"user_create_error_empty_fields": "Benutzername und Passwort sind erforderlich.",
|
||||
"user_create_error_invalid_role": "Ungültige Benutzerrolle.",
|
||||
"user_create_error_exists": "Ein Benutzer mit dem Benutzernamen '{username}' existiert bereits.",
|
||||
"user_create_success": "Benutzer '{username}' erfolgreich erstellt.",
|
||||
"user_create_error_db": "Fehler beim Erstellen des Benutzers in der Datenbank.",
|
||||
"user_delete_error_id_missing": "Benutzer-ID für das Löschen fehlt.",
|
||||
"user_delete_error_self_delete": "Sie können Ihr eigenes Konto nicht löschen.",
|
||||
"user_delete_error_last_admin": "Das letzte Administratorkonto kann nicht gelöscht werden.",
|
||||
"user_delete_success": "Benutzer '{username}' erfolgreich gelöscht.",
|
||||
"user_delete_error_not_found": "Benutzer zum Löschen nicht gefunden.",
|
||||
"user_delete_error_db": "Fehler beim Löschen des Benutzers aus der Datenbank.",
|
||||
"self_delete_not_allowed": "Sie können sich nicht selbst löschen."
|
||||
}
|
84
app/src/Lang/en.json
Normal file
84
app/src/Lang/en.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"app_name": "Certificate Management",
|
||||
"login_title": "Login - Certificate Management",
|
||||
"login_heading": "Log in to the application",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"login_button": "Log In",
|
||||
"dark_mode": "Dark Mode",
|
||||
"light_mode": "Light Mode",
|
||||
"dashboard_title": "Dashboard",
|
||||
"welcome": "Welcome, {username}!",
|
||||
"logout": "Log Out",
|
||||
"certificates": "Certificates",
|
||||
"functional_perimeters": "Functional Perimeters",
|
||||
"users": "Users",
|
||||
"quick_actions": "Quick Actions",
|
||||
"create_new_certificate": "Create a new certificate",
|
||||
"create_new_perimeter": "Create a new perimeter",
|
||||
"new_user": "New user",
|
||||
"certificate_name": "Certificate Name",
|
||||
"type": "Type",
|
||||
"expiration_date": "Expiration Date",
|
||||
"status": "Status",
|
||||
"revoked": "Revoked",
|
||||
"active": "Active",
|
||||
"actions": "Actions",
|
||||
"revoke_certificate": "Revoke",
|
||||
"confirm_revoke": "Are you sure you want to revoke this certificate? This action is irreversible and will invalidate the certificate.",
|
||||
"perimeter_name": "Perimeter Name",
|
||||
"intermediate_cert_file": "Intermediate Certificate File",
|
||||
"created_at": "Created On",
|
||||
"create_perimeter_button": "Create Perimeter",
|
||||
"create_new_user": "Create a new user",
|
||||
"user_role": "Role",
|
||||
"admin": "Administrator",
|
||||
"user": "User",
|
||||
"create_user_button": "Create User",
|
||||
"delete_user": "Delete",
|
||||
"confirm_delete_user": "Are you sure you want to delete this user? This action is irreversible.",
|
||||
"new_certificate_heading": "Create a New Certificate",
|
||||
"subdomain_name": "Subdomain / CN Name",
|
||||
"select_perimeter": "Select a Functional Perimeter",
|
||||
"select_perimeter_placeholder": "Choose a perimeter",
|
||||
"create_certificate": "Create Certificate",
|
||||
"root": "Root",
|
||||
"intermediate": "Intermediate",
|
||||
"simple": "Simple",
|
||||
"back_to_dashboard": "Back to Dashboard",
|
||||
"back_to_cert_list": "Back to Certificate List",
|
||||
"back_to_perimeter_list": "Back to Perimeter List",
|
||||
"back_to_user_list": "Back to User List",
|
||||
"no_certificates_yet": "No certificates have been created yet.",
|
||||
"no_perimeters_yet": "No functional perimeters have been created yet.",
|
||||
"no_users_yet": "No users have been created yet.",
|
||||
"login_error_empty_fields": "Please enter your username and password.",
|
||||
"login_error_credentials": "Incorrect username or password.",
|
||||
"permission_denied": "You do not have the necessary permissions to access this page.",
|
||||
"cert_create_error_empty_fields": "Subdomain name and functional perimeter are required.",
|
||||
"cert_create_error_perimeter_not_found": "Selected functional perimeter not found.",
|
||||
"cert_create_success": "Certificate created successfully.",
|
||||
"cert_create_error": "Error creating certificate: {output}",
|
||||
"cert_revoke_error_id_missing": "Certificate ID missing for revocation.",
|
||||
"cert_revoke_error_not_found": "Certificate not found for revocation.",
|
||||
"cert_revoke_error_ca_revocation": "ROOT and INTERMEDIATE certificates cannot be revoked via the interface for PKI security reasons.",
|
||||
"cert_revoke_error_already_revoked": "This certificate is already revoked.",
|
||||
"cert_revoke_success": "Certificate revoked successfully.",
|
||||
"cert_revoke_error": "Error revoking certificate: {output}",
|
||||
"perimeter_create_error_empty_name": "Functional perimeter name is required.",
|
||||
"perimeter_create_error_exists": "A functional perimeter with this name already exists.",
|
||||
"perimeter_create_success": "Functional perimeter and its intermediate certificate created successfully.",
|
||||
"perimeter_create_error": "Error creating functional perimeter: {output}",
|
||||
"user_create_error_empty_fields": "Username and password are required.",
|
||||
"user_create_error_invalid_role": "Invalid user role.",
|
||||
"user_create_error_exists": "A user with the username '{username}' already exists.",
|
||||
"user_create_success": "User '{username}' created successfully.",
|
||||
"user_create_error_db": "Error creating user in the database.",
|
||||
"user_delete_error_id_missing": "User ID missing for deletion.",
|
||||
"user_delete_error_self_delete": "You cannot delete your own account.",
|
||||
"user_delete_error_last_admin": "Cannot delete the last administrator account.",
|
||||
"user_delete_success": "User '{username}' deleted successfully.",
|
||||
"user_delete_error_not_found": "User not found for deletion.",
|
||||
"user_delete_error_db": "Error deleting user from the database.",
|
||||
"self_delete_not_allowed": "You cannot delete yourself."
|
||||
}
|
84
app/src/Lang/es.json
Normal file
84
app/src/Lang/es.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"app_name": "Gestión de Certificados",
|
||||
"login_title": "Iniciar Sesión - Gestión de Certificados",
|
||||
"login_heading": "Iniciar sesión en la aplicación",
|
||||
"username": "Nombre de usuario",
|
||||
"password": "Contraseña",
|
||||
"login_button": "Iniciar Sesión",
|
||||
"dark_mode": "Modo Oscuro",
|
||||
"light_mode": "Modo Claro",
|
||||
"dashboard_title": "Panel de Control",
|
||||
"welcome": "¡Bienvenido, {username}!",
|
||||
"logout": "Cerrar Sesión",
|
||||
"certificates": "Certificados",
|
||||
"functional_perimeters": "Perímetros Funcionales",
|
||||
"users": "Usuarios",
|
||||
"quick_actions": "Acciones Rápidas",
|
||||
"create_new_certificate": "Crear nuevo certificado",
|
||||
"create_new_perimeter": "Crear nuevo perímetro",
|
||||
"new_user": "Nuevo usuario",
|
||||
"certificate_name": "Nombre del Certificado",
|
||||
"type": "Tipo",
|
||||
"expiration_date": "Fecha de Caducidad",
|
||||
"status": "Estado",
|
||||
"revoked": "Revocado",
|
||||
"active": "Activo",
|
||||
"actions": "Acciones",
|
||||
"revoke_certificate": "Revocar",
|
||||
"confirm_revoke": "¿Estás seguro de que deseas revocar este certificado? Esta acción es irreversible y lo invalidará.",
|
||||
"perimeter_name": "Nombre del Perímetro",
|
||||
"intermediate_cert_file": "Archivo de Certificado Intermedio",
|
||||
"created_at": "Creado el",
|
||||
"create_perimeter_button": "Crear Perímetro",
|
||||
"create_new_user": "Crear nuevo usuario",
|
||||
"user_role": "Rol",
|
||||
"admin": "Administrador",
|
||||
"user": "Usuario",
|
||||
"create_user_button": "Crear Usuario",
|
||||
"delete_user": "Eliminar",
|
||||
"confirm_delete_user": "¿Estás seguro de que deseas eliminar a este usuario? Esta acción es irreversible.",
|
||||
"new_certificate_heading": "Crear un Nuevo Certificado",
|
||||
"subdomain_name": "Nombre de Subdominio / CN",
|
||||
"select_perimeter": "Seleccionar un Perímetro Funcional",
|
||||
"select_perimeter_placeholder": "Elige un perímetro",
|
||||
"create_certificate": "Crear Certificado",
|
||||
"root": "Raíz",
|
||||
"intermediate": "Intermedio",
|
||||
"simple": "Simple",
|
||||
"back_to_dashboard": "Volver al Panel de Control",
|
||||
"back_to_cert_list": "Volver a la Lista de Certificados",
|
||||
"back_to_perimeter_list": "Volver a la Lista de Perímetros",
|
||||
"back_to_user_list": "Volver a la Lista de Usuarios",
|
||||
"no_certificates_yet": "Todavía no se han creado certificados.",
|
||||
"no_perimeters_yet": "Todavía no se han creado perímetros funcionales.",
|
||||
"no_users_yet": "Todavía no se han creado usuarios.",
|
||||
"login_error_empty_fields": "Por favor, introduce tu nombre de usuario y contraseña.",
|
||||
"login_error_credentials": "Nombre de usuario o contraseña incorrectos.",
|
||||
"permission_denied": "No tienes los permisos necesarios para acceder a esta página.",
|
||||
"cert_create_error_empty_fields": "El nombre de subdominio y el perímetro funcional son obligatorios.",
|
||||
"cert_create_error_perimeter_not_found": "Perímetro funcional seleccionado no encontrado.",
|
||||
"cert_create_success": "Certificado creado correctamente.",
|
||||
"cert_create_error": "Error al crear el certificado: {output}",
|
||||
"cert_revoke_error_id_missing": "ID de certificado faltante para la revocación.",
|
||||
"cert_revoke_error_not_found": "Certificado no encontrado para la revocación.",
|
||||
"cert_revoke_error_ca_revocation": "Los certificados ROOT e INTERMEDIOS no pueden ser revocados a través de la interfaz por razones de seguridad PKI.",
|
||||
"cert_revoke_error_already_revoked": "Este certificado ya ha sido revocado.",
|
||||
"cert_revoke_success": "Certificado revocado correctamente.",
|
||||
"cert_revoke_error": "Error al revocar el certificado: {output}",
|
||||
"perimeter_create_error_empty_name": "El nombre del perímetro funcional es obligatorio.",
|
||||
"perimeter_create_error_exists": "Ya existe un perímetro funcional con este nombre.",
|
||||
"perimeter_create_success": "Perímetro funcional y su certificado intermedio creados correctamente.",
|
||||
"perimeter_create_error": "Error al crear el perímetro funcional: {output}",
|
||||
"user_create_error_empty_fields": "El nombre de usuario y la contraseña son obligatorios.",
|
||||
"user_create_error_invalid_role": "Rol de usuario no válido.",
|
||||
"user_create_error_exists": "Ya existe un usuario con el nombre '{username}'.",
|
||||
"user_create_success": "Usuario '{username}' creado correctamente.",
|
||||
"user_create_error_db": "Error al crear el usuario en la base de datos.",
|
||||
"user_delete_error_id_missing": "ID de usuario faltante para la eliminación.",
|
||||
"user_delete_error_self_delete": "No puedes eliminar tu propia cuenta.",
|
||||
"user_delete_error_last_admin": "No se puede eliminar la última cuenta de administrador.",
|
||||
"user_delete_success": "Usuario '{username}' eliminado correctamente.",
|
||||
"user_delete_error_not_found": "Usuario no encontrado para la eliminación.",
|
||||
"user_delete_error_db": "Error al eliminar el usuario de la base de datos.",
|
||||
"self_delete_not_allowed": "No puedes eliminarte a ti mismo."
|
||||
}
|
84
app/src/Lang/fr.json
Normal file
84
app/src/Lang/fr.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"app_name": "Gestion Certificat",
|
||||
"login_title": "Connexion - Gestion Certificat",
|
||||
"login_heading": "Connexion à l'application",
|
||||
"username": "Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"login_button": "Se connecter",
|
||||
"dark_mode": "Mode Sombre",
|
||||
"light_mode": "Mode Clair",
|
||||
"dashboard_title": "Tableau de Bord",
|
||||
"welcome": "Bienvenue, {username} !",
|
||||
"logout": "Déconnexion",
|
||||
"certificates": "Certificats",
|
||||
"functional_perimeters": "Périmètres Fonctionnels",
|
||||
"users": "Utilisateurs",
|
||||
"quick_actions": "Actions Rapides",
|
||||
"create_new_certificate": "Créer un nouveau certificat",
|
||||
"create_new_perimeter": "Créer un nouveau périmètre",
|
||||
"new_user": "Nouvel utilisateur",
|
||||
"certificate_name": "Nom du certificat",
|
||||
"type": "Type",
|
||||
"expiration_date": "Date d'expiration",
|
||||
"status": "Statut",
|
||||
"revoked": "Révoqué",
|
||||
"active": "Actif",
|
||||
"actions": "Actions",
|
||||
"revoke_certificate": "Révoquer",
|
||||
"confirm_revoke": "Êtes-vous sûr de vouloir révoquer ce certificat ? Cette action est irréversible et rendra le certificat invalide.",
|
||||
"perimeter_name": "Nom du périmètre",
|
||||
"intermediate_cert_file": "Fichier Certificat Intermédiaire",
|
||||
"created_at": "Créé le",
|
||||
"create_perimeter_button": "Créer le périmètre",
|
||||
"create_new_user": "Créer un nouvel utilisateur",
|
||||
"user_role": "Rôle",
|
||||
"admin": "Administrateur",
|
||||
"user": "Utilisateur",
|
||||
"create_user_button": "Créer l'utilisateur",
|
||||
"delete_user": "Supprimer",
|
||||
"confirm_delete_user": "Êtes-vous sûr de vouloir supprimer cet utilisateur ? Cette action est irréversible.",
|
||||
"new_certificate_heading": "Créer un nouveau certificat",
|
||||
"subdomain_name": "Nom du sous-domaine / CN",
|
||||
"select_perimeter": "Sélectionner un périmètre fonctionnel",
|
||||
"select_perimeter_placeholder": "Choisissez un périmètre",
|
||||
"create_certificate": "Créer le certificat",
|
||||
"root": "Root",
|
||||
"intermediate": "Intermédiaire",
|
||||
"simple": "Simple",
|
||||
"back_to_dashboard": "Retour au Tableau de Bord",
|
||||
"back_to_cert_list": "Retour à la liste des certificats",
|
||||
"back_to_perimeter_list": "Retour à la liste des périmètres",
|
||||
"back_to_user_list": "Retour à la liste des utilisateurs",
|
||||
"no_certificates_yet": "Aucun certificat n'a encore été créé.",
|
||||
"no_perimeters_yet": "Aucun périmètre fonctionnel n'a encore été créé.",
|
||||
"no_users_yet": "Aucun utilisateur n'a encore été créé.",
|
||||
"login_error_empty_fields": "Veuillez saisir votre nom d'utilisateur et votre mot de passe.",
|
||||
"login_error_credentials": "Nom d'utilisateur ou mot de passe incorrect.",
|
||||
"permission_denied": "Vous n'avez pas les permissions nécessaires pour accéder à cette page.",
|
||||
"cert_create_error_empty_fields": "Le nom du sous-domaine et le périmètre fonctionnel sont requis.",
|
||||
"cert_create_error_perimeter_not_found": "Le périmètre fonctionnel sélectionné est introuvable.",
|
||||
"cert_create_success": "Certificat créé avec succès.",
|
||||
"cert_create_error": "Erreur lors de la création du certificat: {output}",
|
||||
"cert_revoke_error_id_missing": "ID du certificat manquant pour la révocation.",
|
||||
"cert_revoke_error_not_found": "Certificat introuvable pour la révocation.",
|
||||
"cert_revoke_error_ca_revocation": "Les certificats ROOT et INTERMÉDIAIRES ne peuvent pas être révoqués via l'interface pour des raisons de sécurité PKI.",
|
||||
"cert_revoke_error_already_revoked": "Ce certificat est déjà révoqué.",
|
||||
"cert_revoke_success": "Certificat révoqué avec succès.",
|
||||
"cert_revoke_error": "Erreur lors de la révocation du certificat: {output}",
|
||||
"perimeter_create_error_empty_name": "Le nom du périmètre fonctionnel est requis.",
|
||||
"perimeter_create_error_exists": "Un périmètre fonctionnel avec ce nom existe déjà.",
|
||||
"perimeter_create_success": "Périmètre fonctionnel et son certificat intermédiaire créés avec succès.",
|
||||
"perimeter_create_error": "Erreur lors de la création du périmètre fonctionnel: {output}",
|
||||
"user_create_error_empty_fields": "Le nom d'utilisateur et le mot de passe sont requis.",
|
||||
"user_create_error_invalid_role": "Rôle d'utilisateur invalide.",
|
||||
"user_create_error_exists": "Un utilisateur avec le nom '{username}' existe déjà.",
|
||||
"user_create_success": "Utilisateur '{username}' créé avec succès.",
|
||||
"user_create_error_db": "Erreur lors de la création de l'utilisateur dans la base de données.",
|
||||
"user_delete_error_id_missing": "ID de l'utilisateur manquant pour la suppression.",
|
||||
"user_delete_error_self_delete": "Vous ne pouvez pas supprimer votre propre compte.",
|
||||
"user_delete_error_last_admin": "Impossible de supprimer le dernier compte administrateur.",
|
||||
"user_delete_success": "Utilisateur '{username}' supprimé avec succès.",
|
||||
"user_delete_error_not_found": "Utilisateur introuvable pour la suppression.",
|
||||
"user_delete_error_db": "Erreur lors de la suppression de l'utilisateur dans la base de données.",
|
||||
"self_delete_not_allowed": "Vous ne pouvez pas vous supprimer vous-même."
|
||||
}
|
84
app/src/Lang/it.json
Normal file
84
app/src/Lang/it.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"app_name": "Gestione Certificati",
|
||||
"login_title": "Accesso - Gestione Certificati",
|
||||
"login_heading": "Accedi all'applicazione",
|
||||
"username": "Nome utente",
|
||||
"password": "Password",
|
||||
"login_button": "Accedi",
|
||||
"dark_mode": "Modalità Scura",
|
||||
"light_mode": "Modalità Chiara",
|
||||
"dashboard_title": "Dashboard",
|
||||
"welcome": "Benvenuto, {username}!",
|
||||
"logout": "Esci",
|
||||
"certificates": "Certificati",
|
||||
"functional_perimeters": "Perimetri Funzionali",
|
||||
"users": "Utenti",
|
||||
"quick_actions": "Azioni Rapide",
|
||||
"create_new_certificate": "Crea un nuovo certificato",
|
||||
"create_new_perimeter": "Crea un nuovo perimetro",
|
||||
"new_user": "Nuovo utente",
|
||||
"certificate_name": "Nome Certificato",
|
||||
"type": "Tipo",
|
||||
"expiration_date": "Data di Scadenza",
|
||||
"status": "Stato",
|
||||
"revoked": "Revocato",
|
||||
"active": "Attivo",
|
||||
"actions": "Azioni",
|
||||
"revoke_certificate": "Revoca",
|
||||
"confirm_revoke": "Sei sicuro di voler revocare questo certificato? Questa azione è irreversibile e renderà il certificato non valido.",
|
||||
"perimeter_name": "Nome Perimetro",
|
||||
"intermediate_cert_file": "File Certificato Intermedio",
|
||||
"created_at": "Creato il",
|
||||
"create_perimeter_button": "Crea Perimetro",
|
||||
"create_new_user": "Crea un nuovo utente",
|
||||
"user_role": "Ruolo",
|
||||
"admin": "Amministratore",
|
||||
"user": "Utente",
|
||||
"create_user_button": "Crea Utente",
|
||||
"delete_user": "Elimina",
|
||||
"confirm_delete_user": "Sei sicuro di voler eliminare questo utente? Questa azione è irreversibile.",
|
||||
"new_certificate_heading": "Crea un Nuovo Certificato",
|
||||
"subdomain_name": "Nome Sottodominio / CN",
|
||||
"select_perimeter": "Seleziona un Perimetro Funzionale",
|
||||
"select_perimeter_placeholder": "Scegli un perimetro",
|
||||
"create_certificate": "Crea Certificato",
|
||||
"root": "Root",
|
||||
"intermediate": "Intermedio",
|
||||
"simple": "Semplice",
|
||||
"back_to_dashboard": "Torna alla Dashboard",
|
||||
"back_to_cert_list": "Torna all'Elenco Certificati",
|
||||
"back_to_perimeter_list": "Torna all'Elenco Perimetri",
|
||||
"back_to_user_list": "Torna all'Elenco Utenti",
|
||||
"no_certificates_yet": "Nessun certificato è stato ancora creato.",
|
||||
"no_perimeters_yet": "Nessun perimetro funzionale è stato ancora creato.",
|
||||
"no_users_yet": "Nessun utente è stato ancora creato.",
|
||||
"login_error_empty_fields": "Si prega di inserire nome utente e password.",
|
||||
"login_error_credentials": "Nome utente o password errati.",
|
||||
"permission_denied": "Non si dispone delle autorizzazioni necessarie per accedere a questa pagina.",
|
||||
"cert_create_error_empty_fields": "Nome sottodominio e perimetro funzionale sono obbligatori.",
|
||||
"cert_create_error_perimeter_not_found": "Perimetro funzionale selezionato non trovato.",
|
||||
"cert_create_success": "Certificato creato con successo.",
|
||||
"cert_create_error": "Errore durante la creazione del certificato: {output}",
|
||||
"cert_revoke_error_id_missing": "ID certificato mancante per la revoca.",
|
||||
"cert_revoke_error_not_found": "Certificato non trovato per la revoca.",
|
||||
"cert_revoke_error_ca_revocation": "I certificati ROOT e INTERMEDIATE non possono essere revocati tramite l'interfaccia per motivi di sicurezza PKI.",
|
||||
"cert_revoke_error_already_revoked": "Questo certificato è già stato revocato.",
|
||||
"cert_revoke_success": "Certificato revocato con successo.",
|
||||
"cert_revoke_error": "Errore durante la revoca del certificato: {output}",
|
||||
"perimeter_create_error_empty_name": "Il nome del perimetro funzionale è obbligatorio.",
|
||||
"perimeter_create_error_exists": "Esiste già un perimetro funzionale con questo nome.",
|
||||
"perimeter_create_success": "Perimetro funzionale e il suo certificato intermedio creati con successo.",
|
||||
"perimeter_create_error": "Errore durante la creazione del perimetro funzionale: {output}",
|
||||
"user_create_error_empty_fields": "Nome utente e password sono obbligatori.",
|
||||
"user_create_error_invalid_role": "Ruolo utente non valido.",
|
||||
"user_create_error_exists": "Esiste già un utente con il nome '{username}'.",
|
||||
"user_create_success": "Utente '{username}' creato con successo.",
|
||||
"user_create_error_db": "Errore durante la creazione dell'utente nel database.",
|
||||
"user_delete_error_id_missing": "ID utente mancante per l'eliminazione.",
|
||||
"user_delete_error_self_delete": "Non puoi eliminare il tuo account.",
|
||||
"user_delete_error_last_admin": "Impossibile eliminare l'ultimo account amministratore.",
|
||||
"user_delete_success": "Utente '{username}' eliminato con successo.",
|
||||
"user_delete_error_not_found": "Utente non trovato per l'eliminazione.",
|
||||
"user_delete_error_db": "Errore durante l'eliminazione dell'utente dal database.",
|
||||
"self_delete_not_allowed": "Non puoi eliminare te stesso."
|
||||
}
|
84
app/src/Lang/pt.json
Normal file
84
app/src/Lang/pt.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"app_name": "Gestão de Certificados",
|
||||
"login_title": "Login - Gestão de Certificados",
|
||||
"login_heading": "Fazer login na aplicação",
|
||||
"username": "Nome de Utilizador",
|
||||
"password": "Palavra-passe",
|
||||
"login_button": "Entrar",
|
||||
"dark_mode": "Modo Escuro",
|
||||
"light_mode": "Modo Claro",
|
||||
"dashboard_title": "Painel de Controlo",
|
||||
"welcome": "Bem-vindo, {username}!",
|
||||
"logout": "Sair",
|
||||
"certificates": "Certificados",
|
||||
"functional_perimeters": "Perímetros Funcionais",
|
||||
"users": "Utilizadores",
|
||||
"quick_actions": "Ações Rápidas",
|
||||
"create_new_certificate": "Criar novo certificado",
|
||||
"create_new_perimeter": "Criar novo perímetro",
|
||||
"new_user": "Novo utilizador",
|
||||
"certificate_name": "Nome do Certificado",
|
||||
"type": "Tipo",
|
||||
"expiration_date": "Data de Expiração",
|
||||
"status": "Estado",
|
||||
"revoked": "Revogado",
|
||||
"active": "Ativo",
|
||||
"actions": "Ações",
|
||||
"revoke_certificate": "Revogar",
|
||||
"confirm_revoke": "Tem certeza que deseja revogar este certificado? Esta ação é irreversível e invalidará o certificado.",
|
||||
"perimeter_name": "Nome do Perímetro",
|
||||
"intermediate_cert_file": "Ficheiro de Certificado Intermédio",
|
||||
"created_at": "Criado em",
|
||||
"create_perimeter_button": "Criar Perímetro",
|
||||
"create_new_user": "Criar novo utilizador",
|
||||
"user_role": "Função",
|
||||
"admin": "Administrador",
|
||||
"user": "Utilizador",
|
||||
"create_user_button": "Criar Utilizador",
|
||||
"delete_user": "Eliminar",
|
||||
"confirm_delete_user": "Tem certeza que deseja eliminar este utilizador? Esta ação é irreversível.",
|
||||
"new_certificate_heading": "Criar Novo Certificado",
|
||||
"subdomain_name": "Nome do Subdomínio / CN",
|
||||
"select_perimeter": "Selecionar um Perímetro Funcional",
|
||||
"select_perimeter_placeholder": "Escolha um perímetro",
|
||||
"create_certificate": "Criar Certificado",
|
||||
"root": "Raiz",
|
||||
"intermediate": "Intermédio",
|
||||
"simple": "Simples",
|
||||
"back_to_dashboard": "Voltar ao Painel de Controlo",
|
||||
"back_to_cert_list": "Voltar à Lista de Certificados",
|
||||
"back_to_perimeter_list": "Voltar à Lista de Perímetros",
|
||||
"back_to_user_list": "Voltar à Lista de Utilizadores",
|
||||
"no_certificates_yet": "Nenhum certificado foi criado ainda.",
|
||||
"no_perimeters_yet": "Nenhum perímetro funcional foi criado ainda.",
|
||||
"no_users_yet": "Nenhum utilizador foi criado ainda.",
|
||||
"login_error_empty_fields": "Por favor, insira seu nome de utilizador e palavra-passe.",
|
||||
"login_error_credentials": "Nome de utilizador ou palavra-passe incorretos.",
|
||||
"permission_denied": "Não tem as permissões necessárias para aceder a esta página.",
|
||||
"cert_create_error_empty_fields": "O nome do subdomínio e o perímetro funcional são obrigatórios.",
|
||||
"cert_create_error_perimeter_not_found": "Perímetro funcional selecionado não encontrado.",
|
||||
"cert_create_success": "Certificado criado com sucesso.",
|
||||
"cert_create_error": "Erro ao criar certificado: {output}",
|
||||
"cert_revoke_error_id_missing": "ID do certificado em falta para revogação.",
|
||||
"cert_revoke_error_not_found": "Certificado não encontrado para revogação.",
|
||||
"cert_revoke_error_ca_revocation": "Certificados ROOT e INTERMEDIÁRIOS não podem ser revogados através da interface por razões de segurança PKI.",
|
||||
"cert_revoke_error_already_revoked": "Este certificado já está revogado.",
|
||||
"cert_revoke_success": "Certificado revogado com sucesso.",
|
||||
"cert_revoke_error": "Erro ao revogar certificado: {output}",
|
||||
"perimeter_create_error_empty_name": "O nome do perímetro funcional é obrigatório.",
|
||||
"perimeter_create_error_exists": "Já existe um perímetro funcional com este nome.",
|
||||
"perimeter_create_success": "Perímetro funcional e o seu certificado intermédio criados com sucesso.",
|
||||
"perimeter_create_error": "Erro ao criar perímetro funcional: {output}",
|
||||
"user_create_error_empty_fields": "Nome de utilizador e palavra-passe são obrigatórios.",
|
||||
"user_create_error_invalid_role": "Função de utilizador inválida.",
|
||||
"user_create_error_exists": "Já existe um utilizador com o nome '{username}'.",
|
||||
"user_create_success": "Utilizador '{username}' criado com sucesso.",
|
||||
"user_create_error_db": "Erro ao criar utilizador na base de dados.",
|
||||
"user_delete_error_id_missing": "ID de utilizador em falta para eliminação.",
|
||||
"user_delete_error_self_delete": "Não pode eliminar a sua própria conta.",
|
||||
"user_delete_error_last_admin": "Não é possível eliminar a última conta de administrador.",
|
||||
"user_delete_success": "Utilizador '{username}' eliminado com sucesso.",
|
||||
"user_delete_error_not_found": "Utilizador não encontrado para eliminação.",
|
||||
"user_delete_error_db": "Erro ao eliminar utilizador da base de dados.",
|
||||
"self_delete_not_allowed": "Não pode eliminar-se a si mesmo."
|
||||
}
|
117
app/src/Services/AuthService.php
Normal file
117
app/src/Services/AuthService.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Service d'authentification utilisateur.
|
||||
* Gère les connexions, déconnexions et vérifie l'état de l'authentification.
|
||||
*/
|
||||
class AuthService
|
||||
{
|
||||
private $db; // Instance PDO pour l'accès à la base de données
|
||||
private $logService; // Service de journalisation
|
||||
|
||||
/**
|
||||
* Constructeur du service d'authentification.
|
||||
*
|
||||
* @param PDO $db L'instance PDO de la base de données.
|
||||
*/
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
// Le chemin du fichier de log doit être défini dans config/app.php
|
||||
$this->logService = new LogService(APP_LOG_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tente de connecter un utilisateur.
|
||||
*
|
||||
* @param string $username Le nom d'utilisateur.
|
||||
* @param string $password Le mot de passe en clair.
|
||||
* @param string $ipAddress L'adresse IP de la requête de connexion.
|
||||
* @return bool Vrai si la connexion est réussie, faux sinon.
|
||||
*/
|
||||
public function login($username, $password, $ipAddress)
|
||||
{
|
||||
$stmt = $this->db->prepare("SELECT id, username, password, role FROM users WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
// Connexion réussie : enregistre les informations de l session
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
|
||||
// Log de l'action de connexion réussie
|
||||
$this->logService->log('info', "Connexion réussie pour l'utilisateur '{$username}'.", $user['id'], $ipAddress);
|
||||
return true;
|
||||
} else {
|
||||
// Connexion échouée : log de la tentative
|
||||
$this->logService->log('warning', "Tentative de connexion échouée pour l'utilisateur '{$username}'.", null, $ipAddress);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Déconnecte l'utilisateur actuellement connecté.
|
||||
*
|
||||
* @param string $ipAddress L'adresse IP de la requête de déconnexion.
|
||||
* @return bool Vrai si la déconnexion est effectuée.
|
||||
*/
|
||||
public function logout($ipAddress)
|
||||
{
|
||||
$userId = $_SESSION['user_id'] ?? 'inconnu';
|
||||
$username = $_SESSION['username'] ?? 'inconnu';
|
||||
|
||||
// Détruit toutes les données de session
|
||||
session_destroy();
|
||||
$_SESSION = array(); // Vider le tableau $_SESSION
|
||||
|
||||
// Log de l'action de déconnexion
|
||||
$this->logService->log('info', "Déconnexion de l'utilisateur '{$username}'.", $userId, $ipAddress);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un utilisateur est actuellement connecté.
|
||||
*
|
||||
* @return bool Vrai si l'utilisateur est connecté, faux sinon.
|
||||
*/
|
||||
public function isLoggedIn()
|
||||
{
|
||||
return isset($_SESSION['user_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le rôle de l'utilisateur connecté.
|
||||
*
|
||||
* @return string|null Le rôle de l'utilisateur ('admin' ou 'user'), ou null si non connecté.
|
||||
*/
|
||||
public function getUserRole()
|
||||
{
|
||||
return $_SESSION['role'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'ID de l'utilisateur connecté.
|
||||
*
|
||||
* @return int|null L'ID de l'utilisateur, ou null si non connecté.
|
||||
*/
|
||||
public function getUserId()
|
||||
{
|
||||
return $_SESSION['user_id'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nom d'utilisateur de l'utilisateur connecté.
|
||||
*
|
||||
* @return string|null Le nom d'utilisateur, ou null si non connecté.
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $_SESSION['username'] ?? null;
|
||||
}
|
||||
}
|
118
app/src/Services/LanguageService.php
Normal file
118
app/src/Services/LanguageService.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
/**
|
||||
* Service pour gérer la langue de l'application et charger les traductions.
|
||||
*/
|
||||
class LanguageService
|
||||
{
|
||||
private $langDir; // Répertoire où sont stockés les fichiers de traduction
|
||||
private $currentLang; // Langue actuellement sélectionnée
|
||||
|
||||
/**
|
||||
* Constructeur du service de langue.
|
||||
* Initialise la langue actuelle à partir de la session ou une valeur par défaut.
|
||||
*
|
||||
* @param string $langDir Chemin absolu du répertoire des fichiers de traduction.
|
||||
*/
|
||||
public function __construct($langDir)
|
||||
{
|
||||
$this->langDir = rtrim($langDir, '/') . '/'; // Assure qu'il y a un slash final
|
||||
// Récupère la langue de la session ou utilise 'en' par default
|
||||
$this->currentLang = $_SESSION['lang'] ?? 'en';
|
||||
// Vérifie si la langue est supportée, sinon revient à 'en'
|
||||
// CORRIGÉ: La condition était incorrecte
|
||||
if (!in_array($this->currentLang, SUPPORTED_LANGUAGES)) {
|
||||
$this->currentLang = 'en';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la langue de l'application.
|
||||
*
|
||||
* @param string $lang Le code de la langue (ex: 'fr', 'en').
|
||||
* @return bool Vrai si la langue a été définie avec succès, faux sinon.
|
||||
*/
|
||||
public function setLanguage($lang)
|
||||
{
|
||||
if (in_array($lang, SUPPORTED_LANGUAGES)) {
|
||||
$_SESSION['lang'] = $lang;
|
||||
$this->currentLang = $lang;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la langue actuellement sélectionnée.
|
||||
*
|
||||
* @return string Le code de la langue.
|
||||
*/
|
||||
public function getLanguage()
|
||||
{
|
||||
return $this->currentLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge et retourne toutes les traductions pour la langue actuelle.
|
||||
*
|
||||
* @return array Tableau associatif des traductions.
|
||||
*/
|
||||
public function getTranslations()
|
||||
{
|
||||
$filePath = $this->langDir . $this->currentLang . '.json';
|
||||
if (file_exists($filePath)) {
|
||||
$content = file_get_contents($filePath);
|
||||
if ($content === false) {
|
||||
error_log("LanguageService: Impossible de lire le fichier de traduction: " . $filePath);
|
||||
return [];
|
||||
}
|
||||
$translations = json_decode($content, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log("LanguageService: Erreur de décodage JSON pour le fichier: " . $filePath . " - " . json_last_error_msg());
|
||||
return [];
|
||||
}
|
||||
return $translations;
|
||||
}
|
||||
// Fallback à l'anglais si le fichier de la langue actuelle est manquant
|
||||
$englishFilePath = $this->langDir . 'en.json';
|
||||
if (file_exists($englishFilePath)) {
|
||||
$content = file_get_contents($englishFilePath);
|
||||
if ($content === false) {
|
||||
error_log("LanguageService: Impossible de lire le fichier de traduction anglais de secours: " . $englishFilePath);
|
||||
return [];
|
||||
}
|
||||
$translations = json_decode($content, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log("LanguageService: Erreur de décodage JSON pour le fichier anglais de secours: " . $englishFilePath . " - " . json_last_error_msg());
|
||||
return [];
|
||||
}
|
||||
return $translations;
|
||||
}
|
||||
error_log("LanguageService: Aucun fichier de traduction trouvé pour la langue '" . $this->currentLang . "' ou 'en'.");
|
||||
return []; // Retourne un tableau vide si aucune traduction n'est trouvée
|
||||
}
|
||||
|
||||
/**
|
||||
* Traduit une clé donnée.
|
||||
*
|
||||
* @param string $key La clé de traduction.
|
||||
* @param array $replacements Tableau associatif de [placeholder => valeur] pour les remplacements.
|
||||
* @return string La chaîne traduite ou la clé si non trouvée.
|
||||
*/
|
||||
public function __($key, $replacements = [])
|
||||
{
|
||||
// Utilise la variable globale $translations qui est chargée dans index.php
|
||||
global $translations;
|
||||
|
||||
$translatedString = $translations[$key] ?? $key;
|
||||
|
||||
// Effectuer les remplacements de placeholders
|
||||
foreach ($replacements as $placeholder => $value) {
|
||||
$translatedString = str_replace("{" . $placeholder . "}", $value, $translatedString);
|
||||
}
|
||||
|
||||
return $translatedString;
|
||||
}
|
||||
}
|
117
app/src/Services/LanguageService.php.bak
Normal file
117
app/src/Services/LanguageService.php.bak
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
/**
|
||||
* Service pour gérer la langue de l'application et charger les traductions.
|
||||
*/
|
||||
class LanguageService
|
||||
{
|
||||
private $langDir; // Répertoire où sont stockés les fichiers de traduction
|
||||
private $currentLang; // Langue actuellement sélectionnée
|
||||
|
||||
/**
|
||||
* Constructeur du service de langue.
|
||||
* Initialise la langue actuelle à partir de la session ou une valeur par défaut.
|
||||
*
|
||||
* @param string $langDir Chemin absolu du répertoire des fichiers de traduction.
|
||||
*/
|
||||
public function __construct($langDir)
|
||||
{
|
||||
$this->langDir = rtrim($langDir, '/') . '/'; // Assure qu'il y a un slash final
|
||||
// Récupère la langue de la session ou utilise 'en' par défaut
|
||||
$this->currentLang = $_SESSION['lang'] ?? 'en';
|
||||
// Vérifie si la langue est supportée, sinon revient à 'en'
|
||||
if (!in_array($this->currentLang, SUPPORTED_LANGUAGES)) {
|
||||
$this->currentLang = 'en';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la langue de l'application.
|
||||
*
|
||||
* @param string $lang Le code de la langue (ex: 'fr', 'en').
|
||||
* @return bool Vrai si la langue a été définie avec succès, faux sinon.
|
||||
*/
|
||||
public function setLanguage($lang)
|
||||
{
|
||||
if (in_array($lang, SUPPORTED_LANGUAGES)) {
|
||||
$_SESSION['lang'] = $lang;
|
||||
$this->currentLang = $lang;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la langue actuellement sélectionnée.
|
||||
*
|
||||
* @return string Le code de la langue.
|
||||
*/
|
||||
public function getLanguage()
|
||||
{
|
||||
return $this->currentLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge et retourne toutes les traductions pour la langue actuelle.
|
||||
*
|
||||
* @return array Tableau associatif des traductions.
|
||||
*/
|
||||
public function getTranslations()
|
||||
{
|
||||
$filePath = $this->langDir . $this->currentLang . '.json';
|
||||
if (file_exists($filePath)) {
|
||||
$content = file_get_contents($filePath);
|
||||
if ($content === false) {
|
||||
error_log("LanguageService: Impossible de lire le fichier de traduction: " . $filePath);
|
||||
return [];
|
||||
}
|
||||
$translations = json_decode($content, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log("LanguageService: Erreur de décodage JSON pour le fichier: " . $filePath . " - " . json_last_error_msg());
|
||||
return [];
|
||||
}
|
||||
return $translations;
|
||||
}
|
||||
// Fallback à l'anglais si le fichier de la langue actuelle est manquant
|
||||
$englishFilePath = $this->langDir . 'en.json';
|
||||
if (file_exists($englishFilePath)) {
|
||||
$content = file_get_contents($englishFilePath);
|
||||
if ($content === false) {
|
||||
error_log("LanguageService: Impossible de lire le fichier de traduction anglais de secours: " . $englishFilePath);
|
||||
return [];
|
||||
}
|
||||
$translations = json_decode($content, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log("LanguageService: Erreur de décodage JSON pour le fichier anglais de secours: " . $englishFilePath . " - " . json_last_error_msg());
|
||||
return [];
|
||||
}
|
||||
return $translations;
|
||||
}
|
||||
error_log("LanguageService: Aucun fichier de traduction trouvé pour la langue '" . $this->currentLang . "' ou 'en'.");
|
||||
return []; // Retourne un tableau vide si aucune traduction n'est trouvée
|
||||
}
|
||||
|
||||
/**
|
||||
* Traduit une clé donnée.
|
||||
*
|
||||
* @param string $key La clé de traduction.
|
||||
* @param array $replacements Tableau associatif de [placeholder => valeur] pour les remplacements.
|
||||
* @return string La chaîne traduite ou la clé si non trouvée.
|
||||
*/
|
||||
public function __($key, $replacements = [])
|
||||
{
|
||||
// Utilise la variable globale $translations qui est chargée dans index.php
|
||||
global $translations;
|
||||
|
||||
$translatedString = $translations[$key] ?? $key;
|
||||
|
||||
// Effectuer les remplacements de placeholders
|
||||
foreach ($replacements as $placeholder => $value) {
|
||||
$translatedString = str_replace("{" . $placeholder . "}", $value, $translatedString);
|
||||
}
|
||||
|
||||
return $translatedString;
|
||||
}
|
||||
}
|
48
app/src/Services/LogService.php
Normal file
48
app/src/Services/LogService.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
/**
|
||||
* Service pour journaliser les actions de l'application dans un fichier.
|
||||
*/
|
||||
class LogService
|
||||
{
|
||||
private $logFile; // Chemin complet du fichier de log
|
||||
|
||||
/**
|
||||
* Constructeur du service de journalisation.
|
||||
*
|
||||
* @param string $logFile Chemin complet du fichier de log.
|
||||
*/
|
||||
public function __construct($logFile)
|
||||
{
|
||||
$this->logFile = $logFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre un message dans le fichier de log.
|
||||
*
|
||||
* @param string $level Niveau de gravité du log (ex: 'info', 'warning', 'error').
|
||||
* @param string $message Le message à enregistrer.
|
||||
* @param int|null $userId L'ID de l'utilisateur si l'action est liée à un utilisateur.
|
||||
* @param string|null $ipAddress L'adresse IP d'où provient l'action.
|
||||
*/
|
||||
public function log($level, $message, $userId = null, $ipAddress = null)
|
||||
{
|
||||
$timestamp = date('Y-m-d H:i:s'); // Date et heure actuelle
|
||||
$logEntry = sprintf("[%s] [%s] ", $timestamp, strtoupper($level)); // Format de base du log
|
||||
|
||||
// Ajoute l'ID utilisateur si fourni
|
||||
if ($userId !== null) {
|
||||
$logEntry .= "[User: $userId] ";
|
||||
}
|
||||
// Ajoute l'adresse IP si fournie
|
||||
if ($ipAddress !== null) {
|
||||
$logEntry .= "[IP: $ipAddress] ";
|
||||
}
|
||||
$logEntry .= "$message\n"; // Ajoute le message et un saut de ligne
|
||||
|
||||
// Écrit le log dans le fichier, en ajoutant au contenu existant
|
||||
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
|
||||
}
|
||||
}
|
56
app/src/Utils/DarkMode.php
Normal file
56
app/src/Utils/DarkMode.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
/**
|
||||
* Utilitaire pour gérer le mode sombre/clair de l'application via la session.
|
||||
*/
|
||||
class DarkMode
|
||||
{
|
||||
/**
|
||||
* Initialise l'état du mode sombre dans la session si ce n'est pas déjà fait.
|
||||
* Le mode clair est le mode par défaut.
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
if (!isset($_SESSION['dark_mode'])) {
|
||||
$_SESSION['dark_mode'] = false; // false = mode clair, true = mode sombre
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bascule l'état du mode sombre ou le définit explicitement.
|
||||
*
|
||||
* @param string|null $mode 'on' pour activer, 'off' pour désactiver, null pour basculer.
|
||||
*/
|
||||
public static function toggle($mode = null)
|
||||
{
|
||||
if ($mode === 'on') {
|
||||
$_SESSION['dark_mode'] = true;
|
||||
} elseif ($mode === 'off') {
|
||||
$_SESSION['dark_mode'] = false;
|
||||
} else {
|
||||
$_SESSION['dark_mode'] = !($_SESSION['dark_mode'] ?? false); // Bascule l'état actuel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le mode sombre est activé.
|
||||
*
|
||||
* @return bool Vrai si le mode sombre est activé, faux sinon.
|
||||
*/
|
||||
public static function isEnabled()
|
||||
{
|
||||
return $_SESSION['dark_mode'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la classe CSS à appliquer au body HTML en fonction de l'état du mode sombre.
|
||||
*
|
||||
* @return string La classe CSS ('dark-mode') ou une chaîne vide.
|
||||
*/
|
||||
public static function getBodyClass()
|
||||
{
|
||||
return self::isEnabled() ? 'dark-mode' : '';
|
||||
}
|
||||
}
|
51
app/src/Views/auth/login.php
Normal file
51
app/src/Views/auth/login.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// On assume que $translations, $currentLang, $darkModeClass et $error sont définis par le contrôleur
|
||||
// global $translations; // Déjà global dans index.php si utilisé ici
|
||||
// global $currentLang;
|
||||
// global $darkModeClass;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?= htmlspecialchars($currentLang) ?>">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= htmlspecialchars($translations['login_title']) ?></title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="stylesheet" href="/dark-mode.css">
|
||||
</head>
|
||||
<body class="<?= htmlspecialchars($darkModeClass) ?>">
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['login_heading']) ?></h1>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($error); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/login" method="post">
|
||||
<label for="username"><?= htmlspecialchars($translations['username']) ?>:</label><br>
|
||||
<input type="text" id="username" name="username" required autocomplete="username"><br><br>
|
||||
|
||||
<label for="password"><?= htmlspecialchars($translations['password']) ?>:</label><br>
|
||||
<input type="password" id="password" name="password" required autocomplete="current-password"><br><br>
|
||||
|
||||
<button type="submit" class="button primary-button"><?= htmlspecialchars($translations['login_button']) ?></button>
|
||||
</form>
|
||||
|
||||
<div class="options">
|
||||
<!-- Boutons de changement de langue -->
|
||||
<div class="language-switcher">
|
||||
<?php foreach (SUPPORTED_LANGUAGES as $lang): ?>
|
||||
<a href="?lang=<?= htmlspecialchars($lang) ?>" class="<?= $currentLang === $lang ? 'active' : '' ?>"><?= htmlspecialchars(strtoupper($lang)) ?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Bouton de bascule du mode sombre -->
|
||||
<div class="dark-mode-switcher">
|
||||
<a href="?dark_mode=<?= \App\Utils\DarkMode::isEnabled() ? 'off' : 'on' ?>" class="button secondary-button">
|
||||
<?= \App\Utils\DarkMode::isEnabled() ? htmlspecialchars($translations['light_mode']) : htmlspecialchars($translations['dark_mode']) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
32
app/src/Views/certificates/create.php
Normal file
32
app/src/Views/certificates/create.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $perimeters, $translations, $currentLang, $darkModeClass, $errorMessage
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['new_certificate_heading']) ?></h1>
|
||||
<a href="/certificates" class="button secondary-button"><?= htmlspecialchars($translations['back_to_cert_list']) ?></a>
|
||||
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($errorMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/certificates/create" method="post">
|
||||
<label for="subdomain_name"><?= htmlspecialchars($translations['subdomain_name']) ?>:</label><br>
|
||||
<input type="text" id="subdomain_name" name="subdomain_name" required placeholder="ex: www, api, mail"><br><br>
|
||||
|
||||
<label for="functional_perimeter_id"><?= htmlspecialchars($translations['select_perimeter']) ?>:</label><br>
|
||||
<select id="functional_perimeter_id" name="functional_perimeter_id" required>
|
||||
<option value="">-- <?= htmlspecialchars($translations['select_perimeter_placeholder']) ?> --</option>
|
||||
<?php foreach ($perimeters as $perimeter): ?>
|
||||
<option value="<?= htmlspecialchars($perimeter['id']) ?>"><?= htmlspecialchars($perimeter['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select><br><br>
|
||||
|
||||
<button type="submit" class="button primary-button"><?= htmlspecialchars($translations['create_certificate']) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
71
app/src/Views/certificates/index.php
Normal file
71
app/src/Views/certificates/index.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $groupedCertificates, $translations, $currentLang, $darkModeClass, $successMessage, $errorMessage, $userRole
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['certificates']) ?></h1>
|
||||
<div class="actions-bar">
|
||||
<a href="/dashboard" class="button secondary-button"><?= htmlspecialchars($translations['back_to_dashboard']) ?></a>
|
||||
<a href="/certificates/create" class="button primary-button"><?= htmlspecialchars($translations['create_new_certificate']) ?></a>
|
||||
</div>
|
||||
|
||||
<?php if (isset($successMessage)): ?>
|
||||
<p class="success-message"><?= htmlspecialchars($successMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($errorMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($groupedCertificates)): ?>
|
||||
<p><?= htmlspecialchars($translations['no_certificates_yet']) ?></p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($groupedCertificates as $perimeterName => $certsInPerimeter): ?>
|
||||
<h2 class="perimeter-heading"><?= htmlspecialchars($perimeterName) ?></h2>
|
||||
<div class="table-responsive">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= htmlspecialchars($translations['certificate_name']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['type']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['expiration_date']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['status']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['actions']) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($certsInPerimeter as $cert): ?>
|
||||
<tr class="<?= $cert['is_revoked'] ? 'revoked-cert' : '' ?>">
|
||||
<td><?= htmlspecialchars($cert['name']) ?></td>
|
||||
<td><?= htmlspecialchars($translations[$cert['type']] ?? $cert['type']) ?></td>
|
||||
<td><?= htmlspecialchars((new DateTime($cert['expiration_date']))->format('Y-m-d')) ?></td>
|
||||
<td>
|
||||
<?php if ($cert['is_revoked']): ?>
|
||||
<span class="status-revoked"><?= htmlspecialchars($translations['revoked']) ?></span>
|
||||
(<?= htmlspecialchars((new DateTime($cert['revoked_at']))->format('Y-m-d')) ?>)
|
||||
<?php else: ?>
|
||||
<span class="status-active"><?= htmlspecialchars($translations['active']) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
// Seuls les certificats 'simple' et non révoqués peuvent être révoqués via l'interface
|
||||
if (!$cert['is_revoked'] && $cert['type'] === 'simple'): ?>
|
||||
<form action="/certificates/revoke" method="post" class="inline-form" onsubmit="return confirm('<?= htmlspecialchars($translations['confirm_revoke']) ?>');">
|
||||
<input type="hidden" name="certificate_id" value="<?= htmlspecialchars($cert['id']) ?>">
|
||||
<button type="submit" class="button danger-button"><?= htmlspecialchars($translations['revoke_certificate']) ?></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
34
app/src/Views/dashboard/index.php
Normal file
34
app/src/Views/dashboard/index.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $translations, $currentLang, $darkModeClass, $username, $userRole
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= str_replace('{username}', htmlspecialchars($username), htmlspecialchars($translations['welcome'])) ?></h1>
|
||||
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/certificates"><?= htmlspecialchars($translations['certificates']) ?></a></li>
|
||||
<li><a href="/perimeters"><?= htmlspecialchars($translations['functional_perimeters']) ?></a></li>
|
||||
<?php if ($userRole === 'admin'): ?>
|
||||
<li><a href="/users"><?= htmlspecialchars($translations['users']) ?></a></li>
|
||||
<?php endif; ?>
|
||||
<li><a href="/logout" class="button logout-button"><?= htmlspecialchars($translations['logout']) ?></a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<section class="quick-actions">
|
||||
<h2><?= htmlspecialchars($translations['quick_actions']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="/certificates/create" class="button create-button"><?= htmlspecialchars($translations['create_new_certificate']) ?></a></li>
|
||||
<li><a href="/perimeters/create" class="button create-button"><?= htmlspecialchars($translations['create_new_perimeter']) ?></a></li>
|
||||
<?php if ($userRole === 'admin'): ?>
|
||||
<li><a href="/users/create" class="button create-button"><?= htmlspecialchars($translations['new_user']) ?></a></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
24
app/src/Views/perimeters/create.php
Normal file
24
app/src/Views/perimeters/create.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $translations, $currentLang, $darkModeClass, $errorMessage
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['create_new_perimeter']) ?></h1>
|
||||
<a href="/perimeters" class="button secondary-button"><?= htmlspecialchars($translations['back_to_perimeter_list']) ?></a>
|
||||
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($errorMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/perimeters/create" method="post">
|
||||
<label for="name"><?= htmlspecialchars($translations['perimeter_name']) ?>:</label><br>
|
||||
<input type="text" id="name" name="name" required placeholder="ex: Finance, RH, Marketing"><br><br>
|
||||
|
||||
<button type="submit" class="button primary-button"><?= htmlspecialchars($translations['create_perimeter_button']) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
51
app/src/Views/perimeters/index.php
Normal file
51
app/src/Views/perimeters/index.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $perimeters, $translations, $currentLang, $darkModeClass, $successMessage, $errorMessage
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['functional_perimeters']) ?></h1>
|
||||
<div class="actions-bar">
|
||||
<a href="/dashboard" class="button secondary-button"><?= htmlspecialchars($translations['back_to_dashboard']) ?></a>
|
||||
<?php if ($userRole === 'admin'): ?>
|
||||
<a href="/perimeters/create" class="button primary-button"><?= htmlspecialchars($translations['create_new_perimeter']) ?></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (isset($successMessage)): ?>
|
||||
<p class="success-message"><?= htmlspecialchars($successMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($errorMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($perimeters)): ?>
|
||||
<p><?= htmlspecialchars($translations['no_perimeters_yet']) ?></p>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= htmlspecialchars($translations['perimeter_name']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['intermediate_cert_file']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['created_at']) ?></th>
|
||||
<!-- Ajouter des actions ici si nécessaire, comme supprimer un périmètre (avec prudence!) -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($perimeters as $perimeter): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($perimeter['name']) ?></td>
|
||||
<td><?= htmlspecialchars($perimeter['intermediate_cert_name']) ?></td>
|
||||
<td><?= htmlspecialchars((new DateTime($perimeter['created_at']))->format('Y-m-d H:i:s')) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
8
app/src/Views/shared/footer.php
Normal file
8
app/src/Views/shared/footer.php
Normal file
@ -0,0 +1,8 @@
|
||||
</main>
|
||||
<footer class="app-footer">
|
||||
<div class="container">
|
||||
<p>© <?= date('Y') ?> <?= htmlspecialchars($translations['app_name'] ?? 'Gestion Certificat') ?>. Tous droits réservés.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
39
app/src/Views/shared/header.php
Normal file
39
app/src/Views/shared/header.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// Variables attendues: $translations, $currentLang, $darkModeClass
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?= htmlspecialchars($currentLang) ?>">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= htmlspecialchars($translations['app_name'] ?? 'Gestion Certificat') ?></title>
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link rel="stylesheet" href="/css/dark-mode.css">
|
||||
<!-- Font Awesome pour les icônes si besoin -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
</head>
|
||||
<body class="<?= htmlspecialchars($darkModeClass) ?>">
|
||||
<header class="app-header">
|
||||
<div class="header-content container">
|
||||
<div class="app-title">
|
||||
<h1><?= htmlspecialchars($translations['app_name'] ?? 'Gestion Certificat') ?></h1>
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
<div class="language-switcher">
|
||||
<?php foreach (SUPPORTED_LANGUAGES as $lang): ?>
|
||||
<a href="?lang=<?= htmlspecialchars($lang) ?>" class="lang-button <?= $currentLang === $lang ? 'active' : '' ?>"><?= htmlspecialchars(strtoupper($lang)) ?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="dark-mode-switcher">
|
||||
<a href="?dark_mode=<?= \App\Utils\DarkMode::isEnabled() ? 'off' : 'on' ?>" class="dark-mode-button">
|
||||
<?php if (\App\Utils\DarkMode::isEnabled()): ?>
|
||||
<i class="fas fa-sun"></i> <?= htmlspecialchars($translations['light_mode']) ?>
|
||||
<?php else: ?>
|
||||
<i class="fas fa-moon"></i> <?= htmlspecialchars($translations['dark_mode']) ?>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
33
app/src/Views/users/create.php
Normal file
33
app/src/Views/users/create.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $translations, $currentLang, $darkModeClass, $errorMessage
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['create_new_user']) ?></h1>
|
||||
<a href="/users" class="button secondary-button"><?= htmlspecialchars($translations['back_to_user_list']) ?></a>
|
||||
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($errorMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/users/create" method="post">
|
||||
<label for="username"><?= htmlspecialchars($translations['username']) ?>:</label><br>
|
||||
<input type="text" id="username" name="username" required autocomplete="new-username"><br><br>
|
||||
|
||||
<label for="password"><?= htmlspecialchars($translations['password']) ?>:</label><br>
|
||||
<input type="password" id="password" name="password" required autocomplete="new-password"><br><br>
|
||||
|
||||
<label for="role"><?= htmlspecialchars($translations['user_role']) ?>:</label><br>
|
||||
<select id="role" name="role" required>
|
||||
<option value="user"><?= htmlspecialchars($translations['user']) ?></option>
|
||||
<option value="admin"><?= htmlspecialchars($translations['admin']) ?></option>
|
||||
</select><br><br>
|
||||
|
||||
<button type="submit" class="button primary-button"><?= htmlspecialchars($translations['create_user_button']) ?></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
59
app/src/Views/users/index.php
Normal file
59
app/src/Views/users/index.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
// Variables attendues du contrôleur: $users, $translations, $currentLang, $darkModeClass, $successMessage, $errorMessage, $authService (pour getUserId)
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h1><?= htmlspecialchars($translations['users']) ?></h1>
|
||||
<div class="actions-bar">
|
||||
<a href="/dashboard" class="button secondary-button"><?= htmlspecialchars($translations['back_to_dashboard']) ?></a>
|
||||
<a href="/users/create" class="button primary-button"><?= htmlspecialchars($translations['create_new_user']) ?></a>
|
||||
</div>
|
||||
|
||||
<?php if (isset($successMessage)): ?>
|
||||
<p class="success-message"><?= htmlspecialchars($successMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($errorMessage)): ?>
|
||||
<p class="error-message"><?= htmlspecialchars($errorMessage); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($users)): ?>
|
||||
<p><?= htmlspecialchars($translations['no_users_yet']) ?></p>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= htmlspecialchars($translations['username']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['user_role']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['created_at']) ?></th>
|
||||
<th><?= htmlspecialchars($translations['actions']) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($user['username']) ?></td>
|
||||
<td><?= htmlspecialchars($translations[$user['role']] ?? $user['role']) ?></td>
|
||||
<td><?= htmlspecialchars((new DateTime($user['created_at']))->format('Y-m-d H:i:s')) ?></td>
|
||||
<td>
|
||||
<?php if ($user['id'] !== $authService->getUserId()): // Impossible de supprimer son propre compte ?>
|
||||
<form action="/users/delete" method="post" class="inline-form" onsubmit="return confirm('<?= htmlspecialchars($translations['confirm_delete_user']) ?>');">
|
||||
<input type="hidden" name="user_id" value="<?= htmlspecialchars($user['id']) ?>">
|
||||
<button type="submit" class="button danger-button"><?= htmlspecialchars($translations['delete_user']) ?></button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<em><?= htmlspecialchars($translations['self_delete_not_allowed']) ?></em>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once APP_ROOT_DIR . '/src/Views/shared/footer.php';
|
||||
?>
|
33
app/src/config/app.php
Normal file
33
app/src/config/app.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
// Chemin racine de l'application pour les chemins relatifs
|
||||
define('APP_ROOT_DIR', __DIR__ . '/../..');
|
||||
|
||||
// Configuration de la base de données
|
||||
// Ces valeurs sont aussi définies dans docker-compose.yml pour le conteneur MySQL
|
||||
define('DB_HOST', getenv('DB_HOST') ?: 'mysql');
|
||||
define('DB_NAME', getenv('DB_NAME') ?: 'cert_gestion');
|
||||
define('DB_USER', getenv('DB_USER') ?: 'user');
|
||||
define('DB_PASSWORD', getenv('DB_PASSWORD') ?: 'password_secret'); // À CHANGER ABSOLUMENT EN PRODUCTION !
|
||||
|
||||
// Configuration générale de l'application
|
||||
define('APP_NAME', 'Gestion Certificat');
|
||||
define('APP_ENV', 'development'); // 'production' ou 'development'
|
||||
|
||||
// Chemins des dossiers des certificats et scripts OpenSSL dans le conteneur PHP-FPM
|
||||
define('ROOT_CA_PATH', '/opt/tls/root');
|
||||
define('INTERMEDIATE_CA_PATH_BASE', '/opt/tls/intermediate'); // Base pour les CA intermédiaires par périmètre
|
||||
define('SCRIPTS_PATH', '/opt/scripts');
|
||||
|
||||
// Chemin du fichier de log principal de l'application PHP
|
||||
define('APP_LOG_PATH', '/var/log/app/app.log');
|
||||
|
||||
// Liste des langues supportées par l'application
|
||||
define('SUPPORTED_LANGUAGES', ['fr', 'en', 'de', 'it', 'pt', 'es']);
|
||||
|
||||
// Clé secrète pour la sécurité des sessions (TRÈS IMPORTANT !)
|
||||
// Générez une chaîne longue et aléatoire pour la production
|
||||
define('SESSION_SECRET', 'SuperStrongRandomSessionKeyForProduction_ChangeMe_1234567890ABCDEF');
|
||||
|
||||
// URL de base pour le service OCSP (doit correspondre à la configuration Nginx et des certificats)
|
||||
define('OCSP_URL', 'http://ocsp.cert-gestion.local/'); // À adapter à votre domaine réel
|
Reference in New Issue
Block a user