Commit f179a57f authored by Benjamin Franzke's avatar Benjamin Franzke Committed by Benni Mack

[TASK] Propagate "immediate" responses through the middleware stack

When a controller finds some pre-requisites of the called
action are not fulfilled, it can create an 'early' response
and throw it as ImmediateResponseException. This exception
bypasses the middleware stack, and is caught in the Application
to emit the response. This is a big win in contrast to
die/exit calls.

Some middlewares however expect to be always called when the
response bubbles up. Good examples are 'locking' middlewares:
They set up a lock when called, dispatch to inner middlewares,
and tear town the lock when a response is retrieved.

The tear down code of those middlewares is bypassed when
a controller throws an ImmediateResponseException.

The patch introduces a second exception: PropagateResponseException.
This one is caught by a new very inner middleware positioned at the
end of the middleware stack, just before the request is dispatched
to some controller. It then sends the response up the outer
middlewares. This allows middlewares like a 'locking' middleware
to do it's job without being bypassed, and at the same time
allows a controller to bypass any further local processing by
throwing such a response exception.

ImmediateResponseException exists as before and can still be used
to directly bypass the middleware stack and is kept as safety net
in case a PropagateResponseException is thrown within a middleware.
PropagateResponseException therefore extends ImmediateResponseException.

It's however discouraged to throw ImmediateResponseException from
within controllers - they require knowledge on what middlewares
do in their 'tear down' part and there shouldn't be a reason to
bypass them.

Middleware/BackendUserAuthenticator is adapted to properly
handle ImmediateResponseException that would have been thrown
BackendUserAuthentication::backendCheckLogin(). BackendUserAuthentication
is therefore refactored to allow to call the backend login
initialization without (duplicate) login check.
This allows to propagate redirect/maintenance responses.

Releases: master
Resolves: #93007
Change-Id: I291d9d532e7fa289b803e5eef38b23402e57e8ba
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67042Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent a97accab
......@@ -22,7 +22,6 @@ ignoreFiles+="sysext/core/Classes/Database/Driver/PDOStatement.php"
ignoreFiles+="sysext/core/Classes/Database/Driver/PDOStatementImplementation.php"
ignoreFiles+="sysext/core/Classes/Database/Driver/PDOConnection.php"
ignoreFiles+="sysext/frontend/Classes/Typolink/PageLinkBuilder.php"
ignoreFiles+="sysext/backend/Classes/Middleware/BackendUserAuthenticator.php"
foundNewFile=0
oldFilename=""
......
......@@ -36,8 +36,8 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Http\NormalizedParams;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Information\Typo3Information;
use TYPO3\CMS\Core\Localization\LanguageService;
......@@ -651,7 +651,7 @@ class LoginController implements LoggerAwareInterface
*/
protected function redirectToUrl(): void
{
throw new ImmediateResponseException(new RedirectResponse($this->redirectToURL, 303), 1607271511);
throw new PropagateResponseException(new RedirectResponse($this->redirectToURL, 303), 1607271511);
}
/**
......
......@@ -23,9 +23,11 @@ use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Backend\Routing\Route;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Controller\ErrorPageController;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Session\UserSessionManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -73,29 +75,31 @@ class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAut
$GLOBALS['BE_USER']->start();
// Register the backend user as aspect and initializing workspace once for TSconfig conditions
$this->setBackendUserAspect($GLOBALS['BE_USER'], (int)$GLOBALS['BE_USER']->user['workspace_id']);
if ($this->isLoggedInBackendUserRequired($route) && !$this->context->getAspect('backend.user')->isLoggedIn()) {
$uri = GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('login');
$response = new RedirectResponse($uri);
return $this->enrichResponseWithHeadersAndCookieInformation($response, $GLOBALS['BE_USER']);
if ($this->isLoggedInBackendUserRequired($route)) {
if (!$this->context->getAspect('backend.user')->isLoggedIn()) {
$uri = GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('login');
$response = new RedirectResponse($uri);
return $this->enrichResponseWithHeadersAndCookieInformation($response, $GLOBALS['BE_USER']);
}
if (!$GLOBALS['BE_USER']->isUserAllowedToLogin()) {
$content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
'Login Error',
'TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.',
AbstractMessage::ERROR,
1294585860
);
$response = new HtmlResponse($content, 503);
return $this->enrichResponseWithHeadersAndCookieInformation($response, $GLOBALS['BE_USER']);
}
}
try {
$proceedIfNoUserIsLoggedIn = $this->isLoggedInBackendUserRequired($route) === false;
// @todo: Ensure that the runtime exceptions are caught
$GLOBALS['BE_USER']->backendCheckLogin($proceedIfNoUserIsLoggedIn);
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
// Re-setting the user and take the workspace from the user object now
$this->setBackendUserAspect($GLOBALS['BE_USER']);
$response = $handler->handle($request);
$this->sessionGarbageCollection();
} catch (ImmediateResponseException $e) {
$response = $this->enrichResponseWithHeadersAndCookieInformation(
$e->getResponse(),
$GLOBALS['BE_USER']
);
// Re-throw this exception
throw new ImmediateResponseException($response, $e->getCode());
if ($this->context->getAspect('backend.user')->isLoggedIn()) {
$GLOBALS['BE_USER']->initializeBackendLogin();
}
$GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
// Re-setting the user and take the workspace from the user object now
$this->setBackendUserAspect($GLOBALS['BE_USER']);
$response = $handler->handle($request);
$this->sessionGarbageCollection();
return $this->enrichResponseWithHeadersAndCookieInformation($response, $GLOBALS['BE_USER']);
}
......
......@@ -60,5 +60,12 @@ return [
'typo3/cms-backend/output-compression'
]
],
/** internal: do not use or reference this middleware in your own code */
'typo3/cms-core/response-propagation' => [
'target' => \TYPO3\CMS\Core\Middleware\ResponsePropagation::class,
'after' => [
'typo3/cms-backend/response-headers'
]
],
]
];
......@@ -29,7 +29,7 @@ use TYPO3\CMS\Beuser\Service\ModuleDataStorageService;
use TYPO3\CMS\Beuser\Service\UserInformationService;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Pagination\SimplePagination;
......@@ -377,7 +377,7 @@ class BackendUserController extends ActionController
'main',
$GLOBALS['TYPO3_CONF_VARS']['BE']['interfaces'] ? [] : ['commandLI' => '1']
);
throw new ImmediateResponseException(new RedirectResponse($redirectUri, 303), 1607271592);
throw new PropagateResponseException(new RedirectResponse($redirectUri, 303), 1607271592);
}
}
......
......@@ -19,7 +19,7 @@ use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserSessionRepository;
use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -45,7 +45,7 @@ class SwitchBackUserHook
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$redirectUrl = $uriBuilder->buildUriFromRoute('main');
throw new ImmediateResponseException(new RedirectResponse($redirectUrl, 303), 1607271637);
throw new PropagateResponseException(new RedirectResponse($redirectUrl, 303), 1607271637);
}
}
......
......@@ -2192,6 +2192,7 @@ TCAdefaults.sys_note.email = ' . $this->user['email'];
*
* @param bool $proceedIfNoUserIsLoggedIn if this option is set, then there won't be a redirect to the login screen of the Backend - used for areas in the backend which do not need user rights like the login page.
* @throws \RuntimeException
* @todo deprecate
*/
public function backendCheckLogin($proceedIfNoUserIsLoggedIn = false)
{
......@@ -2201,35 +2202,42 @@ TCAdefaults.sys_note.email = ' . $this->user['email'];
throw new ImmediateResponseException(new RedirectResponse($url, 303), 1607271747);
}
} else {
// ...and if that's the case, call these functions
$this->fetchGroupData();
// The groups are fetched and ready for permission checking in this initialization.
// Tables.php must be read before this because stuff like the modules has impact in this
if ($this->isUserAllowedToLogin()) {
// Setting the UC array. It's needed with fetchGroupData first, due to default/overriding of values.
$this->backendSetUC();
if ($this->loginSessionStarted) {
// Also, if there is a recovery link set, unset it now
// this will be moved into its own Event at a later stage.
// If a token was set previously, this is now unset, as it was now possible to log-in
if ($this->user['password_reset_token'] ?? '') {
GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable($this->user_table)
->update($this->user_table, ['password_reset_token' => ''], ['uid' => $this->user['uid']]);
}
// Process hooks
$hooks = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['backendUserLogin'];
foreach ($hooks ?? [] as $_funcRef) {
$_params = ['user' => $this->user];
GeneralUtility::callUserFunction($_funcRef, $_params, $this);
}
}
$this->initializeBackendLogin();
} else {
throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1294585860);
}
}
}
/**
* @internal
*/
public function initializeBackendLogin(): void
{
// The groups are fetched and ready for permission checking in this initialization.
// Tables.php must be read before this because stuff like the modules has impact in this
$this->fetchGroupData();
// Setting the UC array. It's needed with fetchGroupData first, due to default/overriding of values.
$this->backendSetUC();
if ($this->loginSessionStarted) {
// Also, if there is a recovery link set, unset it now
// this will be moved into its own Event at a later stage.
// If a token was set previously, this is now unset, as it was now possible to log-in
if ($this->user['password_reset_token'] ?? '') {
GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable($this->user_table)
->update($this->user_table, ['password_reset_token' => ''], ['uid' => $this->user['uid']]);
}
// Process hooks
$hooks = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['backendUserLogin'];
foreach ($hooks ?? [] as $_funcRef) {
$_params = ['user' => $this->user];
GeneralUtility::callUserFunction($_funcRef, $_params, $this);
}
}
}
/**
* Initialize the internal ->uc array for the backend user
* Will make the overrides if necessary, and write the UC back to the be_users record if changes has happened
......@@ -2312,8 +2320,9 @@ TCAdefaults.sys_note.email = ' . $this->user['email'];
* + backend user is being controlled by an admin user
*
* @return bool Whether a backend user is allowed to access the backend
* @internal
*/
protected function isUserAllowedToLogin()
public function isUserAllowedToLogin()
{
$isUserAllowedToLogin = false;
$adminOnlyMode = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'];
......
......@@ -122,8 +122,9 @@ class CommandLineUserAuthentication extends BackendUserAuthentication
* Only when adminOnly is off (=0), and only allowed for admins and CLI users (=2)
*
* @return bool Whether the CLI user is allowed to access TYPO3
* @internal
*/
protected function isUserAllowedToLogin()
public function isUserAllowedToLogin()
{
return in_array((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'], [0, 2], true);
}
......
......@@ -20,10 +20,15 @@ namespace TYPO3\CMS\Core\Http;
use Psr\Http\Message\ResponseInterface;
/**
* Exception that has to be handled immediately in order to have
* stop current execution and provide the current response. This
* Exception that has to be handled immediately in order to stop
* current execution and provide the current response. This
* exception is used as alternative to previous die() or exit().
*
* Note this exception is only caught by Application classes, throwing
* it will bypass all outer middlewares. It should *not* be thrown by
* controllers, those should usually throw a PropagateResponseException
* instead, allowing outer middlewares to further process the response.
*
* @internal
*/
class ImmediateResponseException extends \Exception
......
<?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\Core\Http;
/**
* Exception that has to be propagated back to the middleware stack
* in order to stop current execution and respond with the given
* response. This exception is used as alternative to previous
* die() or exit() calls.
*
* Note this exception should be used in cases where a controller wants
* to create an 'early' response. An example are failed access checks:
* A controller wants to throw an early 'denied' response without further
* local processing.
*
* When this exception is thrown by a controller, it will be caught by the
* 'very inner' ResponsePropagation middleware. The response is then returned
* to other 'outer' middlewares, allowing them to further operate on the
* response (eg. adding a content-length header) and to do other jobs
* (eg. releasing created locks).
*
* If this exception is thrown within a middleware (as opposed to be thrown
* from within controllers), it will bypass other middlewares and will be
* caught just like the parent ImmediateResponseException. This should be
* used with care, there should be little reason to do so at all.
*
* In general, from within controllers, this response exception
* should be preferred over ImmediateResponseException.
*
* @internal
*/
class PropagateResponseException extends ImmediateResponseException
{
}
<?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\Core\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\PropagateResponseException;
/**
* @internal
*/
class ResponsePropagation implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
try {
$response = $handler->handle($request);
} catch (PropagateResponseException $e) {
$response = $e->getResponse();
}
return $response;
}
}
......@@ -58,6 +58,7 @@ class ServiceProvider extends AbstractServiceProvider
Localization\LocalizationFactory::class => [ static::class, 'getLocalizationFactory' ],
Mail\TransportFactory::class => [ static::class, 'getMailTransportFactory' ],
Messaging\FlashMessageService::class => [ static::class, 'getFlashMessageService' ],
Middleware\ResponsePropagation::class => [ static::class, 'getResponsePropagationMiddleware' ],
Package\FailsafePackageManager::class => [ static::class, 'getFailsafePackageManager' ],
Registry::class => [ static::class, 'getRegistry' ],
Resource\Index\FileIndexRepository::class => [ static::class, 'getFileIndexRepository' ],
......@@ -224,6 +225,11 @@ class ServiceProvider extends AbstractServiceProvider
return self::new($container, Messaging\FlashMessageService::class);
}
public static function getResponsePropagationMiddleware(ContainerInterface $container): Middleware\ResponsePropagation
{
return self::new($container, Middleware\ResponsePropagation::class);
}
public static function getFailsafePackageManager(ContainerInterface $container): Package\FailsafePackageManager
{
$packageManager = $container->get(Package\PackageManager::class);
......
......@@ -56,7 +56,8 @@ class ProductionExceptionHandler implements ExceptionHandlerInterface, LoggerAwa
*/
public function handle(\Exception $exception, AbstractContentObject $contentObject = null, $contentObjectConfiguration = [])
{
// ImmediateResponseException should work similar to exit / die and must therefore not be handled by this ExceptionHandler.
// ImmediateResponseException (and the derived PropagateResponseException) should work similar to
// exit / die and must therefore not be handled by this ExceptionHandler.
if ($exception instanceof ImmediateResponseException) {
throw $exception;
}
......
......@@ -44,7 +44,7 @@ use TYPO3\CMS\Core\Error\Http\PageNotFoundException;
use TYPO3\CMS\Core\Error\Http\ShortcutTargetPageNotFoundException;
use TYPO3\CMS\Core\Exception\Page\RootLineException;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Core\Http\ServerRequestFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Locking\Exception\LockAcquireWouldBlockException;
......@@ -993,7 +993,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$this->getPageAccessFailureReasons()
);
}
throw new ImmediateResponseException($response, 1533931329);
throw new PropagateResponseException($response, 1533931329);
}
$this->setRegisterValueForSysLastChanged($this->page);
......@@ -1098,7 +1098,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
$this->getPageAccessFailureReasons(PageAccessFailureReasons::PAGE_NOT_FOUND)
);
throw new ImmediateResponseException($response, 1533931330);
throw new PropagateResponseException($response, 1533931330);
} catch (PageNotFoundException $e) {
throw new PageNotFoundException($message, 1301648780);
}
......@@ -1114,7 +1114,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
$this->getPageAccessFailureReasons(PageAccessFailureReasons::ACCESS_DENIED_INVALID_PAGETYPE)
);
throw new ImmediateResponseException($response, 1533931343);
throw new PropagateResponseException($response, 1533931343);
} catch (PageNotFoundException $e) {
throw new PageNotFoundException($message, 1301648781);
}
......@@ -1166,7 +1166,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
$this->getPageAccessFailureReasons(PageAccessFailureReasons::ROOTLINE_BROKEN)
);
throw new ImmediateResponseException($response, 1533931350);
throw new PropagateResponseException($response, 1533931350);
} catch (AbstractServerErrorException $e) {
$this->logger->error($message);
$exceptionClass = get_class($e);
......@@ -1183,7 +1183,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
$this->getPageAccessFailureReasons(PageAccessFailureReasons::ACCESS_DENIED_GENERAL)
);
throw new ImmediateResponseException($response, 1533931351);
throw new PropagateResponseException($response, 1533931351);
} catch (AbstractServerErrorException $e) {
$this->logger->warning($message);
$exceptionClass = get_class($e);
......@@ -1780,7 +1780,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
['code' => PageAccessFailureReasons::RENDERING_INSTRUCTIONS_NOT_CONFIGURED]
);
throw new ImmediateResponseException($response, 1533931374);
throw new PropagateResponseException($response, 1533931374);
} catch (AbstractServerErrorException $e) {
$explanation = 'This means that there is no TypoScript object of type PAGE with typeNum=' . $this->type . ' configured.';
$exceptionClass = get_class($e);
......@@ -1842,7 +1842,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
['code' => PageAccessFailureReasons::RENDERING_INSTRUCTIONS_NOT_FOUND]
);
throw new ImmediateResponseException($response, 1533931380);
throw new PropagateResponseException($response, 1533931380);
} catch (AbstractServerErrorException $e) {
$exceptionClass = get_class($e);
throw new $exceptionClass($message, 1294587218);
......@@ -1910,7 +1910,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
'Page is not available in the requested language.',
['code' => PageAccessFailureReasons::LANGUAGE_NOT_AVAILABLE]
);
throw new ImmediateResponseException($response, 1533931388);
throw new PropagateResponseException($response, 1533931388);
}
switch ((string)$languageAspect->getLegacyLanguageMode()) {
case 'strict':
......@@ -1919,7 +1919,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
'Page is not available in the requested language (strict).',
['code' => PageAccessFailureReasons::LANGUAGE_NOT_AVAILABLE_STRICT_MODE]
);
throw new ImmediateResponseException($response, 1533931395);
throw new PropagateResponseException($response, 1533931395);
case 'fallback':
case 'content_fallback':
// Setting content uid (but leaving the sys_language_uid) when a content_fallback
......@@ -1942,7 +1942,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
'Page is not available in the requested language (fallbacks did not apply).',
['code' => PageAccessFailureReasons::LANGUAGE_AND_FALLBACKS_NOT_AVAILABLE]
);
throw new ImmediateResponseException($response, 1533931402);
throw new PropagateResponseException($response, 1533931402);
}
}
break;
......@@ -1989,7 +1989,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$message,
['code' => PageAccessFailureReasons::LANGUAGE_DEFAULT_NOT_AVAILABLE]
);
throw new ImmediateResponseException($response, 1533931423);
throw new PropagateResponseException($response, 1533931423);
}
if ($languageAspect->getId() > 0) {
......
......@@ -151,5 +151,12 @@ return [
'typo3/cms-frontend/content-length-headers',
],
],
/** internal: do not use or reference this middleware in your own code */
'typo3/cms-core/response-propagation' => [
'target' => \TYPO3\CMS\Core\Middleware\ResponsePropagation::class,
'after' => [
'typo3/cms-frontend/output-compression',
]
],
]
];
......@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\ContentObject\Exception;
use Psr\Log\NullLogger;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
......@@ -41,6 +42,21 @@ class ProductionExceptionHandlerTest extends UnitTestCase
$this->subject = new ProductionExceptionHandler();
$this->subject->setLogger(new NullLogger());
}
/**
* @test
*/
public function relayPropagateResponseException()
{
$response = $this->getMockBuilder(HtmlResponse::class)
->disableOriginalConstructor()
->getMock();
$exception = new PropagateResponseException($response, 1607328584);
$this->expectException(PropagateResponseException::class);
$this->subject->handle($exception);
}
/**
* @test
*/
......
......@@ -32,6 +32,7 @@ use TYPO3\CMS\Core\Imaging\IconRegistry;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Core\Localization\Locales;
use TYPO3\CMS\Core\Middleware\NormalizedParamsAttribute as NormalizedParamsMiddleware;
use TYPO3\CMS\Core\Middleware\ResponsePropagation as ResponsePropagationMiddleware;
use TYPO3\CMS\Core\Package\AbstractServiceProvider;
use TYPO3\CMS\Core\Package\FailsafePackageManager;
use TYPO3\CMS\Core\Package\PackageManager;
......@@ -100,6 +101,7 @@ class ServiceProvider extends AbstractServiceProvider
$dispatcher = new MiddlewareDispatcher($requestHandler, [], $container);
// Stack of middlewares, executed LIFO
$dispatcher->lazy(ResponsePropagationMiddleware::class);
$dispatcher->lazy(Middleware\Installer::class);
$dispatcher->add($container->get(Middleware\Maintenance::class));
$dispatcher->lazy(NormalizedParamsMiddleware::class);
......
......@@ -17,7 +17,7 @@ namespace TYPO3\CMS\Tstemplate\Controller;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Http\PropagateResponseException;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
......@@ -156,7 +156,7 @@ class TypoScriptTemplateInformationModuleFunctionController
'SET[templatesOnPage]' => $newId
];
$url = $uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
throw new ImmediateResponseException(new RedirectResponse($url, 303), 1607271781);
throw new PropagateResponseException(new RedirectResponse($url, 303), 1607271781);
}
if ($existTemplate) {
$lang = $this->getLanguageService();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment