Skip to content
Snippets Groups Projects
Commit 0dd2f76d authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[TASK] Have site attribute in filelist module

When the Site construct has been added to TYPO3
in v9, the setup together with PseudoSites was
pretty complex. This has been simplified meanwhile
and the resolved site is usually added to the
backend request as attribute.

Except in cases where GET / POST param "id" is
misused. This is the case in filelist module.

The patch changes the backend related SiteResolver
middleware to *always* add a site attribute to
the request, backend controllers can now rely
on it to be set, even if its just a NullSite.

Resolves: #99121
Related: #86153
Releases: main
Change-Id: I63ea9ed1b5bca5db298f7a3bf000fcb4f777f627
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/76665


Tested-by: default avatarcore-ci <typo3@b13.com>
Tested-by: default avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: default avatarStefan Bürk <stefan@buerk.tech>
Tested-by: default avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarBenni Mack <benni@typo3.org>
Tested-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: default avatarChristian Kuhn <lolli@schwarzbu.ch>
parent f86ddcd7
Branches
Tags
No related merge requests found
...@@ -35,37 +35,33 @@ use TYPO3\CMS\Core\Utility\MathUtility; ...@@ -35,37 +35,33 @@ use TYPO3\CMS\Core\Utility\MathUtility;
*/ */
class SiteResolver implements MiddlewareInterface class SiteResolver implements MiddlewareInterface
{ {
/** public function __construct(
* @var SiteMatcher private readonly SiteMatcher $siteMatcher
*/ ) {
protected $siteMatcher;
public function __construct(SiteMatcher $siteMatcher)
{
$this->siteMatcher = $siteMatcher;
} }
/** /**
* Resolve the site information by checking the page ID ("id" parameter) which is typically used in BE modules * Resolve the site information by checking the page ID ("id" parameter) which is typically
* of type "web". * used in BE modules of type "web".
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/ */
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{ {
$pageId = ($request->getQueryParams()['id'] ?? $request->getParsedBody()['id'] ?? 0); $pageId = ($request->getQueryParams()['id'] ?? $request->getParsedBody()['id'] ?? 0);
// Check if we have a numeric _GET/_POST parameter for "id", then a site information can be resolved based. if (!MathUtility::canBeInterpretedAsInteger($pageId)) {
if (MathUtility::canBeInterpretedAsInteger($pageId)) { // @todo: The "filelist" module abuses "id" to carry a storage like "1:/" around. This
$pageId = (int)$pageId; // should be changed. To *always* have a site attribute attached to the request,
$rootLine = null; // we for now resolve to zero here, leading to NullSite object.
if ($pageId > 0) { // Change "filelist" module to no longer abuse "id" GET argument and throw an
$rootLine = BackendUtility::BEgetRootLine($pageId); // exception here if $pageUid can not be resolved to an int.
} $pageId = 0;
$site = $this->siteMatcher->matchByPageId($pageId, $rootLine); }
$request = $request->withAttribute('site', $site); $pageId = (int)$pageId;
$rootLine = null;
if ($pageId > 0) {
$rootLine = BackendUtility::BEgetRootLine($pageId);
} }
$site = $this->siteMatcher->matchByPageId($pageId, $rootLine);
$request = $request->withAttribute('site', $site);
return $handler->handle($request); return $handler->handle($request);
} }
} }
...@@ -15,7 +15,7 @@ declare(strict_types=1); ...@@ -15,7 +15,7 @@ declare(strict_types=1);
* The TYPO3 project - inspiring people to share! * The TYPO3 project - inspiring people to share!
*/ */
namespace TYPO3\CMS\Backend\Tests\Unit\Middleware; namespace TYPO3\CMS\Backend\Tests\Functional\Middleware;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
...@@ -23,36 +23,31 @@ use Psr\Http\Server\RequestHandlerInterface; ...@@ -23,36 +23,31 @@ use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Backend\Middleware\SiteResolver; use TYPO3\CMS\Backend\Middleware\SiteResolver;
use TYPO3\CMS\Core\Http\JsonResponse; use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Http\ServerRequest;
use TYPO3\CMS\Core\Routing\SiteMatcher; use TYPO3\CMS\Core\Site\Entity\NullSite;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class SiteResolverTest extends UnitTestCase class SiteResolverTest extends FunctionalTestCase
{ {
/** /**
* @test * @test
*/ */
public function requestIsNotModifiedIfPageIdParameterIsNoInteger(): void public function requestHasNullSiteAttributeIfIdParameterIsNoInteger(): void
{ {
$this->expectException(\RuntimeException::class);
$this->expectExceptionCode(1668696350);
$incomingUrl = 'http://localhost:8080/typo3/module/file/FilelistList?token=d7d864db2b26c1d0f0537718b16890f336f4af2b&id=9831:/styleguide/'; $incomingUrl = 'http://localhost:8080/typo3/module/file/FilelistList?token=d7d864db2b26c1d0f0537718b16890f336f4af2b&id=9831:/styleguide/';
$subject = $this->get(SiteResolver::class);
$siteMatcherMock = $this->createMock(SiteMatcher::class);
$subject = new SiteResolver($siteMatcherMock);
$incomingRequest = new ServerRequest($incomingUrl, 'GET'); $incomingRequest = new ServerRequest($incomingUrl, 'GET');
$incomingRequest = $incomingRequest->withQueryParams(['id' => '9831:/styleguide/']); $incomingRequest = $incomingRequest->withQueryParams(['id' => '9831:/styleguide/']);
$requestHandler = new class () implements RequestHandlerInterface { $requestHandler = new class () implements RequestHandlerInterface {
public ServerRequestInterface $incomingRequest;
public function handle(ServerRequestInterface $request): ResponseInterface public function handle(ServerRequestInterface $request): ResponseInterface
{ {
return new JsonResponse([], $request === $this->incomingRequest ? 200 : 500); if ($request->getAttribute('site') instanceof NullSite) {
} throw new \RuntimeException('testing', 1668696350);
public function setIncomingRequest(ServerRequestInterface $incomingRequest): void }
{ return new JsonResponse();
$this->incomingRequest = $incomingRequest;
} }
}; };
$requestHandler->setIncomingRequest($incomingRequest); $subject->process($incomingRequest, $requestHandler);
$response = $subject->process($incomingRequest, $requestHandler);
self::assertEquals(200, $response->getStatusCode());
} }
} }
...@@ -176,13 +176,12 @@ class SiteMatcher implements SingletonInterface ...@@ -176,13 +176,12 @@ class SiteMatcher implements SingletonInterface
* @param int $pageId uid of a page in default language * @param int $pageId uid of a page in default language
* @param array|null $rootLine an alternative root line, if already at and. * @param array|null $rootLine an alternative root line, if already at and.
* @return SiteInterface * @return SiteInterface
* @throws SiteNotFoundException
*/ */
public function matchByPageId(int $pageId, array $rootLine = null): SiteInterface public function matchByPageId(int $pageId, array $rootLine = null): SiteInterface
{ {
try { try {
return $this->finder->getSiteByPageId($pageId, $rootLine); return $this->finder->getSiteByPageId($pageId, $rootLine);
} catch (SiteNotFoundException $e) { } catch (SiteNotFoundException) {
return new NullSite(); return new NullSite();
} }
} }
......
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