From 0f1e0c9fbb626b3eac4a19023f840781e777f1ca Mon Sep 17 00:00:00 2001 From: dh_Agent Date: Tue, 22 Sep 2026 15:06:31 +0000 Subject: [PATCH] Installations-ID bei Manager-Updates erhalten --- Manager/module.php | 23 +++++++++++++++-------- docs/module/Manager/README.md | 10 +++++++--- tests/ManagerModulstrukturTest.php | 16 ++++++++++++++++ tests/Symcon/modules/Manager.php | 19 +++++++++++++++++++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/Manager/module.php b/Manager/module.php index b021e5f..4112e23 100644 --- a/Manager/module.php +++ b/Manager/module.php @@ -114,9 +114,6 @@ class Manager extends IPSModule implements ManagerSchnittstelle $this->RegisterAttributeInteger('LizenzNaechsterVersuch', 0); $this->RegisterAttributeInteger('LizenzBerechtigungenNaechsterVersuch', 0); $this->RegisterAttributeString('LizenzLetzterFehler', ''); - if ($this->ReadAttributeString('LizenzInstallationID') === '') { - $this->WriteAttributeString('LizenzInstallationID', $this->erzeugeUuidV4()); - } // Die alten Timer bleiben deaktiviert registriert, damit bestehende Instanzen sauber migrieren. $this->RegisterTimer('Regelzyklus', 0, ''); @@ -132,6 +129,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle { parent::ApplyChanges(); + $this->initialisiereLizenzInstallationID(); $this->aktualisiereVariablen(); try { @@ -1040,6 +1038,19 @@ class Manager extends IPSModule implements ManagerSchnittstelle } } + private function initialisiereLizenzInstallationID(): string + { + $installationID = trim($this->ReadAttributeString('LizenzInstallationID')); + if ($installationID !== '') { + return $installationID; + } + + $installationID = $this->erzeugeUuidV4(); + $this->WriteAttributeString('LizenzInstallationID', $installationID); + + return $installationID; + } + private function aktualisiereLizenzfreigabe(string $lizenzcode, bool $erzwingen): bool { $normalisiert = Lizenzpruefung::normalisiereCode($lizenzcode); @@ -1051,11 +1062,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle return false; } - $installationID = $this->ReadAttributeString('LizenzInstallationID'); - if ($installationID === '') { - $installationID = $this->erzeugeUuidV4(); - $this->WriteAttributeString('LizenzInstallationID', $installationID); - } + $installationID = $this->initialisiereLizenzInstallationID(); $codeHash = hash('sha256', $normalisiert); $gespeicherterHash = $this->ReadAttributeString('LizenzcodeHash'); $lease = $this->leseLizenzLease(); diff --git a/docs/module/Manager/README.md b/docs/module/Manager/README.md index 9f3664e..7546174 100644 --- a/docs/module/Manager/README.md +++ b/docs/module/Manager/README.md @@ -111,11 +111,15 @@ bis `offlineUntil` verwendbar. Der aktuelle Entwicklungsvertrag setzt diesen Zeitpunkt ungefaehr 14 Tage nach Ausstellung. Danach sperrt der Manager die Regelung, bis der Lizenzserver wieder eine gueltige Antwort liefert. -Die Installations-ID und die Lease liegen in internen Instanzattributen. Bei -einer Migration muss deshalb die vollstaendige Manager-Instanz mitsamt ihren +Die Installations-ID und die Lease liegen in internen Instanzattributen. Die +ID wird erst nach dem Laden bestehender Attribute initialisiert und bleibt bei +Modulupdates, Modul-Neuladen und einem normalen Neustart unveraendert. Bei einer +Migration muss trotzdem die vollstaendige Manager-Instanz mitsamt ihren Attributen uebernommen werden. Eine neu erzeugte Instanz erhaelt eine andere Installations-ID und kann einen bereits gebundenen Code nicht selbststaendig -uebertragen. +uebertragen. Wurde die ID mit einer aelteren Manager-Version bereits ungewollt +geaendert, muss im Lizenzportal einmalig ein Ersatzcode erzeugt und an die nun +stabile ID gebunden werden. ### Status und Fehlerbehebung diff --git a/tests/ManagerModulstrukturTest.php b/tests/ManagerModulstrukturTest.php index fcb9141..e826abe 100644 --- a/tests/ManagerModulstrukturTest.php +++ b/tests/ManagerModulstrukturTest.php @@ -110,6 +110,22 @@ final class ManagerModulstrukturTest extends TestCase $inhalt ); self::assertStringContainsString("RegisterAttributeString('LizenzInstallationID'", $inhalt); + self::assertSame( + 1, + preg_match('/public function Create\(\): void.*?public function ApplyChanges\(\): void/s', $inhalt, $treffer) + ); + self::assertStringNotContainsString( + "WriteAttributeString('LizenzInstallationID'", + $treffer[0] + ); + self::assertStringContainsString( + "parent::ApplyChanges();\n\n \$this->initialisiereLizenzInstallationID();", + $inhalt + ); + self::assertSame(1, substr_count( + $inhalt, + "WriteAttributeString('LizenzInstallationID'" + )); self::assertStringContainsString("RegisterAttributeString('LizenzLease'", $inhalt); self::assertStringContainsString("RegisterAttributeString('LizenzcodeHash'", $inhalt); self::assertStringContainsString('STATUS_LIZENZ_UNGUELTIG', $inhalt); diff --git a/tests/Symcon/modules/Manager.php b/tests/Symcon/modules/Manager.php index 34a83b3..32e0bc1 100644 --- a/tests/Symcon/modules/Manager.php +++ b/tests/Symcon/modules/Manager.php @@ -145,6 +145,25 @@ return static function (TestContext $test): void { return is_array($daten) ? $daten : []; }; + $lizenzInstallationID = static function () use ($test, $managerId): string { + $formular = IPS_GetConfigurationForm($managerId); + if (preg_match( + '/Installations-ID: ([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/i', + $formular, + $treffer + ) !== 1) { + $test->fail('Das Manager-Formular enthaelt keine gueltige Installations-ID.'); + } + + return strtolower($treffer[1]); + }; + $test->runCase('Installations-ID bleibt bei ApplyChanges stabil', static function ( + TestContext $test + ) use ($managerId, $lizenzInstallationID): void { + $vorher = $lizenzInstallationID(); + IPS_ApplyChanges($managerId); + $test->assertSame($vorher, $lizenzInstallationID()); + }); $zustandswert = static function (array $daten, int $instanzId, string $kennung) { foreach ($daten['Verbraucher'] ?? [] as $verbraucher) { if (($verbraucher['InstanzID'] ?? 0) !== $instanzId) {