Add support for the persistent storage of the license data
authorAlexander Ebert <ebert@woltlab.com>
Mon, 25 Sep 2023 14:03:26 +0000 (16:03 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 25 Sep 2023 14:03:26 +0000 (16:03 +0200)
wcfsetup/install/files/lib/acp/page/LicensePage.class.php
wcfsetup/install/files/lib/system/package/license/LicenseApi.class.php
wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php [new file with mode: 0644]

index f7ffccdfa0f4ccda2648c1748f2d3b64e38d1757..5811e65e4bcac541818cbe44743fac8674fbc760 100644 (file)
@@ -28,8 +28,6 @@ final class LicensePage extends AbstractPage
 
     public $neededPermissions = ['admin.configuration.package.canInstallPackage'];
 
-    private LicenseApi $licenseApi;
-
     private array $licenseData;
 
     private int $licenseNumber;
@@ -48,9 +46,7 @@ final class LicensePage extends AbstractPage
     {
         parent::readData();
 
-        $this->licenseApi = new LicenseApi();
-
-        if (!$this->licenseApi->hasLicenseCredentials()) {
+        if (!LicenseApi::hasLicenseCredentials()) {
             return new RedirectResponse(
                 LinkHandler::getInstance()->getControllerLink(
                     LicenseEditForm::class,
@@ -63,7 +59,7 @@ final class LicensePage extends AbstractPage
 
         (new PackageUpdateAction([], 'refreshDatabase'))->executeAction();
 
-        $this->licenseData = $this->licenseApi->fetchLicenseData();
+        $this->licenseData = LicenseApi::fetchFromRemote()->getData();
         if (isset($this->licenseData['license']['licenseID'])) {
             $this->licenseNumber = $this->licenseData['license']['licenseID'];
         }
index eafd783589f437394689f0eebb34c5a6b8440033..0b592ce7a8e20b09fdbcf9a9a9469bf788eb0237 100644 (file)
@@ -2,11 +2,14 @@
 
 namespace wcf\system\package\license;
 
+use CuyZ\Valinor\Mapper\MappingError;
 use CuyZ\Valinor\Mapper\Source\Source;
 use CuyZ\Valinor\MapperBuilder;
 use GuzzleHttp\Psr7\Request;
 use wcf\data\package\update\server\PackageUpdateServer;
 use wcf\system\io\HttpFactory;
+use wcf\system\package\license\exception\MissingCredentials;
+use wcf\system\package\license\exception\ParsingFailed;
 
 /**
  * Provides access to the license data.
@@ -18,21 +21,75 @@ use wcf\system\io\HttpFactory;
  */
 final class LicenseApi
 {
-    private readonly PackageUpdateServer $packageUpdateServer;
+    private readonly array $data;
+    private readonly string $json;
 
-    public function __construct()
+    private const LICENSE_FILE = \WCF_DIR . 'license.php';
+
+    private function __construct(string $json)
+    {
+        $this->json = $json;
+        $this->data = $this->parseLicenseData($this->json);
+
+        $this->updateLicenseFile();
+    }
+
+    public function getData(): array
+    {
+        return $this->data;
+    }
+
+    private function updateLicenseFile(): void
+    {
+        @\file_put_contents(
+            self::LICENSE_FILE,
+            \sprintf(
+                <<<'EOT'
+                return '%s';
+                EOT,
+                $this->json,
+            )
+        );
+    }
+
+    private function parseLicenseData(string $json): array
     {
-        $this->packageUpdateServer = PackageUpdateServer::getWoltLabUpdateServer();
+        try {
+            /** @var array $result */
+            $result = (new MapperBuilder())
+                ->allowSuperfluousKeys()
+                ->mapper()
+                ->map(
+                    <<<'EOT'
+                    array {
+                        status: 200,
+                        license: array {
+                            authCode?: string,
+                            licenseID?: int,
+                            type: string,
+                            expiryDates?: array<string, int>,
+                            ckeditorLicenseKey?: string,
+                        },
+                        pluginstore: array<string, string>,
+                        woltlab: array<string, string>,
+                    }
+                    EOT,
+                    Source::json($json)
+                );
+        } catch (MappingError $e) {
+            throw new ParsingFailed($e);
+        }
+
+        return $result;
     }
 
-    public function fetchLicenseData(): array|object
+    public static function fetchFromRemote(): LicenseApi
     {
-        if (!$this->hasLicenseCredentials()) {
-            // TODO
-            throw new \Exception("no credentials");
+        if (!self::hasLicenseCredentials()) {
+            throw new MissingCredentials();
         }
 
-        $authData = $this->packageUpdateServer->getAuthData();
+        $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData();
 
         $request = new Request(
             'POST',
@@ -48,30 +105,28 @@ final class LicenseApi
         );
 
         $response = HttpFactory::makeClientWithTimeout(5)->send($request);
-        return (new MapperBuilder())
-            ->allowSuperfluousKeys()
-            ->mapper()
-            ->map(
-                <<<'EOT'
-                    array {
-                        status: 200,
-                        license: array {
-                            authCode?: string,
-                            licenseID?: int,
-                            type: string,
-                            expiryDates?: array<string, int>,
-                        },
-                        pluginstore: array<string, string>,
-                        woltlab: array<string, string>,
-                    }
-                    EOT,
-                Source::json($response->getBody())
-            );
+
+        return new LicenseApi($response->getBody());
+    }
+
+    public static function readFromFile(): ?LicenseApi
+    {
+        if (!\is_readable(self::LICENSE_FILE)) {
+            return null;
+        }
+
+        $content = \file_get_contents(self::LICENSE_FILE);
+
+        try {
+            return new LicenseApi($content);
+        } catch (ParsingFailed) {
+            return null;
+        }
     }
 
-    public function hasLicenseCredentials(): bool
+    public static function hasLicenseCredentials(): bool
     {
-        $authData = $this->packageUpdateServer->getAuthData();
+        $authData = PackageUpdateServer::getWoltLabUpdateServer()->getAuthData();
         if (empty($authData['username']) || empty($authData['password'])) {
             return false;
         }
diff --git a/wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php b/wcfsetup/install/files/lib/system/package/license/exception/MissingCredentials.class.php
new file mode 100644 (file)
index 0000000..34868e2
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+
+namespace wcf\system\package\license\exception;
+
+/**
+ * Rejected attempt to query the license list endpoint without supplying any
+ * credentials which are stored using the primary package update server.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+final class MissingCredentials extends \Exception
+{
+    public function __construct()
+    {
+        parent::__construct('Cannot fetch the license data without any stored credentials.');
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php b/wcfsetup/install/files/lib/system/package/license/exception/ParsingFailed.class.php
new file mode 100644 (file)
index 0000000..de6e933
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+namespace wcf\system\package\license\exception;
+
+use CuyZ\Valinor\Mapper\MappingError;
+
+/**
+ * The license data does not match the expectations of the license API.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+final class ParsingFailed extends \Exception
+{
+    public function __construct(MappingError $previous)
+    {
+        parent::__construct('The provided license data cannot be parsed.', 0, $previous);
+    }
+}