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
+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;
}
}