Anmeldung nur noch ueber AWL-Auth - eigener JWT-Pfad entfernt
ACHTUNG, Schnitt: Geraete mit einer App-Fassung, die noch ein modul-eigenes netdiag-JWT benutzt, koennen sich nicht mehr anmelden. Eddy hat den APK-Rollout am 17.08.2026 als abgeschlossen bestaetigt. Mehr als Aufraeumen: Der alte Pfad pruefte die Signatur eines selbst ausgestellten Tokens und holte damit einen Benutzer aus der Datenbank - an der Sitzungsliste von awlauth vorbei. "Geraet abmelden" hatte darauf keine Wirkung; ein verlorenes Handy blieb bis zum Ablauf der TTL angemeldet. Jetzt gibt es genau eine Stelle, an der Sitzungen entstehen und enden. Entfernt: der Rueckfallweg in auth.php (ohne aktives AWL-Auth jetzt 503 statt zweitem Weg), netdiag_jwt_encode/decode/secret samt Base64-URL-Helfern, die Konstanten NETDIAG_API_JWT_SECRET und NETDIAG_API_TOKEN_TTL aus dem Descriptor, das TTL-Feld aus dem Setup. Bestehende llx_const-Werte bleiben stehen - ein Loeschlauf beim Modul-Update waere das groessere Risiko. Die Gueltigkeit kommt jetzt allein aus AWLAUTH_TTL (awlauth-Setup, Standard 7 Tage). Genau darauf hat Eddy hingewiesen: das ist im Auth-Modul geregelt. Sprachdateien de_DE und en_US nachgezogen, beide wieder deckungsgleich. Geprueft gegen die Testinstanz: falsches Passwort -> sauberer awlauth-Fehler, ungueltiges Token -> 401, kein Token -> 401. Kein PHP-Fehler durch die entfernten Funktionen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9bea09845f
commit
219d489951
7 changed files with 98 additions and 159 deletions
37
ChangeLog.md
37
ChangeLog.md
|
|
@ -1,5 +1,42 @@
|
|||
# Changelog NetDiag
|
||||
|
||||
## 1.2.0 — 2026-08-17
|
||||
|
||||
### Anmeldung nur noch über AWL-Auth — der eigene JWT-Pfad ist weg
|
||||
|
||||
**Achtung, das ist ein Schnitt:** Geräte mit einer App-Fassung, die noch ein
|
||||
modul-eigenes netdiag-JWT benutzt, können sich nicht mehr anmelden und brauchen
|
||||
die aktuelle APK. Eddy hat den Rollout am 17.08.2026 als abgeschlossen bestätigt.
|
||||
|
||||
Warum das mehr ist als Aufräumen: Der alte Pfad prüfte die Signatur eines selbst
|
||||
ausgestellten Tokens und holte damit einen Benutzer aus der Datenbank — **an der
|
||||
Sitzungsliste von awlauth vorbei**. „Gerät abmelden" hatte auf ein solches Token
|
||||
keine Wirkung; ein verlorenes Handy blieb bis zum Ablauf der TTL angemeldet. Jetzt
|
||||
gibt es genau eine Stelle, an der Sitzungen entstehen und enden.
|
||||
|
||||
- `api/auth.php`: der Rückfallweg (`checkLoginPassEntity` + eigenes JWT) ist
|
||||
entfernt. Ohne aktives AWL-Auth antwortet die Anmeldung mit 503 und einer
|
||||
klaren Meldung, statt auf einen zweiten Weg auszuweichen.
|
||||
- `api/netdiag_api.lib.php`: `netdiag_api_authenticate()` prüft nur noch
|
||||
`awlauth_verify_token()`. Die Hilfsfunktionen `netdiag_jwt_encode/decode`,
|
||||
`netdiag_jwt_secret` und die Base64-URL-Helfer sind ersatzlos entfallen.
|
||||
- **Konstanten `NETDIAG_API_JWT_SECRET` und `NETDIAG_API_TOKEN_TTL`** sind aus
|
||||
dem Modul-Descriptor raus. Bestehende Werte in `llx_const` werden bewusst
|
||||
NICHT gelöscht — sie schaden nicht, und ein Löschlauf beim Modul-Update ist
|
||||
das größere Risiko.
|
||||
- **TTL-Feld aus `admin/setup.php` entfernt.** Die Gültigkeit kommt jetzt allein
|
||||
aus `AWLAUTH_TTL` (awlauth-Setup, Standard 7 Tage, dort 1–180 Tage
|
||||
einstellbar). Zwei Stellschrauben für dieselbe Sache sind eine Fehlerquelle —
|
||||
die hiesige hätte nach diesem Umbau still gar nichts mehr getan. An ihrer
|
||||
Stelle steht jetzt ein Verweis.
|
||||
- Sprachdateien de_DE und en_US nachgezogen, beide wieder deckungsgleich
|
||||
(57 Schlüssel).
|
||||
|
||||
Geprüft gegen die lokale Testinstanz: Anmeldung mit falschem Passwort → sauberer
|
||||
Fehler von awlauth; API mit ungültigem Token → 401 „Token ungültig oder
|
||||
abgelaufen"; ohne Token → 401 „Kein Token übermittelt". Kein PHP-Fehler durch
|
||||
die entfernten Funktionen.
|
||||
|
||||
## 1.1.7 — 2026-08-17
|
||||
|
||||
### PDF-Zweig und Feldtabelle für den Geräte-Vergleich
|
||||
|
|
|
|||
|
|
@ -70,11 +70,6 @@ $action = GETPOST('action', 'aZ09');
|
|||
|
||||
// Konstanten speichern
|
||||
if ($action == 'updateconst') {
|
||||
$ttl = GETPOSTINT('NETDIAG_API_TOKEN_TTL');
|
||||
if ($ttl < 60) {
|
||||
$ttl = 604800;
|
||||
}
|
||||
dolibarr_set_const($db, 'NETDIAG_API_TOKEN_TTL', $ttl, 'chaine', 0, '', $conf->entity);
|
||||
dolibarr_set_const($db, 'NETDIAG_APK_URL', GETPOST('NETDIAG_APK_URL', 'alpha'), 'chaine', 0, '', $conf->entity);
|
||||
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
|
||||
header("Location: ".$_SERVER["PHP_SELF"]);
|
||||
|
|
@ -104,9 +99,15 @@ print '<input type="hidden" name="action" value="updateconst">';
|
|||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre"><td>'.$langs->trans("Parameter").'</td><td>'.$langs->trans("Value").'</td></tr>';
|
||||
|
||||
print '<tr class="oddeven"><td>'.$langs->trans("NETDIAG_API_TOKEN_TTL");
|
||||
print ' '.$form->textwithpicto('', $langs->trans("NETDIAG_API_TOKEN_TTLTooltip")).'</td>';
|
||||
print '<td><input type="number" name="NETDIAG_API_TOKEN_TTL" value="'.dol_escape_htmltag(getDolGlobalString('NETDIAG_API_TOKEN_TTL', '604800')).'" min="60" class="width150"></td></tr>';
|
||||
/*
|
||||
* Die Gültigkeit der Anmeldung wird NICHT mehr hier eingestellt. Sie kommt
|
||||
* seit 17.08.2026 allein aus `AWLAUTH_TTL` im Setup des Moduls AWL-Auth
|
||||
* (Standard 7 Tage). Zwei Stellschrauben für dieselbe Sache sind eine
|
||||
* Fehlerquelle: die hiesige wirkte nur auf den inzwischen entfernten
|
||||
* modul-eigenen Anmeldeweg und hätte still nichts mehr getan.
|
||||
*/
|
||||
print '<tr class="oddeven"><td>'.$langs->trans("NetDiagTokenTTLMoved").'</td>';
|
||||
print '<td class="opacitymedium">'.$langs->trans("NetDiagTokenTTLMovedValue").'</td></tr>';
|
||||
|
||||
print '<tr class="oddeven"><td>'.$langs->trans("NETDIAG_APK_URL");
|
||||
print ' '.$form->textwithpicto('', $langs->trans("NETDIAG_APK_URLTooltip")).'</td>';
|
||||
|
|
|
|||
60
api/auth.php
60
api/auth.php
|
|
@ -55,11 +55,25 @@ if ($login === '' || $password === '') {
|
|||
require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||
|
||||
if (netdiag_awlauth_available()) {
|
||||
// ---------------------------------------------------------------------
|
||||
// Zentrale Anmeldung über awlauth (Rate-Limit, Passwortprüfung,
|
||||
// SSO-Eignung) — dieselbe Prüfung wie in allen anderen AWL-Apps.
|
||||
// ---------------------------------------------------------------------
|
||||
/*
|
||||
* Anmeldung ausschließlich über awlauth — Rate-Limit, Passwortprüfung,
|
||||
* SSO-Eignung und die zentrale Sitzungsliste kommen von dort, wie in allen
|
||||
* anderen AWL-Apps.
|
||||
*
|
||||
* Der frühere Rückfallweg mit modul-eigenem JWT ist am 17.08.2026 entfallen
|
||||
* (APK-Rollout abgeschlossen). Er stellte Tokens aus, die an der
|
||||
* awlauth-Geräteliste vorbeiliefen: „Gerät abmelden" hatte darauf keine
|
||||
* Wirkung. Lieber ein klarer Fehler als eine zweite, unsichtbare Sitzungsart.
|
||||
*
|
||||
* Die Gültigkeit bestimmt jetzt allein `AWLAUTH_TTL` (awlauth-Setup,
|
||||
* Standard 7 Tage) — es gibt keine konkurrierende Einstellung mehr im
|
||||
* NetDiag-Setup.
|
||||
*/
|
||||
if (!netdiag_awlauth_available()) {
|
||||
netdiag_api_error('Anmeldung nicht möglich: das Modul AWL-Auth ist nicht aktiv', 503);
|
||||
}
|
||||
|
||||
{
|
||||
$res = awlauth_login($login, $password);
|
||||
if (empty($res['success'])) {
|
||||
netdiag_api_error($res['error'], (int) $res['http']);
|
||||
|
|
@ -87,42 +101,6 @@ if (netdiag_awlauth_available()) {
|
|||
}
|
||||
$token = $issued['token'];
|
||||
$ttl = (int) $issued['expiresIn'];
|
||||
} else {
|
||||
// ---------------------------------------------------------------------
|
||||
// Fallback ohne awlauth: modul-eigenes JWT (Alt-Verhalten).
|
||||
// ---------------------------------------------------------------------
|
||||
$entitytocheck = (int) $conf->entity;
|
||||
$authmode = (getDolGlobalString('MAIN_AUTHENTICATION_MODE') ? getDolGlobalString('MAIN_AUTHENTICATION_MODE') : 'dolibarr');
|
||||
$resultlogin = checkLoginPassEntity($login, $password, $entitytocheck, explode(',', $authmode));
|
||||
|
||||
if (empty($resultlogin)) {
|
||||
// Kurze Verzögerung gegen Brute-Force
|
||||
sleep(1);
|
||||
netdiag_api_error('Login fehlgeschlagen', 401);
|
||||
}
|
||||
|
||||
$user = new User($db);
|
||||
if ($user->fetch('', $resultlogin, '', 0, $entitytocheck) <= 0 || empty($user->id)) {
|
||||
netdiag_api_error('Benutzer nicht gefunden', 401);
|
||||
}
|
||||
if (!empty($user->statut) && $user->statut == 0) {
|
||||
netdiag_api_error('Benutzer deaktiviert', 403);
|
||||
}
|
||||
$user->loadRights();
|
||||
|
||||
if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
||||
netdiag_api_error('Keine Berechtigung für NetDiag', 403);
|
||||
}
|
||||
|
||||
$ttl = (int) getDolGlobalString('NETDIAG_API_TOKEN_TTL', '604800');
|
||||
if ($ttl < 60) {
|
||||
$ttl = 604800;
|
||||
}
|
||||
|
||||
$token = netdiag_jwt_encode(array(
|
||||
'sub' => (int) $user->id,
|
||||
'name' => $user->getFullName($langs),
|
||||
), $ttl);
|
||||
}
|
||||
|
||||
netdiag_api_respond(array(
|
||||
|
|
|
|||
|
|
@ -192,94 +192,10 @@ function netdiag_api_error($message, $httpstatus = 400)
|
|||
netdiag_api_respond(array('error' => $message), $httpstatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-URL-kodieren (JWT-konform, ohne Padding).
|
||||
*
|
||||
* @param string $data Rohdaten
|
||||
* @return string Kodierter String
|
||||
*/
|
||||
function netdiag_base64url_encode($data)
|
||||
{
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-URL-dekodieren.
|
||||
*
|
||||
* @param string $data Kodierter String
|
||||
* @return string Rohdaten
|
||||
*/
|
||||
function netdiag_base64url_decode($data)
|
||||
{
|
||||
return base64_decode(strtr($data, '-_', '+/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Geheimen JWT-Schlüssel des Moduls holen.
|
||||
*
|
||||
* @return string Schlüssel
|
||||
*/
|
||||
function netdiag_jwt_secret()
|
||||
{
|
||||
$secret = getDolGlobalString('NETDIAG_API_JWT_SECRET');
|
||||
if (empty($secret)) {
|
||||
// Fallback: Instanz-eindeutiger Wert (sollte nach Modulaktivierung nicht eintreten)
|
||||
$secret = md5(DOL_DOCUMENT_ROOT.getDolGlobalString('MAIN_INFO_SOCIETE_NOM'));
|
||||
}
|
||||
return $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT (HS256) erzeugen.
|
||||
*
|
||||
* @param array<string,mixed> $payload Nutzdaten (sub, name, exp werden ergänzt)
|
||||
* @param int $ttl Gültigkeit in Sekunden
|
||||
* @return string Signiertes Token
|
||||
*/
|
||||
function netdiag_jwt_encode($payload, $ttl)
|
||||
{
|
||||
$header = array('alg' => 'HS256', 'typ' => 'JWT');
|
||||
$now = dol_now();
|
||||
$payload['iat'] = $now;
|
||||
$payload['exp'] = $now + $ttl;
|
||||
|
||||
$seg = array();
|
||||
$seg[] = netdiag_base64url_encode(json_encode($header));
|
||||
$seg[] = netdiag_base64url_encode(json_encode($payload));
|
||||
$signinginput = implode('.', $seg);
|
||||
$signature = hash_hmac('sha256', $signinginput, netdiag_jwt_secret(), true);
|
||||
$seg[] = netdiag_base64url_encode($signature);
|
||||
|
||||
return implode('.', $seg);
|
||||
}
|
||||
|
||||
/**
|
||||
* JWT prüfen und Nutzdaten zurückgeben.
|
||||
*
|
||||
* @param string $token JWT
|
||||
* @return array<string,mixed>|null Nutzdaten oder null bei ungültig/abgelaufen
|
||||
*/
|
||||
function netdiag_jwt_decode($token)
|
||||
{
|
||||
$parts = explode('.', (string) $token);
|
||||
if (count($parts) !== 3) {
|
||||
return null;
|
||||
}
|
||||
list($h, $p, $s) = $parts;
|
||||
$expected = hash_hmac('sha256', $h.'.'.$p, netdiag_jwt_secret(), true);
|
||||
$given = netdiag_base64url_decode($s);
|
||||
if (!hash_equals($expected, $given)) {
|
||||
return null;
|
||||
}
|
||||
$payload = json_decode(netdiag_base64url_decode($p), true);
|
||||
if (!is_array($payload)) {
|
||||
return null;
|
||||
}
|
||||
if (empty($payload['exp']) || $payload['exp'] < dol_now()) {
|
||||
return null;
|
||||
}
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Token aus Request lesen (Authorization-Header oder ?jwt=).
|
||||
|
|
@ -327,25 +243,25 @@ function netdiag_api_authenticate($db)
|
|||
netdiag_api_error('Kein Token übermittelt', 401);
|
||||
}
|
||||
|
||||
$user = null;
|
||||
|
||||
// 1. Zentrales Auth-Modul
|
||||
if (netdiag_awlauth_available()) {
|
||||
/*
|
||||
* Ausschließlich über das zentrale Auth-Modul. Der Übergangspfad mit dem
|
||||
* eigenen netdiag-JWT ist am 17.08.2026 entfallen, nachdem Eddy den
|
||||
* APK-Rollout als abgeschlossen bestätigt hat.
|
||||
*
|
||||
* Warum das ein Gewinn ist und nicht nur Aufräumen: Der alte Pfad prüfte
|
||||
* die Signatur eines selbst ausgestellten Tokens und holte damit einen
|
||||
* Benutzer aus der Datenbank — an der Sitzungsliste von awlauth vorbei.
|
||||
* „Gerät abmelden" dort hatte auf ein solches Token keine Wirkung; ein
|
||||
* verlorenes Handy blieb bis zum Ablauf der TTL angemeldet. Jetzt gibt es
|
||||
* genau eine Stelle, an der Sitzungen enden.
|
||||
*
|
||||
* Ohne aktives awlauth verweigert die API bewusst den Dienst, statt auf
|
||||
* einen zweiten Weg auszuweichen.
|
||||
*/
|
||||
if (!netdiag_awlauth_available()) {
|
||||
netdiag_api_error('Anmeldung nicht möglich: das Modul AWL-Auth ist nicht aktiv', 503);
|
||||
}
|
||||
$user = awlauth_verify_token($token);
|
||||
}
|
||||
|
||||
// 2. Übergang: altes netdiag-JWT
|
||||
if ($user === null) {
|
||||
$payload = netdiag_jwt_decode($token);
|
||||
if ($payload !== null && !empty($payload['sub'])) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
|
||||
$legacy = new User($db);
|
||||
if ($legacy->fetch((int) $payload['sub']) > 0 && !empty($legacy->id)) {
|
||||
$legacy->loadRights();
|
||||
$user = $legacy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($user === null || empty($user->id)) {
|
||||
netdiag_api_error('Token ungültig oder abgelaufen', 401);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class modNetDiag extends DolibarrModules
|
|||
$this->editor_url = '';
|
||||
$this->editor_squarred_logo = '';
|
||||
|
||||
$this->version = '1.1.7';
|
||||
$this->version = '1.2.0';
|
||||
|
||||
// Konstanten-Name in llx_const für Modul-Status
|
||||
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
|
||||
|
|
@ -120,9 +120,12 @@ class modNetDiag extends DolibarrModules
|
|||
// Konstanten beim Aktivieren anlegen
|
||||
// (Key, Typ, Wert, Beschreibung, sichtbar, current/allentities, deleteonunactive)
|
||||
$this->const = array(
|
||||
1 => array('NETDIAG_API_JWT_SECRET', 'chaine', dol_hash(dol_print_date(dol_now(), 'dayhourrfc').mt_rand(), 'md5'), 'Geheimer Schlüssel zum Signieren der API-JWT', 0, 'current', 1),
|
||||
2 => array('NETDIAG_API_TOKEN_TTL', 'chaine', '604800', 'Gültigkeit des API-Tokens in Sekunden (Standard 7 Tage)', 1, 'current', 0),
|
||||
3 => array('NETDIAG_APK_URL', 'chaine', 'https://git.data-it-solution.de/api/packages/data-it/generic/netdiag-apk/latest/NetDiag.apk', 'Download-URL der Android-App (für QR-Code im Admin)', 1, 'current', 0),
|
||||
// NETDIAG_API_JWT_SECRET und NETDIAG_API_TOKEN_TTL sind am 17.08.2026
|
||||
// entfallen: die Anmeldung läuft ausschließlich über awlauth, die
|
||||
// Gültigkeit bestimmt dort AWLAUTH_TTL. Bestehende Werte in llx_const
|
||||
// werden bewusst NICHT gelöscht — sie schaden nicht, und ein
|
||||
// Löschlauf beim Modul-Update ist das größere Risiko.
|
||||
1 => array('NETDIAG_APK_URL', 'chaine', 'https://git.data-it-solution.de/api/packages/data-it/generic/netdiag-apk/latest/NetDiag.apk', 'Download-URL der Android-App (für QR-Code im Admin)', 1, 'current', 0),
|
||||
);
|
||||
|
||||
if (!isModEnabled("netdiag")) {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,11 @@ NetDiagSetup = NetDiag Einrichtung
|
|||
NetDiagSetupPage = NetDiag Einrichtungsseite
|
||||
NetDiagAbout = Über NetDiag
|
||||
NetDiagAboutPage = NetDiag Info-Seite
|
||||
NETDIAG_API_TOKEN_TTL = Token-Gültigkeit (Sekunden)
|
||||
NETDIAG_API_TOKEN_TTLTooltip = Wie lange ein API-Token der mobilen App gültig bleibt. Standard 604800 (7 Tage).
|
||||
# Entfallen am 17.08.2026: die Gültigkeit kommt jetzt aus AWLAUTH_TTL im Setup
|
||||
# des Moduls AWL-Auth. Schlüssel bleiben stehen, damit ältere Protokolle und
|
||||
# Screenshots weiterhin auflösbar sind.
|
||||
NetDiagTokenTTLMoved = Gültigkeit der Anmeldung
|
||||
NetDiagTokenTTLMovedValue = Wird zentral im Modul AWL-Auth eingestellt (Standard 7 Tage).
|
||||
NETDIAG_APK_URL = Download-URL der App (APK)
|
||||
NETDIAG_APK_URLTooltip = Adresse, unter der die Android-App (APK) liegt. Wird als QR-Code angezeigt.
|
||||
NetDiagApiSecretInfo = Der JWT-Schlüssel wurde beim Aktivieren automatisch erzeugt und ist nicht editierbar.
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ NetDiagSetup = NetDiag setup
|
|||
NetDiagSetupPage = NetDiag setup page
|
||||
NetDiagAbout = About NetDiag
|
||||
NetDiagAboutPage = NetDiag about page
|
||||
NETDIAG_API_TOKEN_TTL = Token validity (seconds)
|
||||
NETDIAG_API_TOKEN_TTLTooltip = How long a mobile app API token stays valid. Default 604800 (7 days).
|
||||
# Removed 2026-08-17: validity now comes from AWLAUTH_TTL in the AWL-Auth module setup.
|
||||
NetDiagTokenTTLMoved = Login validity
|
||||
NetDiagTokenTTLMovedValue = Configured centrally in the AWL-Auth module (default 7 days).
|
||||
NETDIAG_APK_URL = App download URL (APK)
|
||||
NETDIAG_APK_URLTooltip = Address where the Android app (APK) is hosted. Shown as a QR code.
|
||||
NetDiagApiSecretInfo = The JWT secret was generated automatically on activation and is not editable.
|
||||
|
|
|
|||
Loading…
Reference in a new issue