* GPL v3+ * * Token-Handling für die Eplan-PWA-Anbindung. * Kein JWT — ein simpler Shared-Secret-Token reicht, weil nur der * ElektroPlan-Backend-Container den Endpoint anspricht (Server-zu-Server * über HTTPS). Kein User-Kontext, keine Rotation per Request. */ /** * Lazy-init: Token aus llx_const laden, falls leer frisch erzeugen. * @return string 64-Zeichen hex Token */ function eplanTokenHolen() { global $db, $conf; $token = getDolGlobalString('EPLAN_PWA_SECRET'); if (!empty($token)) { return $token; } $token = bin2hex(random_bytes(32)); dolibarr_set_const($db, 'EPLAN_PWA_SECRET', $token, 'chaine', 0, 'PWA-Token', $conf->entity); return $token; } /** * Neuen Token erzeugen (überschreibt den alten). * @return string neuer Token */ function eplanTokenRotieren() { global $db, $conf; $token = bin2hex(random_bytes(32)); dolibarr_set_const($db, 'EPLAN_PWA_SECRET', $token, 'chaine', 0, 'PWA-Token', $conf->entity); return $token; } /** * Vergleicht einen übergebenen Token mit dem gespeicherten (timing-safe). * @param string $eingang Token aus dem Request * @return bool */ function eplanTokenPruefen($eingang) { if (empty($eingang)) return false; $gespeichert = getDolGlobalString('EPLAN_PWA_SECRET'); if (empty($gespeichert)) return false; return hash_equals($gespeichert, (string) $eingang); } /** * Token aus Request holen: bevorzugt Header X-Eplan-Token, sonst Query-Param `token`. * @return string|null */ function eplanTokenAusRequest() { $header = $_SERVER['HTTP_X_EPLAN_TOKEN'] ?? ''; if (!empty($header)) return $header; return $_GET['token'] ?? $_POST['token'] ?? null; } /** * File-basierter Brute-Force-Schutz (KB #354): * Pro IP maximal 10 fehlgeschlagene Versuche in 15 Minuten, danach blockiert. * Bei Erfolg wird der Zähler zurückgesetzt. * @return bool true wenn Request erlaubt, false wenn geblockt */ function eplanRateLimit($erfolg = null) { global $conf; $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; $dir = DOL_DATA_ROOT.'/eplan/loginattempts'; if (!is_dir($dir)) @mkdir($dir, 0755, true); $datei = $dir.'/'.sha1($ip).'.json'; $now = time(); $fenster = 900; // 15 Minuten $limit = 10; $daten = ['count' => 0, 'first' => $now]; if (file_exists($datei)) { $daten = json_decode(file_get_contents($datei), true) ?: $daten; if ($now - ($daten['first'] ?? 0) > $fenster) { $daten = ['count' => 0, 'first' => $now]; } } if ($erfolg === true) { @unlink($datei); return true; } if ($erfolg === false) { $daten['count']++; @file_put_contents($datei, json_encode($daten)); } return ($daten['count'] ?? 0) < $limit; }