Compare commits

..

27 Commits

Author SHA1 Message Date
Tom
b5146cde65 Update snmp_status.php
Funktion Hinzugefügt damit log files nicht unendlich wachsen jeweils ein 500kb backup und ein 500kb aktueller file
2025-12-03 17:54:12 +01:00
Tom
c03a99d07a Updated Snmp and Interface
Ich habe das UI Testweise nochmal erweitert und die Art wie wir die SNMP Daten abrufen und anzeigen nochmal überarbeitet. Es gibt jetzt eine kleine API die über Javascript in Intervallen die Daten aktualisiert. In der Datei Changelog sind nochmal die genauen Änderungen erklärt.
2025-12-03 17:06:10 +01:00
f9f9b1f99b feature/UI-Update (#12)
Co-authored-by: blaerf <blaerf@gmx.de>
Reviewed-on: https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/pulls/12
2025-11-29 06:32:50 +00:00
daba87e48f feature/UI-Update (#11)
Co-authored-by: blaerf <blaerf@gmx.de>
Reviewed-on: https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/pulls/11
2025-11-29 05:42:36 +00:00
bce1a0c719 sidebar, topbar ausblenden wenn nicht angemeldet (#10)
Co-authored-by: blaerf <blaerf@gmx.de>
Reviewed-on: https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/pulls/10
2025-11-29 05:24:37 +00:00
d013b6cb8a Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	public/views/layout.php
2025-11-28 23:30:56 +01:00
bde7b7c7ce Merge branch 'feature/UI-Update' into develop
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2025-11-28 23:27:54 +01:00
191666736b sidebar, topbar ausblenden wenn nicht angemeldet 2025-11-28 23:24:37 +01:00
2637715e48 sidebar, topbar ausblenden wenn nicht angemeldet 2025-11-28 23:07:07 +01:00
bef54a7553 feature/UI-Update (#8)
Co-authored-by: blaerf <blaerf@gmx.de>
Reviewed-on: https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/pulls/8
2025-11-28 21:58:13 +00:00
bdd79a66cb LAyout aufgeräumt 2025-11-28 22:57:33 +01:00
3eac9a659e Pfad form action geändert 2025-11-28 22:28:52 +01:00
d1bafa28d9 Text geändert 2025-11-28 22:27:16 +01:00
cfcc8f2d0c Dateien gelöscht (#7)
Co-authored-by: blaerf <blaerf@gmx.de>
Reviewed-on: https://git.eckertplayground.de/taarly/PHP_AdminTool_Projekt/pulls/7
2025-11-28 21:23:41 +00:00
28be78dceb Dateien gelöscht 2025-11-28 22:23:08 +01:00
ab296ad0dc Merge branch 'feature/UI-Update' into develop
# Conflicts:
#	public/views/login.php
2025-11-28 15:45:02 +01:00
f68db3ab1e Layout modularisiert 2025-11-28 15:34:38 +01:00
905ce67742 Layout modularisiert 2025-11-28 15:23:25 +01:00
8cd07a73a0 Pfade angepasst 2025-11-28 13:54:33 +01:00
77ac0577e2 Pfade angepasst 2025-11-28 13:52:07 +01:00
7f7cab75e5 Layout modularisiert 2025-11-28 10:41:40 +01:00
6ad293ee4f hinweis eingefügt 2025-11-27 23:07:03 +01:00
5acc6d93c9 hinweis eingefügt 2025-11-27 23:05:51 +01:00
dc75190642 hinweis eingefügt 2025-11-27 23:04:19 +01:00
b82d16a064 hinweis eingefügt 2025-11-27 23:03:05 +01:00
195f19a6f2 hinweis eingefügt 2025-11-27 22:59:21 +01:00
6c67d6437e hinweis eingefügt 2025-11-27 22:26:12 +01:00
7 changed files with 48 additions and 295 deletions

View File

@ -6,7 +6,6 @@ declare(strict_types=1);
namespace App\Controllers;
use App\Services\Ldap\LdapAuthService;
use App\Services\Logging\LoggingService;
/**
* Zuständig für alles rund um den Login:
@ -27,9 +26,6 @@ class AuthController
/** @var LdapAuthService Service, der die eigentliche LDAP/AD-Authentifizierung übernimmt */
private LdapAuthService $ldapAuthService;
/** @var LoggingService Logger für technische Fehler */
private LoggingService $logger;
/**
* Übergibt die Konfiguration an den Controller und initialisiert den LDAP-Authentifizierungsservice.
*
@ -43,9 +39,6 @@ class AuthController
// LdapAuthService mit dem Teilbereich "ldap" aus der Konfiguration initialisieren.
// Wenn 'ldap' nicht gesetzt ist, wird ein leeres Array übergeben (Fail fast erfolgt dann im Service).
$this->ldapAuthService = new LdapAuthService($config['ldap'] ?? []);
// LoggingService mit dem Teilbereich "logging" aus der Konfiguration initialisieren.
$this->logger = new LoggingService($config['logging'] ?? []);
}
/**
@ -61,14 +54,14 @@ class AuthController
// Wichtig: Die View erwartet aktuell die Variable $error.
return [
'view' => $viewPath,
'data' => [
'error' => $errorMessage,
'loginPage' => true,
],
'pageTitle' => 'Login',
'view' => $viewPath,
'data' => [
'error' => $errorMessage,
'loginPage' => true,
],
'pageTitle' => 'Login',
// Beim Login ist typischerweise kein Menüpunkt aktiv.
'activeMenu' => null,
'activeMenu' => null,
];
}
@ -93,25 +86,10 @@ class AuthController
// false = Anmeldedaten fachlich ungültig (Benutzer/Passwort falsch)
$authenticated = $this->ldapAuthService->authenticate($username, $password);
} catch (\Throwable $exception) {
// HIER ist vorher dein Fehler entstanden:
// - showLoginForm() wurde nur aufgerufen, das Ergebnis aber ignoriert
// - danach kam ein "return;" ohne Rückgabewert → Rückgabetyp array wurde verletzt
// Technischen Fehler ausführlich ins Log schreiben
$this->logger->logException(
'Technischer Fehler bei der Anmeldung.',
$exception,
[
'route' => 'login.submit',
'username' => $username,
'remote_addr'=> $_SERVER['REMOTE_ADDR'] ?? null,
]
);
// Für den Benutzer nur eine allgemeine, aber verständliche Meldung anzeigen
// Technischer Fehler (z. B. LDAP-Server nicht erreichbar, falsche Konfiguration).
// In diesem Fall wird eine technische Fehlermeldung im Login-Formular angezeigt.
return $this->showLoginForm(
'Technischer Fehler bei der Anmeldung. Bitte versuchen Sie es später erneut '
. 'oder wenden Sie sich an den Administrator.'
'Technischer Fehler bei der Anmeldung: ' . $exception->getMessage()
);
}
@ -129,15 +107,15 @@ class AuthController
// Benutzerinformationen in der Session hinterlegen.
// Diese Daten werden später von requireLogin() bzw. im Layout ausgewertet.
$_SESSION[$sessionKey] = [
'username' => $username,
'login_at' => date('c'), // ISO-8601 Datum/Zeit der Anmeldung
'username' => $username,
'login_at' => date('c'), // ISO-8601 Datum/Zeit der Anmeldung
];
// Nach erfolgreicher Anmeldung zum Dashboard weiterleiten.
// Kein direkter header()-Aufruf, sondern ein Redirect-Result
// für die zentrale Steuerung in index.php.
return [
'redirect' => 'index.php?route=dashboard',
'redirect' => 'index.php?route=dashboard',
];
}
@ -157,7 +135,7 @@ class AuthController
// Redirect-Result zur Login-Seite.
return [
'redirect' => 'index.php?route=login',
'redirect' => 'index.php?route=login',
];
}
}

View File

@ -6,7 +6,6 @@ declare(strict_types=1);
namespace App\Controllers;
use App\Services\Ldap\LdapDirectoryService;
use App\Services\Logging\LoggingService;
/**
* Controller für die Benutzer- und Gruppenanzeige.
@ -31,9 +30,6 @@ class UserManagementController
/** @var LdapDirectoryService Service für das Lesen von Benutzern und Gruppen aus dem LDAP/AD */
private LdapDirectoryService $directoryService;
/** @var LoggingService Logger für technische Fehler */
private LoggingService $logger;
/**
* @param array<string, mixed> $config Vollständige Konfiguration aus config.php
*/
@ -47,9 +43,6 @@ class UserManagementController
// Directory-Service initialisieren, der die eigentliche LDAP-Arbeit übernimmt.
$this->directoryService = new LdapDirectoryService($ldapConfig);
// Logging-Service initialisieren.
$this->logger = new LoggingService($config['logging'] ?? []);
}
/**
@ -70,34 +63,25 @@ class UserManagementController
$users = $this->directoryService->getUsers();
$groups = $this->directoryService->getGroups();
} catch (\Throwable $exception) {
// Technische Details ins Log, für den Benutzer eine allgemeine Meldung.
$this->logger->logException(
'Fehler beim Laden von Benutzern/Gruppen.',
$exception,
[
'route' => 'users',
'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? null,
]
);
$error = 'Fehler beim Laden von Benutzern/Gruppen. '
. 'Bitte versuchen Sie es später erneut oder wenden Sie sich an den Administrator.';
// Sämtliche technischen Fehler (z. B. Verbindungs- oder Konfigurationsprobleme)
// werden hier in eine für den Benutzer lesbare Fehlermeldung übersetzt.
$error = 'Fehler beim Laden von Benutzern/Gruppen: ' . $exception->getMessage();
}
// Pfad zur eigentlichen View-Datei bestimmen.
$viewPath = __DIR__ . '/../../public/views/users.php';
return [
'view' => $viewPath,
'data' => [
// Die View erwartet aktuell $users, $groups, $error.
'users' => $users,
'groups' => $groups,
'error' => $error,
'loginPage' => false,
],
'pageTitle' => 'Benutzer & Gruppen',
'activeMenu' => 'users',
'view' => $viewPath,
'data' => [
// Die View erwartet aktuell $users, $groups, $error.
'users' => $users,
'groups' => $groups,
'error' => $error,
'loginPage' => false,
],
'pageTitle' => 'Benutzer & Gruppen',
'activeMenu' => 'users',
];
}
}

View File

@ -58,8 +58,7 @@ class LdapConnectionHelper
// Verbindung zum LDAP/AD-Server herstellen.
// ldap_connect liefert entweder ein Verbindungs-Handle (Resource) oder false.
$uri = "ldap://".$server . ':' . $port;
$connection = ldap_connect($uri);
$connection = ldap_connect($server, $port);
// Wenn keine Verbindung aufgebaut werden konnte, Exception werfen.
if ($connection === false) {

View File

@ -1,133 +0,0 @@
<?php
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
declare(strict_types=1);
namespace App\Services\Logging;
use DateTimeImmutable;
/**
* Einfacher File-Logger für die AdminTool-Anwendung.
*
* Ziele:
* - Technische Details werden in eine Log-Datei unter public/logs/ geschrieben.
* - In der Weboberfläche erscheinen nur verständliche, fachliche Fehlermeldungen.
*/
class LoggingService
{
/** @var string Vollständiger Pfad zum Log-Verzeichnis */
private string $logDir;
/** @var string Dateiname der Log-Datei */
private string $logFile;
/** @var int Minimale Log-Stufe, ab der geschrieben wird. */
private int $minLevel;
/**
* Zuordnung der Log-Level zu numerischen Werten zur Filterung.
*
* @var array<string, int>
*/
private const LEVEL_MAP = [
'debug' => 100,
'info' => 200,
'warning' => 300,
'error' => 400,
];
/**
* @param array<string, mixed> $config Teilkonfiguration "logging" aus config.php
*/
public function __construct(array $config)
{
// Standard: public/logs relativ zum Projektroot
$baseDir = $config['log_dir'] ?? (__DIR__ . '/../../../public/logs');
$fileName = $config['log_file'] ?? 'app.log';
$level = strtolower((string)($config['min_level'] ?? 'info'));
$this->logDir = rtrim($baseDir, DIRECTORY_SEPARATOR);
$this->logFile = $fileName;
$this->minLevel = self::LEVEL_MAP[$level] ?? self::LEVEL_MAP['info'];
$this->ensureLogDirectoryExists();
}
/**
* Stellt sicher, dass das Log-Verzeichnis existiert.
*/
private function ensureLogDirectoryExists(): void
{
if (is_dir($this->logDir) === true) {
return;
}
if (@mkdir($this->logDir, 0775, true) === false && is_dir($this->logDir) === false) {
// Wenn das Anlegen fehlschlägt, wenigstens einen Eintrag im PHP-Error-Log hinterlassen.
error_log(sprintf('LoggingService: Konnte Log-Verzeichnis "%s" nicht anlegen.', $this->logDir));
}
}
/**
* Allgemeiner Log-Eintrag.
*
* @param string $level Log-Level (debug|info|warning|error)
* @param string $message Nachrichtentext
* @param array<string, mixed> $context Zusätzliche Kontextinformationen
*/
public function log(string $level, string $message, array $context = []): void
{
$level = strtolower($level);
$numericLevel = self::LEVEL_MAP[$level] ?? self::LEVEL_MAP['error'];
// Alles unterhalb der minimalen Stufe ignorieren.
if ($numericLevel < $this->minLevel) {
return;
}
$timestamp = (new DateTimeImmutable())->format('Y-m-d H:i:s');
$contextJson = $context === []
? '{}'
: (string)json_encode($context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$line = sprintf(
"[%s] %-7s %s %s%s",
$timestamp,
strtoupper($level),
$message,
$contextJson,
PHP_EOL
);
$filePath = $this->logDir . DIRECTORY_SEPARATOR . $this->logFile;
if (@file_put_contents($filePath, $line, FILE_APPEND | LOCK_EX) === false) {
// Fallback, damit Fehler beim Logging selbst nicht die App zerschießen.
error_log(sprintf('LoggingService: Konnte in Log-Datei "%s" nicht schreiben.', $filePath));
}
}
/**
* Komfortmethode, um Exceptions strukturiert zu loggen.
*
* @param string $message Kurzer Kontexttext zur Exception
* @param \Throwable $exception Die geworfene Exception
* @param array<string, mixed> $context Zusätzlicher Kontext (Route, Benutzername, Remote-IP, ...)
*/
public function logException(string $message, \Throwable $exception, array $context = []): void
{
$exceptionContext = [
'exception_class' => get_class($exception),
'exception_message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
];
$mergedContext = array_merge($context, $exceptionContext);
$this->log('error', $message, $mergedContext);
}
}

View File

@ -49,14 +49,4 @@ return [
'storage_used' => '1.3.6.1.2.1.25.2.3.1.6',
],
],
// Logging-Konfiguration
'logging' => [
// Standard: public/logs relativ zum Projekt-Root
'log_dir' => __DIR__ . '/../public/logs',
// Name der Logdatei
'log_file' => 'app.log',
// Minimale Stufe: debug, info, warning, error
'min_level' => 'info',
],
];

View File

@ -19,14 +19,9 @@ declare(strict_types=1);
* - Alle neuen Routen sollten über den Switch-Block am Ende ergänzt werden.
*/
// Eine neue Session wird gestartet und die entsprechende Variable ($_SESSION) angelegt
// oder eine bestehende wird fortgesetzt.
session_start();
// PHP-Fehler erfassen, aber veraltete Hinweise (E_DEPRECATED) ignorieren,
// weil sie sonst im Zusammenspiel mit IIS/fastcgi zu 500-Fehlern führen können.
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
ini_set('display_errors', '0');
// Eine neue Session wird gestartet und die entsprechende Variable ($_SESSION) angelegt oder eine bestehende wird fortgesetzt.
session_start();
/*
* Registriert eine Autoload-Funktion für Klassen mit dem Namespace-Präfix "App\".
@ -36,7 +31,7 @@ ini_set('display_errors', '0');
*/
spl_autoload_register(
static function (string $class): void {
$prefix = 'App\\';
$prefix = 'App\\';
$baseDir = __DIR__ . '/../app/';
$len = strlen($prefix);
@ -45,7 +40,7 @@ spl_autoload_register(
}
$relativeClass = substr($class, $len);
$file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';
$file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';
if (file_exists($file) === true) {
require $file;
@ -53,11 +48,11 @@ spl_autoload_register(
}
);
// Layout-Funktion einbinden (renderLayout)
require __DIR__ . '/views/layout.php';
// Die Konfigurationsdatei liefert ein assoziatives Array mit den Teilbereichen
// "ldap", "snmp", "security" und "logging" (u. a. Session-Keys, Timeout-Einstellungen, OIDs, Log-Pfade).
// "ldap", "snmp" und "security" (u. a. Session-Keys, Timeout-Einstellungen, OIDs).
$configPath = __DIR__ . '/../config/config.php';
if (file_exists($configPath) === false) {
// Fail fast: ohne Konfiguration macht die App keinen Sinn
@ -73,61 +68,6 @@ $config = require $configPath;
use App\Controllers\AuthController;
use App\Controllers\DashboardController;
use App\Controllers\UserManagementController;
use App\Services\Logging\LoggingService;
// Globalen Logger initialisieren, damit auch Fehler außerhalb der Controller
// (z. B. in index.php selbst) sauber protokolliert werden.
$globalLogger = new LoggingService($config['logging'] ?? []);
/**
* Globale Fehlerbehandlung:
* - PHP-Fehler (Warnings, Notices, ...) werden in den Logger geschrieben.
* - Unbehandelte Exceptions werden ebenfalls geloggt und führen zu einer generischen 500er-Meldung.
*/
set_error_handler(
static function (
int $severity,
string $message,
string $file = '',
int $line = 0
) use ($globalLogger): bool {
// Fehler nur loggen, wenn sie durch error_reporting() nicht unterdrückt sind.
if ((error_reporting() & $severity) === 0) {
return false;
}
$globalLogger->log(
'error',
'PHP-Fehler: ' . $message,
[
'severity' => $severity,
'file' => $file,
'line' => $line,
]
);
// false zurückgeben = PHP darf seinen Standard-Handler zusätzlich verwenden
// (der Browser sieht wegen display_errors=0 trotzdem nichts).
return false;
}
);
set_exception_handler(
static function (\Throwable $exception) use ($globalLogger): void {
$globalLogger->logException(
'Unbehandelte Exception in der Anwendung.',
$exception,
[
'request_uri' => $_SERVER['REQUEST_URI'] ?? null,
'route' => $_GET['route'] ?? null,
]
);
http_response_code(500);
echo 'Es ist ein unerwarteter Fehler aufgetreten. '
. 'Bitte versuchen Sie es später erneut oder wenden Sie sich an den Administrator.';
}
);
/**
* Hilfsfunktion: Prüft, ob ein Benutzer eingeloggt ist.
@ -159,24 +99,20 @@ function handleResult(?array $result): void
return;
}
// Redirect-Result
if (isset($result['redirect']) === true) {
header('Location: ' . (string)$result['redirect']);
exit;
}
// View-Result
$contentView = (string)($result['view'] ?? '');
$viewData = (array)($result['data'] ?? []);
// Standard: Wir gehen davon aus, dass es KEINE Loginseite ist,
// außer der Controller sagt explizit etwas anderes.
if (array_key_exists('loginPage', $viewData) === false) {
if (!array_key_exists('loginPage', $viewData)) {
$viewData['loginPage'] = false;
}
$pageTitle = (string)($result['pageTitle'] ?? '');
$activeMenu = $result['activeMenu'] ?? null;
$pageTitle = (string)($result['pageTitle'] ?? '');
$activeMenu = $result['activeMenu'] ?? null;
if ($contentView === '' || file_exists($contentView) === false) {
http_response_code(500);
@ -185,33 +121,30 @@ function handleResult(?array $result): void
}
// Hier rufen wir jetzt die Layout-Funktion aus layout.php auf
renderLayout(
$contentView,
$viewData,
$pageTitle,
is_string($activeMenu) ? $activeMenu : null
);
renderLayout($contentView, $viewData, $pageTitle, is_string($activeMenu) ? $activeMenu : null);
}
// Zentrale Controller der Anwendung initialisieren und ihnen die vollständige Konfiguration übergeben.
// Die Controller holen sich daraus bei Bedarf ihre spezifischen Teilkonfigurationen (z. B. "ldap" oder "snmp").
// Jeder Controller erzeugt intern seinen eigenen LoggingService aus $config['logging'].
$authController = new AuthController($config);
$dashboardController = new DashboardController($config);
$authController = new AuthController($config);
$dashboardController = new DashboardController($config);
$userManagementController = new UserManagementController($config);
// Route aus dem Query-Parameter lesen. Standardroute ist "login",
// sodass nicht angemeldete Benutzer automatisch auf die Login-Seite geführt werden.
$route = $_GET['route'] ?? 'login';
// Einfache Router-Logik: Jede Route ruft eine Controller-Methode auf und
// übergibt deren View-Result an handleResult(). Neue Seiten werden hier ergänzt.
switch ($route) {
case 'login':
// Login-Formular anzeigen (ggf. mit Fehlermeldung)
$result = $authController->showLoginForm();
handleResult($result);
break;
break;
case 'login.submit':
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
@ -245,3 +178,5 @@ switch ($route) {
echo 'Route nicht gefunden.';
break;
}

View File

@ -44,7 +44,7 @@ declare(strict_types=1);
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="usersTable">
<table class="table table-bordered" id="usersTable" width="100%" cellspacing="0">
<thead>
<tr>
<th><input type="checkbox" name="selectAllUsers"></th>
@ -83,7 +83,7 @@ declare(strict_types=1);
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="groupsTable">
<table class="table table-bordered" id="groupsTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Gruppenname (sAMAccountName)</th>