LoginButtonHook: additional buttons to be displayed below the login form

This commit is contained in:
Alexander A. Klimov 2025-11-05 10:33:48 +01:00
parent 70f29827f9
commit f83bf89cd1
6 changed files with 133 additions and 5 deletions

View file

@ -3,17 +3,23 @@
namespace Icinga\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Application\ClassLoader;
use Icinga\Application\Hook\AuthenticationHook;
use Icinga\Application\Hook\LoginButton\LoginButton;
use Icinga\Application\Hook\LoginButtonHook;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Common\Database;
use Icinga\Exception\AuthenticationException;
use Icinga\Forms\Authentication\ButtonForm;
use Icinga\Forms\Authentication\LoginForm;
use Icinga\Web\Controller;
use Icinga\Web\Helper\CookieHelper;
use Icinga\Web\RememberMe;
use Icinga\Web\Url;
use RuntimeException;
use Throwable;
/**
* Application wide controller for authentication
@ -93,7 +99,33 @@ class AuthenticationController extends Controller
}
$form->handleRequest();
}
$loginButtons = [];
$request = ServerRequest::fromGlobals();
foreach (LoginButtonHook::all() as $class => $hook) {
try {
foreach ($hook->getButtons() as $index => $button) {
assert($button instanceof LoginButton);
$loginButtons[] = (new ButtonForm(
"$class!$index",
$button,
ClassLoader::classBelongsToModule($class) ? ClassLoader::extractModuleName($class) : null
))
->on(ButtonForm::ON_SUCCESS, function () use ($button): void {
($button->onClick)();
})
->handleRequest($request);
}
} catch (Throwable $e) {
Logger::error('Failed to execute login button hook: %s', $e);
continue;
}
}
$this->view->form = $form;
$this->view->loginButtons = $loginButtons;
$this->view->defaultTitle = $this->translate('Icinga Web 2 Login');
$this->view->requiresSetup = $requiresSetup;
}

View file

@ -0,0 +1,42 @@
<?php
/* Icinga Web 2 | (c) 2025 Icinga GmbH | GPLv2+ */
namespace Icinga\Forms\Authentication;
use Icinga\Application\Hook\LoginButton\LoginButton;
use Icinga\Web\Session;
use ipl\Html\Form;
use ipl\Html\FormElement\SubmitButtonElement;
use ipl\Html\Html;
use ipl\Web\Common\CsrfCounterMeasure;
use ipl\Web\Common\FormUid;
/**
* Form for user authentication via external identity providers
*/
class ButtonForm extends Form
{
use FormUid;
use CsrfCounterMeasure;
public function __construct(
string $name,
protected readonly LoginButton $button,
protected readonly ?string $moduleName = null
) {
$this->defaultAttributes['name'] = $name;
}
protected function assemble(): void
{
$button = new SubmitButtonElement('btn_submit', $this->button->attributes);
if ($this->moduleName) {
$button->addAttributes(['class' => "icinga-module module-$this->moduleName"]);
}
$this->addElement($button->addHtml($this->button->content));
$this->addHtml($this->createUidElement());
$this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId()));
}
}

View file

@ -22,6 +22,7 @@
) ?></p>
<?php endif ?>
<?= $this->form ?>
<?= implode('', $this->loginButtons) ?>
<div id="login-footer">
<p>Icinga Web 2 &copy; 2013-<?= date('Y') ?></p>
<?= $this->qlink($this->translate('icinga.com'), 'https://icinga.com') ?>

View file

@ -0,0 +1,25 @@
<?php
/* Icinga Web 2 | (c) 2025 Icinga GmbH | GPLv2+ */
namespace Icinga\Application\Hook\LoginButton;
use Closure;
use ipl\Html\Attributes;
use ipl\Html\ValidHtml;
readonly class LoginButton
{
/**
* Create an additional button to be displayed below the login form
*
* @param Closure $onClick What to do if the button is pressed
* @param ValidHtml $content What to show in the button, see also {@link Html::wantHtml()}
* @param ?Attributes $attributes Additional <button> attributes, e.g. title
*/
public function __construct(
public Closure $onClick,
public ValidHtml $content,
public ?Attributes $attributes = null
) {
}
}

View file

@ -0,0 +1,28 @@
<?php
/* Icinga Web 2 | (c) 2025 Icinga GmbH | GPLv2+ */
namespace Icinga\Application\Hook;
use Icinga\Application\Hook;
use Icinga\Application\Hook\LoginButton\LoginButton;
abstract class LoginButtonHook
{
/**
* @return LoginButton[]
*/
abstract public function getButtons(): array;
/**
* @return static[]
*/
public static function all(): array
{
return Hook::all('LoginButton');
}
public static function register(): void
{
Hook::register('LoginButton', static::class, static::class, true);
}
}

View file

@ -74,12 +74,12 @@
}
}
input[type="submit"]:focus {
outline: 3px solid;
outline-color: @icinga-blue-light;
}
input[type="submit"], button {
&:focus {
outline: 3px solid;
outline-color: @icinga-blue-light;
}
input[type=submit] {
border-radius: .25em;
background: @icinga-secondary;
color: white;