Skip to content

Commit 779f026

Browse files
committed
Rewrite authentication forms to ipl forms
Removed description for `rememberme` input, because it wasn't displayed anywhere.
1 parent c5f6460 commit 779f026

5 files changed

Lines changed: 221 additions & 174 deletions

File tree

application/controllers/AuthenticationController.php

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
use Icinga\Application\Logger;
1414
use Icinga\Authentication\LoginButton;
1515
use Icinga\Authentication\LoginButtonForm;
16+
use Icinga\Authentication\Auth;
17+
use Icinga\Authentication\User\ExternalBackend;
1618
use Icinga\Common\Database;
1719
use Icinga\Exception\AuthenticationException;
18-
use Icinga\Forms\Authentication\Cancel2FAForm;
1920
use Icinga\Forms\Authentication\Challenge2FAForm;
2021
use Icinga\Forms\Authentication\LoginForm;
2122
use Icinga\Web\Controller;
2223
use Icinga\Web\Helper\CookieHelper;
2324
use Icinga\Web\RememberMe;
2425
use Icinga\Web\Session;
2526
use Icinga\Web\Url;
27+
use ipl\Html\Contract\Form;
2628
use RuntimeException;
2729
use Throwable;
2830

@@ -58,11 +60,47 @@ public function loginAction()
5860
&& $user->getTwoFactorEnabled()
5961
&& Session::getSession()->get('2fa_must_challenge_token', false)
6062
) {
61-
$form = new Challenge2FAForm();
62-
$cancel2faForm = new Cancel2FAForm();
63-
$cancel2faForm->handleRequest();
63+
$form = (new Challenge2FAForm())
64+
->setAction(Url::fromRequest()->getAbsoluteUrl())
65+
->on(Form::ON_SUBMIT, function (Challenge2FAForm $form) {
66+
if ($redirectUrl = $form->getRedirectUrl()) {
67+
$this->redirectNow($redirectUrl);
68+
}
69+
})
70+
->on(Form::ON_SENT, function (Challenge2FAForm $form) {
71+
$isCsrfValid = $form->getElement('CSRFToken')->isValid();
72+
$isCancelPressed = $form->getPressedSubmitElement()?->getName() === $form::SUBMIT_CANCEL;
73+
74+
if ($isCsrfValid && $isCancelPressed) {
75+
Session::getSession()->purge();
76+
$this->redirectNow(Url::fromRequest());
77+
}
78+
});
6479
} else {
65-
$form = new LoginForm();
80+
$form = (new LoginForm())
81+
->setAction(Url::fromRequest()->getAbsoluteUrl())
82+
->on(Form::ON_SUBMIT, function (LoginForm $form) {
83+
if ($redirectUrl = $form->getRedirectUrl()) {
84+
$this->redirectNow($redirectUrl);
85+
}
86+
})
87+
->on(Form::ON_REQUEST, function ($request, LoginForm $form) {
88+
$auth = Auth::getInstance();
89+
$onlyExternal = true;
90+
// TODO(el): This may be set on the auth chain once iterated. See Auth::authExternal().
91+
foreach ($auth->getAuthChain() as $backend) {
92+
if (! $backend instanceof ExternalBackend) {
93+
$onlyExternal = false;
94+
}
95+
}
96+
if ($onlyExternal) {
97+
$form->addMessage($this->translate(
98+
'You\'re currently not authenticated using any of the web server\'s authentication'
99+
. 'mechanisms. Make sure you\'ll configure such, otherwise you\'ll not be able to login.'
100+
));
101+
$form->onError();
102+
}
103+
});
66104

67105
if (RememberMe::hasCookie() && $this->hasDb()) {
68106
$authenticated = false;
@@ -99,7 +137,7 @@ public function loginAction()
99137
$this->httpBadRequest('nope');
100138
}
101139
} else {
102-
$redirectUrl = $form->getRedirectUrl();
140+
$redirectUrl = $form->createRedirectUrl();
103141
}
104142

105143
$this->redirectNow($redirectUrl);
@@ -114,7 +152,7 @@ public function loginAction()
114152
->sendResponse();
115153
exit;
116154
}
117-
$form->handleRequest();
155+
$form->handleRequest(ServerRequest::fromGlobals());
118156
}
119157

120158
$loginButtons = [];

application/forms/Authentication/Cancel2FAForm.php

Lines changed: 0 additions & 61 deletions
This file was deleted.

application/forms/Authentication/Challenge2FAForm.php

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,50 @@
66

77
use Exception;
88
use Icinga\Application\Hook\AuthenticationHook;
9+
use Icinga\Application\Icinga;
910
use Icinga\Application\Logger;
1011
use Icinga\Authentication\Auth;
1112
use Icinga\Authentication\TwoFactorTotp;
13+
use Icinga\Common\Database;
14+
use Icinga\Web\Response;
1215
use Icinga\Web\Session;
1316
use Icinga\Web\Url;
17+
use ipl\Html\Attributes;
18+
use ipl\Html\FormDecoration\RenderElementDecorator;
19+
use ipl\Web\Common\CsrfCounterMeasure;
20+
use ipl\Web\Common\FormUid;
21+
use ipl\Web\Compat\CompatForm;
1422

15-
class Challenge2FAForm extends LoginForm
23+
class Challenge2FAForm extends CompatForm
1624
{
17-
public function init(): void
25+
use CsrfCounterMeasure;
26+
use Database;
27+
use FormUid;
28+
29+
const SUBMIT_VERIFY = 'btn_submit_verify';
30+
31+
const SUBMIT_CANCEL = 'btn_submit_cancel';
32+
33+
public function __construct()
1834
{
19-
$this->setRequiredCue(null);
20-
$this->setName('form_challenge_2fa');
21-
$this->setSubmitLabel($this->translate('Verify'));
22-
$this->setProgressLabel($this->translate('Verifying'));
35+
$this->addAttributes(Attributes::create(['name' => '2fa_challenge_form']));
2336
}
2437

25-
public function createElements(array $formData): void
38+
/**
39+
* Return the current Response
40+
*
41+
* @return Response
42+
*/
43+
protected function getResponse(): Response
2644
{
45+
return Icinga::app()->getFrontController()->getResponse();
46+
}
47+
48+
protected function assemble(): void
49+
{
50+
$this->addCsrfCounterMeasure(Session::getSession()->getId());
51+
$this->addElement($this->createUidElement());
52+
2753
$this->addElement(
2854
'text',
2955
'token',
@@ -33,7 +59,31 @@ public function createElements(array $formData): void
3359
'placeholder' => $this->translate('Please enter your 2FA token'),
3460
'autocomplete' => 'off',
3561
'autocapitalize' => 'off',
62+
'decorators' => [
63+
'RenderElement' => new RenderElementDecorator(),
64+
'Errors' => ['name' => 'Errors', 'options' => ['class' => 'errors']]
65+
]
66+
]
67+
);
3668

69+
$this->addElement(
70+
'submit',
71+
self::SUBMIT_VERIFY,
72+
[
73+
'data-progress-label' => $this->translate('Verifying'),
74+
'label' => $this->translate('Verify'),
75+
]
76+
);
77+
78+
$this->addElement(
79+
'submit',
80+
self::SUBMIT_CANCEL,
81+
[
82+
'ignore' => true,
83+
'formnovalidate' => true,
84+
'class' => 'btn-cancel',
85+
'label' => $this->translate('Cancel'),
86+
'data-progress-label' => $this->translate('Canceling')
3787
]
3888
);
3989

@@ -46,7 +96,7 @@ public function createElements(array $formData): void
4696
);
4797
}
4898

49-
public function onSuccess(): bool
99+
protected function onSuccess(): void
50100
{
51101
$user = Auth::getInstance()->getUser();
52102
$twoFactor = TwoFactorTotp::loadFromDb($this->getDb(), $user->getUsername());
@@ -73,11 +123,9 @@ public function onSuccess(): bool
73123

74124
$this->getResponse()->setRerenderLayout(true);
75125

76-
return true;
126+
$this->setRedirectUrl(Url::fromRequest());
77127
}
78128

79-
$this->getElement('token')->addError($this->translate('Token is invalid!'));
80-
81-
return false;
129+
$this->getElement('token')->addMessage($this->translate('Token is invalid!'));
82130
}
83131
}

0 commit comments

Comments
 (0)