<woltlab-core-notice type="info">{lang}wcf.user.login.forceLogin{/lang}</woltlab-core-notice>
{/if}
-{if !$errorField|empty && $errorField == 'cookie'}
- <woltlab-core-notice type="error">{lang}wcf.user.login.error.cookieRequired{/lang}</woltlab-core-notice>
-{else}
- {include file='shared_formError'}
-{/if}
-
-<form id="loginForm" method="post" action="{$loginController}">
- <dl{if $errorField == 'username'} class="formError"{/if}>
- <dt>
- <label for="username">{lang}wcf.user.usernameOrEmail{/lang}</label> <span class="formFieldRequired">*</span>
- </dt>
- <dd>
- <input type="text" id="username" name="username" value="{$username}" required autofocus class="long" autocomplete="username">
- {if $errorField == 'username'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.user.username.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- </dd>
- </dl>
-
- <dl{if $errorField == 'password'} class="formError"{/if}>
- <dt>
- <label for="password">{lang}wcf.user.password{/lang}</label> <span class="formFieldRequired">*</span>
- </dt>
- <dd>
- <input type="password" id="password" name="password" value="{$password}" required class="long" autocomplete="current-password">
- {if $errorField == 'password'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.user.password.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- {if $__userAuthConfig->canChangePassword}
- <small><a href="{link controller='LostPassword'}{/link}">{lang}wcf.user.lostPassword{/lang}</a></small>
- {/if}
- </dd>
- </dl>
-
- {event name='fields'}
-
- {include file='shared_captcha' supportsAsyncCaptcha=true}
-
- <div class="formSubmit">
- <input type="submit" value="{lang}wcf.user.button.login{/lang}" accesskey="s">
- {csrfToken}
- </div>
-
- {include file='thirdPartySsoButtons'}
-</form>
-
-<p class="formFieldRequiredNotice">
- <span class="formFieldRequired">*</span>
- {lang}wcf.global.form.required{/lang}
-</p>
+{unsafe:$form->getHtml()}
{include file='authFlowFooter'}
use wcf\data\user\User;
use wcf\data\user\UserProfile;
use wcf\event\user\authentication\UserLoggedIn;
-use wcf\form\AbstractCaptchaForm;
+use wcf\form\AbstractForm;
+use wcf\form\AbstractFormBuilderForm;
use wcf\system\event\EventHandler;
use wcf\system\exception\NamedUserException;
use wcf\system\exception\UserInputException;
+use wcf\system\form\builder\field\CaptchaFormField;
+use wcf\system\form\builder\field\PasswordFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\form\builder\field\validation\FormFieldValidator;
use wcf\system\request\LinkHandler;
use wcf\system\request\RequestHandler;
+use wcf\system\user\authentication\DefaultUserAuthentication;
use wcf\system\user\authentication\EmailUserAuthentication;
use wcf\system\user\authentication\LoginRedirect;
use wcf\system\user\authentication\UserAuthenticationFactory;
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*/
-class LoginForm extends AbstractCaptchaForm
+class LoginForm extends AbstractFormBuilderForm
{
- /**
- * given login username
- * @var string
- */
- public $username = '';
+ protected bool $useCaptcha = false;
+ protected ?User $user = null;
- /**
- * given login password
- * @var string
- */
- public $password = '';
+ #[\Override]
+ protected function createForm()
+ {
+ parent::createForm();
+
+ $this->form->appendChildren([
+ TextFormField::create('username')
+ ->label('wcf.user.usernameOrEmail')
+ ->required()
+ ->autoFocus()
+ ->maximumLength(255),
+ PasswordFormField::create('password')
+ ->label('wcf.user.password')
+ ->required()
+ ->passwordStrengthMeter(false)
+ ->removeFieldClass('medium')
+ ->addFieldClass('long')
+ ->autocomplete("current-password")
+ ->addValidator(new FormFieldValidator(
+ 'passwordValidator',
+ $this->validatePassword(...)
+ )),
+ CaptchaFormField::create()
+ ->available($this->useCaptcha)
+ ->objectType(CAPTCHA_TYPE)
+ ]);
+ }
- /**
- * user object
- * @var User
- */
- public $user;
+ #[\Override]
+ public function finalizeForm()
+ {
+ parent::finalizeForm();
- /**
- * @inheritDoc
- */
- public $useCaptcha = false;
+ $this->renameSubmitButton();
+ }
+ #[\Override]
public function __run()
{
WCF::getTPL()->assign([
return parent::__run();
}
- /**
- * @inheritDoc
- */
+ #[\Override]
public function readParameters()
{
parent::readParameters();
}
}
- /**
- * @inheritDoc
- */
- public function readFormParameters()
+ protected function validatePassword(PasswordFormField $passwordFormField): void
{
- parent::readFormParameters();
-
- if (isset($_POST['username'])) {
- $this->username = StringUtil::trim($_POST['username']);
- }
- if (isset($_POST['password'])) {
- $this->password = $_POST['password'];
- }
- }
+ $usernameFormField = $this->form->getNodeById('username');
+ \assert($usernameFormField instanceof TextFormField);
+ $handleException = null;
- /**
- * Validates the user access data.
- */
- protected function validateUser()
- {
try {
$this->user = UserAuthenticationFactory::getInstance()
->getUserAuthentication()
- ->loginManually($this->username, $this->password);
+ ->loginManually($usernameFormField->getValue(), $passwordFormField->getValue());
} catch (UserInputException $e) {
- if ($e->getField() == 'username') {
+ if (
+ \get_class(UserAuthenticationFactory::getInstance()->getUserAuthentication()) === DefaultUserAuthentication::class
+ && $e->getField() == 'username'
+ ) {
try {
$this->user = EmailUserAuthentication::getInstance()
- ->loginManually($this->username, $this->password);
+ ->loginManually($usernameFormField->getValue(), $passwordFormField->getValue());
} catch (UserInputException $e2) {
if ($e2->getField() == 'username') {
- throw $e;
+ $handleException = $e;
+ } else {
+ $handleException = $e2;
}
- throw $e2;
}
} else {
- throw $e;
+ $handleException = $e;
}
}
- }
- /**
- * @inheritDoc
- */
- public function submit()
- {
- parent::submit();
-
- // save authentication failure
- if (ENABLE_USER_AUTHENTICATION_FAILURE) {
- if ($this->errorField == 'username' || $this->errorField == 'password') {
- $user = User::getUserByUsername($this->username);
- if (!$user->userID) {
- $user = User::getUserByEmail($this->username);
- }
+ if ($handleException !== null) {
+ if ($handleException->getField() == 'username') {
+ $usernameFormField->addValidationError(
+ new FormFieldValidationError(
+ $handleException->getType(),
+ 'wcf.user.username.error.' . $handleException->getType(),
+ [
+ 'username' => $usernameFormField->getValue(),
+ ]
+ )
+ );
+ } else if ($handleException->getField() == 'password') {
+ $passwordFormField->addValidationError(
+ new FormFieldValidationError(
+ $handleException->getType(),
+ 'wcf.user.password.error.' . $handleException->getType()
+ )
+ );
+ } else {
+ throw new \LogicException('unreachable');
+ }
- $action = new UserAuthenticationFailureAction([], 'create', [
- 'data' => [
- 'environment' => RequestHandler::getInstance()->isACPRequest() ? 'admin' : 'user',
- 'userID' => $user->userID ?: null,
- 'username' => \mb_substr($this->username, 0, 100),
- 'time' => TIME_NOW,
- 'ipAddress' => UserUtil::getIpAddress(),
- 'userAgent' => UserUtil::getUserAgent(),
- 'validationError' => 'invalid' . \ucfirst($this->errorField),
- ],
- ]);
- $action->executeAction();
+ $this->saveAuthenticationFailure($handleException->getField(), $usernameFormField->getValue());
+ }
- if ($this->captchaObjectType) {
- $this->captchaObjectType->getProcessor()->reset();
- }
+ if (RequestHandler::getInstance()->isACPRequest() && $this->user !== null) {
+ $userProfile = new UserProfile($this->user);
+ if (!$userProfile->getPermission('admin.general.canUseAcp')) {
+ $usernameFormField->addValidationError(
+ new FormFieldValidationError(
+ 'acpNotAuthorized',
+ 'wcf.user.username.error.acpNotAuthorized',
+ [
+ 'username' => $usernameFormField->getValue(),
+ ]
+ )
+ );
}
}
- }
-
- /**
- * @inheritDoc
- */
- public function validate()
- {
- parent::validate();
if (!WCF::getSession()->hasValidCookie()) {
- throw new UserInputException('cookie');
+ $this->form->invalid();
+ $this->form->errorMessage('wcf.user.login.error.cookieRequired');
}
+ }
- // error handling
- if (empty($this->username)) {
- throw new UserInputException('username');
+ protected function saveAuthenticationFailure(string $errorField, string $username): void
+ {
+ if (!ENABLE_USER_AUTHENTICATION_FAILURE) {
+ return;
}
- if (empty($this->password)) {
- throw new UserInputException('password');
+ $user = User::getUserByUsername($username);
+ if (!$user->userID) {
+ $user = User::getUserByEmail($username);
}
- $this->validateUser();
-
- if (RequestHandler::getInstance()->isACPRequest() && $this->user !== null) {
- $userProfile = new UserProfile($this->user);
- if (!$userProfile->getPermission('admin.general.canUseAcp')) {
- throw new UserInputException('username', 'acpNotAuthorized');
- }
- }
+ $action = new UserAuthenticationFailureAction([], 'create', [
+ 'data' => [
+ 'environment' => RequestHandler::getInstance()->isACPRequest() ? 'admin' : 'user',
+ 'userID' => $user->userID ?: null,
+ 'username' => \mb_substr($username, 0, 100),
+ 'time' => TIME_NOW,
+ 'ipAddress' => UserUtil::getIpAddress(),
+ 'userAgent' => UserUtil::getUserAgent(),
+ 'validationError' => 'invalid' . \ucfirst($errorField),
+ ],
+ ]);
+ $action->executeAction();
}
- /**
- * @inheritDoc
- */
+ #[\Override]
public function save()
{
- parent::save();
+ AbstractForm::save();
- // change user
$needsMultifactor = WCF::getSession()->changeUserAfterMultifactorAuthentication($this->user);
if (!$needsMultifactor) {
WCF::getSession()->registerReauthentication();
/**
* Performs the redirect after successful authentication.
*/
- protected function performRedirect(bool $needsMultifactor = false)
+ protected function performRedirect(bool $needsMultifactor = false): void
{
if ($needsMultifactor) {
$url = LinkHandler::getInstance()->getLink('MultifactorAuthentication');
exit;
}
- /**
- * @inheritDoc
- */
- public function assignVariables()
+ private function renameSubmitButton(): void
{
- parent::assignVariables();
-
- WCF::getTPL()->assign([
- 'username' => $this->username,
- 'password' => $this->password,
- 'loginController' => LinkHandler::getInstance()->getControllerLink(static::class),
- ]);
+ $this->form->getButton('submitButton')->label('wcf.user.button.login');
}
}