From 650ea2b597dca690cc2e3c85cde19e44fbd9856a Mon Sep 17 00:00:00 2001 From: Johannes Rauh Date: Thu, 30 Apr 2026 10:50:40 +0200 Subject: [PATCH] Migrate `AuthenticationController` to `CompatController` Convert `AuthenticationController` from the legacy Zend Controller to `CompatController`, dropping `login.phtml` in favour of the new `LoginPage` widget and `addContent()`. The view variable assignments are replaced by `setTitle()` and `addContent(new LoginPage(...))`. Improve `httpBadRequest()` message for external redirect attempts. In `CompatController`, `$this->controls` is the tab bar area rendered above the page content. When no tabs are added it still emits an empty `
` wrapper. Setting `$this->view->compact = true` suppresses that wrapper entirely, keeping the login page markup clean. Handle the redirect on success in an `ON_SUBMIT` event handler. Delegate the external-backend-only check to `LoginForm::onRequest()` via `ON_REQUEST`. Use `$this->getServerRequest()` instead of `ServerRequest::fromGlobals()`. Two structural fixes required by the changed DOM nesting: - `login.less`: height `100%` -> `100vh` (`#login` is now inside `.content` which has no explicit height, so percentage inheritance breaks) - `history.js`: `#layout > #login` -> `#layout #login` (direct-child selector breaks because `#login` is now a grandchild of `#layout` through `.content`) --- .../controllers/AuthenticationController.php | 37 +++++++---- .../views/scripts/authentication/login.phtml | 63 ------------------- public/css/icinga/login.less | 2 +- public/js/icinga/history.js | 2 +- 4 files changed, 27 insertions(+), 77 deletions(-) delete mode 100644 application/views/scripts/authentication/login.phtml diff --git a/application/controllers/AuthenticationController.php b/application/controllers/AuthenticationController.php index 3f97644c1..f4a877595 100644 --- a/application/controllers/AuthenticationController.php +++ b/application/controllers/AuthenticationController.php @@ -5,7 +5,6 @@ namespace Icinga\Controllers; -use GuzzleHttp\Psr7\ServerRequest; use Icinga\Application\ClassLoader; use Icinga\Application\Hook\AuthenticationHook; use Icinga\Application\Hook\LoginButtonHook; @@ -16,17 +15,20 @@ use Icinga\Authentication\LoginButtonForm; use Icinga\Common\Database; use Icinga\Exception\AuthenticationException; use Icinga\Forms\Authentication\LoginForm; -use Icinga\Web\Controller; use Icinga\Web\Helper\CookieHelper; use Icinga\Web\RememberMe; use Icinga\Web\Url; +use Icinga\Web\Widget\LoginPage; +use ipl\Html\Contract\Form; +use ipl\Web\Compat\CompatController; +use Psr\Http\Message\ServerRequestInterface; use RuntimeException; use Throwable; /** * Application wide controller for authentication */ -class AuthenticationController extends Controller +class AuthenticationController extends CompatController { use Database; @@ -49,7 +51,17 @@ class AuthenticationController extends Controller if (($requiresSetup = $icinga->requiresSetup()) && $icinga->setupTokenExists()) { $this->redirectNow(Url::fromPath('setup')); } - $form = new LoginForm(); + + $form = (new LoginForm()) + ->setAction(Url::fromRequest()->getAbsoluteUrl()) + ->on(Form::ON_SUBMIT, function (LoginForm $form) { + if ($redirectUrl = $form->getRedirectUrl()) { + $this->redirectNow($redirectUrl); + } + }) + ->on(Form::ON_REQUEST, function (ServerRequestInterface $_, LoginForm $form) { + $form->onRequest(); + }); if (RememberMe::hasCookie() && $this->hasDb()) { $authenticated = false; @@ -81,14 +93,16 @@ class AuthenticationController extends Controller if ($redirect) { $redirectUrl = Url::fromPath($redirect, [], $this->getRequest()); if ($redirectUrl->isExternal()) { - $this->httpBadRequest('nope'); + $this->httpBadRequest('Redirect to an external host is not allowed'); } } else { - $redirectUrl = $form->getRedirectUrl(); + $redirectUrl = $form->createRedirectUrl(); } $this->redirectNow($redirectUrl); } + + $request = $this->getServerRequest(); if (! $requiresSetup) { $cookies = new CookieHelper($this->getRequest()); if (! $cookies->isSupported()) { @@ -99,11 +113,10 @@ class AuthenticationController extends Controller ->sendResponse(); exit; } - $form->handleRequest(); + $form->handleRequest($request); } $loginButtons = []; - $request = ServerRequest::fromGlobals(); foreach (LoginButtonHook::all() as $class => $hook) { try { @@ -126,10 +139,10 @@ class AuthenticationController extends Controller } } - $this->view->form = $form; - $this->view->loginButtons = $loginButtons; - $this->view->defaultTitle = $this->translate('Icinga Web 2 Login'); - $this->view->requiresSetup = $requiresSetup; + // Suppress the rendering of controls bar + $this->view->compact = true; + $this->setTitle($this->translate('Icinga Web 2 Login')); + $this->addContent(new LoginPage($form, $loginButtons, $requiresSetup)); } /** diff --git a/application/views/scripts/authentication/login.phtml b/application/views/scripts/authentication/login.phtml deleted file mode 100644 index bf538d337..000000000 --- a/application/views/scripts/authentication/login.phtml +++ /dev/null @@ -1,63 +0,0 @@ -
- -
    -
  • - qlink( - null, - 'https://www.facebook.com/icinga', - null, - array( - 'target' => '_blank', - 'icon' => 'facebook-squared', - 'title' => $this->translate('Icinga on Facebook') - ) - ) ?> -
  • -
  • qlink( - null, - 'https://github.com/Icinga', - null, - array( - 'target' => '_blank', - 'icon' => 'github-circled', - 'title' => $this->translate('Icinga on GitHub') - ) - ) ?> -
  • -
-
-
img('img/orb-analytics.png'); ?>
-
img('img/orb-automation.png'); ?>
-
img('img/orb-cloud.png'); ?>
-
img('img/orb-icinga.png'); ?>
-
img('img/orb-infrastructure.png'); ?>
-
img('img/orb-metrics.png'); ?>
-
img('img/orb-notifications.png'); ?>
diff --git a/public/css/icinga/login.less b/public/css/icinga/login.less index cbb5148fb..feb24d8a1 100644 --- a/public/css/icinga/login.less +++ b/public/css/icinga/login.less @@ -4,7 +4,7 @@ // Login page styles #login { - height: 100%; + height: 100vh; background-color: @menu-bg-color; background-image: url(../img/icingaweb2-background-orbs.jpg); background-repeat: no-repeat; diff --git a/public/js/icinga/history.js b/public/js/icinga/history.js index 836bf9e1f..21a7d185e 100644 --- a/public/js/icinga/history.js +++ b/public/js/icinga/history.js @@ -230,7 +230,7 @@ applyLocationBar: function (onload = false) { let col2State = this.getCol2State(); - if (onload && document.querySelector('#layout > #login')) { + if (onload && document.querySelector('#layout #login')) { // The user landed on the login let redirectInput = document.querySelector('#login form input[name=redirect]'); redirectInput.value = redirectInput.value + col2State;