From 078fdc84ab9e1d17031445b37f13923c2227b148 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 8 Jul 2025 14:30:16 +0200 Subject: [PATCH] Manager: Perform module loading asynchronously So that authentication can suspend it. There are cases, e.g. cube, where authentication is required in run.php. During bootstrapping loading modules is mostly required to load libraries, register routes and hooks. Most of the time authentication is not required for these, but if it is, evaluation is now interrupted and continued after authentication has actually been performed. I don't see a real risk for any breaking change here, since authentication happens shortly after. It actually avoids a breaking change, since without this, cube's Icinga DB support would break or at least malfunction. And cube is only a single example. refs #5265 --- .../Icinga/Application/Modules/Manager.php | 11 +++++++- library/Icinga/Authentication/Auth.php | 25 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Application/Modules/Manager.php b/library/Icinga/Application/Modules/Manager.php index 55d074d70..9114f3ad4 100644 --- a/library/Icinga/Application/Modules/Manager.php +++ b/library/Icinga/Application/Modules/Manager.php @@ -3,6 +3,7 @@ namespace Icinga\Application\Modules; +use Fiber; use Icinga\Application\ApplicationBootstrap; use Icinga\Application\Icinga; use Icinga\Application\Logger; @@ -187,8 +188,16 @@ class Manager public function loadEnabledModules() { if (! $this->loadedAllEnabledModules) { + $async = ! $this->app->isCli(); foreach ($this->listEnabledModules() as $name) { - $this->loadModule($name); + if ($async) { + // May be suspended during authentication and resumed upon finish + (new Fiber(function () use ($name) { + $this->loadModule($name); + }))->start(); + } else { + $this->loadModule($name); + } } $this->loadedAllEnabledModules = true; diff --git a/library/Icinga/Authentication/Auth.php b/library/Icinga/Authentication/Auth.php index c2fbbeb4f..ee4deac1c 100644 --- a/library/Icinga/Authentication/Auth.php +++ b/library/Icinga/Authentication/Auth.php @@ -4,6 +4,7 @@ namespace Icinga\Authentication; use Exception; +use Fiber; use Icinga\Application\Config; use Icinga\Application\Hook\AuditHook; use Icinga\Application\Icinga; @@ -59,6 +60,15 @@ class Auth */ private bool $authenticated = false; + /** + * Fibers that were suspended during authentication + * + * This is used to resume fibers after authentication has been performed. + * + * @var Fiber[] + */ + private array $suspendedFibers = []; + /** * @see getInstance() */ @@ -97,7 +107,13 @@ class Auth public function isAuthenticated() { if (! $this->authenticated) { - trigger_error('Authentication can no longer be triggered implicitly', E_USER_DEPRECATED); + $fiber = Fiber::getCurrent(); + if ($fiber !== null) { + $this->suspendedFibers[] = $fiber; + Fiber::suspend(); + } else { + trigger_error('Authentication can no longer be triggered implicitly', E_USER_DEPRECATED); + } } return $this->user !== null; @@ -250,6 +266,13 @@ class Auth $this->authenticated = true; + foreach ($this->suspendedFibers as $fiber) { + // Resume all suspended fibers + $fiber->resume(); + } + + $this->suspendedFibers = []; + return $this; }