Compare commits

..

2 commits

Author SHA1 Message Date
0779440347 Trigger: Modul-Deploy [deploy]
All checks were successful
Deploy netdiag / deploy (push) Successful in 13s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:55:53 +02:00
219d489951 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>
2026-08-17 17:55:53 +02:00
7 changed files with 98 additions and 159 deletions

View file

@ -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 1180 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

View file

@ -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>';

View file

@ -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(

View file

@ -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);

View file

@ -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")) {

View file

@ -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.

View file

@ -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.