first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\UserAspect;
|
||||
use TYPO3\CMS\Core\Context\WorkspaceAspect;
|
||||
use TYPO3\CMS\Core\Core\Bootstrap;
|
||||
use TYPO3\CMS\Core\Http\NormalizedParams;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This middleware authenticates a Backend User (be_user) (pre)-viewing a frontend page.
|
||||
*
|
||||
* This middleware also ensures that $GLOBALS['LANG'] is available, however it is possible that
|
||||
* a different middleware later-on might unset the BE_USER as he/she is not allowed to preview a certain
|
||||
* page due to rights management. As this can only happen once the page ID is resolved, this will happen
|
||||
* after the routing middleware.
|
||||
*
|
||||
* Currently, this middleware depends on the availability of $GLOBALS['TSFE'], however, this is solely
|
||||
* due to backwards-compatibility and will be disabled in the future.
|
||||
*/
|
||||
class BackendUserAuthenticator implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Creates a frontend user authentication object, tries to authenticate a user
|
||||
* and stores the object in $GLOBALS['TSFE']->fe_user.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// PRE BE_USER HOOK
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'])) {
|
||||
trigger_error('The "preBeUser" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] as $_funcRef) {
|
||||
$_params = [];
|
||||
GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
|
||||
}
|
||||
}
|
||||
|
||||
// Initializing a possible logged-in Backend User
|
||||
// If the backend cookie is set,
|
||||
// we proceed and check if a backend user is logged in.
|
||||
$backendUserObject = null;
|
||||
if (isset($request->getCookieParams()[BackendUserAuthentication::getCookieName()])) {
|
||||
$backendUserObject = $this->initializeBackendUser($request);
|
||||
}
|
||||
|
||||
$GLOBALS['BE_USER'] = $backendUserObject;
|
||||
|
||||
// POST BE_USER HOOK
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'])) {
|
||||
$_params = [
|
||||
'BE_USER' => &$GLOBALS['BE_USER']
|
||||
];
|
||||
trigger_error('The "postBeUser" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'] as $_funcRef) {
|
||||
GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
|
||||
}
|
||||
}
|
||||
|
||||
// Load specific dependencies which are necessary for a valid Backend User
|
||||
// like $GLOBALS['LANG'] for labels in the language of the BE User, the router, and ext_tables.php for all modules
|
||||
// So things like Frontend Editing and Admin Panel can use this for generating links to the TYPO3 Backend.
|
||||
if ($GLOBALS['BE_USER'] instanceof FrontendBackendUserAuthentication) {
|
||||
Bootstrap::initializeLanguageObject();
|
||||
Bootstrap::initializeBackendRouter();
|
||||
Bootstrap::loadExtTables();
|
||||
$this->setBackendUserAspect(GeneralUtility::makeInstance(Context::class), $GLOBALS['BE_USER']);
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the backend user object and returns it.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return FrontendBackendUserAuthentication|null the backend user object or null if there was no valid user found
|
||||
* @throws \TYPO3\CMS\Core\Exception
|
||||
*/
|
||||
protected function initializeBackendUser(ServerRequestInterface $request)
|
||||
{
|
||||
// New backend user object
|
||||
$backendUserObject = GeneralUtility::makeInstance(FrontendBackendUserAuthentication::class);
|
||||
$backendUserObject->start();
|
||||
$backendUserObject->unpack_uc();
|
||||
if (!empty($backendUserObject->user['uid'])) {
|
||||
$backendUserObject->fetchGroupData();
|
||||
}
|
||||
// Unset the user initialization if any setting / restriction applies
|
||||
if (!$this->isAuthenticated($backendUserObject, $request->getAttribute('normalizedParams'))) {
|
||||
$backendUserObject = null;
|
||||
}
|
||||
return $backendUserObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing the access checks that the TYPO3 CMS bootstrap script does before a user is ever logged in.
|
||||
*
|
||||
* @param FrontendBackendUserAuthentication $user
|
||||
* @param NormalizedParams $normalizedParams
|
||||
* @return bool Returns TRUE if access is OK
|
||||
*/
|
||||
protected function isAuthenticated(FrontendBackendUserAuthentication $user, NormalizedParams $normalizedParams)
|
||||
{
|
||||
// Check IP
|
||||
$ipMask = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'] ?? '');
|
||||
if ($ipMask && !GeneralUtility::cmpIP($normalizedParams->getRemoteAddress(), $ipMask)) {
|
||||
return false;
|
||||
}
|
||||
// Check SSL (https)
|
||||
if ((bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] && !$normalizedParams->isHttps()) {
|
||||
return false;
|
||||
}
|
||||
return $user->backendCheckLogin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the backend user as aspect
|
||||
*
|
||||
* @param Context $context
|
||||
* @param BackendUserAuthentication|null $user
|
||||
*/
|
||||
protected function setBackendUserAspect(Context $context, BackendUserAuthentication $user)
|
||||
{
|
||||
$context->setAspect('backend.user', GeneralUtility::makeInstance(UserAspect::class, $user));
|
||||
$context->setAspect('workspace', GeneralUtility::makeInstance(WorkspaceAspect::class, $user->workspace));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
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\NullResponse;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* Send content-length header.
|
||||
* Notice that all HTML content outside the length of the content-length header will be cut off!
|
||||
* Therefore content of unknown length from included PHP-scripts and if admin users are logged
|
||||
* in (admin panel might show...) or if debug mode is turned on, we disable it!
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ContentLengthResponseHeader implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Adds the content length
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$response = $handler->handle($request);
|
||||
if (
|
||||
!($response instanceof NullResponse)
|
||||
&& $GLOBALS['TSFE'] instanceof TypoScriptFrontendController
|
||||
&& $GLOBALS['TSFE']->isOutputting()) {
|
||||
if (
|
||||
(!isset($GLOBALS['TSFE']->config['config']['enableContentLengthHeader']) || $GLOBALS['TSFE']->config['config']['enableContentLengthHeader'])
|
||||
&& !$GLOBALS['TSFE']->isBackendUserLoggedIn() && !$GLOBALS['TYPO3_CONF_VARS']['FE']['debug']
|
||||
&& !$GLOBALS['TSFE']->config['config']['debug'] && !$GLOBALS['TSFE']->doWorkspacePreview()
|
||||
) {
|
||||
$response = $response->withHeader('Content-Length', (string)$response->getBody()->getSize());
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Exception;
|
||||
use TYPO3\CMS\Core\Http\Dispatcher;
|
||||
use TYPO3\CMS\Core\Http\NullResponse;
|
||||
use TYPO3\CMS\Core\Http\Response;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Lightweight alternative to regular frontend requests; used when $_GET[eID] is set.
|
||||
* In the future, logic from the EidUtility will be moved to this class, however in most cases
|
||||
* a custom PSR-15 middleware will be better suited for whatever job the eID functionality does currently.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class EidHandler implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Dispatches the request to the corresponding eID class or eID script
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
* @throws Exception
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$eID = $request->getParsedBody()['eID'] ?? $request->getQueryParams()['eID'] ?? null;
|
||||
|
||||
if ($eID === null) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
// Remove any output produced until now
|
||||
ob_clean();
|
||||
|
||||
/** @var Response $response */
|
||||
$response = GeneralUtility::makeInstance(Response::class);
|
||||
|
||||
if (empty($eID) || !isset($GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'][$eID])) {
|
||||
return $response->withStatus(404, 'eID not registered');
|
||||
}
|
||||
|
||||
$configuration = $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'][$eID];
|
||||
|
||||
// Simple check to make sure that it's not an absolute file (to use the fallback)
|
||||
if (strpos($configuration, '::') !== false || is_callable($configuration)) {
|
||||
/** @var Dispatcher $dispatcher */
|
||||
$dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
|
||||
$request = $request->withAttribute('target', $configuration);
|
||||
return $dispatcher->dispatch($request, $response) ?? new NullResponse();
|
||||
}
|
||||
trigger_error(
|
||||
'eID "' . $eID . '" is registered with a script to a file. This behaviour will be removed in TYPO3 v10.0.'
|
||||
. ' Register eID with a class::method syntax like "\MyVendor\MyExtension\Controller\MyEidController::myMethod" instead.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$scriptPath = GeneralUtility::getFileAbsFileName($configuration);
|
||||
if ($scriptPath === '') {
|
||||
throw new Exception('Registered eID has invalid script path.', 1518042216);
|
||||
}
|
||||
include $scriptPath;
|
||||
return new NullResponse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\UserAspect;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
|
||||
|
||||
/**
|
||||
* This middleware authenticates a Frontend User (fe_users).
|
||||
* A valid $GLOBALS['TSFE'] object is needed for the time being, being fully backwards-compatible.
|
||||
*/
|
||||
class FrontendUserAuthenticator implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Creates a frontend user authentication object, tries to authenticate a user
|
||||
* and stores the object in $GLOBALS['TSFE']->fe_user.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$frontendUser = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
|
||||
|
||||
// List of page IDs where to look for frontend user records
|
||||
$pid = $request->getParsedBody()['pid'] ?? $request->getQueryParams()['pid'] ?? 0;
|
||||
if ($pid) {
|
||||
$frontendUser->checkPid_value = implode(',', GeneralUtility::intExplode(',', $pid));
|
||||
}
|
||||
|
||||
// Check if a session is transferred, and update the cookie parameters
|
||||
$frontendSessionKey = $request->getParsedBody()['FE_SESSION_KEY'] ?? $request->getQueryParams()['FE_SESSION_KEY'] ?? '';
|
||||
if ($frontendSessionKey) {
|
||||
$request = $this->transferFrontendUserSession($frontendUser, $request, $frontendSessionKey);
|
||||
}
|
||||
|
||||
// Authenticate now
|
||||
$frontendUser->start();
|
||||
$frontendUser->unpack_uc();
|
||||
|
||||
// Keep the backwards-compatibility for TYPO3 v9, to have the fe_user within the global TSFE object
|
||||
$GLOBALS['TSFE']->fe_user = $frontendUser;
|
||||
|
||||
// Call hook for possible manipulation of frontend user object
|
||||
// This hook is kept for compatibility reasons, however, it should be fairly simple to add a custom middleware
|
||||
// for this purpose
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'])) {
|
||||
trigger_error('The "initFEuser" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
$_params = ['pObj' => &$GLOBALS['TSFE']];
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] as $_funcRef) {
|
||||
GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
|
||||
}
|
||||
}
|
||||
|
||||
// Register the frontend user as aspect
|
||||
$this->setFrontendUserAspect(GeneralUtility::makeInstance(Context::class), $frontendUser);
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* It's possible to transfer a frontend user session via a GET/POST parameter 'FE_SESSION_KEY'.
|
||||
* In the future, this logic should be moved into the FrontendUserAuthentication object directly,
|
||||
* but only if FrontendUserAuthentication does not request superglobals (like $_COOKIE) anymore.
|
||||
*
|
||||
* @param FrontendUserAuthentication $frontendUser
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $frontendSessionKey
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
protected function transferFrontendUserSession(
|
||||
FrontendUserAuthentication $frontendUser,
|
||||
ServerRequestInterface $request,
|
||||
string $frontendSessionKey
|
||||
): ServerRequestInterface {
|
||||
list($sessionId, $hash) = explode('-', $frontendSessionKey);
|
||||
// If the session key hash check is OK, set the cookie
|
||||
if (hash_equals(md5($sessionId . '/' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), (string)$hash)) {
|
||||
$cookieName = FrontendUserAuthentication::getCookieName();
|
||||
|
||||
// keep the global cookie overwriting for now, as long as FrontendUserAuthentication does not
|
||||
// use the request object for fetching the cookie information.
|
||||
$_COOKIE[$cookieName] = $sessionId;
|
||||
if (isset($_SERVER['HTTP_COOKIE'])) {
|
||||
// See http://forge.typo3.org/issues/27740
|
||||
$_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $sessionId;
|
||||
}
|
||||
// Add the cookie to the Server Request object
|
||||
$cookieParams = $request->getCookieParams();
|
||||
$cookieParams[$cookieName] = $sessionId;
|
||||
$request = $request->withCookieParams($cookieParams);
|
||||
// @deprecated: we override the current request because it was enriched by cookie information here.
|
||||
$GLOBALS['TYPO3_REQUEST'] = $request;
|
||||
$frontendUser->forceSetCookie = true;
|
||||
$frontendUser->dontSetCookie = false;
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the frontend user as aspect
|
||||
*
|
||||
* @param Context $context
|
||||
* @param AbstractUserAuthentication $user
|
||||
*/
|
||||
protected function setFrontendUserAspect(Context $context, AbstractUserAuthentication $user)
|
||||
{
|
||||
$context->setAspect('frontend.user', GeneralUtility::makeInstance(UserAspect::class, $user));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
|
||||
/**
|
||||
* Checks whether the whole Frontend should be put into "Page unavailable mode"
|
||||
* unless the devIPMask matches the current visitor's IP.
|
||||
*
|
||||
* The global setting $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_force']
|
||||
* is used for turning on the maintenance mode.
|
||||
*/
|
||||
class MaintenanceMode implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Calls the "unavailableAction" of the error controller if the system is in maintenance mode.
|
||||
* This only applies if the REMOTE_ADDR does not match the devIpMask
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_force']
|
||||
&& !GeneralUtility::cmpIP(
|
||||
$request->getAttribute('normalizedParams')->getRemoteAddress(),
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
|
||||
)
|
||||
) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, 'This page is temporarily unavailable.');
|
||||
}
|
||||
// Continue the regular stack if no maintenance mode is active
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Frontend\Utility\CompressionUtility;
|
||||
|
||||
/**
|
||||
* Sets up output compression
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OutputCompression implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Clears all output and checks if a compression level is set
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// Throw away all output that may have happened during bootstrapping by weird extensions
|
||||
ob_clean();
|
||||
// Initialize output compression if configured
|
||||
$this->initializeOutputCompression();
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize output compression if configured
|
||||
*/
|
||||
protected function initializeOutputCompression()
|
||||
{
|
||||
if ($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] && extension_loaded('zlib')) {
|
||||
if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'])) {
|
||||
@ini_set('zlib.output_compression_level', (string)$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel']);
|
||||
}
|
||||
ob_start([GeneralUtility::makeInstance(CompressionUtility::class), 'compressionOutputHandler']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
|
||||
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
|
||||
|
||||
/**
|
||||
* This middleware validates given request parameters against the common "cHash" functionality.
|
||||
*/
|
||||
class PageArgumentValidator implements MiddlewareInterface, LoggerAwareInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* The cHash Service class used for cHash related functionality
|
||||
*
|
||||
* @var CacheHashCalculator
|
||||
*/
|
||||
protected $cacheHashCalculator;
|
||||
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @param TypoScriptFrontendController|null $controller
|
||||
*/
|
||||
public function __construct(TypoScriptFrontendController $controller = null)
|
||||
{
|
||||
$this->controller = $controller ?? $GLOBALS['TSFE'];
|
||||
$this->cacheHashCalculator = GeneralUtility::makeInstance(CacheHashCalculator::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the &cHash parameter against the other $queryParameters / GET parameters
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$pageNotFoundOnValidationError = (bool)($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError'] ?? true);
|
||||
$pageArguments = $request->getAttribute('routing', null);
|
||||
if ($this->controller->no_cache && !$pageNotFoundOnValidationError) {
|
||||
// No need to test anything if caching was already disabled.
|
||||
} else {
|
||||
// Evaluate the cache hash parameter or dynamic arguments when coming from a Site-based routing
|
||||
if ($pageArguments instanceof PageArguments) {
|
||||
$queryParams = $pageArguments->getDynamicArguments();
|
||||
} else {
|
||||
$queryParams = $request->getQueryParams();
|
||||
}
|
||||
if (!empty($queryParams) && !$this->evaluateCacheHashParameter($queryParams, $pageNotFoundOnValidationError)) {
|
||||
// cHash was given, but nothing to be calculated, so let's do a redirect to the current page
|
||||
// but without the cHash
|
||||
if ($this->controller->cHash && empty($this->controller->cHash_array)) {
|
||||
$uri = $request->getUri();
|
||||
unset($queryParams['cHash']);
|
||||
$uri = $uri->withQuery(HttpUtility::buildQueryString($queryParams));
|
||||
return new RedirectResponse($uri, 308);
|
||||
}
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'Request parameters could not be validated (&cHash comparison failed)',
|
||||
['code' => PageAccessFailureReasons::CACHEHASH_COMPARISON_FAILED]
|
||||
);
|
||||
}
|
||||
}
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a hash string based on additional parameters in the url.
|
||||
*
|
||||
* Calculated hash is stored in $this->controller->cHash_array.
|
||||
* This is used to cache pages with more parameters than just id and type.
|
||||
*
|
||||
* @see TypoScriptFrontendController::reqCHash()
|
||||
* @param array<string, string> $queryParams GET parameters
|
||||
* @param bool $pageNotFoundOnCacheHashError see $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError']
|
||||
* @return bool if false, then a PageNotFound response is triggered
|
||||
*/
|
||||
protected function evaluateCacheHashParameter(array $queryParams, bool $pageNotFoundOnCacheHashError): bool
|
||||
{
|
||||
if ($this->controller->cHash !== '') {
|
||||
// Make sure we use the page uid and not the page alias
|
||||
$queryParams['id'] = $this->controller->id;
|
||||
$relevantParameters = $this->cacheHashCalculator->getRelevantParameters(HttpUtility::buildQueryString($queryParams));
|
||||
$this->controller->cHash_array = $relevantParameters;
|
||||
// cHash was given, but nothing to be calculated, so cHash is unset and all is good.
|
||||
if (empty($relevantParameters)) {
|
||||
$this->logger->notice('The incoming cHash "' . $this->controller->cHash . '" is given but not needed. cHash is unset');
|
||||
return false;
|
||||
}
|
||||
$calculatedCacheHash = $this->cacheHashCalculator->calculateCacheHash($relevantParameters);
|
||||
if (!hash_equals($calculatedCacheHash, $this->controller->cHash)) {
|
||||
// Early return to trigger the error controller
|
||||
if ($pageNotFoundOnCacheHashError) {
|
||||
return false;
|
||||
}
|
||||
$this->controller->no_cache = true;
|
||||
$this->getTimeTracker()->setTSlogMessage('The incoming cHash "' . $this->controller->cHash . '" and calculated cHash "' . $calculatedCacheHash . '" did not match, so caching was disabled. The fieldlist used was "' . implode(',', array_keys($this->controller->cHash_array)) . '"', 2);
|
||||
}
|
||||
// No cHash is set, check if that is correct
|
||||
} elseif ($this->cacheHashCalculator->doParametersRequireCacheHash(HttpUtility::buildQueryString($queryParams))) {
|
||||
// Will disable caching
|
||||
$this->controller->reqCHash();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TimeTracker
|
||||
*/
|
||||
protected function getTimeTracker(): TimeTracker
|
||||
{
|
||||
return GeneralUtility::makeInstance(TimeTracker::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\UserAspect;
|
||||
use TYPO3\CMS\Core\Context\WorkspaceAspect;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendWorkspaceRestriction;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\Routing\RouteNotFoundException;
|
||||
use TYPO3\CMS\Core\Routing\SiteRouteResult;
|
||||
use TYPO3\CMS\Core\Site\Entity\Site;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteInterface;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
|
||||
|
||||
/**
|
||||
* Process the ID, type and other parameters.
|
||||
* After this point we have an array, TSFE->page, which is the page-record of the current page, $TSFE->id.
|
||||
*
|
||||
* Now, if there is a backend user logged in and he has NO access to this page,
|
||||
* then re-evaluate the id shown!
|
||||
*/
|
||||
class PageResolver implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
public function __construct(TypoScriptFrontendController $controller = null)
|
||||
{
|
||||
$this->controller = $controller ?? $GLOBALS['TSFE'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the page ID
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// First, resolve the root page of the site, the Page ID of the current domain
|
||||
if (($site = $request->getAttribute('site', null)) instanceof SiteInterface) {
|
||||
$this->controller->domainStartPage = $site->getRootPageId();
|
||||
}
|
||||
$language = $request->getAttribute('language', null);
|
||||
|
||||
$hasSiteConfiguration = $language instanceof SiteLanguage && $site instanceof Site;
|
||||
|
||||
// Resolve the page ID based on TYPO3's native routing functionality
|
||||
if ($hasSiteConfiguration) {
|
||||
/** @var SiteRouteResult $previousResult */
|
||||
$previousResult = $request->getAttribute('routing', null);
|
||||
if (!$previousResult) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'The requested page does not exist',
|
||||
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
|
||||
);
|
||||
}
|
||||
|
||||
$requestId = (string)($request->getQueryParams()['id'] ?? '');
|
||||
if (!empty($requestId)) {
|
||||
$page = $this->resolvePageId($requestId);
|
||||
if ($page === null) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'The requested page does not exist',
|
||||
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
|
||||
);
|
||||
}
|
||||
// Legacy URIs (?id=12345) takes precedence, not matter if a route is given
|
||||
$pageArguments = new PageArguments(
|
||||
(int)($page['l10n_parent'] ?: $page['uid']),
|
||||
(string)($request->getQueryParams()['type'] ?? '0'),
|
||||
[],
|
||||
[],
|
||||
$request->getQueryParams()
|
||||
);
|
||||
} else {
|
||||
// Check for the route
|
||||
try {
|
||||
$pageArguments = $site->getRouter()->matchRequest($request, $previousResult);
|
||||
} catch (RouteNotFoundException $e) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'The requested page does not exist',
|
||||
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!$pageArguments->getPageId()) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'The requested page does not exist',
|
||||
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
|
||||
);
|
||||
}
|
||||
|
||||
$this->controller->id = $pageArguments->getPageId();
|
||||
$this->controller->type = $pageArguments->getPageType() ?? $this->controller->type;
|
||||
$this->controller->MP = $pageArguments->getArguments()['MP'] ?? $this->controller->MP;
|
||||
$request = $request->withAttribute('routing', $pageArguments);
|
||||
// stop in case arguments are dirty (=defined twice in route and GET query parameters)
|
||||
if ($pageArguments->areDirty()) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'The requested URL is not distinct',
|
||||
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
|
||||
);
|
||||
}
|
||||
|
||||
// merge the PageArguments with the request query parameters
|
||||
$queryParams = array_replace_recursive($request->getQueryParams(), $pageArguments->getArguments());
|
||||
$request = $request->withQueryParams($queryParams);
|
||||
$this->controller->setPageArguments($pageArguments);
|
||||
|
||||
// At this point, we later get further route modifiers
|
||||
// for bw-compat we update $GLOBALS[TYPO3_REQUEST] to be used later in TSFE.
|
||||
$GLOBALS['TYPO3_REQUEST'] = $request;
|
||||
} else {
|
||||
// old-school page resolving for realurl, cooluri etc.
|
||||
$this->controller->siteScript = $request->getAttribute('normalizedParams')->getSiteScript();
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'])) {
|
||||
trigger_error('The "checkAlternativeIdMethods-PostProc" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
$this->checkAlternativeIdMethods($this->controller);
|
||||
}
|
||||
}
|
||||
|
||||
$this->controller->determineId();
|
||||
|
||||
// No access? Then remove user & Re-evaluate the page-id
|
||||
if ($this->controller->isBackendUserLoggedIn() && !$GLOBALS['BE_USER']->doesUserHaveAccess($this->controller->page, Permission::PAGE_SHOW)) {
|
||||
unset($GLOBALS['BE_USER']);
|
||||
// Register an empty backend user as aspect
|
||||
$this->setBackendUserAspect(GeneralUtility::makeInstance(Context::class), null);
|
||||
if (!$hasSiteConfiguration) {
|
||||
$this->checkAlternativeIdMethods($this->controller);
|
||||
}
|
||||
$this->controller->determineId();
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides ways to bypass the '?id=[xxx]&type=[xx]' format, using either PATH_INFO or Server Rewrites
|
||||
*
|
||||
* Two options:
|
||||
* 1) Use PATH_INFO (also Apache) to extract id and type from that var. Does not require any special modules compiled with apache. (less typical)
|
||||
* 2) Using hook which enables features like those provided from "realurl" extension (AKA "Speaking URLs")
|
||||
*
|
||||
* @param TypoScriptFrontendController $tsfe
|
||||
*/
|
||||
protected function checkAlternativeIdMethods(TypoScriptFrontendController $tsfe)
|
||||
{
|
||||
// Call post processing function for custom URL methods.
|
||||
$_params = ['pObj' => &$tsfe];
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'] ?? [] as $_funcRef) {
|
||||
GeneralUtility::callUserFunction($_funcRef, $_params, $tsfe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $pageId
|
||||
* @return array|null
|
||||
*/
|
||||
protected function resolvePageId(string $pageId): ?array
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('pages');
|
||||
$queryBuilder
|
||||
->getRestrictions()
|
||||
->removeAll()
|
||||
->add(GeneralUtility::makeInstance(DeletedRestriction::class))
|
||||
->add(GeneralUtility::makeInstance(FrontendWorkspaceRestriction::class));
|
||||
|
||||
if (MathUtility::canBeInterpretedAsInteger($pageId)) {
|
||||
$constraint = $queryBuilder->expr()->eq(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
|
||||
);
|
||||
} else {
|
||||
$constraint = $queryBuilder->expr()->eq(
|
||||
'alias',
|
||||
$queryBuilder->createNamedParameter($pageId, \PDO::PARAM_STR)
|
||||
);
|
||||
}
|
||||
|
||||
$statement = $queryBuilder
|
||||
->select('uid', 'l10n_parent', 'pid')
|
||||
->from('pages')
|
||||
->where($constraint)
|
||||
->execute();
|
||||
|
||||
$page = $statement->fetch();
|
||||
if (empty($page)) {
|
||||
return null;
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the backend user as aspect
|
||||
*
|
||||
* @param Context $context
|
||||
* @param BackendUserAuthentication $user
|
||||
*/
|
||||
protected function setBackendUserAspect(Context $context, BackendUserAuthentication $user = null)
|
||||
{
|
||||
$context->setAspect('backend.user', GeneralUtility::makeInstance(UserAspect::class, $user));
|
||||
$context->setAspect('workspace', GeneralUtility::makeInstance(WorkspaceAspect::class, $user ? $user->workspace : 0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface as PsrRequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Routing\PageArguments;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* Initialization of TypoScriptFrontendController
|
||||
*
|
||||
* Do all necessary preparation steps for rendering
|
||||
*
|
||||
* @internal this middleware might get removed in TYPO3 v10.0.
|
||||
*/
|
||||
class PrepareTypoScriptFrontendRendering implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* @var TimeTracker
|
||||
*/
|
||||
protected $timeTracker;
|
||||
|
||||
public function __construct(TypoScriptFrontendController $controller = null, TimeTracker $timeTracker = null)
|
||||
{
|
||||
$this->controller = $controller ?: $GLOBALS['TSFE'];
|
||||
$this->timeTracker = $timeTracker ?: GeneralUtility::makeInstance(TimeTracker::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize TypoScriptFrontendController to the point right before rendering of the page is triggered
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param PsrRequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, PsrRequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// Get from cache
|
||||
$this->timeTracker->push('Get Page from cache');
|
||||
// Locks may be acquired here
|
||||
$this->controller->getFromCache();
|
||||
$this->timeTracker->pull();
|
||||
// Get config if not already gotten
|
||||
// After this, we should have a valid config-array ready
|
||||
$this->controller->getConfigArray();
|
||||
|
||||
// Merge Query Parameters with config.defaultGetVars
|
||||
// This is done in getConfigArray as well, but does not override the current middleware request object
|
||||
// Since we want to stay in sync with this, the option needs to be set as well.
|
||||
$pageArguments = $request->getAttribute('routing');
|
||||
if (!empty($this->controller->config['config']['defaultGetVars.'] ?? null)) {
|
||||
$modifiedGetVars = GeneralUtility::removeDotsFromTS($this->controller->config['config']['defaultGetVars.']);
|
||||
if ($pageArguments instanceof PageArguments) {
|
||||
$pageArguments = $pageArguments->withQueryArguments($modifiedGetVars);
|
||||
$this->controller->setPageArguments($pageArguments);
|
||||
$request = $request->withAttribute('routing', $pageArguments);
|
||||
}
|
||||
if (!empty($request->getQueryParams())) {
|
||||
ArrayUtility::mergeRecursiveWithOverrule($modifiedGetVars, $request->getQueryParams());
|
||||
}
|
||||
$request = $request->withQueryParams($modifiedGetVars);
|
||||
$GLOBALS['TYPO3_REQUEST'] = $request;
|
||||
}
|
||||
|
||||
// Setting language and locale
|
||||
$this->timeTracker->push('Setting language and locale');
|
||||
$this->controller->settingLanguage();
|
||||
$this->controller->settingLocale();
|
||||
$this->timeTracker->pull();
|
||||
|
||||
// Convert POST data to utf-8 for internal processing if metaCharset is different
|
||||
if ($this->controller->metaCharset !== 'utf-8' && is_array($_POST) && !empty($_POST)) {
|
||||
$this->convertCharsetRecursivelyToUtf8($_POST, $this->controller->metaCharset);
|
||||
$GLOBALS['HTTP_POST_VARS'] = $_POST;
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$this->convertCharsetRecursivelyToUtf8($parsedBody, $this->controller->metaCharset);
|
||||
$request = $request->withParsedBody($parsedBody);
|
||||
$GLOBALS['TYPO3_REQUEST'] = $request;
|
||||
}
|
||||
|
||||
// @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0
|
||||
$this->controller->initializeRedirectUrlHandlers(true);
|
||||
|
||||
// Hook for processing data submission to extensions
|
||||
// This is done at this point, because we need the config values
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'])) {
|
||||
trigger_error('The "checkDataSubmission" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] as $className) {
|
||||
GeneralUtility::makeInstance($className)->checkDataSubmission($this->controller);
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Small helper function to convert charsets for arrays to UTF-8
|
||||
*
|
||||
* @param mixed $data given by reference (string/array usually)
|
||||
* @param string $fromCharset convert FROM this charset
|
||||
*/
|
||||
protected function convertCharsetRecursivelyToUtf8(&$data, string $fromCharset)
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($data[$key])) {
|
||||
$this->convertCharsetRecursivelyToUtf8($data[$key], $fromCharset);
|
||||
} elseif (is_string($data[$key])) {
|
||||
$data[$key] = mb_convert_encoding($data[$key], 'utf-8', $fromCharset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Calls a hook before processing a request for the TYPO3 Frontend.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
class PreprocessRequestHook implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Hook to preprocess the current request
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// Legacy functionality to check if any hook modified global GET/POST
|
||||
// This is a safety net, see RequestHandler for how this is validated.
|
||||
// This information is just a compat layer which will be removed in TYPO3 v10.0.
|
||||
$request = $request->withAttribute('_originalGetParameters', $_GET);
|
||||
if ($request->getMethod() === 'POST') {
|
||||
$request = $request->withAttribute('_originalPostParameters', $_POST);
|
||||
}
|
||||
// Set original parameters
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'])) {
|
||||
trigger_error('The "preprocessRequest" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preprocessRequest'] as $hookFunction) {
|
||||
$hookParameters = [];
|
||||
GeneralUtility::callUserFunction($hookFunction, $hookParameters, $hookParameters);
|
||||
}
|
||||
}
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Context\DateTimeAspect;
|
||||
use TYPO3\CMS\Core\Context\UserAspect;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Middleware for handling preview settings
|
||||
* used when simulating / previewing pages or content through query params when
|
||||
* previewing access or time restricted content via for example backend preview links
|
||||
*/
|
||||
class PreviewSimulator implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Context\Context
|
||||
*/
|
||||
private $context;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->context = GeneralUtility::makeInstance(Context::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates preview settings if a backend user is logged in
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
if ((bool)$this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false)) {
|
||||
$simulatingDate = $this->simulateDate($request);
|
||||
$simulatingGroup = $this->simulateUserGroup($request);
|
||||
$showHiddenRecords = ($this->context->hasAspect('visibility') ? $this->context->getAspect('visibility')->includeHidden() : false);
|
||||
$GLOBALS['TSFE']->fePreview = ($simulatingDate || $simulatingGroup || $showHiddenRecords);
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate dates for preview functionality
|
||||
* When previewing a time restricted page from the backend, the parameter ADMCMD_simTime it added containing
|
||||
* a timestamp with the time to preview. The globals 'SIM_EXEC_TIME' and 'SIM_ACCESS_TIME' and the 'DateTimeAspect'
|
||||
* are used to simulate rendering at that point in time.
|
||||
* Ideally the global access is removed in future versions.
|
||||
* This functionality needs to be loaded after BackendAuthenticator as it is only relevant for
|
||||
* logged in backend users and needs to be done before any page resolving starts.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function simulateDate(ServerRequestInterface $request): bool
|
||||
{
|
||||
$simulatedDate = null;
|
||||
$queryTime = $request->getQueryParams()['ADMCMD_simTime'] ?? false;
|
||||
if (!$queryTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$simulatedDate = new \DateTimeImmutable('@' . $queryTime);
|
||||
if (!$simulatedDate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$GLOBALS['SIM_EXEC_TIME'] = $queryTime;
|
||||
$GLOBALS['SIM_ACCESS_TIME'] = $queryTime - $queryTime % 60;
|
||||
$this->context->setAspect(
|
||||
'date',
|
||||
GeneralUtility::makeInstance(
|
||||
DateTimeAspect::class,
|
||||
$simulatedDate
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate user group for preview functionality
|
||||
* When previewing a page with a usergroup restriction, the parameter ADMCMD_simUser = <groupId> will be added
|
||||
* to the preview url. Simulation happens.
|
||||
* legacy: via TSFE member variables (->fe_user->user[<groupColumn>])
|
||||
* new: via Context::UserAspect
|
||||
* This functionality needs to be loaded after BackendAuthenticator as it is only relevant for
|
||||
* logged in backend users and needs to be done before any page resolving starts.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function simulateUserGroup(ServerRequestInterface $request): bool
|
||||
{
|
||||
$simulateUserGroup = (int)($request->getQueryParams()['ADMCMD_simUser'] ?? 0);
|
||||
if (!$simulateUserGroup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$frontendUser = $GLOBALS['TSFE']->fe_user;
|
||||
$frontendUser->user[$frontendUser->usergroup_column] = $simulateUserGroup;
|
||||
// let's fake having a user with that group, too
|
||||
$frontendUser->user['uid'] = PHP_INT_MAX;
|
||||
$this->context->setAspect(
|
||||
'frontend.user',
|
||||
GeneralUtility::makeInstance(
|
||||
UserAspect::class,
|
||||
$frontendUser,
|
||||
[$simulateUserGroup]
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
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\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
|
||||
/**
|
||||
* Checks mount points, shortcuts and redirects to the target.
|
||||
* Alternatively, checks if the current page is an redirect to an external page
|
||||
*
|
||||
* @internal this middleware might get removed in TYPO3 v10.0.
|
||||
*/
|
||||
class ShortcutAndMountPointRedirect implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
private $controller;
|
||||
|
||||
public function __construct(TypoScriptFrontendController $controller = null)
|
||||
{
|
||||
$this->controller = $controller ?: $GLOBALS['TSFE'];
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
// Check for shortcut page and mount point redirect
|
||||
$redirectToUri = $this->getRedirectUri($request);
|
||||
if ($redirectToUri !== null && $redirectToUri !== (string)$request->getUri()) {
|
||||
return new RedirectResponse($redirectToUri, 307);
|
||||
}
|
||||
|
||||
// See if the current page is of doktype "External URL", if so, do a redirect as well.
|
||||
if (empty($this->controller->config['config']['disablePageExternalUrl'] ?? null)
|
||||
&& PageRepository::DOKTYPE_LINK === (int)$this->controller->page['doktype']) {
|
||||
$externalUrl = $this->prefixExternalPageUrl(
|
||||
$this->controller->page['url'],
|
||||
$request->getAttribute('normalizedParams')->getSiteUrl()
|
||||
);
|
||||
if (!empty($externalUrl)) {
|
||||
return new RedirectResponse($externalUrl, 303);
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
protected function getRedirectUri(ServerRequestInterface $request): ?string
|
||||
{
|
||||
$redirectToUri = $this->controller->getRedirectUriForShortcut($request);
|
||||
if ($redirectToUri !== null) {
|
||||
return $redirectToUri;
|
||||
}
|
||||
return $this->controller->getRedirectUriForMountPoint($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the redirect URL for the input page row IF the doktype is set to 3.
|
||||
*
|
||||
* @param string $redirectTo The page row to return URL type for
|
||||
* @param string $sitePrefix if no protocol or relative path given, the site prefix is added
|
||||
* @return string The URL from based on the external page URL given with a prefix.
|
||||
*/
|
||||
protected function prefixExternalPageUrl(string $redirectTo, string $sitePrefix): string
|
||||
{
|
||||
$uI = parse_url($redirectTo);
|
||||
// If relative path, prefix Site URL
|
||||
// If it's a valid email without protocol, add "mailto:"
|
||||
if (!($uI['scheme'] ?? false)) {
|
||||
if (GeneralUtility::validEmail($redirectTo)) {
|
||||
$redirectTo = 'mailto:' . $redirectTo;
|
||||
} elseif ($redirectTo[0] !== '/') {
|
||||
$redirectTo = $sitePrefix . $redirectTo;
|
||||
}
|
||||
}
|
||||
return $redirectTo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Routing\SiteRouteResult;
|
||||
use TYPO3\CMS\Core\Site\Entity\Site;
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
|
||||
|
||||
/**
|
||||
* Resolves redirects of site if base is not /
|
||||
* Can be replaced or extended by extensions if GeoIP-based or user-agent based language redirects need to happen.
|
||||
*
|
||||
* Please note that the redirect usually does not contain the Query Parameters, as special query parameters
|
||||
* like "id", "L" and "cHash" could then result in an error loop.
|
||||
* One special case (adding a "/") is keeping the query parameters though.
|
||||
*/
|
||||
class SiteBaseRedirectResolver implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Redirect to default language if required
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$site = $request->getAttribute('site', null);
|
||||
$language = $request->getAttribute('language', null);
|
||||
$routeResult = $request->getAttribute('routing', null);
|
||||
|
||||
// Usually called when "https://www.example.com" was entered, but all sites have "https://www.example.com/lang-key/"
|
||||
// So a redirect to the first possible language is done.
|
||||
if ($site instanceof Site && !($language instanceof SiteLanguage)) {
|
||||
if ($routeResult instanceof SiteRouteResult && $routeResult->getTail() === '') {
|
||||
$language = $site->getDefaultLanguage();
|
||||
return new RedirectResponse($language->getBase(), 307);
|
||||
}
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'The requested page does not exist',
|
||||
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
|
||||
);
|
||||
}
|
||||
|
||||
// Language is found, and hidden but also not visible to the BE user, this needs to fail
|
||||
if ($language instanceof SiteLanguage && !$this->isLanguageEnabled($language, $GLOBALS['BE_USER'] ?? null)) {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
|
||||
$request,
|
||||
'Page is not available in the requested language.',
|
||||
['code' => PageAccessFailureReasons::LANGUAGE_NOT_AVAILABLE]
|
||||
);
|
||||
}
|
||||
|
||||
if ($language instanceof SiteLanguage && $routeResult instanceof SiteRouteResult) {
|
||||
$requestedUri = $request->getUri();
|
||||
$tail = $routeResult->getTail();
|
||||
// a URL was called via "/fr-FR/" but the page is actually called "/fr-FR", let's do a redirect
|
||||
if ($tail === '/') {
|
||||
$uri = $requestedUri->withPath(rtrim($requestedUri->getPath(), '/'));
|
||||
return new RedirectResponse($uri, 307);
|
||||
}
|
||||
}
|
||||
return $handler->handle($request);
|
||||
}
|
||||
/**
|
||||
* Checks if the language is allowed in Frontend, if not, check if there is valid BE user
|
||||
*
|
||||
* @param SiteLanguage|null $language
|
||||
* @param BackendUserAuthentication|null $user
|
||||
* @return bool
|
||||
*/
|
||||
protected function isLanguageEnabled(SiteLanguage $language, BackendUserAuthentication $user = null): bool
|
||||
{
|
||||
// language is hidden, check if a possible backend user is allowed to access the language
|
||||
if ($language->enabled() || ($user instanceof BackendUserAuthentication && $user->checkLanguageAccess($language->getLanguageId()))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\Routing\SiteMatcher;
|
||||
use TYPO3\CMS\Core\Routing\SiteRouteResult;
|
||||
use TYPO3\CMS\Core\Site\SiteFinder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Identifies if a site is configured for the request, based on "id" and "L" GET/POST parameters, or the requested
|
||||
* string.
|
||||
*
|
||||
* If a site is found, the request is populated with the found language+site objects. If none is found, the main magic
|
||||
* is handled by the PageResolver middleware.
|
||||
*/
|
||||
class SiteResolver implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var SiteMatcher
|
||||
*/
|
||||
protected $matcher;
|
||||
|
||||
public function __construct(SiteMatcher $matcher = null)
|
||||
{
|
||||
$this->matcher = $matcher ?? GeneralUtility::makeInstance(
|
||||
SiteMatcher::class,
|
||||
GeneralUtility::makeInstance(SiteFinder::class)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the site/language information by checking the page ID or the URL.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
/** @var SiteRouteResult $routeResult */
|
||||
$routeResult = $this->matcher->matchRequest($request);
|
||||
$request = $request->withAttribute('site', $routeResult->getSite());
|
||||
$request = $request->withAttribute('language', $routeResult->getLanguage());
|
||||
$request = $request->withAttribute('routing', $routeResult);
|
||||
|
||||
// At this point, we later get further route modifiers
|
||||
// for bw-compat we update $GLOBALS[TYPO3_REQUEST] to be used later in TSFE.
|
||||
$GLOBALS['TYPO3_REQUEST'] = $request;
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
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\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Http\RequestFactory;
|
||||
use TYPO3\CMS\Core\Http\Response;
|
||||
use TYPO3\CMS\Core\LinkHandling\LinkService;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException;
|
||||
use TYPO3\CMS\Core\Routing\RouterInterface;
|
||||
use TYPO3\CMS\Core\Site\Entity\Site;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Resolves static routes - can return configured content directly or load content from file / urls
|
||||
*/
|
||||
class StaticRouteResolver implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Checks if there is a valid site with route configuration.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
if (($site = $request->getAttribute('site', null)) instanceof Site &&
|
||||
($configuration = $site->getConfiguration()['routes'] ?? null)
|
||||
) {
|
||||
$path = ltrim($request->getUri()->getPath(), '/');
|
||||
$routeConfig = $this->getApplicableStaticRoute($configuration, $site, $path);
|
||||
if (is_array($routeConfig)) {
|
||||
try {
|
||||
[$content, $contentType] = $this->resolveByType($request, $site, $routeConfig['type'], $routeConfig);
|
||||
} catch (InvalidRouteArgumentsException $e) {
|
||||
return new Response('Invalid route', 404, ['Content-Type' => 'text/plain']);
|
||||
}
|
||||
|
||||
return new HtmlResponse($content, 200, ['Content-Type' => $contentType]);
|
||||
}
|
||||
}
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the proper configuration for the static route in the static route configuration. Mainly:
|
||||
* - needs to have a valid "route" property
|
||||
* - needs to have a "type"
|
||||
*
|
||||
* @param array $staticRouteConfiguration the "routes" part of the site configuration
|
||||
* @param Site $site the current site where the configuration is based on
|
||||
* @param string $uriPath the path of the current request - used to match the "route" value of a single static route
|
||||
* @return array|null the configuration for the static route that matches, or null if no route is given
|
||||
*/
|
||||
protected function getApplicableStaticRoute(array $staticRouteConfiguration, Site $site, string $uriPath): ?array
|
||||
{
|
||||
$routeNames = array_map(function (?string $route) use ($site) {
|
||||
if ($route === null || $route === '') {
|
||||
return null;
|
||||
}
|
||||
return ltrim(trim($site->getBase()->getPath(), '/') . '/' . ltrim($route, '/'), '/');
|
||||
}, array_column($staticRouteConfiguration, 'route'));
|
||||
// Remove empty routes which would throw an error (could happen within creating a false route in the GUI)
|
||||
$routeNames = array_filter($routeNames);
|
||||
|
||||
if (in_array($uriPath, $routeNames, true)) {
|
||||
$key = array_search($uriPath, $routeNames, true);
|
||||
// Only allow routes with a type "given"
|
||||
if (isset($staticRouteConfiguration[$key]['type'])) {
|
||||
return $staticRouteConfiguration[$key];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File $file
|
||||
* @return array
|
||||
*/
|
||||
protected function getFromFile(File $file): array
|
||||
{
|
||||
$content = $file->getContents();
|
||||
$contentType = $file->getMimeType();
|
||||
return [$content, $contentType];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @return array
|
||||
*/
|
||||
protected function getFromUri(string $uri): array
|
||||
{
|
||||
$requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
|
||||
$response = $requestFactory->request($uri);
|
||||
$contentType = 'text/plain; charset=utf-8';
|
||||
$content = '';
|
||||
if ($response->getStatusCode() === 200) {
|
||||
$content = $response->getBody()->getContents();
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
}
|
||||
|
||||
return [$content, $contentType];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param Site $site
|
||||
* @param array $urlParams
|
||||
* @return string
|
||||
* @throws InvalidRouteArgumentsException
|
||||
*/
|
||||
protected function getPageUri(ServerRequestInterface $request, Site $site, array $urlParams): string
|
||||
{
|
||||
$parameters = [];
|
||||
// Add additional parameters, if set via TypoLink
|
||||
if (isset($urlParams['parameters'])) {
|
||||
parse_str($urlParams['parameters'], $parameters);
|
||||
}
|
||||
$parameters['type'] = $urlParams['pagetype'] ?? 0;
|
||||
$parameters['_language'] = $request->getAttribute('language', null);
|
||||
$uri = $site->getRouter()->generateUri(
|
||||
(int)$urlParams['pageuid'],
|
||||
$parameters,
|
||||
'',
|
||||
RouterInterface::ABSOLUTE_URL
|
||||
);
|
||||
return (string)$uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @param Site $site
|
||||
* @param string $type
|
||||
* @param array $routeConfig
|
||||
* @return array
|
||||
* @throws InvalidRouteArgumentsException
|
||||
*/
|
||||
protected function resolveByType(ServerRequestInterface $request, Site $site, string $type, array $routeConfig): array
|
||||
{
|
||||
switch ($type) {
|
||||
case 'staticText':
|
||||
$content = $routeConfig['content'];
|
||||
$contentType = 'text/plain; charset=utf-8';
|
||||
break;
|
||||
case 'uri':
|
||||
$linkService = GeneralUtility::makeInstance(LinkService::class);
|
||||
$urlParams = $linkService->resolve($routeConfig['source']);
|
||||
if ($urlParams['type'] === 'url' || $urlParams['type'] === 'page') {
|
||||
$uri = $urlParams['url'] ?? $this->getPageUri($request, $site, $urlParams);
|
||||
[$content, $contentType] = $this->getFromUri($uri);
|
||||
} elseif ($urlParams['type'] === 'file') {
|
||||
[$content, $contentType] = $this->getFromFile($urlParams['file']);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Can only handle URIs of type page, url or file.', 1537348076);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException(
|
||||
'Can only handle static file configurations with type uri or staticText.',
|
||||
1537348083
|
||||
);
|
||||
}
|
||||
return [$content, $contentType];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Initializes the time tracker (singleton) for the whole TYPO3 Frontend
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TimeTrackerInitialization implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* Starting time tracking (by setting up a singleton object)
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']) ?: 'be_typo_user';
|
||||
$timeTracker = GeneralUtility::makeInstance(
|
||||
TimeTracker::class,
|
||||
!empty($request->getCookieParams()[$configuredCookieName])
|
||||
);
|
||||
$timeTracker->start();
|
||||
$timeTracker->push('');
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Frontend\Middleware;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
use Doctrine\DBAL\Exception\ConnectionException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\ErrorController;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* Creates an instance of TypoScriptFrontendController and makes this globally available
|
||||
* via $GLOBALS['TSFE'].
|
||||
*
|
||||
* For now, GeneralUtility::_GP() is used in favor of $request->getQueryParams() due to
|
||||
* hooks who could have $_GET/$_POST modified before.
|
||||
*
|
||||
* @internal this middleware might get removed in TYPO3 v10.0.
|
||||
*/
|
||||
class TypoScriptFrontendInitialization implements MiddlewareInterface, LoggerAwareInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
/**
|
||||
* Creates an instance of TSFE and sets it as a global variable,
|
||||
* also pings the database in order ensure a valid database connection.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$GLOBALS['TSFE'] = GeneralUtility::makeInstance(
|
||||
TypoScriptFrontendController::class,
|
||||
null,
|
||||
GeneralUtility::_GP('id'),
|
||||
GeneralUtility::_GP('type'),
|
||||
null,
|
||||
GeneralUtility::_GP('cHash'),
|
||||
null,
|
||||
GeneralUtility::_GP('MP')
|
||||
);
|
||||
if (GeneralUtility::_GP('no_cache')) {
|
||||
$GLOBALS['TSFE']->set_no_cache('&no_cache=1 has been supplied, so caching is disabled! URL: "' . (string)$request->getUri() . '"');
|
||||
}
|
||||
|
||||
// Set up the database connection and see if the connection can be established
|
||||
try {
|
||||
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
|
||||
$connection->connect();
|
||||
} catch (ConnectionException | \RuntimeException $exception) {
|
||||
$message = 'Cannot connect to the configured database';
|
||||
$this->logger->emergency($message, ['exception' => $exception]);
|
||||
try {
|
||||
return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, $message);
|
||||
} catch (ServiceUnavailableException $e) {
|
||||
throw new ServiceUnavailableException($message, 1526013723);
|
||||
}
|
||||
}
|
||||
// Call post processing function for DB connection:
|
||||
if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'])) {
|
||||
trigger_error('The "connectToDB" hook will be removed in TYPO3 v10.0 in favor of PSR-15. Use a middleware instead.', E_USER_DEPRECATED);
|
||||
$_params = ['pObj' => &$GLOBALS['TSFE']];
|
||||
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] as $_funcRef) {
|
||||
GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user