mirror of
https://github.com/tips-of-mine/gestion-certificats2.git
synced 2025-06-28 09:18:42 +02:00
Merge pull request #9 from tips-of-mine/feat/certificate-download
Implémenter la fonctionnalité de téléchargement des certificats depui…
This commit is contained in:
@ -186,6 +186,7 @@ $router->addRoute('GET', '/certificates', 'CertificateController@index', true);
|
||||
$router->addRoute('GET', '/certificates/create', 'CertificateController@showCreateForm', true);
|
||||
$router->addRoute('POST', '/certificates/create', 'CertificateController@create', true);
|
||||
$router->addRoute('POST', '/certificates/revoke', 'CertificateController@revoke', true);
|
||||
$router->addRoute('GET', '/certificates/download', 'CertificateController@download', true);
|
||||
$router->addRoute('GET', '/perimeters', 'PerimeterController@index', true);
|
||||
$router->addRoute('GET', '/perimeters/create', 'PerimeterController@showCreateForm', true);
|
||||
$router->addRoute('POST', '/perimeters/create', 'PerimeterController@create', true);
|
||||
|
@ -248,4 +248,118 @@ class CertificateController
|
||||
header('Location: /certificates');
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère le téléchargement des fichiers de certificats et clés.
|
||||
*/
|
||||
public function download()
|
||||
{
|
||||
if (!$this->authService->isLoggedIn()) {
|
||||
header('Location: /login');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Récupérer les paramètres de la requête
|
||||
$type = $_GET['type'] ?? null;
|
||||
$fileName = $_GET['file'] ?? null;
|
||||
$perimeterName = $_GET['perimeter'] ?? null; // Utilisé pour intermédiaires et simples
|
||||
|
||||
// Log de la tentative de téléchargement
|
||||
$userId = $this->authService->getUserId();
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$this->logService->log('info', "Download attempt: type='{$type}', file='{$fileName}', perimeter='{$perimeterName}'", $userId, $ipAddress);
|
||||
|
||||
// Validation basique des paramètres
|
||||
if (empty($type) || empty($fileName)) {
|
||||
$_SESSION['error'] = 'Missing download parameters.';
|
||||
$this->logService->log('warn', "Missing download parameters.", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Sécurisation des noms de fichier et de périmètre
|
||||
if (basename($fileName) !== $fileName || ($perimeterName && basename($perimeterName) !== $perimeterName)) {
|
||||
$_SESSION['error'] = 'Invalid characters in file or perimeter name.';
|
||||
$this->logService->log('error', "Invalid characters in file or perimeter name for download.", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
$filePath = '';
|
||||
|
||||
switch ($type) {
|
||||
case 'root':
|
||||
if ($fileName === 'ca.cert.pem') {
|
||||
$filePath = ROOT_CA_PATH . '/certs/' . $fileName;
|
||||
} elseif ($fileName === 'ca.key.pem') {
|
||||
if ($this->authService->getUserRole() === 'admin') {
|
||||
$filePath = ROOT_CA_PATH . '/private/' . $fileName;
|
||||
} else {
|
||||
$_SESSION['error'] = 'Unauthorized to download root key.';
|
||||
$this->logService->log('error', "Unauthorized attempt to download root CA key by user ID: {$userId}", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error'] = 'Invalid file specified for root certificate type.';
|
||||
$this->logService->log('warn', "Invalid file '{$fileName}' specified for root certificate type.", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
break;
|
||||
case 'intermediate':
|
||||
if (empty($perimeterName)) {
|
||||
$_SESSION['error'] = 'Perimeter name missing for intermediate certificate.';
|
||||
$this->logService->log('warn', "Perimeter name missing for intermediate certificate download.", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
$filePath = INTERMEDIATE_CA_PATH_BASE . '/' . $perimeterName . '/certs/' . $fileName;
|
||||
break;
|
||||
case 'simple':
|
||||
if (empty($perimeterName)) {
|
||||
$_SESSION['error'] = 'Perimeter name missing for simple certificate.';
|
||||
$this->logService->log('warn', "Perimeter name missing for simple certificate download.", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
$filePath = INTERMEDIATE_CA_PATH_BASE . '/' . $perimeterName . '/certs/' . $fileName;
|
||||
break;
|
||||
default:
|
||||
$_SESSION['error'] = 'Invalid certificate type for download.';
|
||||
$this->logService->log('warn', "Invalid certificate type for download: {$type}", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!empty($filePath) && file_exists($filePath) && is_readable($filePath)) {
|
||||
$mimeType = 'application/octet-stream';
|
||||
$fileExtension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
|
||||
|
||||
if ($fileExtension === 'pem' || $fileExtension === 'key' || $fileExtension === 'crt' || $fileExtension === 'cer') {
|
||||
$mimeType = 'application/x-pem-file';
|
||||
}
|
||||
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: ' . $mimeType);
|
||||
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . filesize($filePath));
|
||||
|
||||
if (ob_get_level()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
readfile($filePath);
|
||||
$this->logService->log('info', "File '{$filePath}' downloaded successfully.", $userId, $ipAddress);
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION['error'] = 'File not found or not readable: ' . htmlspecialchars($fileName);
|
||||
$this->logService->log('error', "File not found or not readable for download: {$filePath} (Type: {$type}, File: {$fileName}, Perimeter: {$perimeterName})", $userId, $ipAddress);
|
||||
header('Location: /dashboard');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Services\AuthService;
|
||||
use App\Services\LanguageService;
|
||||
use App\Utils\DarkMode;
|
||||
// Ensure App\Core\Database is imported
|
||||
use App\Core\Database;
|
||||
|
||||
/**
|
||||
* Contrôleur pour la page du tableau de bord.
|
||||
@ -14,14 +15,19 @@ class DashboardController
|
||||
{
|
||||
private $authService;
|
||||
private $langService;
|
||||
private $db; // Property to hold the database instance
|
||||
|
||||
/**
|
||||
* Constructeur du DashboardController.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// Initialize database connection here if it's meant to be a class property
|
||||
// For now, authService initializes its own, and index() gets a new instance.
|
||||
// If $this->db was intended, it should be $this->db = Database::getInstance();
|
||||
$this->authService = new AuthService(Database::getInstance());
|
||||
$this->langService = new LanguageService(APP_ROOT_DIR . '/src/Lang/');
|
||||
// $this->db = Database::getInstance(); // Uncomment if $db should be a class property accessible in index() via $this->db
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,6 +48,78 @@ class DashboardController
|
||||
$darkModeClass = DarkMode::getBodyClass();
|
||||
$userRole = $this->authService->getUserRole(); // Pour afficher/masquer certains éléments
|
||||
|
||||
// Initialize database connection
|
||||
// If $this->db was initialized in constructor, use $db = $this->db;
|
||||
$db = Database::getInstance(); // Using a local instance as per original structure
|
||||
|
||||
// Initialize structured certificates array
|
||||
$structuredCertificates = [
|
||||
'root' => null,
|
||||
'intermediates' => [],
|
||||
];
|
||||
|
||||
// Fetch Root Certificate
|
||||
$stmt = $db->prepare("SELECT name FROM certificates WHERE type = 'root' LIMIT 1");
|
||||
$stmt->execute();
|
||||
$rootCert = $stmt->fetch();
|
||||
|
||||
if ($rootCert) {
|
||||
$structuredCertificates['root'] = [
|
||||
'name' => $rootCert['name'],
|
||||
'cert_path' => ROOT_CA_PATH . '/certs/ca.cert.pem',
|
||||
'key_path' => ROOT_CA_PATH . '/private/ca.key.pem', // Corrected path as per instructions
|
||||
];
|
||||
} else {
|
||||
// Handle case where root certificate is not found, though unlikely
|
||||
// You might want to log this or set a default structure
|
||||
$structuredCertificates['root'] = [
|
||||
'name' => 'N/A',
|
||||
'cert_path' => null,
|
||||
'key_path' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// Fetch Intermediate Certificates
|
||||
$stmt = $db->prepare("
|
||||
SELECT c.id, c.name, c.functional_perimeter_id, fp.name as perimeter_name
|
||||
FROM certificates c
|
||||
JOIN functional_perimeters fp ON c.functional_perimeter_id = fp.id
|
||||
WHERE c.type = 'intermediate'
|
||||
ORDER BY fp.name ASC, c.name ASC
|
||||
");
|
||||
$stmt->execute();
|
||||
$intermediateCerts = $stmt->fetchAll();
|
||||
|
||||
foreach ($intermediateCerts as $interCert) {
|
||||
$intermediateData = [
|
||||
'id' => $interCert['id'],
|
||||
'name' => $interCert['name'],
|
||||
'perimeter_name' => $interCert['perimeter_name'],
|
||||
'functional_perimeter_id' => $interCert['functional_perimeter_id'], // Pass perimeter_id for linking
|
||||
'final_certificates' => [],
|
||||
];
|
||||
|
||||
// Fetch Final Certificates for this Intermediate
|
||||
// Ensure functional_perimeter_id is used in the query for 'simple' certificates
|
||||
$stmtFinal = $db->prepare("
|
||||
SELECT name, type, expiration_date, is_revoked
|
||||
FROM certificates
|
||||
WHERE type = 'simple' AND functional_perimeter_id = ?
|
||||
ORDER BY name ASC
|
||||
");
|
||||
// Use $interCert['functional_perimeter_id'] which is the ID of the perimeter for this intermediate cert
|
||||
$stmtFinal->execute([$interCert['functional_perimeter_id']]);
|
||||
$finalCerts = $stmtFinal->fetchAll();
|
||||
|
||||
if ($finalCerts) {
|
||||
foreach ($finalCerts as $finalCert) {
|
||||
$intermediateData['final_certificates'][] = $finalCert;
|
||||
}
|
||||
}
|
||||
$structuredCertificates['intermediates'][] = $intermediateData;
|
||||
}
|
||||
|
||||
// Pass data to the view
|
||||
require_once APP_ROOT_DIR . '/src/Views/dashboard/index.php';
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,68 @@ require_once APP_ROOT_DIR . '/src/Views/shared/header.php';
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="certificates-overview">
|
||||
<h2><?= htmlspecialchars($translations['certificates_overview_title'] ?? 'Certificates Overview') ?></h2>
|
||||
|
||||
<!-- Root Certificate -->
|
||||
<h3><?= htmlspecialchars($translations['root_certificate_title'] ?? 'Root Certificate') ?></h3>
|
||||
<?php if (isset($structuredCertificates['root']) && $structuredCertificates['root']): ?>
|
||||
<div>
|
||||
<p><strong><?= htmlspecialchars($translations['name'] ?? 'Name:') ?></strong> <?= htmlspecialchars($structuredCertificates['root']['name']) ?></p>
|
||||
<p>
|
||||
<a href="/certificates/download?type=root&file=ca.cert.pem" class="button">
|
||||
<?= htmlspecialchars($translations['download_certificate_pem'] ?? 'Download Certificate (.pem)') ?>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/certificates/download?type=root&file=ca.key.pem" class="button">
|
||||
<?= htmlspecialchars($translations['download_key_pem'] ?? 'Download Private Key (.key)') ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p><?= htmlspecialchars($translations['root_certificate_not_configured'] ?? 'Root certificate is not configured.') ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Intermediate Certificates -->
|
||||
<h3><?= htmlspecialchars($translations['intermediate_certificates_title'] ?? 'Intermediate Certificates') ?></h3>
|
||||
<?php if (isset($structuredCertificates['intermediates']) && !empty($structuredCertificates['intermediates'])): ?>
|
||||
<?php foreach ($structuredCertificates['intermediates'] as $intermediate): ?>
|
||||
<div class="intermediate-certificate">
|
||||
<h4><?= htmlspecialchars($intermediate['name']) ?> (<?= htmlspecialchars($translations['perimeter'] ?? 'Perimeter:') ?> <?= htmlspecialchars($intermediate['perimeter_name']) ?>)</h4>
|
||||
<p>
|
||||
<a href="/certificates/download?type=intermediate&perimeter=<?= urlencode($intermediate['perimeter_name']) ?>&file=intermediate.cert.pem" class="button">
|
||||
<?= htmlspecialchars($translations['download_certificate_pem'] ?? 'Download Certificate (.pem)') ?>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h5><?= htmlspecialchars($translations['associated_final_certificates_title'] ?? 'Associated Final Certificates') ?></h5>
|
||||
<?php if (isset($intermediate['final_certificates']) && !empty($intermediate['final_certificates'])): ?>
|
||||
<ul>
|
||||
<?php foreach ($intermediate['final_certificates'] as $finalCert): ?>
|
||||
<li>
|
||||
<?= htmlspecialchars($finalCert['name']) ?>
|
||||
(<?= htmlspecialchars($translations['type'] ?? 'Type:') ?> <?= htmlspecialchars($finalCert['type']) ?>,
|
||||
<?= htmlspecialchars($translations['expires'] ?? 'Expires:') ?> <?= htmlspecialchars($finalCert['expiration_date']) ?>,
|
||||
<?= htmlspecialchars($translations['revoked'] ?? 'Revoked:') ?> <?= $finalCert['is_revoked'] ? htmlspecialchars($translations['yes'] ?? 'Yes') : htmlspecialchars($translations['no'] ?? 'No') ?>)
|
||||
<p>
|
||||
<a href="/certificates/download?type=simple&perimeter=<?= urlencode($intermediate['perimeter_name']) ?>&file=<?= urlencode($finalCert['name']) ?>" class="button download-button-small">
|
||||
<?= htmlspecialchars($translations['download_certificate_pem'] ?? 'Download Certificate (.pem)') ?>
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p><?= htmlspecialchars($translations['no_associated_final_certificates'] ?? 'No final certificates associated with this intermediate.') ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p><?= htmlspecialchars($translations['no_intermediate_certificates_found'] ?? 'No intermediate certificates found.') ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
Reference in New Issue
Block a user