TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:17 +02:00
commit 629541cb4c
86 changed files with 5360 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/vendor/
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Configuration;
/**
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class IncompleteConfigurationException extends \Exception {}
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Configuration;
use Symfony\Component\Mime\Address;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Crypto\HashAlgo;
use TYPO3\CMS\Core\Crypto\HashService;
use TYPO3\CMS\Core\Crypto\Random;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class RecoveryConfiguration
{
protected readonly string $forgotHash;
protected ?Address $replyTo = null;
protected Address $sender;
protected readonly array $settings;
protected string $mailTemplateName = '';
protected ?int $timestamp = null;
public function __construct(
protected readonly Context $context,
ConfigurationManagerInterface $configurationManager,
Random $random,
HashService $hashService
) {
$this->settings = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
$this->forgotHash = $this->getLifeTimeTimestamp() . '|' . $this->generateHash($random, $hashService);
$this->resolveFromTypoScript();
}
/**
* Returns the forgot hash.
*/
public function getForgotHash(): string
{
return $this->forgotHash;
}
/**
* Returns email template name configured in TypoScript
*/
public function getMailTemplateName(): string
{
return $this->mailTemplateName;
}
/**
* Returns TTL timestamp of the forgot hash
*/
public function getLifeTimeTimestamp(): int
{
if ($this->timestamp === null) {
$lifetimeInHours = (int)($this->settings['forgotLinkHashValidTime'] ?? 0) ?: 12;
$currentTimestamp = $this->context->getPropertyFromAspect('date', 'timestamp');
$this->timestamp = $currentTimestamp + 3600 * $lifetimeInHours;
}
return $this->timestamp;
}
/**
* Returns reply-to address if configured otherwise null.
*/
public function getReplyTo(): ?Address
{
return $this->replyTo;
}
/**
* Returns the sender. Normally the current typo3 installation.
*/
public function getSender(): Address
{
return $this->sender;
}
protected function generateHash(Random $random, HashService $hashService): string
{
$randomString = $random->generateRandomHexString(16);
return $hashService->hmac($randomString, self::class, HashAlgo::SHA3_256);
}
protected function resolveFromTypoScript(): void
{
$fromAddress = ($this->settings['email_from'] ?? null) ?: $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'];
if (empty($fromAddress)) {
throw new IncompleteConfigurationException(
'Either "$GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'defaultMailFromAddress\']" or extension key "plugin.tx_felogin_login.settings.email_from" cannot be empty!',
1573825624
);
}
$fromName = ($this->settings['email_fromName'] ?? null) ?: $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName'];
if (empty($fromName)) {
throw new IncompleteConfigurationException(
'Either "$GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'defaultMailFromName\']" or extension key "plugin.tx_felogin_login.settings.email_fromName" cannot be empty!',
1573825625
);
}
$this->sender = new Address($fromAddress, $fromName);
if (!empty($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToAddress'])) {
if ($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToName']) {
$this->replyTo = new Address(
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToAddress'],
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToName']
);
} else {
$this->replyTo = new Address(
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailReplyToAddress']
);
}
}
$this->mailTemplateName = (string)($this->settings['email']['templateName'] ?? '');
if (empty($this->mailTemplateName)) {
throw new IncompleteConfigurationException(
'Key "plugin.tx_felogin_login.settings.email.templateName" cannot be empty! Ensure that TypoScript is properly included.',
1584998393
);
}
}
}
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Configuration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* A class that holds and manages all states relevant for handling redirects
*
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class RedirectConfiguration
{
protected array $modes;
public function __construct(
array|string|null $mode,
protected string $firstMode,
protected int $pageOnLogin,
protected string $domains,
protected int $pageOnLoginError,
protected int $pageOnLogout
) {
$this->modes = is_array($mode) ? $mode : GeneralUtility::trimExplode(',', $mode ?? '', true);
}
public function getModes(): array
{
return $this->modes;
}
public function getFirstMode(): string
{
return $this->firstMode;
}
public function getPageOnLogin(): int
{
return $this->pageOnLogin;
}
public function getDomains(): string
{
return $this->domains;
}
public function getPageOnLoginError(): int
{
return $this->pageOnLoginError;
}
public function getPageOnLogout(): int
{
return $this->pageOnLogout;
}
/**
* Factory when creating a configuration out of Extbase / plugin settings.
*/
public static function fromSettings(array $settings): self
{
return new RedirectConfiguration(
($settings['redirectMode'] ?? ''),
(string)($settings['redirectFirstMethod'] ?? ''),
(int)($settings['redirectPageLogin'] ?? 0),
(string)($settings['domains'] ?? ''),
(int)($settings['redirectPageLoginError'] ?? 0),
(int)($settings['redirectPageLogout'] ?? 0)
);
}
}
+248
View File
@@ -0,0 +1,248 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Authentication\LoginType;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Security\RequestToken;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\FrontendLogin\Configuration\RedirectConfiguration;
use TYPO3\CMS\FrontendLogin\Event\BeforeRedirectEvent;
use TYPO3\CMS\FrontendLogin\Event\LoginConfirmedEvent;
use TYPO3\CMS\FrontendLogin\Event\LoginErrorOccurredEvent;
use TYPO3\CMS\FrontendLogin\Event\LogoutConfirmedEvent;
use TYPO3\CMS\FrontendLogin\Event\ModifyLoginFormViewEvent;
use TYPO3\CMS\FrontendLogin\Redirect\RedirectHandler;
/**
* Used for plugin login
*
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class LoginController extends ActionController
{
public const MESSAGEKEY_DEFAULT = 'welcome';
public const MESSAGEKEY_ERROR = 'error';
public const MESSAGEKEY_LOGOUT = 'logout';
protected string $loginType = '';
protected string $redirectUrl = '';
protected RedirectConfiguration $configuration;
public function __construct(
protected readonly RedirectHandler $redirectHandler,
protected readonly Context $context,
protected readonly PageRepository $pageRepository
) {}
/**
* Initialize redirects
*/
public function initializeAction(): void
{
$this->loginType = (string)($this->request->getParsedBody()['logintype'] ?? $this->request->getQueryParams()['logintype'] ?? '');
$this->configuration = RedirectConfiguration::fromSettings($this->settings);
if ($this->isLoginOrLogoutInProgress() && !$this->isRedirectDisabled()) {
$this->redirectUrl = $this->redirectHandler->processRedirect(
$this->request,
$this->loginType,
$this->configuration,
$this->request->hasArgument('redirectReferrer') ? $this->request->getArgument('redirectReferrer') : ''
);
}
}
/**
* Show login form
*/
public function loginAction(): ResponseInterface
{
if ($this->isLogoutSuccessful()) {
$this->eventDispatcher->dispatch(new LogoutConfirmedEvent($this, $this->view, $this->request));
} elseif ($this->hasLoginErrorOccurred()) {
$this->eventDispatcher->dispatch(new LoginErrorOccurredEvent($this->request));
}
if (($forwardResponse = $this->handleLoginForwards()) !== null) {
return $forwardResponse;
}
if (($redirectResponse = $this->handleRedirect()) !== null) {
return $redirectResponse;
}
$this->eventDispatcher->dispatch(new ModifyLoginFormViewEvent($this->view, $this->request));
$storagePageIds = ($GLOBALS['TYPO3_CONF_VARS']['FE']['checkFeUserPid'] ?? false)
? $this->pageRepository->getPageIdsRecursive(GeneralUtility::intExplode(',', (string)($this->settings['pages'] ?? ''), true), (int)($this->settings['recursive'] ?? 0))
: [];
$this->view->assignMultiple(
[
'messageKey' => $this->getStatusMessageKey(),
'permaloginStatus' => $this->getPermaloginStatus(),
'redirectURL' => $this->redirectHandler->getLoginFormRedirectUrl($this->request, $this->configuration, $this->isRedirectDisabled()),
'redirectReferrer' => $this->request->hasArgument('redirectReferrer') ? (string)$this->request->getArgument('redirectReferrer') : '',
'referer' => $this->redirectHandler->getReferrerForLoginForm($this->request, $this->settings),
'noRedirect' => $this->isRedirectDisabled(),
'requestToken' => RequestToken::create('core/user-auth/fe')
->withMergedParams(['pid' => implode(',', $storagePageIds)]),
]
);
return $this->htmlResponse();
}
/**
* User overview for logged in users
*/
public function overviewAction(bool $showLoginMessage = false): ResponseInterface
{
if (!$this->context->getAspect('frontend.user')->isLoggedIn()) {
return new ForwardResponse('login');
}
$this->eventDispatcher->dispatch(new LoginConfirmedEvent($this, $this->view, $this->request));
if (($redirectResponse = $this->handleRedirect()) !== null) {
return $redirectResponse;
}
$this->view->assignMultiple(
[
'user' => $this->request->getAttribute('frontend.user')->user,
'showLoginMessage' => $showLoginMessage,
]
);
return $this->htmlResponse();
}
/**
* Show logout form. Note, that this action should never process any redirects.
*/
public function logoutAction(): ResponseInterface
{
$this->view->assignMultiple(
[
'user' => $this->request->getAttribute('frontend.user')->user,
'noRedirect' => $this->isRedirectDisabled(),
]
);
return $this->htmlResponse();
}
/**
* Handles the redirect when $this->redirectUrl is not empty
*/
protected function handleRedirect(): ?ResponseInterface
{
if ($this->redirectUrl !== '') {
$event = new BeforeRedirectEvent($this->loginType, $this->redirectUrl, $this->request);
$this->eventDispatcher->dispatch($event);
if ($event->getRedirectUrl() !== '') {
return $this->redirectToUri($event->getRedirectUrl());
}
}
return null;
}
/**
* Handle forwards to overview and logout actions from login action
*/
protected function handleLoginForwards(): ?ResponseInterface
{
if ($this->shouldRedirectToOverview()) {
return (new ForwardResponse('overview'))->withArguments(['showLoginMessage' => true]);
}
if ($this->context->getAspect('frontend.user')->isLoggedIn()) {
return new ForwardResponse('logout');
}
return null;
}
/**
* The permanent login checkbox should only be shown if permalogin is not deactivated (-1),
* not forced to be always active (2) and lifetime is greater than 0
*/
protected function getPermaloginStatus(): int
{
$permaLogin = (int)$GLOBALS['TYPO3_CONF_VARS']['FE']['permalogin'];
return $this->isPermaloginDisabled($permaLogin) ? -1 : $permaLogin;
}
protected function isPermaloginDisabled(int $permaLogin): bool
{
return $permaLogin > 1
|| (int)($this->settings['showPermaLogin'] ?? 0) === 0
|| $GLOBALS['TYPO3_CONF_VARS']['FE']['lifetime'] === 0;
}
/**
* Redirect to overview on login successful and setting showLogoutFormAfterLogin disabled
*/
protected function shouldRedirectToOverview(): bool
{
return $this->context->getAspect('frontend.user')->isLoggedIn()
&& (LoginType::tryFrom($this->loginType) === LoginType::LOGIN)
&& !($this->settings['showLogoutFormAfterLogin'] ?? 0);
}
/**
* Return message key based on user login status
*/
protected function getStatusMessageKey(): string
{
$messageKey = self::MESSAGEKEY_DEFAULT;
if ($this->hasLoginErrorOccurred()) {
$messageKey = self::MESSAGEKEY_ERROR;
} elseif (LoginType::tryFrom($this->loginType) === LoginType::LOGOUT) {
$messageKey = self::MESSAGEKEY_LOGOUT;
}
return $messageKey;
}
protected function isLoginOrLogoutInProgress(): bool
{
$type = LoginType::tryFrom($this->loginType);
return $type === LoginType::LOGIN || $type === LoginType::LOGOUT;
}
/**
* Is redirect disabled by setting or noredirect GET/POST parameter
*/
protected function isRedirectDisabled(): bool
{
return
(int)($this->request->getParsedBody()['noredirect'] ?? $this->request->getQueryParams()['noredirect'] ?? 0) === 1
|| ($this->settings['noredirect'] ?? false)
|| ($this->settings['redirectDisable'] ?? false);
}
protected function isLogoutSuccessful(): bool
{
return LoginType::tryFrom($this->loginType) === LoginType::LOGOUT && !$this->context->getAspect('frontend.user')->isLoggedIn();
}
protected function hasLoginErrorOccurred(): bool
{
return LoginType::tryFrom($this->loginType) === LoginType::LOGIN && !$this->context->getAspect('frontend.user')->isLoggedIn();
}
}
@@ -0,0 +1,314 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Configuration\Features;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
use TYPO3\CMS\Core\Crypto\HashAlgo;
use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\PasswordPolicy\Event\EnrichPasswordValidationContextDataEvent;
use TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyAction;
use TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyValidator;
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
use TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface;
use TYPO3\CMS\Core\Session\SessionManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Error\Error;
use TYPO3\CMS\Extbase\Error\Result;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException;
use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\FrontendLogin\Configuration\RecoveryConfiguration;
use TYPO3\CMS\FrontendLogin\Domain\Repository\FrontendUserRepository;
use TYPO3\CMS\FrontendLogin\Event\PasswordChangeEvent;
use TYPO3\CMS\FrontendLogin\Service\RecoveryService;
/**
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class PasswordRecoveryController extends ActionController
{
public function __construct(
protected RecoveryService $recoveryService,
protected FrontendUserRepository $userRepository,
protected RecoveryConfiguration $recoveryConfiguration,
protected readonly Features $features,
protected readonly PageRepository $pageRepository,
protected RateLimiterFactoryInterface $rateLimiterFactory
) {}
/**
* Shows the recovery form. If $userIdentifier is set, an email will be sent, if the corresponding user exists and
* has a valid email address set.
*/
public function recoveryAction(?string $userIdentifier = null): ResponseInterface
{
if (empty($userIdentifier)) {
return $this->htmlResponse();
}
$storagePageIds = ($GLOBALS['TYPO3_CONF_VARS']['FE']['checkFeUserPid'] ?? false)
? $this->pageRepository->getPageIdsRecursive(GeneralUtility::intExplode(',', (string)($this->settings['pages'] ?? ''), true), (int)($this->settings['recursive'] ?? 0))
: [];
$userData = $this->userRepository->findUserByUsernameOrEmailOnPages($userIdentifier, $storagePageIds);
if ($userData
&& GeneralUtility::validEmail($userData['email'])
&& !$this->hasExceededMaximumAttemptsForReset($userData['email'])
) {
$hash = $this->recoveryConfiguration->getForgotHash();
$this->userRepository->updateForgotHashForUserByUid($userData['uid'], $this->hashService->hmac($hash, self::class, HashAlgo::SHA3_256));
$this->recoveryService->sendRecoveryEmail($this->request, $userData, $hash);
}
// Prevent time based information disclosure by waiting a random time before sending a response. This prevents
// that the response time can be an indicator if the used username or email exists or not. Wait a random time
// between 200 milliseconds and 3 seconds.
usleep(random_int(200000, 3000000));
// Always show the default message and never notify about a potential rate limit, because this would reveal,
// that a given user identifier is actually valid.
$this->addFlashMessage($this->getTranslation('forgot_reset_message_emailSent'));
return $this->redirect('login', 'Login', 'felogin');
}
protected function hasExceededMaximumAttemptsForReset(string $email): bool
{
$limiter = $this->rateLimiterFactory->create($email);
$limit = $limiter->consume();
return !$limit->isAccepted();
}
/**
* Validate the hash argument and make sure that:
*
* - it is in the expected format
* - it is not expired
* - a fe_user with the given hash exists
*
* If one of the checks fail, a redirect response to the recoveryAction() is returned
*/
protected function validateHashArgument(): ?ResponseInterface
{
$hash = $this->request->hasArgument('hash') ? $this->request->getArgument('hash') : '';
$hash = is_string($hash) ? $hash : '';
if (!$this->validateHashFormat($hash)) {
return $this->redirect('recovery', 'PasswordRecovery', 'felogin');
}
$timestamp = (int)GeneralUtility::trimExplode('|', $hash)[0];
$currentTimestamp = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
// timestamp is expired or hash can not be assigned to a user
if ($currentTimestamp > $timestamp || !$this->userRepository->existsUserWithHash($this->hashService->hmac($hash, self::class, HashAlgo::SHA3_256))) {
/** @var ExtbaseRequestParameters $extbaseRequestParameters */
$extbaseRequestParameters = clone $this->request->getAttribute('extbase');
$originalResult = $extbaseRequestParameters->getOriginalRequestMappingResults();
$originalResult->addError(new Error($this->getTranslation('change_password_notvalid_message'), 1554994253));
$extbaseRequestParameters->setOriginalRequestMappingResults($originalResult);
$this->request = $this->request->withAttribute('extbase', $extbaseRequestParameters);
return (new ForwardResponse('recovery'))
->withControllerName('PasswordRecovery')
->withExtensionName('felogin')
->withArgumentsValidationResult($originalResult);
}
return null;
}
/**
* Show the change password form if a valid hash is available.
*/
public function showChangePasswordAction(string $hash = ''): ResponseInterface
{
// Validate hash (lifetime, format and fe_user with hash persistence)
if (($response = $this->validateHashArgument()) instanceof ResponseInterface) {
return $response;
}
$this->view->assignMultiple([
'hash' => $hash,
'passwordRequirements' => $this->getPasswordPolicyValidator()->getRequirements(),
]);
return $this->htmlResponse();
}
/**
* Validates the hash argument, the entered password and passwordRepeat values. If one of the values is considered
* as invalid, a response object with validation errors in the mapping results is returned.
*
* @throws NoSuchArgumentException
*/
public function validateHashAndPasswords()
{
// Validate hash (lifetime, format and fe_user with hash persistence)
if (($response = $this->validateHashArgument()) instanceof ResponseInterface) {
return $response;
}
// Exit early if newPass or newPassRepeat is not set.
/** @var ExtbaseRequestParameters $extbaseRequestParameters */
$extbaseRequestParameters = clone $this->request->getAttribute('extbase');
$originalResult = $extbaseRequestParameters->getOriginalRequestMappingResults();
$argumentsExist = $this->request->hasArgument('newPass') && $this->request->hasArgument('newPassRepeat');
$argumentsEmpty = empty($this->request->getArgument('newPass')) || empty($this->request->getArgument('newPassRepeat'));
if (!$argumentsExist || $argumentsEmpty) {
$originalResult->addError(new Error(
$this->getTranslation('empty_password_and_password_repeat'),
1554971665
));
return (new ForwardResponse('showChangePassword'))
->withControllerName('PasswordRecovery')
->withExtensionName('felogin')
->withArguments(['hash' => $this->request->getArgument('hash')])
->withArgumentsValidationResult($originalResult);
}
$this->validateNewPassword($originalResult);
// if an error exists, forward with all messages to the change password form
if ($originalResult->hasErrors()) {
return (new ForwardResponse('showChangePassword'))
->withControllerName('PasswordRecovery')
->withExtensionName('felogin')
->withArguments(['hash' => $this->request->getArgument('hash')])
->withArgumentsValidationResult($originalResult);
}
}
/**
* Change actual password. Hash $newPass and update the user with the corresponding $hash.
*
* @throws AspectNotFoundException
* @throws InvalidPasswordHashException
*/
public function changePasswordAction(string $newPass, string $hash): ResponseInterface
{
if (($response = $this->validateHashAndPasswords()) instanceof ResponseInterface) {
return $response;
}
$hashedPassword = GeneralUtility::makeInstance(PasswordHashFactory::class)
->getDefaultHashInstance('FE')
->getHashedPassword($newPass);
$hmac = $this->hashService->hmac($hash, self::class, HashAlgo::SHA3_256);
$user = $this->userRepository->findOneByForgotPasswordHash($hmac);
$event = new PasswordChangeEvent($user, $hashedPassword, $newPass, $this->request);
$this->eventDispatcher->dispatch($event);
$this->userRepository->updatePasswordAndInvalidateHash($hmac, $hashedPassword);
$this->invalidateUserSessions($user['uid']);
$this->addFlashMessage($this->getTranslation('change_password_done_message'));
return $this->redirect('login', 'Login', 'felogin', ['redirectReferrer' => 'off']);
}
/**
* @throws NoSuchArgumentException
*/
protected function validateNewPassword(Result $originalResult): void
{
$newPass = $this->request->getArgument('newPass');
// make sure the user entered the password twice
if ($newPass !== $this->request->getArgument('newPassRepeat')) {
$originalResult->addError(new Error($this->getTranslation('password_must_match_repeated'), 1554912163));
}
$hash = $this->request->getArgument('hash');
$userData = $this->userRepository->findOneByForgotPasswordHash($this->hashService->hmac($hash, self::class, HashAlgo::SHA3_256));
// Validate against password policy
$passwordPolicyValidator = $this->getPasswordPolicyValidator();
$contextData = new ContextData(
loginMode: 'FE',
currentPasswordHash: $userData['password']
);
$contextData->setData('currentUsername', $userData['username']);
$contextData->setData('currentFirstname', $userData['first_name']);
$contextData->setData('currentLastname', $userData['last_name']);
$event = $this->eventDispatcher->dispatch(
new EnrichPasswordValidationContextDataEvent(
$contextData,
$userData,
self::class
)
);
$contextData = $event->getContextData();
if (!$passwordPolicyValidator->isValidPassword($newPass, $contextData)) {
foreach ($passwordPolicyValidator->getValidationErrors() as $validationError) {
$validationResult = new Result();
$validationResult->addError(new Error($validationError, 1667647475));
$originalResult->merge($validationResult);
}
}
}
/**
* Wrapper to mock LocalizationUtility::translate
*/
protected function getTranslation(string $key): string
{
return (string)LocalizationUtility::translate($key, 'felogin');
}
/**
* Validates that $hash is in the expected format (timestamp|forgot_hash)
*/
protected function validateHashFormat(string $hash): bool
{
return !empty($hash) && strpos($hash, '|') === 10;
}
/**
* Invalidate all frontend user sessions by given user id
*/
protected function invalidateUserSessions(int $userId): void
{
$sessionManager = GeneralUtility::makeInstance(SessionManager::class);
$sessionBackend = $sessionManager->getSessionBackend('FE');
$sessionManager->invalidateAllSessionsByUserId($sessionBackend, $userId);
}
protected function getPasswordPolicyValidator(): PasswordPolicyValidator
{
$passwordPolicy = $GLOBALS['TYPO3_CONF_VARS']['FE']['passwordPolicy'] ?? 'default';
return GeneralUtility::makeInstance(
PasswordPolicyValidator::class,
PasswordPolicyAction::UPDATE_USER_PASSWORD,
is_string($passwordPolicy) ? $passwordPolicy : ''
);
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Domain\Repository;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
/**
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class FrontendUserGroupRepository
{
protected readonly Connection $connection;
public function __construct(ConnectionPool $connectionPool)
{
$this->connection = $connectionPool->getConnectionForTable('fe_groups');
}
public function findRedirectPageIdByGroupId(int $groupId): ?int
{
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->getRestrictions()->removeAll();
$query = $queryBuilder
->select('felogin_redirectPid')
->from('fe_groups')
->where(
$queryBuilder->expr()->neq('felogin_redirectPid', $this->connection->quote('')),
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($groupId, Connection::PARAM_INT))
)
->setMaxResults(1);
$column = $query->executeQuery()->fetchOne();
return $column === false ? null : (int)$column;
}
}
@@ -0,0 +1,141 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Domain\Repository;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
/**
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
readonly class FrontendUserRepository
{
protected Connection $connection;
public function __construct(
protected Context $context,
ConnectionPool $connectionPool,
) {
$this->connection = $connectionPool->getConnectionForTable('fe_users');
}
/**
* Change the password for a user based on forgot password hash.
*/
public function updatePasswordAndInvalidateHash(string $forgotPasswordHash, string $hashedPassword): void
{
$queryBuilder = $this->connection->createQueryBuilder();
$currentTimestamp = $this->context->getPropertyFromAspect('date', 'timestamp');
$query = $queryBuilder
->update('fe_users')
->set('password', $hashedPassword)
->set('felogin_forgotHash', $this->connection->quote(''), false)
->set('tstamp', $currentTimestamp)
->where($queryBuilder->expr()->eq('felogin_forgotHash', $queryBuilder->createNamedParameter($forgotPasswordHash)));
$query->executeStatement();
}
/**
* Returns true if a user exists with hash as `felogin_forgothash`, otherwise false.
*/
public function existsUserWithHash(string $hash): bool
{
$queryBuilder = $this->connection->createQueryBuilder();
$query = $queryBuilder
->count('uid')
->from('fe_users')
->where($queryBuilder->expr()->eq('felogin_forgotHash', $queryBuilder->createNamedParameter($hash)));
return (bool)$query->executeQuery()->fetchOne();
}
/**
* Sets forgot hash for passed user uid.
*/
public function updateForgotHashForUserByUid(int $uid, string $hash): void
{
$queryBuilder = $this->connection->createQueryBuilder();
$query = $queryBuilder
->update('fe_users')
->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, $this->connection::PARAM_INT)))
->set('felogin_forgotHash', $hash);
$query->executeStatement();
}
/**
* Fetches an array with all columns (except the password) from the fe_users table for the given username or
* email on the given pages. Returns null, if user was not found or if user has no email address set.
*/
public function findUserByUsernameOrEmailOnPages(string $usernameOrEmail, array $pages = []): ?array
{
if ($usernameOrEmail === '') {
return null;
}
$queryBuilder = $this->connection->createQueryBuilder();
$query = $queryBuilder
->select('*')
->from('fe_users')
->where(
$queryBuilder->expr()->or(
$queryBuilder->expr()->eq('username', $queryBuilder->createNamedParameter($usernameOrEmail)),
$queryBuilder->expr()->eq('email', $queryBuilder->createNamedParameter($usernameOrEmail)),
),
$queryBuilder->expr()->neq('email', $this->connection->quote('')),
);
if (!empty($pages)) {
// respect storage pid
$query->andWhere($queryBuilder->expr()->in('pid', $pages));
}
$result = $query->executeQuery()->fetchAssociative() ?: null;
if ($result) {
unset($result['password']);
}
return $result;
}
public function findOneByForgotPasswordHash(string $hash): ?array
{
if ($hash === '') {
return null;
}
$queryBuilder = $this->connection->createQueryBuilder();
$query = $queryBuilder
->select('*')
->from('fe_users')
->where($queryBuilder->expr()->eq('felogin_forgotHash', $queryBuilder->createNamedParameter($hash)))
->setMaxResults(1);
$row = $query->executeQuery()->fetchAssociative();
return is_array($row) ? $row : null;
}
public function findRedirectIdPageByUserId(int $uid): ?int
{
$queryBuilder = $this->connection->createQueryBuilder();
$queryBuilder->getRestrictions()->removeAll();
$query = $queryBuilder
->select('felogin_redirectPid')
->from('fe_users')
->where(
$queryBuilder->expr()->neq('felogin_redirectPid', $this->connection->quote('')),
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, Connection::PARAM_INT))
)
->setMaxResults(1);
$column = $query->executeQuery()->fetchOne();
return $column === false ? null : (int)$column;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\View\ViewInterface;
use TYPO3\CMS\FrontendLogin\Controller\LoginController;
/**
* A confirmation notification when an login/logout action has successfully arrived at the plugin, via the view and the controller, multiple
* information can be overridden in Event Listeners.
*/
abstract class AbstractConfirmedEvent
{
public function __construct(
protected readonly LoginController $controller,
private ViewInterface $view,
protected readonly ServerRequestInterface $request
) {}
public function getController(): LoginController
{
return $this->controller;
}
public function getView(): ViewInterface
{
return $this->view;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use Psr\Http\Message\ServerRequestInterface;
/**
* Notification before a redirect is made, which also allows to modify
* the actual redirect URL. Setting the redirect to an empty string
* will avoid triggering a redirect.
*/
final class BeforeRedirectEvent
{
public function __construct(
private readonly string $loginType,
private string $redirectUrl,
private readonly ServerRequestInterface $request,
) {}
public function getLoginType(): string
{
return $this->loginType;
}
public function getRedirectUrl(): string
{
return $this->redirectUrl;
}
public function setRedirectUrl(string $redirectUrl): void
{
$this->redirectUrl = $redirectUrl;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
/**
* A notification when a log in has successfully arrived at the plugin, via the view and the controller, multiple
* information can be overridden in Event Listeners.
*/
final class LoginConfirmedEvent extends AbstractConfirmedEvent {}
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use Psr\Http\Message\ServerRequestInterface;
/**
* A notification if something went wrong while trying to log in a user.
*/
final readonly class LoginErrorOccurredEvent
{
public function __construct(
private ServerRequestInterface $request
) {}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
/**
* A notification when a log out has successfully arrived at the plugin, via the view and the controller, multiple
* information can be overridden in Event Listeners.
*/
final class LogoutConfirmedEvent extends AbstractConfirmedEvent {}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\View\ViewInterface;
/**
* Allows to inject custom variables into the login form.
*/
final readonly class ModifyLoginFormViewEvent
{
public function __construct(
private ViewInterface $view,
private ServerRequestInterface $request
) {}
public function getView(): ViewInterface
{
return $this->view;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use Psr\Http\Message\ServerRequestInterface;
/**
* Allows to modify the result of the redirect URL validation (e.g. allow redirect to specific external URLs).
*/
final class ModifyRedirectUrlValidationResultEvent
{
public function __construct(
private readonly string $redirectUrl,
private bool $validationResult,
private readonly ServerRequestInterface $request,
) {}
public function getRedirectUrl(): string
{
return $this->redirectUrl;
}
public function getValidationResult(): bool
{
return $this->validationResult;
}
public function setValidationResult(bool $validationResult): void
{
$this->validationResult = $validationResult;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use Psr\Http\Message\ServerRequestInterface;
/**
* Informal event that contains information about the password which was set, and is about to be stored in the database.
*/
final readonly class PasswordChangeEvent
{
public function __construct(
private array $user,
private string $passwordHash,
private string $rawPassword,
private ServerRequestInterface $request
) {}
public function getUser(): array
{
return $this->user;
}
public function getHashedPassword(): string
{
return $this->passwordHash;
}
public function getRawPassword(): string
{
return $this->rawPassword;
}
public function getRequest(): ServerRequestInterface
{
return $this->request;
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Authentication\Event\BeforeRequestTokenProcessedEvent;
use TYPO3\CMS\Core\Security\RequestToken;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
/**
* Process request token.
*/
final class ProcessRequestTokenListener
{
#[AsEventListener('felogin-process-request-token')]
public function __invoke(BeforeRequestTokenProcessedEvent $event): void
{
$user = $event->getUser();
$requestToken = $event->getRequestToken();
if (!$user instanceof FrontendUserAuthentication || !$requestToken instanceof RequestToken) {
return;
}
$pidParam = (string)($requestToken->params['pid'] ?? '');
if ($user->checkPid) {
$user->checkPid_value = $pidParam;
}
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Event;
use TYPO3\CMS\Core\Mail\FluidEmail;
/**
* Event that contains the email to be sent to the user when they request a new password.
* More
*
* Additional validation can happen here.
*/
final readonly class SendRecoveryEmailEvent
{
public function __construct(private FluidEmail $email, private array $user) {}
public function getUserInformation(): array
{
return $this->user;
}
public function getEmail(): FluidEmail
{
return $this->email;
}
}
+249
View File
@@ -0,0 +1,249 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Redirect;
use TYPO3\CMS\Core\Authentication\LoginType;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\FrontendLogin\Configuration\RedirectConfiguration;
use TYPO3\CMS\FrontendLogin\Validation\RedirectUrlValidator;
/**
* Resolve felogin related redirects based on the current login type and the selected configuration (redirect mode)
*
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class RedirectHandler
{
protected bool $userIsLoggedIn = false;
public function __construct(
protected RedirectModeHandler $redirectModeHandler,
protected RedirectUrlValidator $redirectUrlValidator,
Context $context
) {
$this->userIsLoggedIn = (bool)$context->getPropertyFromAspect('frontend.user', 'isLoggedIn');
}
/**
* Process redirect modes. This method searches for a redirect url using all configured modes and returns it.
*/
public function processRedirect(RequestInterface $request, string $loginType, RedirectConfiguration $configuration, string $redirectModeReferrer): string
{
if ($this->isUserLoginFailedAndLoginErrorActive($configuration->getModes(), $loginType)) {
return $this->redirectModeHandler->redirectModeLoginError($request, $configuration->getPageOnLoginError());
}
$redirectUrlList = [];
foreach ($configuration->getModes() as $redirectMode) {
$redirectUrl = '';
$type = LoginType::tryFrom($loginType);
if ($type === LoginType::LOGIN) {
$redirectUrl = $this->handleSuccessfulLogin($request, $redirectMode, $configuration->getPageOnLogin(), $configuration->getDomains(), $redirectModeReferrer);
} elseif ($type === LoginType::LOGOUT) {
$redirectUrl = $this->handleSuccessfulLogout($request, $redirectMode, $configuration->getPageOnLogout());
}
if ($redirectUrl !== '') {
$redirectUrlList[] = $redirectUrl;
}
}
return $this->fetchReturnUrlFromList($redirectUrlList, $configuration->getFirstMode());
}
/**
* Get alternative logout form redirect url if logout and page not accessible
*/
protected function getLogoutRedirectUrl(RequestInterface $request, array $redirectModes, int $redirectPageLogout = 0): string
{
if ($this->userIsLoggedIn && $this->isRedirectModeActive($redirectModes, RedirectMode::LOGOUT)) {
return $this->redirectModeHandler->redirectModeLogout($request, $redirectPageLogout);
}
return $this->getGetpostRedirectUrl($request, $redirectModes);
}
/**
* Is used for alternative redirect urls on redirect mode "getpost"
*/
protected function getGetpostRedirectUrl(RequestInterface $request, array $redirectModes): string
{
return $this->isRedirectModeActive($redirectModes, RedirectMode::GETPOST)
? $this->getRedirectUrlRequestParam($request)
: '';
}
/**
* Handle redirect mode logout
*/
protected function handleSuccessfulLogout(RequestInterface $request, string $redirectMode, int $redirectPageLogout): string
{
if ($redirectMode === RedirectMode::LOGOUT) {
return $this->redirectModeHandler->redirectModeLogout($request, $redirectPageLogout);
}
return '';
}
/**
* Base on setting redirectFirstMethod get first or last entry from redirect url list.
*/
protected function fetchReturnUrlFromList(array $redirectUrlList, string $redirectFirstMethod): string
{
if (count($redirectUrlList) === 0) {
return '';
}
// Remove empty values, but keep "0" as value (that's why "strlen" is used as second parameter)
$redirectUrlList = array_filter($redirectUrlList, static function (string $value): bool {
return strlen($value) > 0;
});
return $redirectFirstMethod
? array_shift($redirectUrlList)
: array_pop($redirectUrlList);
}
/**
* Generate redirect_url for case that the user was successfully logged in
*/
protected function handleSuccessfulLogin(RequestInterface $request, string $redirectMode, int $redirectPageLogin = 0, string $domains = '', string $redirectModeReferrer = ''): string
{
if (!$this->userIsLoggedIn) {
return '';
}
// Logintype is needed because the login-page wouldn't be accessible anymore after a login (would always redirect)
switch ($redirectMode) {
case RedirectMode::GROUP_LOGIN:
$redirectUrl = $this->redirectModeHandler->redirectModeGroupLogin($request);
break;
case RedirectMode::USER_LOGIN:
$redirectUrl = $this->redirectModeHandler->redirectModeUserLogin($request);
break;
case RedirectMode::LOGIN:
$redirectUrl = $this->redirectModeHandler->redirectModeLogin($request, $redirectPageLogin);
break;
case RedirectMode::GETPOST:
$redirectUrl = $this->getRedirectUrlRequestParam($request);
break;
case RedirectMode::REFERRER:
$redirectUrl = $this->redirectModeHandler->redirectModeReferrer($request, $redirectModeReferrer);
break;
case RedirectMode::REFERRER_DOMAINS:
$redirectUrl = $this->redirectModeHandler->redirectModeReferrerDomains($request, $domains, $redirectModeReferrer);
break;
default:
$redirectUrl = '';
}
return $redirectUrl;
}
protected function isUserLoginFailedAndLoginErrorActive(array $redirectModes, string $loginType): bool
{
return LoginType::tryFrom($loginType) === LoginType::LOGIN
&& !$this->userIsLoggedIn
&& $this->isRedirectModeActive($redirectModes, RedirectMode::LOGIN_ERROR);
}
protected function isRedirectModeActive(array $redirectModes, string $mode): bool
{
return in_array($mode, $redirectModes, true);
}
/**
* Returns the redirect Url that should be used in login form template for GET/POST redirect mode
*/
public function getLoginFormRedirectUrl(
RequestInterface $request,
RedirectConfiguration $configuration,
bool $redirectDisabled
): string {
if (!$redirectDisabled) {
return $this->getGetpostRedirectUrl($request, $configuration->getModes());
}
return '';
}
/**
* Determines the `referer` variable used in the login form for loginMode=referer depending on the
* following evaluation order:
*
* - HTTP POST parameter `referer`
* - HTTP GET parameter `referer`
* - HTTP_REFERER
* - URL of initiating request in case plugin has been called via sub-request
*
* The evaluated `referer` is only returned, if it is considered valid.
*/
public function getReferrerForLoginForm(RequestInterface $request, array $settings): string
{
// Early return, if redirectMode is not configured to respect the referrer
if (!$this->isReferrerRedirectEnabled($settings)) {
return '';
}
// Early return, if redirectReferrer is not enabled in current context (e.g., after a password reset)
if (($request->getQueryParams()['tx_felogin_login']['redirectReferrer'] ?? '') === 'off') {
return '';
}
$referrer = (string)(
$request->getParsedBody()['referer']
?? $request->getQueryParams()['referer']
?? $request->getServerParams()['HTTP_REFERER']
?? ''
);
// If the current request was initiated via sub-request, we use the URI of the original request as referrer
if ($originalRequest = $request->getAttribute('originalRequest', false)) {
$referrer = (string)$originalRequest->getUri();
}
if ($this->redirectUrlValidator->isValid($request, $referrer)) {
return $referrer;
}
return '';
}
/**
* Returns whether redirect based on the referrer is enabled
*/
protected function isReferrerRedirectEnabled(array $settings): bool
{
$referrerRedirectModes = [RedirectMode::REFERRER, RedirectMode::REFERRER_DOMAINS];
$configuredRedirectModes = GeneralUtility::trimExplode(',', $settings['redirectMode'] ?? '');
return count(array_intersect($configuredRedirectModes, $referrerRedirectModes)) > 0;
}
/**
* Returns validated redirect url contained in request param return_url or redirect_url
*/
private function getRedirectUrlRequestParam(RequestInterface $request): string
{
// If config.typolinkLinkAccessRestrictedPages is set, the var is return_url
$returnUrlFromRequest = (string)($request->getParsedBody()['return_url'] ?? $request->getQueryParams()['return_url'] ?? null);
$redirectUrlFromRequest = (string)($request->getParsedBody()['redirect_url'] ?? $request->getQueryParams()['redirect_url'] ?? null);
$redirectUrl = $returnUrlFromRequest ?: $redirectUrlFromRequest;
return $this->redirectUrlValidator->isValid($request, $redirectUrl) ? $redirectUrl : '';
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Redirect;
/**
* Contains the different redirect modes types
*
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
final class RedirectMode
{
public const string LOGIN = 'login';
public const string LOGOUT = 'logout';
public const string LOGIN_ERROR = 'loginError';
public const string GETPOST = 'getpost';
public const string USER_LOGIN = 'userLogin';
public const string GROUP_LOGIN = 'groupLogin';
public const string REFERRER = 'referer';
public const string REFERRER_DOMAINS = 'refererDomains';
}
+181
View File
@@ -0,0 +1,181 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Redirect;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\FrontendLogin\Domain\Repository\FrontendUserGroupRepository;
use TYPO3\CMS\FrontendLogin\Domain\Repository\FrontendUserRepository;
use TYPO3\CMS\FrontendLogin\Validation\RedirectUrlValidator;
/**
* Do felogin related redirects
*
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
readonly class RedirectModeHandler
{
public function __construct(
protected UriBuilder $uriBuilder,
protected RedirectUrlValidator $redirectUrlValidator,
private FrontendUserRepository $frontendUserRepository,
private FrontendUserGroupRepository $frontendUserGroupRepository
) {}
/**
* Handle redirect mode groupLogin
*/
public function redirectModeGroupLogin(RequestInterface $request): string
{
$groups = $request->getAttribute('frontend.user')->userGroups;
if (empty($groups)) {
return '';
}
$groupUids = array_keys($groups);
// Take the first group with a redirect page
foreach ($groupUids as $groupUid) {
$redirectPageId = (int)$this->frontendUserGroupRepository
->findRedirectPageIdByGroupId($groupUid);
if ($redirectPageId > 0) {
return $this->buildUriForPageUid($request, $redirectPageId);
}
}
return '';
}
/**
* Handle redirect mode userLogin
*/
public function redirectModeUserLogin(RequestInterface $request): string
{
$userUid = (int)$request->getAttribute('frontend.user')->user['uid'];
$redirectPageId = $this->frontendUserRepository->findRedirectIdPageByUserId($userUid);
if ($redirectPageId === null) {
return '';
}
return $this->buildUriForPageUid($request, $redirectPageId);
}
/**
* Handle redirect mode login
*/
public function redirectModeLogin(RequestInterface $request, int $redirectPageLogin): string
{
$redirectUrl = '';
if ($redirectPageLogin !== 0) {
$redirectUrl = $this->buildUriForPageUid($request, $redirectPageLogin);
}
return $redirectUrl;
}
/**
* Handle redirect mode referrer
*/
public function redirectModeReferrer(RequestInterface $request, string $redirectReferrer): string
{
$redirectUrl = '';
if ($redirectReferrer !== 'off') {
// Avoid forced logout, when trying to login immediately after a logout
$redirectUrl = preg_replace('/[&?]logintype=[a-z]+/', '', $this->getReferrer($request));
}
return $redirectUrl ?? '';
}
/**
* Handle redirect mode refererDomains
*/
public function redirectModeReferrerDomains(RequestInterface $request, string $domains, string $redirectReferrer): string
{
$redirectUrl = '';
if ($redirectReferrer !== '') {
return '';
}
// Auto redirect.
// Feature to redirect to the page where the user came from (HTTP_REFERER).
// Allowed domains to redirect to, can be configured with plugin.tx_felogin_login.domains
// also avoid redirect when logging in after changing password
if ($domains) {
$url = $this->getReferrer($request);
// Is referring url allowed to redirect?
$match = [];
if (preg_match('#^https?://([[:alnum:].-]+)/#', $url, $match)) {
$redirectDomain = $match[1];
$found = false;
foreach (GeneralUtility::trimExplode(',', $domains, true) as $domain) {
if (preg_match('/(?:^|\\.)' . preg_quote($domain, '/') . '$/', $redirectDomain)) {
$found = true;
break;
}
}
if (!$found) {
$url = '';
}
}
// Avoid forced logout, when trying to login immediately after a logout
if ($url) {
$redirectUrl = preg_replace('/[&?]logintype=[a-z]+/', '', $url);
}
}
return $redirectUrl ?? '';
}
/**
* Handle redirect mode loginError after login-error
*/
public function redirectModeLoginError(RequestInterface $request, int $redirectPageLoginError = 0): string
{
$redirectUrl = '';
if ($redirectPageLoginError > 0) {
$redirectUrl = $this->buildUriForPageUid($request, $redirectPageLoginError);
}
return $redirectUrl;
}
/**
* Handle redirect mode logout
*/
public function redirectModeLogout(RequestInterface $request, int $redirectPageLogout): string
{
$redirectUrl = '';
if ($redirectPageLogout > 0) {
$redirectUrl = $this->buildUriForPageUid($request, $redirectPageLogout);
}
return $redirectUrl;
}
protected function buildUriForPageUid(RequestInterface $request, int $pageUid): string
{
$this->uriBuilder->reset();
$this->uriBuilder->setRequest($request);
$this->uriBuilder->setTargetPageUid($pageUid);
return $this->uriBuilder->build();
}
protected function getReferrer(RequestInterface $request): string
{
$referrer = '';
$requestReferrer = (string)($request->getParsedBody()['referer'] ?? $request->getQueryParams()['referer'] ?? '');
if ($this->redirectUrlValidator->isValid($request, $requestReferrer)) {
$referrer = $requestReferrer;
}
return $referrer;
}
}
+132
View File
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Service;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mime\Address;
use TYPO3\CMS\Core\Mail\FluidEmail;
use TYPO3\CMS\Core\Mail\MailerInterface;
use TYPO3\CMS\Core\Mail\TemplatedEmailFactory;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\FrontendLogin\Configuration\RecoveryConfiguration;
use TYPO3\CMS\FrontendLogin\Event\SendRecoveryEmailEvent;
/**
* @internal this is a concrete TYPO3 implementation and solely used for EXT:felogin and not part of TYPO3's Core API.
*/
class RecoveryService
{
protected array $settings;
public function __construct(
protected readonly MailerInterface $mailer,
protected readonly TemplatedEmailFactory $templatedEmailFactory,
protected EventDispatcherInterface $eventDispatcher,
ConfigurationManagerInterface $configurationManager,
protected RecoveryConfiguration $recoveryConfiguration,
protected UriBuilder $uriBuilder
) {
$this->settings = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
}
/**
* Sends an email with an absolute link including the given forgot hash to the passed user
* with instructions to recover the account.
*
* @throws TransportExceptionInterface
*/
public function sendRecoveryEmail(RequestInterface $request, array $userData, string $hash): void
{
$receiver = new Address($userData['email'], $this->getReceiverName($userData));
$email = $this->prepareMail($request, $receiver, $hash, $userData);
$event = new SendRecoveryEmailEvent($email, $userData);
$this->eventDispatcher->dispatch($event);
$this->mailer->send($event->getEmail());
}
/**
* Get display name from values. Fallback to username if none of the "_name" fields is set.
*/
protected function getReceiverName(array $userInformation): string
{
$displayName = trim(
sprintf(
'%s%s%s',
$userInformation['first_name'],
$userInformation['middle_name'] ? " {$userInformation['middle_name']}" : '',
$userInformation['last_name'] ? " {$userInformation['last_name']}" : ''
)
);
return $displayName ? $displayName . ' (' . $userInformation['username'] . ')' : $userInformation['username'];
}
/**
* Create email object from configuration.
*/
protected function prepareMail(RequestInterface $request, Address $receiver, string $hash, array $userData): FluidEmail
{
$url = $this->uriBuilder
->reset()
->setRequest($request)
->setCreateAbsoluteUri(true)
->uriFor(
'showChangePassword',
['hash' => $hash],
'PasswordRecovery',
'felogin',
'Login'
);
$variables = [
'receiverName' => $receiver->getName(),
'userData' => $userData,
'url' => $url,
'validUntil' => date($this->settings['dateFormat'] ?? 'Y-m-d H:i', $this->recoveryConfiguration->getLifeTimeTimestamp()),
];
$mail = $this->templatedEmailFactory->createWithOverrides(
$this->settings['email']['templateRootPaths'] ?? [],
$this->settings['email']['layoutRootPaths'] ?? [],
$this->settings['email']['partialRootPaths'] ?? [],
$request,
);
$mail->subject($this->getEmailSubject())
->from($this->recoveryConfiguration->getSender())
->to($receiver)
->assignMultiple($variables)
->setTemplate($this->recoveryConfiguration->getMailTemplateName());
$replyTo = $this->recoveryConfiguration->getReplyTo();
if ($replyTo) {
$mail->addReplyTo($replyTo);
}
return $mail;
}
protected function getEmailSubject(): string
{
return LocalizationUtility::translate('password_recovery_mail_header', 'felogin');
}
}
+120
View File
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\FrontendLogin\Validation;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\FrontendLogin\Event\ModifyRedirectUrlValidationResultEvent;
/**
* Used to check if a referrer or a redirect URL is valid to be used as within Frontend Logins
* for redirects.
*
* @internal for now as it might get adopted for further streamlining against other validation paradigms
*/
readonly class RedirectUrlValidator
{
public function __construct(
protected SiteFinder $siteFinder,
protected EventDispatcherInterface $eventDispatcher,
protected LoggerInterface $logger,
) {}
/**
* Checks if a given URL is valid / properly sanitized and/or the domain is known to TYPO3.
*/
public function isValid(RequestInterface $request, string $value): bool
{
if ($value === '') {
return false;
}
// Validate the URL
$result = false;
if ($this->isRelativeUrl($request, $value) || $this->isInCurrentDomain($request, $value) || $this->isInLocalDomain($value)) {
$result = true;
}
// Allow to change the validation result via a PSR-14 event
$event = new ModifyRedirectUrlValidationResultEvent($value, $result, $request);
$event = $this->eventDispatcher->dispatch($event);
$result = $event->getValidationResult();
// URL is not allowed
if (!$result) {
$this->logger->debug('Url "{url}" was not accepted.', ['url' => $value]);
}
return $result;
}
/**
* Determines whether the URL is on the current host and belongs to the
* current TYPO3 installation. The scheme part is ignored in the comparison.
*/
protected function isInCurrentDomain(RequestInterface $request, string $url): bool
{
$urlWithoutSchema = preg_replace('#^https?://#', '', $url) ?? '';
$siteUrlWithoutSchema = preg_replace('#^https?://#', '', $request->getAttribute('normalizedParams')->getSiteUrl()) ?? '';
// this condition only exists to satisfy phpstan, which complains that this could be an array, too.
if (is_array($siteUrlWithoutSchema)) {
$siteUrlWithoutSchema = $siteUrlWithoutSchema[0];
}
return str_starts_with($urlWithoutSchema . '/', $request->getAttribute('normalizedParams')->getHttpHost() . '/')
&& str_starts_with($urlWithoutSchema, $siteUrlWithoutSchema);
}
/**
* Determines whether the URL matches a domain known to TYPO3.
*/
protected function isInLocalDomain(string $url): bool
{
if (!GeneralUtility::isValidUrl($url)) {
return false;
}
$parsedUrl = parse_url($url);
if ($parsedUrl['scheme'] === 'http' || $parsedUrl['scheme'] === 'https') {
$host = $parsedUrl['host'];
foreach ($this->siteFinder->getAllSites() as $site) {
if ($site->getBase()->getHost() === $host) {
return true;
}
}
}
return false;
}
/**
* Determines whether the URL is relative to the current TYPO3 installation.
*/
protected function isRelativeUrl(RequestInterface $request, string $url): bool
{
$url = GeneralUtility::sanitizeLocalUrl($url, $request);
if (!empty($url)) {
$parsedUrl = @parse_url($url);
if ($parsedUrl !== false && !isset($parsedUrl['scheme']) && !isset($parsedUrl['host'])) {
// If the relative URL starts with a slash, we need to check if it's within the current site path
return $parsedUrl['path'][0] !== '/' || str_starts_with($parsedUrl['path'], $request->getAttribute('normalizedParams')->getSitePath());
}
}
return false;
}
}
+297
View File
@@ -0,0 +1,297 @@
<T3DataStructure>
<sheets>
<sDEF>
<ROOT>
<sheetTitle>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.sheet_general</sheetTitle>
<type>array</type>
<el>
<settings.showForgotPassword>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.show_forgot_password</label>
<config>
<type>check</type>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
</numIndex>
</items>
</config>
</settings.showForgotPassword>
<settings.showPermaLogin>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.show_permalogin</label>
<config>
<default>1</default>
<type>check</type>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
</numIndex>
</items>
</config>
</settings.showPermaLogin>
<settings.showLogoutFormAfterLogin>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.show_logoutFormAfterLogin</label>
<config>
<default></default>
<type>check</type>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
</numIndex>
</items>
</config>
</settings.showLogoutFormAfterLogin>
<settings.pages>
<exclude>1</exclude>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.user_storage</label>
<config>
<type>group</type>
<allowed>pages</allowed>
<size>3</size>
<maxitems>22</maxitems>
<minitems>0</minitems>
</config>
</settings.pages>
<settings.recursive>
<label>LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.recursive</label>
<config>
<type>select</type>
<renderType>selectSingle</renderType>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.0</label>
<value></value>
</numIndex>
<numIndex index="1" type="array">
<label>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.1</label>
<value>1</value>
</numIndex>
<numIndex index="2" type="array">
<label>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.2</label>
<value>2</value>
</numIndex>
<numIndex index="3" type="array">
<label>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.3</label>
<value>3</value>
</numIndex>
<numIndex index="4" type="array">
<label>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.4</label>
<value>4</value>
</numIndex>
<numIndex index="5" type="array">
<label>LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.5</label>
<value>250</value>
</numIndex>
</items>
<minitems>0</minitems>
<maxitems>1</maxitems>
<size>1</size>
</config>
</settings.recursive>
</el>
</ROOT>
</sDEF>
<s_redirect>
<ROOT>
<sheetTitle>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.sheet_redirect</sheetTitle>
<type>array</type>
<el>
<settings.redirectMode>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode</label>
<config>
<type>select</type>
<renderType>selectMultipleSideBySide</renderType>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.0</label>
<value>groupLogin</value>
</numIndex>
<numIndex index="1" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.1</label>
<value>userLogin</value>
</numIndex>
<numIndex index="2" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.2</label>
<value>login</value>
</numIndex>
<numIndex index="3" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.3</label>
<value>logout</value>
</numIndex>
<numIndex index="4" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.4</label>
<value>loginError</value>
</numIndex>
<numIndex index="5" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.5</label>
<value>getpost</value>
</numIndex>
<numIndex index="6" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.6</label>
<value>referer</value>
</numIndex>
<numIndex index="7" type="array">
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectMode.I.7</label>
<value>refererDomains</value>
</numIndex>
</items>
<size>8</size>
<minitems>0</minitems>
<maxitems>8</maxitems>
</config>
</settings.redirectMode>
<settings.redirectFirstMethod>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectFirstMethod</label>
<config>
<type>check</type>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enabled</label>
</numIndex>
</items>
</config>
</settings.redirectFirstMethod>
<settings.redirectPageLogin>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectPageLogin</label>
<config>
<type>group</type>
<allowed>pages</allowed>
<size>1</size>
<maxitems>1</maxitems>
<minitems>0</minitems>
</config>
</settings.redirectPageLogin>
<settings.redirectPageLoginError>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectPageLoginError</label>
<config>
<type>group</type>
<allowed>pages</allowed>
<size>1</size>
<maxitems>1</maxitems>
<minitems>0</minitems>
</config>
</settings.redirectPageLoginError>
<settings.redirectPageLogout>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectPageLogout</label>
<config>
<type>group</type>
<allowed>pages</allowed>
<size>1</size>
<maxitems>1</maxitems>
<minitems>0</minitems>
</config>
</settings.redirectPageLogout>
<settings.redirectDisable>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.redirectDisable</label>
<config>
<type>check</type>
<items type="array">
<numIndex index="0" type="array">
<label>LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:disable</label>
</numIndex>
</items>
</config>
</settings.redirectDisable>
</el>
</ROOT>
</s_redirect>
<s_messages>
<ROOT>
<sheetTitle>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.sheet_messages</sheetTitle>
<type>array</type>
<el>
<settings.welcome_header>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.welcome_header</label>
<config>
<type>input</type>
<size>30</size>
</config>
</settings.welcome_header>
<settings.welcome_message>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.welcome_message</label>
<config>
<type>text</type>
<cols>30</cols>
<rows>5</rows>
</config>
</settings.welcome_message>
<settings.success_header>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.success_header</label>
<config>
<type>input</type>
<size>30</size>
</config>
</settings.success_header>
<settings.success_message>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.success_message</label>
<config>
<type>text</type>
<cols>30</cols>
<rows>5</rows>
</config>
</settings.success_message>
<settings.error_header>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.error_header</label>
<config>
<type>input</type>
<size>30</size>
</config>
</settings.error_header>
<settings.error_message>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.error_message</label>
<config>
<type>text</type>
<cols>30</cols>
<rows>5</rows>
</config>
</settings.error_message>
<settings.status_header>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.status_header</label>
<config>
<type>input</type>
<size>30</size>
</config>
</settings.status_header>
<settings.status_message>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.status_message</label>
<config>
<type>text</type>
<cols>30</cols>
<rows>5</rows>
</config>
</settings.status_message>
<settings.logout_header>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.logout_header</label>
<config>
<type>input</type>
<size>30</size>
</config>
</settings.logout_header>
<settings.logout_message>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.logout_message</label>
<config>
<type>text</type>
<cols>30</cols>
<rows>5</rows>
</config>
</settings.logout_message>
<settings.forgot_header>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.forgot_header</label>
<config>
<type>input</type>
<size>30</size>
</config>
</settings.forgot_header>
<settings.forgot_reset_message>
<label>LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.forgot_message</label>
<config>
<type>text</type>
<cols>30</cols>
<rows>5</rows>
</config>
</settings.forgot_reset_message>
</el>
</ROOT>
</s_messages>
</sheets>
</T3DataStructure>
+21
View File
@@ -0,0 +1,21 @@
services:
_defaults:
autowire: true
autoconfigure: true
public: false
TYPO3\CMS\FrontendLogin\:
resource: '../Classes/*'
feloginPasswordRecovery.rateLimiterFactory:
class: TYPO3\CMS\Core\RateLimiter\RateLimiterFactory
arguments:
$config:
id: 'felogin-password-recovery'
policy: 'sliding_window'
limit: 5
interval: '15 minutes'
TYPO3\CMS\FrontendLogin\Controller\PasswordRecoveryController:
arguments:
$rateLimiterFactory: '@feloginPasswordRecovery.rateLimiterFactory'
+1
View File
@@ -0,0 +1 @@
name: typo3/felogin
+178
View File
@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="EXT:felogin/Configuration/Sets/Felogin/labels.xlf" date="2024-09-05T08:00:00Z" product-name="felogin">
<header/>
<body>
<trans-unit id="label">
<source>Frontend Login</source>
</trans-unit>
<trans-unit id="categories.felogin">
<source>Frontend Login</source>
</trans-unit>
<trans-unit id="settings.felogin.pid">
<source>User Storage Page</source>
</trans-unit>
<trans-unit id="settings.description.felogin.pid">
<source>Define the Storage Folder with the Website User Records, using a comma separated list or single value</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive">
<source>Recursive</source>
</trans-unit>
<trans-unit id="settings.description.felogin.recursive">
<source>If set, also subfolder at configured recursive levels of the User Storage Page will be used</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive.enum.0">
<source>0</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive.enum.1">
<source>1</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive.enum.2">
<source>2</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive.enum.3">
<source>3</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive.enum.4">
<source>4</source>
</trans-unit>
<trans-unit id="settings.felogin.recursive.enum.255">
<source>255</source>
</trans-unit>
<trans-unit id="settings.felogin.showForgotPassword">
<source>Display Password Recovery Link</source>
</trans-unit>
<trans-unit id="settings.description.felogin.showForgotPassword">
<source>If set, the section in the template to display the link to the forgot password dialog is visible.</source>
</trans-unit>
<trans-unit id="settings.felogin.showPermaLogin">
<source>Display Remember Login Option</source>
</trans-unit>
<trans-unit id="settings.description.felogin.showPermaLogin">
<source>If set, the section in the template to display the option to remember the login (with a cookie) is visible.</source>
</trans-unit>
<trans-unit id="settings.felogin.showLogoutFormAfterLogin">
<source>Disable redirect after successful login, but display logout-form</source>
</trans-unit>
<trans-unit id="settings.description.felogin.showLogoutFormAfterLogin">
<source>If set, the logout form will be displayed immediately after successful login.</source>
</trans-unit>
<trans-unit id="settings.felogin.emailFrom">
<source>Email Sender Address</source>
</trans-unit>
<trans-unit id="settings.description.felogin.emailFrom">
<source>email address used as sender of the change password emails</source>
</trans-unit>
<trans-unit id="settings.felogin.emailFromName">
<source>Email Sender Name</source>
</trans-unit>
<trans-unit id="settings.description.felogin.emailFromName">
<source>Name used as sender of the change password emails</source>
</trans-unit>
<trans-unit id="settings.felogin.replyToEmail">
<source>Reply-to email Address</source>
</trans-unit>
<trans-unit id="settings.description.felogin.replyToEmail">
<source>Reply-to address used in the change password emails</source>
</trans-unit>
<trans-unit id="settings.felogin.dateFormat">
<source>Date format</source>
</trans-unit>
<trans-unit id="settings.description.felogin.dateFormat">
<source>Format for the link is valid until message (forgot password email)</source>
</trans-unit>
<trans-unit id="settings.felogin.email.layoutRootPath">
<source>Layout root path</source>
</trans-unit>
<trans-unit id="settings.description.felogin.email.layoutRootPath">
<source>Path to layout directory used for emails</source>
</trans-unit>
<trans-unit id="settings.felogin.email.templateRootPath">
<source>Template root path</source>
</trans-unit>
<trans-unit id="settings.description.felogin.email.templateRootPath">
<source>Path to template directory used for emails</source>
</trans-unit>
<trans-unit id="settings.felogin.email.partialRootPath">
<source>Partial root path</source>
</trans-unit>
<trans-unit id="settings.description.felogin.email.partialRootPath">
<source>Path to partial directory used for emails</source>
</trans-unit>
<trans-unit id="settings.felogin.email.templateName">
<source>Template name for emails.</source>
</trans-unit>
<trans-unit id="settings.description.felogin.email.templateName">
<source>HTML emails get the .html file extension, plaintext emails get the .txt file extension.</source>
</trans-unit>
<trans-unit id="settings.felogin.redirectMode">
<source>Redirect Mode</source>
</trans-unit>
<trans-unit id="settings.description.felogin.redirectMode">
<source>Comma separated list of redirect modes.\
Possible values: groupLogin, userLogin, login, getpost, referer, refererDomains, loginError, logout.\
Warning: redirects only work if neither the plugin nor the page it is displayed on are set to `hide at login`.</source>
</trans-unit>
<trans-unit id="settings.felogin.redirectFirstMethod">
<source>Use First Supported Mode from Selection</source>
</trans-unit>
<trans-unit id="settings.description.felogin.redirectFirstMethod">
<source>If set the first method from redirectMode which is possible will be used</source>
</trans-unit>
<trans-unit id="settings.felogin.redirectPageLogin">
<source>After Successful Login Redirect to Page</source>
</trans-unit>
<trans-unit id="settings.description.felogin.redirectPageLogin">
<source>Page id to redirect to after Login</source>
</trans-unit>
<trans-unit id="settings.felogin.redirectPageLoginError">
<source>After Failed Login Redirect to Page</source>
</trans-unit>
<trans-unit id="settings.description.felogin.redirectPageLoginError">
<source>Page id to redirect to after Login Error</source>
</trans-unit>
<trans-unit id="settings.felogin.redirectPageLogout">
<source>After Logout Redirect to Page</source>
</trans-unit>
<trans-unit id="settings.description.felogin.redirectPageLogout">
<source>Page id to redirect to after Logout</source>
</trans-unit>
<trans-unit id="settings.felogin.redirectDisable">
<source>Disable Redirect</source>
</trans-unit>
<trans-unit id="settings.description.felogin.redirectDisable">
<source>If set redirecting is disabled</source>
</trans-unit>
<trans-unit id="settings.felogin.forgotLinkHashValidTime">
<source>Time in hours how long the link for forgot password is valid</source>
</trans-unit>
<trans-unit id="settings.description.felogin.forgotLinkHashValidTime">
<source>How many hours the link for forgot password is valid</source>
</trans-unit>
<trans-unit id="settings.felogin.domains">
<source>Allowed Referrer-Redirect-Domains</source>
</trans-unit>
<trans-unit id="settings.description.felogin.domains">
<source>Comma separated list of domains which are allowed for the referrer redirect mode</source>
</trans-unit>
<trans-unit id="settings.felogin.view.templateRootPath">
<source>Path to template root (frontend)</source>
</trans-unit>
<trans-unit id="settings.description.felogin.view.templateRootPath">
<source>Path to template directory used for the plugin in the frontend. Extends the default template location.</source>
</trans-unit>
<trans-unit id="settings.felogin.view.partialRootPath">
<source>Path to template partials (frontend)</source>
</trans-unit>
<trans-unit id="settings.description.felogin.view.partialRootPath">
<source>Path to partial directory for the plugin in the frontend. Extends the default partial location.</source>
</trans-unit>
<trans-unit id="settings.felogin.view.layoutRootPath">
<source>Path to template layouts (frontend)</source>
</trans-unit>
<trans-unit id="settings.description.felogin.view.layoutRootPath">
<source>Path to layout directory used for the plugin in the frontend. Can be used to introduce a custom layout.</source>
</trans-unit>
</body>
</file>
</xliff>
@@ -0,0 +1,107 @@
categories:
felogin: ~
settings:
felogin.pid:
default: '0'
type: string
category: felogin
felogin.recursive:
default: '0'
type: string
enum:
- '0'
- '1'
- '2'
- '3'
- '4'
- '255'
category: felogin
felogin.showForgotPassword:
default: false
type: bool
category: felogin
felogin.showPermaLogin:
default: false
type: bool
category: felogin
felogin.showLogoutFormAfterLogin:
default: false
type: bool
category: felogin
felogin.emailFrom:
default: ''
type: string
category: felogin
felogin.emailFromName:
default: ''
type: string
category: felogin
felogin.replyToEmail:
default: ''
type: string
category: felogin
felogin.dateFormat:
default: 'Y-m-d H:i'
type: string
category: felogin
felogin.email.layoutRootPath:
default: ''
type: string
category: felogin
felogin.email.templateRootPath:
default: 'EXT:felogin/Resources/Private/Email/Templates/'
type: string
category: felogin
felogin.email.partialRootPath:
default: ''
type: string
category: felogin
felogin.email.templateName:
default: PasswordRecovery
type: string
category: felogin
felogin.redirectMode:
default: ''
type: string
category: felogin
felogin.redirectFirstMethod:
default: false
type: bool
category: felogin
felogin.redirectPageLogin:
default: 0
type: int
category: felogin
felogin.redirectPageLoginError:
default: 0
type: int
category: felogin
felogin.redirectPageLogout:
default: 0
type: int
category: felogin
felogin.redirectDisable:
default: false
type: bool
category: felogin
felogin.forgotLinkHashValidTime:
default: 12
type: int
category: felogin
felogin.domains:
default: ''
type: string
category: felogin
felogin.view.templateRootPath:
default: ''
type: string
category: felogin
felogin.view.partialRootPath:
default: ''
type: string
category: felogin
felogin.view.layoutRootPath:
default: ''
type: string
category: felogin
@@ -0,0 +1 @@
@import 'EXT:felogin/Configuration/TypoScript/setup.typoscript'
+22
View File
@@ -0,0 +1,22 @@
<?php
defined('TYPO3') or die();
call_user_func(static function () {
// Adds the redirect field to the fe_groups table
$additionalColumns = [
'felogin_redirectPid' => [
'exclude' => true,
'label' => 'LLL:EXT:felogin/Resources/Private/Language/Database.xlf:felogin_redirectPid',
'config' => [
'type' => 'group',
'allowed' => 'pages',
'size' => 1,
'relationship' => 'manyToOne',
],
],
];
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('fe_groups', $additionalColumns);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('fe_groups', 'felogin_redirectPid', '', 'after:subgroup');
});
+29
View File
@@ -0,0 +1,29 @@
<?php
defined('TYPO3') or die();
call_user_func(static function () {
// Adds the redirect field and the forgotHash field to the fe_users-table
$additionalColumns = [
'felogin_redirectPid' => [
'exclude' => true,
'label' => 'LLL:EXT:felogin/Resources/Private/Language/Database.xlf:felogin_redirectPid',
'config' => [
'type' => 'group',
'allowed' => 'pages',
'size' => 1,
'relationship' => 'manyToOne',
],
],
'felogin_forgotHash' => [
'exclude' => true,
'label' => 'LLL:EXT:felogin/Resources/Private/Language/Database.xlf:felogin_forgotHash',
'config' => [
'type' => 'passthrough',
],
],
];
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('fe_users', $additionalColumns);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes('fe_users', 'felogin_redirectPid', '', 'after:usergroup');
});
@@ -0,0 +1,15 @@
<?php
defined('TYPO3') or die();
call_user_func(static function () {
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
'Felogin',
'Login',
'LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.CType.felogin_login.title',
'mimetypes-x-content-login',
'forms',
'LLL:EXT:felogin/Resources/Private/Language/Database.xlf:tt_content.CType.felogin_login.description',
'FILE:EXT:felogin/Configuration/FlexForms/Login.xml',
);
});
@@ -0,0 +1,71 @@
# customsubcategory=01_Storage=Storage
# customsubcategory=02_Template=Template
# customsubcategory=03_Features=Features
# customsubcategory=04_EMail=Email
# customsubcategory=05_Redirects=Redirects
# customsubcategory=06_Security=Security
styles.content.loginform {
# cat=Frontend Login/01_Storage/100; type=string; label= User Storage Page: Define the Storage Folder with the Website User Records, using a comma separated list or single value
pid = 0
# cat=Frontend Login/01_Storage/101; type=options [0,1,2,3,4,255]; label= Recursive: If set, also subfolder at configured recursive levels of the User Storage Page will be used
recursive = 0
# cat=Frontend Login/03_Features/100; type=boolean; label= Display Password Recovery Link: If set, the section in the template to display the link to the forgot password dialogue is visible.
showForgotPassword = 0
# cat=Frontend Login/03_Features/101; type=boolean; label= Display Remember Login Option: If set, the section in the template to display the option to remember the login (with a cookie) is visible.
showPermaLogin = 0
# cat=Frontend Login/03_Features/102; type=boolean; label= Disable redirect after successful login, but display logout-form: If set, the logout form will be displayed immediately after successful login.
showLogoutFormAfterLogin = 0
# cat=Frontend Login/04_EMail/100; type=string; label= Email Sender Address: email address used as sender of the change password emails
emailFrom =
# cat=Frontend Login/04_EMail/101; type=string; label= Email Sender Name: Name used as sender of the change password emails
emailFromName =
# cat=Frontend Login/04_EMail/102; type=string; label= Reply to email Address: Reply to address used in the change password emails
replyToEmail =
# cat=Frontend Login/04_Email/103; type=string; label= Date format: Format for the link is valid until message (forgot password email)
dateFormat = Y-m-d H:i
email {
# cat=Frontend Login/04_EMail/104; type=string; label= Layout root path: Path to layout directory used for emails
layoutRootPath =
# cat=Frontend Login/04_EMail/105; type=string; label= Template root path: Path to template directory used for emails
templateRootPath = EXT:felogin/Resources/Private/Email/Templates/
# cat=Frontend Login/04_EMail/106; type=string; label= Partial root path: Path to partial directory used for emails
partialRootPath =
# cat=Frontend Login/04_EMail/107; type=string; label= Template name for emails. Plaintext emails get the .txt file extension.
templateName = PasswordRecovery
}
# cat=Frontend Login/05_Redirects/101; type=string; label= Redirect Mode: Comma separated list of redirect modes. Possible values: groupLogin, userLogin, login, getpost, referer, refererDomains, loginError, logout
redirectMode =
# cat=Frontend Login/05_Redirects/102; type=boolean; label= Use First Supported Mode from Selection: If set the first method from redirectMode which is possible will be used
redirectFirstMethod = 0
# cat=Frontend Login/05_Redirects/103; type=int+; label= After Successful Login Redirect to Page: Page id to redirect to after Login
redirectPageLogin = 0
# cat=Frontend Login/05_Redirects/104; type=int+; label= After Failed Login Redirect to Page: Page id to redirect to after Login Error
redirectPageLoginError = 0
# cat=Frontend Login/05_Redirects/105; type=int+; label= After Logout Redirect to Page: Page id to redirect to after Logout
redirectPageLogout = 0
# cat=Frontend Login/05_Redirects/106; type=boolean; label= Disable Redirect: If set redirecting is disabled
redirectDisable = 0
# cat=Frontend Login/06_Security/100; type=int+; label= Time in hours how long the link for forgot password is valid: How many hours the link for forgot password is valid
forgotLinkHashValidTime = 12
# cat=Frontend Login/06_Security/102; type=string; label= Allowed Referrer-Redirect-Domains: Comma separated list of domains which are allowed for the referrer redirect mode
domains =
}
plugin {
tx_felogin_login {
view {
# cat=Frontend Login/02_Template/102; type=string; label= Path to template root (FE)
templateRootPath =
# cat=Frontend Login/02_Template/103; type=string; label= Path to template partials (FE)
partialRootPath =
# cat=Frontend Login/02_Template/104; type=string; label= Path to template layouts (FE)
layoutRootPath =
}
}
}
+58
View File
@@ -0,0 +1,58 @@
plugin.tx_felogin_login {
view {
templateRootPaths.10 = {$plugin.tx_felogin_login.view.templateRootPath ?? $felogin.view.templateRootPath}
partialRootPaths.10 = {$plugin.tx_felogin_login.view.partialRootPath ?? $felogin.view.partialRootPath}
layoutRootPaths.10 = {$plugin.tx_felogin_login.view.layoutRootPath ?? $felogin.view.layoutRootPath}
}
ignoreFlexFormSettingsIfEmpty = showForgotPassword
ignoreFlexFormSettingsIfEmpty := addToList(showPermaLogin)
ignoreFlexFormSettingsIfEmpty := addToList(showLogoutFormAfterLogin)
ignoreFlexFormSettingsIfEmpty := addToList(pages)
ignoreFlexFormSettingsIfEmpty := addToList(recursive)
ignoreFlexFormSettingsIfEmpty := addToList(redirectMode)
ignoreFlexFormSettingsIfEmpty := addToList(redirectFirstMethod)
ignoreFlexFormSettingsIfEmpty := addToList(redirectPageLogin)
ignoreFlexFormSettingsIfEmpty := addToList(redirectPageLoginError)
ignoreFlexFormSettingsIfEmpty := addToList(redirectPageLogout)
ignoreFlexFormSettingsIfEmpty := addToList(redirectDisable)
settings {
pages = {$styles.content.loginform.pid ?? $felogin.pid}
recursive = {$styles.content.loginform.recursive ?? $felogin.recursive}
# Template
dateFormat = {$styles.content.loginform.dateFormat ?? $felogin.dateFormat}
showForgotPassword = {$styles.content.loginform.showForgotPassword ?? $felogin.showForgotPassword}
showPermaLogin = {$styles.content.loginform.showPermaLogin ?? $felogin.showPermaLogin}
showLogoutFormAfterLogin = {$styles.content.loginform.showLogoutFormAfterLogin ?? $felogin.showLogoutFormAfterLogin}
# Email Settings
email_from = {$styles.content.loginform.emailFrom ?? $felogin.emailFrom}
email_fromName = {$styles.content.loginform.emailFromName ?? $felogin.emailFromName}
email {
templateName = {$styles.content.loginform.email.templateName ?? $felogin.email.templateName}
layoutRootPaths {
20 = {$styles.content.loginform.email.layoutRootPath ?? $felogin.email.layoutRootPath}
}
templateRootPaths {
20 = {$styles.content.loginform.email.templateRootPath ?? $felogin.email.templateRootPath}
}
partialRootPaths {
20 = {$styles.content.loginform.email.partialRootPath ?? $felogin.email.partialRootPath}
}
}
# Redirect Settings
redirectMode = {$styles.content.loginform.redirectMode ?? $felogin.redirectMode}
redirectFirstMethod = {$styles.content.loginform.redirectFirstMethod ?? $felogin.redirectFirstMethod}
redirectPageLogin = {$styles.content.loginform.redirectPageLogin ?? $felogin.redirectPageLogin}
redirectPageLoginError = {$styles.content.loginform.redirectPageLoginError ?? $felogin.redirectPageLoginError}
redirectPageLogout = {$styles.content.loginform.redirectPageLogout ?? $felogin.redirectPageLogout}
redirectDisable = {$styles.content.loginform.redirectDisable ?? $felogin.redirectDisable}
# Security
forgotLinkHashValidTime = {$styles.content.loginform.forgotLinkHashValidTime ?? $felogin.forgotLinkHashValidTime}
domains = {$styles.content.loginform.domains ?? $felogin.domains}
}
}
+21
View File
@@ -0,0 +1,21 @@
.. include:: /Includes.rst.txt
.. _configuration:
=============
Configuration
=============
All configuration options are available in the FlexForm, as settings of the
site set and as TypoScript setup.
The settings are interpreted in the following order, the last one takes
precedence:
.. include:: _SettingsOrder.rst.txt
.. toctree::
:caption: Topics
:glob:
:titlesonly:
*
@@ -0,0 +1,24 @@
.. include:: /Includes.rst.txt
.. _settings-editor:
===============
Settings editor
===============
When the :ref:`site set for the frontend login <configuration-site-set>` is included,
the settings for EXT:felogin become available in the editor.
You can find the available site settings in module
:guilabel:`Sites > Setup > Settings`
You can change individual settings here. If the site settings are writable
you can hit the :guilabel:`Save` button and the settings will be written
directly to the site settings.
If the settings are not writable you can click the :guilabel:`YAML export`
button to export the settings. These can then be added by a developer with
sufficient rights.
The available settings are also described in detail in
:ref:`configuration-site-set-settings`.
+111
View File
@@ -0,0 +1,111 @@
:navigation-title: Site Sets
.. include:: /Includes.rst.txt
.. _configuration-site-sets-include:
============================================
Site set configuration of the Frontend Login
============================================
.. versionadded:: 13.1
Site sets were added.
The system extension :composer:`typo3/cms-felogin` provides the site
set "Frontend Login".
The different methods of setting are taking precedence in the following order:
.. include:: _SettingsOrder.rst.txt
.. contents::
:caption: Content on this page
:depth: 1
.. _configuration-site-set:
Include the site set
====================
Include the site set "Frontend Login" via the :ref:`site set in the site
configuration <t3coreapi:site-sets>` or the custom
:ref:`site package's site set <t3sitepackage:site_set>`.
.. figure:: /Images/SiteSet.png
Add the site set "Frontend Login"
This will change your site configuration file as follows:
.. literalinclude:: _site_config.diff
:caption: config/sites/my-site/config.yaml (diff)
If your site has a custom :ref:`site package <t3sitepackage:start>`, you
can also add the "Frontend Login" set as dependency in your site set's configuration:
.. literalinclude:: _site_package_set.diff
:caption: EXT:my_site_package/Configuration/Sets/MySite/config.yaml (diff)
.. _configuration-site-set-settings:
Settings for the "Frontend Login" site set
==========================================
.. versionadded:: 13.1
These settings were added with the site sets in TYPO3 v13.1.
See also: :ref:`configuration-examples-felogin-pid`.
If you plan to migrate from TypoScript setup settings to site settings see
:ref:`configuration-migration`.
These settings can be adjusted in the :ref:`settings-editor`.
.. typo3:site-set-settings:: PROJECT:/Configuration/Sets/Felogin/settings.definitions.yaml
:name: felogin
:type:
:Label: max=36
:caption: Settings of "Frontend Login"
.. _configuration-migration:
Migration from TypoScript setup settings to site settings
=========================================================
The site settings are named like the TypoScript constants used before
site sets. However the TypoScript constants are not always named the same
like the :ref:`TypoScript setup settings <plugin-tx-felogin-login>`.
For each :ref:`TypoScript setup / FlexForm setting <typo3/cms-felogin:plugin-tx-felogin-login>`
we list the corresponding site set setting in the overview table of the configuration values.
For example, the setting :confval:`felogin.pid <felogin-felogin-pid>` sets
setting :ref:`pages <pages>`.
Bear that in mind when migrating from TypoScript setup to site set settings.
.. _configuration-examples-felogin-pid:
Example: Set the user storage page using the site set settings
==============================================================
After you :ref:`included the site set <configuration-site-set>` you can use
the :ref:`site set settings <configuration-site-set-settings>` to configure
the frontend login plugin's behaviour and layout site-wide.
See also :ref:`Adding site settings <t3coreapi:sitehandling-settings-add>`.
You can add the settings to your :ref:`Site settings <t3coreapi:sitehandling-settings>`
or to the settings of your
:ref:`custom site package extension <t3sitepackage:start>`.
To add the settings to your site settings, edit the file
:file:`config/sites/<my_site>/settings.yaml` in Composer-based installations
or :file:`typo3conf/sites/<my_site>/settings.yaml` in legacy installations. If
the file does not exist yet, create one. Use the setting
:confval:`felogin.pid <felogin-felogin-pid>` to set the storage folder. If
its subfolders should also be included, additionally use setting
:confval:`felogin.recursive <felogin-felogin-recursive>`.
.. literalinclude:: _settings.yaml
:caption: config/sites/<my_site>/settings.yaml | typo3conf/sites/<my_site>/settings.yaml
+293
View File
@@ -0,0 +1,293 @@
:navigation-title: TypoScript
.. include:: /Includes.rst.txt
.. _configuration-typoscript:
==============================================
TypoScript configuration of the Frontend Login
==============================================
.. contents::
:caption: Content on this page
:depth: 1
.. _plugin-tx-felogin-login:
TypoScript setup / FlexForm settings
====================================
Most of these plugin settings can be set with the following methods, the top
bottom most taking precedence:
.. include:: _SettingsOrder.rst.txt
See also :ref:`configuration-examples-flexform`.
.. confval-menu::
:name: typoscript
:display: table
:type:
:Site set setting:
.. _showforgotpassword:
.. confval:: showForgotPassword
:name: typoscript-showForgotPassword
:type: bool
If set, the section in the template to display the link to the forgot
password dialogue is visible.
.. important::
Be aware that having this option disabled also prevents the plugin to
display the forgot password form. For instance if you access the link
directly.
.. _showpermalogin:
.. confval:: showPermaLogin
:name: typoscript-showPermaLogin
:type: bool
If set, the section in the template to display the option to remember
the login (with a cookie) is visible.
.. _showlogoutformafterlogin:
.. confval:: showLogoutFormAfterLogin
:name: typoscript-showLogoutFormAfterLogin
:type: bool
If set, the logout form will be displayed immediately after successful
login.
.. note::
Setting this option will disable the redirect options!
Instead of redirecting the plugin will show the logout form.
.. _pages:
.. confval:: pages
:name: typoscript-pages
:type: string
:Site set setting: :confval:`felogin.pid <felogin-felogin-pid>`
:TypoScript Constant: {$styles.content.loginform.pid}
Define the User Storage Page with the Website User Records, using a
comma separated list or a single value (page id).
.. _recursive:
.. confval:: recursive
:name: typoscript-recursive
:type: int
:Site set setting: :confval:`felogin.recursive <felogin-felogin-recursive>`
:TypoScript Constant: {$styles.content.loginform.recursive}
If set, also any subfolders of the User Storage Page will be used
at configured recursive levels
.. _redirectmode:
.. confval:: redirectMode
:name: typoscript-redirectMode
:type: string
:Site set setting: :confval:`felogin.redirectMode <felogin-felogin-redirectmode>`
:TypoScript Constant: {$styles.content.loginform.redirectMode}
Comma separated list of redirect modes. Possible values:
``groupLogin``, ``userLogin``, ``login``, ``getpost``, ``referer``,
``refererDomains``, ``loginError``, ``logout``
See section on redirect modes for details.
.. _redirectfirstmethod:
.. confval:: redirectFirstMethod
:name: typoscript-redirectFirstMethod
:type: bool
:Site set setting: :confval:`felogin.redirectFirstMethod <felogin-felogin-redirectfirstmethod>`
:TypoScript Constant: {$styles.content.loginform.redirectFirstMethod}
If set the first method from redirectMode which is possible will be
used
.. _redirectpagelogin:
.. confval:: redirectPageLogin
:name: typoscript-redirectPageLogin
:type: integer
:Site set setting: :confval:`felogin.redirectPageLogin <felogin-felogin-redirectpagelogin>`
:TypoScript Constant: {$styles.content.loginform.redirectPageLogin}
Page id to redirect to after Login
.. _redirectpageloginerror:
.. confval:: redirectPageLoginError
:name: typoscript-redirectPageLoginError
:type: integer
:Site set setting: :confval:`felogin.redirectPageLoginError <felogin-felogin-redirectpageloginerror>`
:TypoScript Constant: {$styles.content.loginform.redirectPageLoginError}
Page id to redirect to after Login Error
.. _redirectpagelogout:
.. confval:: redirectPageLogout
:name: typoscript-redirectPageLogout
:type: integer
:Site set setting:
:TypoScript Constant: {$styles.content.loginform.redirectPageLogout}
Page id to redirect to after Logout
.. _redirectdisable:
.. confval:: redirectDisable
:name: typoscript-redirectDisable
:type: bool
:Site set setting: :confval:`felogin.redirectPageLogout <felogin-felogin-redirectpagelogout>`
:TypoScript Constant: {$styles.content.loginform.redirectDisable}
If set redirecting is disabled
.. _dateformat:
.. confval:: dateFormat
:name: typoscript-dateFormat
:type: date-conf
:Site set setting: :confval:`felogin.dateFormat <felogin-felogin-dateformat>`
:TypoScript Constant: Y-m-d H:i
Format for the link is valid until message (forgot password email)
.. _email-from:
.. confval:: email_from
:name: typoscript-email-from
:type: string
Email address used as sender of the change password emails
.. _email-fromname:
.. confval:: email_fromName
:name: typoscript-email-fromName
:type: string
Name used as sender of the change password emails
.. confval:: email
:name: typoscript-email
.. confval:: email.templateName
:name: typoscript-email.templateName
:type: string
:Site set setting: :confval:`felogin.email.templateName <felogin-felogin-email-templatename>`
:TypoScript Constant: {$styles.content.loginform.email.templateName}
Template name for emails. Plaintext emails get the .txt file extension.
.. confval:: email.layoutRootPaths
:name: typoscript-email.layoutRootPaths
:type: array
:Site set setting: :confval:`felogin.email.templateRootPath <felogin-felogin-email-templaterootpath>`
:TypoScript Constant: {$styles.content.loginform.email.layoutRootPath}
Path to layout directory used for emails
.. confval:: email.templateRootPaths
:name: typoscript-email.templateRootPaths
:type: array
:Site set setting: :confval:`felogin.email.templateRootPath <felogin-felogin-email-templaterootpath>`
:TypoScript Constant: {$styles.content.loginform.email.templateRootPaths}
Path to template directory used for emails
.. confval:: email.partialRootPaths
:name: typoscript-email.partialRootPaths
:type: array
:Site set setting: :confval:`felogin.email.partialRootPath <felogin-felogin-email-partialrootpath>`
:TypoScript Constant: {$styles.content.loginform.email.partialRootPaths}
Path to partial directory used for emails
.. confval:: forgotLinkHashValidTime
:name: typoscript-forgotLinkHashValidTime
:type: integer
:Site set setting: :confval:`felogin.forgotLinkHashValidTime <felogin-felogin-forgotlinkhashvalidtime>`
:TypoScript Constant: {$styles.content.loginform.forgotLinkHashValidTime}
Time in hours how long the link for forgot password is valid
.. _domains:
.. confval:: domains
:name: typoscript-domains
:type: string
Comma separated list of domains which are allowed for the referrer
redirect mode
.. _configuration-examples-typoscript-constant:
Example: Set the default storage page via TypoScript constant
=============================================================
You can use the :ref:`TypoScript provider <t3coreapi:site-sets-typoscript>`
or other means of :ref:`setting the TypoScript constants <t3tsref:using-and-setting>`.
.. versionchanged:: 13.1
It is recommended to use the :ref:`configuration-site-set-settings`
instead, as TypoScript constants will be phased out in the future.
.. literalinclude:: _constants.typoscript
:caption: config/sites/MySite/constants.typoscript
.. _configuration-examples-typoscript:
Example: Set the default storage page via TypoScript setup
==========================================================
In order to set the default storage page to a more dynamic value, use
the TypoScript setup. Use the :ref:`TypoScript provider <t3coreapi:site-sets-typoscript>`
or other means of ref:`setting the TypoScript setup <t3tsref:using-and-setting>`.
.. literalinclude:: _setup.typoscript
:caption: config/sites/MySite/constants.typoscript
.. _configuration-examples-flexform:
Example: Override the default storage page in the plugin's FlexForm
===================================================================
If you set any FlexForm setting within the content element representing the
plugin to a **non-empty value** it will override any other setting not matter if it
is made via site settings, TypoScript constant ot TypoScript setup. Empty values
take no effect if a default was set by other means.
In the backend module :guilabel:`Content > Layout` edit the content element containing
the login form. Go to tab :guilabel:`Plugin` and sub tab :guilabel:`General`.
You should see a form similar to the following:
.. figure:: /Images/GeneralSettings.png
:alt: A screenshot showing the "General" tab of the plugin settings
Settings in the tab :guilabel:`General` of the plugin tab
Choose the desired page or pages in the field with label
:guilabel:`User Storage Page` (key :confval:`settings.pages <typoscript-pages>`).
.. tip::
It is sometimes hard to determine, which label in the FlexForm corresponds
to which key in the :ref:`FlexForm reference <plugin-tx-felogin-login>`.
Turn on the :confval:`backend debug mode <t3coreapi:globals-typo3-conf-vars-be-debug>`
to get a visual hint in the backend for the keys of the FlexForm field.
.. figure:: /Images/FlexFormKey.png
:alt: A screenshot showing FlexForm Field with key `settings.pages`
The corresponding FlexForm field :confval:`settings.pages <typoscript-pages>`
in backend debug mode.
@@ -0,0 +1,5 @@
* The corresponding :ref:`site set setting <configuration-site-set-settings>`
* The corresponding :ref:`TypoScript constant <configuration-examples-typoscript-constant>`
* Value set in :ref:`TypoScript setup <t3tsref:using-and-setting>` in the
scope :ref:`plugin.tx_felogin_login.settings <plugin-tx-felogin-login>`
* Setting from the :ref:`FlexForm of the plugin <plugin-tx-felogin-login>`
@@ -0,0 +1,4 @@
styles.content.loginform {
pid = 42
recursive = 255
}
@@ -0,0 +1,4 @@
felogin:
pid: 42
recursive: 255
@@ -0,0 +1,5 @@
[{$tx_my_extension.settings.feature1Enabled} == 1]
plugin.tx_felogin_login.settings.pid = 123
[ELSE]
plugin.tx_felogin_login.settings.pid = 42
[END]
@@ -0,0 +1,5 @@
base: 'https://example.com/'
rootPageId: 1
dependencies:
+ - typo3/felogin
- typo3/fluid-styled-content-css
@@ -0,0 +1,9 @@
name: my-vendor/my-site-package
label: My Site Package Set
settings:
website:
background:
color: '#386492'
dependencies:
+ - typo3/felogin
- typo3/fluid-styled-content-css
+60
View File
@@ -0,0 +1,60 @@
.. include:: /Includes.rst.txt
.. _psr14events:
=============
PSR-14 events
=============
The following PSR-14 events are available to extend the extension:
AfterUserLoggedInEvent
======================
Trigger any kind of action when a frontend user has been successfully logged in.
:ref:`More details <t3coreapi:AfterUserLoggedInEvent>`
BeforeRedirectEvent
===================
Notification before a redirect is made.
:ref:`More details <t3coreapi:BeforeRedirectEvent>`
LoginConfirmedEvent
===================
A notification when a log in has successfully arrived at the plugin, via the
view and the controller, multiple information can be overridden in event
listeners. :ref:`More details <t3coreapi:LoginConfirmedEvent>`
LoginErrorOccurredEvent
=======================
A notification if something went wrong while trying to log in a user.
:ref:`More details <t3coreapi:LoginErrorOccurredEvent>`
LogoutConfirmedEvent
====================
A notification when a log out has successfully arrived at the plugin, via
the view and the controller, multiple information can be overridden in
event listeners. :ref:`More details <t3coreapi:LogoutConfirmedEvent>`
ModifyLoginFormViewEvent
========================
Allows to inject custom variables into the login form.
:ref:`More details <t3coreapi:ModifyLoginFormViewEvent>`
PasswordChangeEvent
===================
Event that contains information about the password which was set,
and is about to be stored in the database.
:ref:`More details <t3coreapi:PasswordChangeEvent>`
SendRecoveryEmailEvent
======================
Event that contains the email to be sent to the user when they request a
new password. :ref:`More details <t3coreapi:SendRecoveryEmailEvent>`
+199
View File
@@ -0,0 +1,199 @@
.. include:: /Includes.rst.txt
.. _examples:
========
Examples
========
In this section some common situations are described:
.. contents::
:local:
.. _login-and-back-to-original-page:
Send visitors to login page and redirect to original page
=========================================================
A common situation is that visitors who go to a page with access
restrictions should go to a login page first and after logging in
should be send back to the page they originally requested.
Assume we have a login page with id `2`.
Using TypoScript we can still display links to access restricted pages
and send visitors to the login page:
.. code-block:: typoscript
config {
typolinkLinkAccessRestrictedPages = 2
typolinkLinkAccessRestrictedPages_addParams = &return_url=###RETURN_URL###
}
On the login page the login form must be configured to redirect to the
original page:
.. code-block:: typoscript
plugin.tx_felogin_login.settings.redirectMode = getpost
(This option can also be set in the flexform configuration of the
felogin content element)
If visitors will directly enter the URL of an access restricted page
they will be sent to the first page in the rootline to which they have
access. Sending those direct visits to a login page is not a job of
the felogin plugin, but requires a custom page-not-found handler. In this sense,
we refer to :ref:`felogin-how-to-implement-403redirect-error-handler`.
.. _login-link-visibility:
Login link visible when not logged in and logout link visible when logged in
============================================================================
Again TypoScript will help you out. The page with the login form has
id=2:
.. code-block:: typoscript
10 = TEXT
10 {
value = Login
typolink.parameter = 2
}
[frontend.user.isLoggedIn]
10.value = Logout
10.typolink.additionalParams = &logintype=logout
[end]
Of course there can be solutions with :typoscript:`HMENU` items, etc.
.. _felogin-how-to-implement-403redirect-error-handler:
Custom error handler implementation for 403 redirects
=====================================================
This section explains how to utilize a custom error handler
to catch 403 restricted page errors and allow to forward
to a login form, and then redirect back to the originating
page after successful login.
.. rst-class:: bignums
#. You need the following site settings in the error handling
.. figure:: ../Images/felogin_site_settings_error_handling.png
:caption: Error Handling tab of site configuration module
:class: with-shadow
:guilabel:`Error Handling` tab of Site Configuration module
There you add the custom 403 error handler and configure
the error handler, you create in the following steps.
.. todo:: Future TYPO3 versions may do this automatically
see https://review.typo3.org/c/Packages/TYPO3.CMS/+/81945
.. seealso::
:ref:`Error handling in site configuration <t3coreapi:sitehandling-errorHandling>`
#. Look up the page ID where a login form (like with EXT:felogin) is placed
This page ID is needed in the following step, so that the error
handler will know, where to forward an unauthenticated user to, so
that a login can be performed.
Ideally, this should be done by configuring a page ID via the
site settings, and referring back to a named ID. See
:ref:`PHP API: accessing site configuration <t3coreapi:sitehandling-php-api>`
for more information. For reduced complexity, this example uses
a hard-coded page ID.
#. Create a new error handler :file:`RedirectLoginErrorHandler.php`
Create a PHP error handler class like the following in a custom
extension, like your own :ref:`sitepackage <t3sitepackage:start>`:
.. literalinclude:: _RedirectLoginErrorHandler.php
:caption: EXT:my_sitepackage/Classes/Error/PageErrorHandler/RedirectLoginErrorHandler.php
:language: php
Adapt the constant :php:`PAGE_ID_LOGIN_FORM` to match the
page ID from the previous step.
Since there is no proper way how to do it otherwise, we put in the page ID
of the login form hard-coded into the file :file:`RedirectLoginErrorHandler.php`
and define a constant :php:`PAGE_ID_LOGIN_FORM` for it. In the example
above, this is set to `656`.
#. In your EXT:felogin plugin, make sure you selected "Defined by GET/POST
Parameters" as first redirect mode
.. figure:: ../Images/SettingsRedirectCustomErrorHandler.png
:caption: Plugin > Redirects tab of Login Form content element
:class: with-shadow
:guilabel:`Plugin > Redirects` tab of :guilabel:`Login Form` content element
You need to configure the login form that receives your redirect in a
way, that allows to evaluate submitted URL parameters. In `EXT:felogin`,
this is achieved via this :guilabel:`Redirect Mode` (which can also be set
through TypoScript configuration, see :confval:`redirectMode <typoscript-redirectmode>`.
Your login form will probably also need to define a specific target page
for normal logins (independent from the error handler redirect), so you
should also add a `redirectMode` like `login` to your list, and set
a target page in :confval:`redirectPageLogin <typoscript-redirectpagelogin>`.
#. Testing the custom error handler
Clear the caches, for example via the backend module
:guilabel:`System > Maintenance`.
Then open any access-restricted page
in an incognito browser window to be sure that
you are not logged in yet. Here we will use the example
URL :samp:`https://example.org/restricted/page`.
When everything is configured correctly and if you are not logged in
yet, then you should be redirected to your login page like
:samp:`https://example.org/login` (example page ID `656`).
After entering proper frontend user credentials, you should be redirected
back to :samp:`https://example.org/restricted/page`, the page where you
wanted to get to initially.
.. hint::
When you have multiple site configurations, be sure to access
the correct one. This means where both the login form is located,
and the custom error handler is configured for.
.. hint::
Do not copy the generated link from the address URL after you clicked
:guilabel:`View webpage` from the backend, and then just paste it into
the URL bar of the incognito window. The reason is that when
being logged in to the backend, a possibly simulated frontend user
login can affect your tests.
.. hint::
Do not get confused when the URL
:samp:`https://example.org/restricted/page` will be forwarded to a URL
like
:samp:`https://example.org/login?return_url=https%3A%2F%2Fexample.org%3A8443%2Frestricted%2Fpage&cHash=d0e92f9f9f7b3ca98a2e5e688ad22de9`
when you want to access the restricted page in the first place.
These are the `getpost` redirect parameters that are evaluated by
`EXT:felogin`. Now type in the user credentials of the already created
frontend user and you should get redirected to the desired
page :samp:`https://example.org/restricted/page`.
This example was taken from
`[FEATURE] Introduce ErrorHandler for 403 errors with redirect option <https://review.typo3.org/c/Packages/TYPO3.CMS/+/81945>`__
which works in TYPO3 v11 and v12, and has been integrated to TYPO3 v13, where it can be used
without a custom implementation.
@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace MyVendor\MySitePackage\Error\PageErrorHandler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Controller\ErrorPageController;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
/**
* An error handler that redirects to a configured page, where the login
* process is handled. Passes a configurable URL parameter (`return_url` or
* `redirect_url`) to the target page.
*/
final class RedirectLoginErrorHandler implements PageErrorHandlerInterface
{
private const int PAGE_ID_LOGIN_FORM = 656;
private readonly int $loginRedirectPid;
private readonly string $loginRedirectParameter;
private readonly Context $context;
private readonly LinkService $linkService;
private readonly ErrorPageController $errorPageController;
public function __construct(private readonly int $statusCode)
{
$configuration = [
// TODO: Replace with $siteSettings[...] or something else
'loginRedirectTarget' => 't3://page?uid=' . self::PAGE_ID_LOGIN_FORM,
'loginRedirectParameter' => 'return_url',
];
$this->context = GeneralUtility::makeInstance(Context::class);
$this->linkService = GeneralUtility::makeInstance(LinkService::class);
$this->errorPageController = GeneralUtility::makeInstance(ErrorPageController::class);
$urlParams = $this->linkService->resolve($configuration['loginRedirectTarget']);
$this->loginRedirectPid = (int)($urlParams['pageuid'] ?? 0);
$this->loginRedirectParameter = $configuration['loginRedirectParameter'];
}
public function handlePageError(
ServerRequestInterface $request,
string $message,
array $reasons = []
): ResponseInterface {
$this->checkHandlerConfiguration();
if ($this->shouldHandleRequest($reasons)) {
return $this->handleLoginRedirect($request);
}
// Show general error message with a 403 HTTP status code
return $this->getGenericAccessDeniedResponse($message);
}
private function getGenericAccessDeniedResponse(string $reason): ResponseInterface
{
$reason = $reason ? ' Reason: ' . $reason : '';
$content = $this->errorPageController->errorAction(
'Page Not Found',
sprintf('The page did not exist or was inaccessible.%s', $reason),
0,
$this->statusCode,
);
return new HtmlResponse($content, $this->statusCode);
}
private function handleLoginRedirect(ServerRequestInterface $request): ResponseInterface
{
if ($this->isLoggedIn()) {
return $this->getGenericAccessDeniedResponse(
'The requested page was not accessible with the provided credentials'
);
}
/** @var Site $site */
$site = $request->getAttribute('site');
$language = $request->getAttribute('language');
$loginUrl = $site->getRouter()->generateUri(
$this->loginRedirectPid,
[
'_language' => $language,
$this->loginRedirectParameter => (string)$request->getUri(),
]
);
return new RedirectResponse($loginUrl);
}
private function shouldHandleRequest(array $reasons): bool
{
if (!isset($reasons['code'])) {
return false;
}
$accessDeniedReasons = [
PageAccessFailureReasons::ACCESS_DENIED_PAGE_NOT_RESOLVED,
PageAccessFailureReasons::ACCESS_DENIED_SUBSECTION_NOT_RESOLVED,
];
$isAccessDenied = in_array($reasons['code'], $accessDeniedReasons, true);
return $isAccessDenied || $this->isSimulatedBackendGroup();
}
private function isLoggedIn(): bool
{
if ($this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn')) {
return true;
}
return $this->isSimulatedBackendGroup();
}
private function isSimulatedBackendGroup(): bool
{
if (!$this->context->getPropertyFromAspect('backend.user', 'isLoggedIn')) {
return false;
}
// look for special "any group"
$groups = $this->context->getPropertyFromAspect('frontend.user', 'groupIds');
return $groups[1] === -2;
}
private function checkHandlerConfiguration(): void
{
if ($this->loginRedirectPid === 0) {
throw new \RuntimeException('No loginRedirectTarget configured for LoginRedirect errorhandler', 1700813537);
}
if ($this->statusCode !== 403) {
throw new \RuntimeException(sprintf('Invalid HTTP status code %d for LoginRedirect errorhandler', $this->statusCode), 1700813545);
}
}
}
+31
View File
@@ -0,0 +1,31 @@
.. include:: /Includes.rst.txt
.. _get-post-paremeters:
=======================
GET and POST parameters
=======================
The extension uses several GET and POST parameters to define or override
redirect settings.
.. _noredirect:
noredirect
----------
.. container:: table-row
Parameter
noredirect
Evaluation
GET and POST
Data type
string
Description
If set to :php:`1`, no redirect will be processed after a successful
login.
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

+1
View File
@@ -0,0 +1 @@
.. You can put central messages to display on all pages here
+57
View File
@@ -0,0 +1,57 @@
.. include:: /Includes.rst.txt
====================
TYPO3 Frontend Login
====================
:Extension key:
felogin
:Package name:
typo3/cms-felogin
:Version:
|release|
:Language:
en
:Author:
TYPO3 contributors
:License:
This document is published under the
`Open Content License <https://www.openhub.net/licenses/opl>`__.
:Rendered:
|today|
----
This extension provides a template-based plugin that allows website users to log
in to the TYPO3 frontend.
----
**Table of Contents:**
.. toctree::
:maxdepth: 2
:titlesonly:
Introduction/Index
Installation/Index
UsersManual/Index
LoginMechanism/Index
Configuration/Index
GetPostParameters/Index
Events/Index
Examples/Index
KnownProblems/Index
.. Meta Menu
.. toctree::
:hidden:
Sitemap
+57
View File
@@ -0,0 +1,57 @@
.. include:: /Includes.rst.txt
.. _installation:
============
Installation
============
This extension is part of the TYPO3 Core, but not installed by default.
.. contents:: Table of contents
:local:
Installation with Composer
==========================
Check whether you are already using the extension with:
.. code-block:: bash
composer show | grep felogin
This should either give you no result or something similar to:
.. code-block:: none
typo3/cms-felogin v12.4.11
If it is not installed yet, use the ``composer require`` command to install
the extension:
.. code-block:: bash
composer require typo3/cms-felogin
The given version depends on the version of the TYPO3 Core you are using.
Installation without Composer
=============================
In an installation without Composer, the extension is already shipped but might
not be activated yet. Activate it as follows:
#. In the backend, navigate to the :guilabel:`System > Extensions`
module.
#. Click the :guilabel:`Activate` icon for the Frontend Login extension.
.. figure:: /Images/InstallActivate.png
:class: with-border
:alt: Extension manager showing Frontend Login extension
Extension manager showing Frontend Login extension
Next steps
==========
:ref:`Configure the Frontend Login <configuration>`.
+60
View File
@@ -0,0 +1,60 @@
.. include:: /Includes.rst.txt
.. _introduction:
============
Introduction
============
.. _what-does-it-do:
What does it do?
================
The Frontend Login for Website Users (felogin) extension is a general
purpose extension for frontend logins. In addition to the actual login
box, it includes several methods for redirecting after login/logout
and includes forgot password functionality.
.. _screenshots:
Screenshots
===========
.. _general-settings:
General Settings
----------------
.. figure:: ../Images/GeneralSettings.png
:alt: General Settings
The plugin's general settings
.. _redirect-configuration:
Redirect Configuration
----------------------
.. figure:: ../Images/RedirectConfiguration.png
:alt: Redirect Configuration
Configuration of the redirection options
.. hint::
Be sure that in the overall `Access` tab under `User Group Access rights` the content
element and even the page itself is not set to `Hide at login`, otherwise the redirect
to the given page will not work.
.. _messages-tab:
Messages Tab
------------
.. figure:: ../Images/MessagesConfiguration.png
:alt: Messages Configuration
Configuration of the various messages (screenshot shows not all options)
+20
View File
@@ -0,0 +1,20 @@
.. include:: /Includes.rst.txt
.. _known-problems:
==============
Known Problems
==============
- If there is more than one felogin plugin on a page the password
recovery option can cause problems. This is a general problem with
plugins, but in this case the cause is a small hash in the forgot
password form which is stored in the frontend user session data.
With multiple instances on a page only one of the hashes is
stored and only one of the forgot password forms will work. Make sure
there is only one felogin plugin on the page where the password
recovery form is displayed.
- If usergroup access rights of the plugin are defined to
:guilabel:`Hide at login`, all felogin code (e.g. redirects, PSR-14 events)
will not be executed after a user successfully logged in.
@@ -0,0 +1,19 @@
.. include:: /Includes.rst.txt
.. _display:
==================
What is displayed?
==================
If there is no frontend user logged in, the login form will be
shown.
If there is a logged in frontend user, the logout form is shown.
If the forgot password link was used, the form to reset a password
based on username or email address will be shown.
If the password reset link was followed from an email, the form to
change the password will be shown.
+22
View File
@@ -0,0 +1,22 @@
.. include:: /Includes.rst.txt
.. _login-mechanism:
===============
Login mechanism
===============
In order to properly use the felogin plugin and its advanced
capabilities (such as redirect options) it is important to understand
the mechanism of frontend user login in TYPO3 CMS.
.. toctree::
:maxdepth: 5
:titlesonly:
:glob:
Display/Index
LoginProcess/Index
RedirectModes/Index
@@ -0,0 +1,22 @@
.. include:: /Includes.rst.txt
.. _login-process:
=================
The login process
=================
After the form is submitted the TYPO3 CMS authentication services will
validate the login credentials. After this process felogin will handle
the rest. This means that the felogin plugin must be visible for the
user who has logged in.
Felogin will then check any redirect options and generate the
appropriate content.
.. caution::
- Do not use the login status of a frontend user as authorization,
but **always** rely on user groups.
- Only use different storage folders for frontend users if this is really
necessary due to organizational reasons.
@@ -0,0 +1,103 @@
.. include:: /Includes.rst.txt
.. _redirect-modes:
==============
Redirect Modes
==============
The following redirect options are supported.
.. _defined-by-usergroup-record:
Defined by Usergroup Record
===========================
Within a Website usergroup record, you can specify a page where
usergroup members will be redirected after login.
.. _defined-by-user-record:
Defined by User Record
======================
This is identical to the redirection option for "defined by Usergroup
Record" but applies to a single website user instead of an entire user
group.
.. _after-login-ts-or-flexform:
After Login (TS or Flexform)
============================
This redirect page is set either in TypoScript
(:typoscript:`plugin.tx_felogin_login.settings.redirectPageLogin`) or in the
FlexForm of the felogin plugin.
.. _after-logout-ts-or-flexform:
After Logout (TS or Flexform)
=============================
Defines the redirect page after a user has logged out. Again, it can
be set in TypoScript or in the felogin plugin's FlexForm.
.. _after-login-error-ts-of-flexform:
After Login Error (TS of Flexform)
==================================
Defines the redirect page after a login error occurs. Can be set in
TypoScript or in the felogin plugin's FlexForm.
.. _defined-by-get-post-vars:
Defined by GET/POST Parameters
==============================
Redirect the visitor based on the GET/POST parameters :code:`redirect_url`.
If the TypoScript configuration
:typoscript:`config.typolinkLinkAccessRestrictedPages` is set, the GET/POST
parameter :code:`redirect_url` is used.
Example URL:
.. code-block:: text
https://example.org/index.php?id=12&redirect_url=https%3A%2F%2Fexample%2Eorg%2Fdestiny%2F
.. _defined-by-referrer:
Defined by Referrer
===================
The referrer page is used for the redirect. This basically means that
the user is sent back to the page he originally came from.
.. _defined-by-domain-entries:
Defined by Domain entries
=========================
Same as :guilabel:`Defined by Referrer`, except that only the domains listed in
:typoscript:`plugin.tx_felogin_login.domains` are allowed. If someone is sent to the
login page coming from a domain which is not listed, the redirect will
not happen.
By using the option :guilabel:`Use First Supported Mode from Selection` you can
define several fallback methods.
.. note::
It is only possible to use domains, which are known to TYPO3. This means,
that domains must be configured as :code:`base` in site settings for websites
in the current TYPO3 instance.
+9
View File
@@ -0,0 +1,9 @@
:template: sitemap.html
.. include:: /Includes.rst.txt
=======
Sitemap
=======
.. The sitemap.html template will insert here the page tree automatically.
+58
View File
@@ -0,0 +1,58 @@
.. include:: /Includes.rst.txt
.. _users-manual:
============
Users manual
============
The felogin extension requires no special configuration. All options
are available in the plugin's FlexForm as shown in the :ref:`screenshots`.
.. _using-plugin:
Using the plugin
================
The felogin plugin is available through the Content Wizard as :guilabel:`Login Form`:
.. figure:: ../Images/ContentElementWizard.png
:alt: The content element wizard
The Login Form plugin in the content element wizard
.. _storage-folder:
Choosing a user storage page for website users
==============================================
In order for Website Users to be able to log in, the "Frontend login" plugin
must know where the records are stored. There are two possibilities
for setting this storage folder:
The site's integrator may have set a default value for the
:confval:`User Storage Page <felogin-felogin-pid>` or using the
:ref:`settings-editor`. If you use the default
folder to store frontend users in your project there is nothing to do here.
If your project needs multiple storage folders for frontend users or
if there is no default storage folder set, see :ref:`Example: Override the
default storage page in the plugin's FlexForm <configuration-examples-flexform>`.
.. _access-restrictions:
Access restrictions on the felogin plugin
=========================================
A very common issue is, that the felogin plugin is set to Access:
:guilabel:`Hide at login`. After the core has processed the login request, the
page will be rendered without the felogin plugin. If there are redirect options
active they will **not be executed**, simply because the felogin plugin is
hidden.
Of course setting the felogin plugin to :guilabel:`Hide at login` and having
redirect options together doesn't really makes sense.
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<guides xmlns="https://www.phpdoc.org/guides" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.phpdoc.org/guides ../vendor/phpdocumentor/guides-cli/resources/schema/guides.xsd"
links-are-relative="true">
<extension class="\T3Docs\Typo3DocsTheme\DependencyInjection\Typo3DocsThemeExtension"
project-home="https://extensions.typo3.org/extension/felogin/"
project-contact="https://typo3.slack.com/archives/C025BQLFA"
project-repository="https://github.com/typo3/typo3"
project-issues="https://forge.typo3.org/projects/typo3cms-core/issues"
edit-on-github-branch="main"
edit-on-github="typo3/typo3"
edit-on-github-directory="typo3/sysext/felogin/Documentation/"
typo3-core-preferred="main"
interlink-shortcode="typo3/cms-felogin"
/>
<project title="Frontend Login"
release="main (development)"
version="main (development)"
copyright="since 2008 by the TYPO3 contributors"
/>
</guides>
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+11
View File
@@ -0,0 +1,11 @@
===========================
TYPO3 extension ``felogin``
===========================
This extension provides a template-based plugin that allows website users to log
in to the TYPO3 frontend.
:Repository: https://github.com/typo3/typo3
:Issues: https://forge.typo3.org/
:Read online: https://docs.typo3.org/c/typo3/cms-felogin/main/en-us/
:Packagist: https://packagist.org/packages/typo3/cms-felogin
@@ -0,0 +1,16 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:layout name="SystemEmail" />
<f:section name="Main">
<f:spaceless>
<f:variable name="recoveryLink">
<f:link.external target="_blank" uri="{url}"><f:translate domain="felogin.messages" key="password_recovery_link"/></f:link.external>
</f:variable>
{f:translate(
domain: 'felogin.messages',
key: 'forgot_validate_reset_password_html',
arguments: '{ 0: "{receiverName -> f:format.htmlspecialchars()}", 1: recoveryLink, 2: validUntil }'
) -> f:format.html()}
</f:spaceless>
</f:section>
</html>
@@ -0,0 +1,9 @@
<f:layout name="SystemEmail" />
<f:section name="Main">
{f:translate(
domain: 'felogin.messages',
key: 'forgot_validate_reset_password_plaintext',
arguments: {0: receiverName, 1: url, 2: validUntil}
) -> f:format.raw()}
</f:section>
+167
View File
@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="EXT:felogin/Resources/Private/Language/Database.xlf" date="2011-10-17T20:22:32Z" product-name="felogin">
<header/>
<body>
<trans-unit id="tt_content.CType_pi1">
<source>Website User Login</source>
</trans-unit>
<trans-unit id="tt_content.CType.felogin_login.title">
<source>Login Form</source>
</trans-unit>
<trans-unit id="tt_content.CType.felogin_login.description">
<source>Login/logout form used to password protect pages allowing only authorised website users and groups access.</source>
</trans-unit>
<trans-unit id="felogin_redirectPid">
<source>Redirect at Login to Page (felogin)</source>
</trans-unit>
<trans-unit id="felogin_forgotHash">
<source>Forgot hash</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.general_header">
<source>General Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.general_message">
<source>General Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirect_header">
<source>Redirect Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirect_message">
<source>Redirect Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.welcome_header">
<source>Welcome Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.welcome_message">
<source>Welcome Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.success_header">
<source>Login Success Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.success_message">
<source>Login Success Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.error_header">
<source>Login Error Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.error_message">
<source>Login Error Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.status_header">
<source>Status Display Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.status_message">
<source>Status Display Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.logout_header">
<source>Logout Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.logout_message">
<source>Logout Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.forgot_header">
<source>Forgot Password Header</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.forgot_message">
<source>Forgot Password Message</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.sheet_general">
<source>General</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.sheet_redirect">
<source>Redirects</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.sheet_messages">
<source>Messages</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.show_forgot_password">
<source>Display Password Recovery Link</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.show_permalogin">
<source>Display Remember Login Option</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.show_logoutFormAfterLogin">
<source>Disable redirect after successful login, but display logout-form</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelectmode">
<source>FE group select mode:</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelectmode_showAll">
<source>Show all</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelectmode_showSelected">
<source>Show selected</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelectmode_DontShowSelected">
<source>Don't show selected</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelectmode_FromTS">
<source>(from Typoscript)</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelection">
<source>FE group selection:</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.groupSelection_noGroup">
<source>no group</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.field_manualOrder">
<source>Using fieldlists below:</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.field_orderList">
<source>User Fields/list:</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.field_orderDetails">
<source>User Fields/details:</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode">
<source>Redirect Mode</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.0">
<source>Defined by Usergroup Record</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.1">
<source>Defined by User Record</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.2">
<source>After Login (TS or Flexform)</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.3">
<source>After Logout (TS or Flexform)</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.4">
<source>After Login Error (TS or Flexform)</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.5">
<source>Defined by GET/POST Parameters</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.6">
<source>Defined by Referrer</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectMode.I.7">
<source>Defined by Domain Entries</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectFirstMethod">
<source>Use First Supported Mode from Selection</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectDisable">
<source>Disable Redirect</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectPageLogin">
<source>After Successful Login Redirect to Page</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectPageLoginError">
<source>After Failed Login Redirect to Page</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.redirectPageLogout">
<source>After Logout Redirect to Page</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.template_file">
<source>Template File</source>
</trans-unit>
<trans-unit id="tt_content.pi_flexform.user_storage">
<source>User Storage Page</source>
</trans-unit>
</body>
</file>
</xliff>
+172
View File
@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" datatype="plaintext" original="EXT:felogin/Resources/Private/Language/locallang.xlf" date="2011-10-17T20:22:32Z" product-name="felogin">
<header/>
<body>
<trans-unit id="login_user_info">
<source>You are now logged in as '%s'</source>
</trans-unit>
<trans-unit id="welcome_header">
<source>User login</source>
</trans-unit>
<trans-unit id="welcome_message">
<source>Enter your username and password here in order to log in on the website</source>
</trans-unit>
<trans-unit id="logout_header">
<source>You have logged out.</source>
</trans-unit>
<trans-unit id="logout_message">
<source>You just logged out from your user session on this website. You can login again or as another user by the form below.</source>
</trans-unit>
<trans-unit id="error_header">
<source>Login failure</source>
</trans-unit>
<trans-unit id="error_message" xml:space="preserve">
<source>An error occurred during login. Most likely you didn't enter the username or password correctly.
Be certain that you enter them precisely as they are, including upper/lower case.
Another possibility is that cookies might be disabled in your web browser.</source>
</trans-unit>
<trans-unit id="success_header">
<source>Login successful</source>
</trans-unit>
<trans-unit id="success_message">
<source>You are now logged in as '###USER###'</source>
</trans-unit>
<trans-unit id="status_header">
<source>Current status</source>
</trans-unit>
<trans-unit id="status_message">
<source>This is your current status</source>
</trans-unit>
<trans-unit id="username">
<source>Username</source>
</trans-unit>
<trans-unit id="password">
<source>Password</source>
</trans-unit>
<trans-unit id="login">
<source>Login</source>
</trans-unit>
<trans-unit id="permalogin">
<source>Stay logged in</source>
</trans-unit>
<trans-unit id="logout">
<source>Logout</source>
</trans-unit>
<trans-unit id="send_password">
<source>Send password</source>
</trans-unit>
<trans-unit id="reset_password">
<source>Reset Password</source>
</trans-unit>
<trans-unit id="change_password_header">
<source>Change your password</source>
</trans-unit>
<trans-unit id="change_password_description">
<source>Please enter your new password twice.</source>
</trans-unit>
<trans-unit id="password_requirement">
<source>Ensure that your new password matches the following requirements:</source>
</trans-unit>
<trans-unit id="change_password_nolinkprefix_message">
<source>Error: there is no prefix for the link. Please set one of the following in your typoscript: plugin.tx_felogin_pi1.feloginBaseURL = http://yourdomain/, config.baseURL = http://yourdomain/, config.absRefPrefix = /</source>
</trans-unit>
<trans-unit id="change_password_notvalid_message">
<source>The link you clicked is not valid. Please repeat the forgot password procedure.</source>
</trans-unit>
<trans-unit id="change_password_notequal_message">
<source>The passwords are not equal, please enter your new password twice. Password needs a minimum length of %s chars.</source>
</trans-unit>
<trans-unit id="change_password_tooshort_message">
<source>The password length is too short. Please enter your new password twice. Password needs a minimum length of %s chars.</source>
</trans-unit>
<trans-unit id="change_password_done_message">
<source>Your password has been saved. You can now login with your new password.</source>
</trans-unit>
<trans-unit id="change_password">
<source>Change your password</source>
</trans-unit>
<trans-unit id="newpassword_label1">
<source>Enter new password</source>
</trans-unit>
<trans-unit id="newpassword_label2">
<source>Repeat new password</source>
</trans-unit>
<trans-unit id="your_email">
<source>Your email</source>
</trans-unit>
<trans-unit id="forgot_header">
<source>Forgot your password?</source>
</trans-unit>
<trans-unit id="forgot_email_password" xml:space="preserve">
<source>Your password
Hi %s
Your username is "%s"
Your password is "%s"</source>
</trans-unit>
<trans-unit id="forgot_email_nopassword" xml:space="preserve">
<source>Your password
Hi %s
We couldn't find a username for this email address and so cannot send the password to you. Probably you misspelled the email address (upper/lower case makes a difference) or maybe you even didn't register yet?</source>
</trans-unit>
<trans-unit id="forgot_validate_reset_password" xml:space="preserve">
<source>Your new password
Dear %s,
This email was sent in response to your request to reset your password. Please click on the link below.
%s
For security reasons, this link is only active until %s. If you do not visit the link before then, you will need to repeat the password reset steps.</source>
</trans-unit>
<trans-unit id="forgot_message">
<source>Please enter the email address by which you registered your user account. Then press "Send password" and your password will immediately be emailed to you. Make sure to spell your email address correctly.</source>
</trans-unit>
<trans-unit id="forgot_message_emailSent">
<source>Your password has now been sent to the email address %s</source>
</trans-unit>
<trans-unit id="forgot_reset_message">
<source>Please enter your username or email address. Instructions for resetting the password will be immediately emailed to you.</source>
</trans-unit>
<trans-unit id="forgot_reset_message_emailSent">
<source>An email has been sent to the address stored in your account and contains a link to reset your password. If you do not receive an email, your account or email address was not found.</source>
</trans-unit>
<trans-unit id="forgot_header_backToLogin">
<source>Return to login form</source>
</trans-unit>
<trans-unit id="enter_your_data">
<source>Username or email address</source>
</trans-unit>
<trans-unit id="password_recovery_mail_header">
<source>Your new password</source>
</trans-unit>
<trans-unit id="password_recovery_link">
<source>Password recovery link</source>
</trans-unit>
<trans-unit id="password_recovery_link_expired">
<source>Your password recovery link is expired.</source>
</trans-unit>
<trans-unit id="empty_password_and_password_repeat">
<source>New password and new password repeat cannot be empty</source>
</trans-unit>
<trans-unit id="password_must_match_repeated">
<source>New Password must match repeated password.</source>
</trans-unit>
<trans-unit id="forgot_validate_reset_password_html" xml:space="preserve">
<source>&lt;p&gt;Dear %s,&lt;/p&gt;
&lt;p&gt;This email was sent in response to your request to reset your password. Please click on the link below.&lt;/p&gt;
&lt;p&gt;%s&lt;/p&gt;
&lt;p&gt;For security reasons, this link is only active until %s. If you do not visit the link before then, you will need to repeat the password reset steps.&lt;/p&gt;</source>
</trans-unit>
<trans-unit id="forgot_validate_reset_password_plaintext" xml:space="preserve">
<source>Dear %s,
This email was sent in response to your request to reset your password. Please click on the link below.
%s
For security reasons, this link is only active until %s. If you do not visit the link before then, you will need to repeat the password reset steps.</source>
</trans-unit>
</body>
</file>
</xliff>
@@ -0,0 +1,12 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:spaceless>
<f:if condition="{settings.{key}}">
<f:then>
{settings.{key}}
</f:then>
<f:else>
<f:translate domain="felogin.messages" key="{key}"/>
</f:else>
</f:if>
</f:spaceless>
</html>
@@ -0,0 +1,17 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:form.validationResults>
<f:if condition="{validationResults.flattenedErrors}">
<ul>
<f:for each="{validationResults.flattenedErrors}" as="errors" key="propertyPath">
<li>{propertyPath}
<ul>
<f:for each="{errors}" as="error">
<li>{error.code}: {error}</li>
</f:for>
</ul>
</li>
</f:for>
</ul>
</f:if>
</f:form.validationResults>
</html>
@@ -0,0 +1,81 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:flashMessages/>
<f:if condition="{messageKey}">
<h3>
<f:render partial="RenderLabelOrMessage" arguments="{key: '{messageKey}_header'}"/>
</h3>
<p>
<f:render partial="RenderLabelOrMessage" arguments="{key: '{messageKey}_message'}"/>
</p>
</f:if>
<f:form target="_top" fieldNamePrefix="" action="login" requestToken="{requestToken}" spellcheck="false">
<f:render section="content" arguments="{_all}"/>
</f:form>
<f:if condition="{settings.showForgotPassword}">
<f:link.action action="recovery" controller="PasswordRecovery">
<f:render partial="RenderLabelOrMessage" arguments="{key: 'forgot_header'}"/>
</f:link.action>
</f:if>
<f:section name="content">
<fieldset>
<legend>
<f:translate domain="felogin.messages" key="login"/>
</legend>
<div>
<label for="tx-felogin-input-username">
<f:translate domain="felogin.messages" key="username"/>
</label>
<f:form.textfield name="user" required="true" autocomplete="username" id="tx-felogin-input-username"/>
</div>
<div>
<label for="tx-felogin-input-password">
<f:translate domain="felogin.messages" key="password"/>
</label>
<f:form.password name="pass" required="required" autocomplete="current-password" id="tx-felogin-input-password"/>
</div>
<f:if condition="{permaloginStatus} > -1">
<div>
<label for="permalogin">
<f:translate domain="felogin.messages" key="permalogin"/>
</label>
<f:if condition="{permaloginStatus} == 1">
<f:then>
<f:form.hidden name="permalogin" value="0" disabled="disabled"/>
<f:form.checkbox name="permalogin" id="permalogin" value="1" checked="checked"/>
</f:then>
<f:else>
<f:form.hidden name="permalogin" value="0"/>
<f:form.checkbox name="permalogin" id="permalogin" value="1"/>
</f:else>
</f:if>
</div>
</f:if>
<div>
<f:form.submit name="submit" value="{f:translate(domain: 'felogin.messages', key: 'login')}"/>
</div>
<div class="felogin-hidden">
<f:form.hidden name="logintype" value="login"/>
<f:if condition="{redirectURL}!=''">
<f:form.hidden name="redirect_url" value="{redirectURL}" />
</f:if>
<f:if condition="{referer}!=''">
<f:form.hidden name="referer" value="{referer}" />
</f:if>
<f:if condition="{redirectReferrer}!=''">
<f:form.hidden name="redirectReferrer" value="off" />
</f:if>
<f:if condition="{noRedirect}!=''">
<f:form.hidden name="noredirect" value="1" />
</f:if>
</div>
</fieldset>
</f:section>
</html>
@@ -0,0 +1,33 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<h3>
<f:render partial="RenderLabelOrMessage" arguments="{key: 'status_header'}"/>
</h3>
<p>
<f:render partial="RenderLabelOrMessage" arguments="{key: 'status_message'}"/>
</p>
<f:form action="login" target="_top" fieldNamePrefix="">
<fieldset>
<legend>
<f:translate domain="felogin.messages" key="logout"/>
</legend>
<div>
<label for="tx-felogin-input-logout">
<f:translate domain="felogin.messages" key="username"/>
</label>
{user.username}
</div>
<div>
<f:form.submit id="tx-felogin-input-logout" name="submit" value="{f:translate(domain: 'felogin.messages', key: 'logout')}"/>
</div>
<div class="felogin-hidden">
<f:form.hidden name="logintype" value="logout"/>
<f:if condition="{noRedirect}!=''">
<f:form.hidden name="noredirect" value="1" />
</f:if>
</div>
</fieldset>
</f:form>
</html>
@@ -0,0 +1,10 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:if condition="{showLoginMessage}">
<h3>
<f:render partial="RenderLabelOrMessage" arguments="{key: 'success_header'}"/>
</h3>
</f:if>
<f:translate arguments="{0: '{user.username}'}" domain="felogin.messages" key="login_user_info"/>
</html>
@@ -0,0 +1,33 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<h3>
<f:render partial="RenderLabelOrMessage" arguments="{key: 'forgot_header'}"/>
</h3>
<p>
<f:render partial="RenderLabelOrMessage" arguments="{key: 'forgot_reset_message'}"/>
</p>
<f:render partial="ValidationErrors"/>
<f:form action="recovery" method="post">
<fieldset>
<legend>
<f:render partial="RenderLabelOrMessage" arguments="{key: 'reset_password'}"/>
</legend>
<div>
<label for="tx-felogin-input-data">
<f:translate domain="felogin.messages" key="enter_your_data"/>
</label>
<f:form.textfield name="userIdentifier" id="tx-felogin-input-data"/>
</div>
<div>
<f:form.submit value="{f:translate(domain: 'felogin.messages', key: 'reset_password')}"/>
</div>
</fieldset>
</f:form>
<p>
<f:link.action action="login" controller="Login">
<f:translate domain="felogin.messages" key="forgot_header_backToLogin"/>
</f:link.action>
</p>
</html>
@@ -0,0 +1,51 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<h3>
<f:translate domain="felogin.messages" key="change_password_header"/>
</h3>
<p>
<f:if condition="{passwordRequirements}">
<f:translate domain="felogin.messages" key="change_password_description" />
</f:if>
</p>
<f:render partial="ValidationErrors"/>
<f:form action="changePassword" method="post" spellcheck="false">
<fieldset>
<legend>
<f:translate domain="felogin.messages" key="change_password"/>
</legend>
<div>
<label for="tx-felogin-input-newpassword-1">
<f:translate domain="felogin.messages" key="newpassword_label1"/>
</label>
<f:form.password name="newPass" required="required" autocomplete="new-password" id="tx-felogin-input-newpassword-1"/>
</div>
<div>
<label for="tx-felogin-input-newpassword-2">
<f:translate domain="felogin.messages" key="newpassword_label2"/>
</label>
<f:form.password name="newPassRepeat" required="required" autocomplete="new-password" id="tx-felogin-input-newpassword-2"/>
</div>
<f:form.hidden name="hash" value="{hash}"/>
<div>
<f:form.submit value="{f:translate(domain: 'felogin.messages', key: 'change_password')}"/>
</div>
</fieldset>
</f:form>
<f:if condition="{passwordRequirements}">
<p><f:translate domain="felogin.messages" key="password_requirement"/></p>
<ul>
<f:for each="{passwordRequirements}" as="passwordRequirement">
<li>{passwordRequirement}</li>
</f:for>
</ul>
</f:if>
<p>
<f:link.action action="login" controller="Login">
<f:translate domain="felogin.messages" key="forgot_header_backToLogin"/>
</f:link.action>
</p>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

+56
View File
@@ -0,0 +1,56 @@
{
"name": "typo3/cms-felogin",
"type": "typo3-cms-framework",
"description": "TYPO3 CMS Frontend Login - A template-based plugin to log in website users in the TYPO3 frontend.",
"homepage": "https://typo3.community/",
"funding": [
{
"type": "membership",
"url": "https://typo3.org/membership"
}
],
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "TYPO3 Core Team",
"email": "typo3cms@typo3.org",
"role": "Developer"
}
],
"support": {
"issues": "https://forge.typo3.org/issues/",
"forum": "https://talk.typo3.org/",
"source": "https://github.com/TYPO3/typo3/",
"docs": "https://docs.typo3.org/c/typo3/cms-felogin/main/en-us/",
"rss": "https://news.typo3.com/rss/",
"chat": "https://typo3.community/meet/slack/",
"security": "https://typo3.org/security/"
},
"config": {
"sort-packages": true
},
"require": {
"typo3/cms-core": "15.0.*@dev"
},
"conflict": {
"typo3/cms": "*"
},
"extra": {
"branch-alias": {
"dev-main": "15.0.x-dev"
},
"typo3/cms": {
"Package": {
"partOfFactoryDefault": true
},
"extension-key": "felogin"
}
},
"autoload": {
"psr-4": {
"TYPO3\\CMS\\FrontendLogin\\": "Classes/"
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
use TYPO3\CMS\FrontendLogin\Controller\LoginController;
use TYPO3\CMS\FrontendLogin\Controller\PasswordRecoveryController;
defined('TYPO3') or die();
// Add default TypoScript
ExtensionManagementUtility::addTypoScriptConstants(
"@import 'EXT:felogin/Configuration/TypoScript/constants.typoscript'",
false
);
ExtensionManagementUtility::addTypoScriptSetup(
"@import 'EXT:felogin/Configuration/TypoScript/setup.typoscript'",
false
);
ExtensionUtility::configurePlugin(
'Felogin',
'Login',
[
LoginController::class => ['login', 'overview'],
PasswordRecoveryController::class => ['recovery', 'showChangePassword', 'changePassword'],
],
[
LoginController::class => ['login', 'overview'],
PasswordRecoveryController::class => ['recovery', 'showChangePassword', 'changePassword'],
],
);
+5
View File
@@ -0,0 +1,5 @@
CREATE TABLE fe_users (
# type=passthrough needs manual configuration
felogin_forgotHash varchar(160) default '' ,
KEY felogin_forgotHash (felogin_forgotHash)
);