diff --git a/typo3/sysext/redirects/Classes/Http/Middleware/RedirectHandler.php b/typo3/sysext/redirects/Classes/Http/Middleware/RedirectHandler.php index de3434c14db372a88384face24ef0c5b9f55ee36..1196610ced42d58d8648d78bdae37736c47fb6a7 100644 --- a/typo3/sysext/redirects/Classes/Http/Middleware/RedirectHandler.php +++ b/typo3/sysext/redirects/Classes/Http/Middleware/RedirectHandler.php @@ -61,7 +61,7 @@ class RedirectHandler implements MiddlewareInterface, LoggerAwareInterface // If the matched redirect is found, resolve it, and check further if (is_array($matchedRedirect)) { - $url = $this->redirectService->getTargetUrl($matchedRedirect, $request->getQueryParams(), $request->getAttribute('frontend.user'), $request->getUri(), $request->getAttribute('site')); + $url = $this->redirectService->getTargetUrl($matchedRedirect, $request); if ($url instanceof UriInterface) { $this->logger->debug('Redirecting', ['record' => $matchedRedirect, 'uri' => $url]); $response = $this->buildRedirectResponse($url, $matchedRedirect); diff --git a/typo3/sysext/redirects/Classes/Service/RedirectService.php b/typo3/sysext/redirects/Classes/Service/RedirectService.php index 9b8c12c034f8c2af6dc425730a31e81b82a8f237..00ce0a69ce8d722458b4a30c6ffede58f70b8e83 100644 --- a/typo3/sysext/redirects/Classes/Service/RedirectService.php +++ b/typo3/sysext/redirects/Classes/Service/RedirectService.php @@ -17,11 +17,13 @@ declare(strict_types=1); namespace TYPO3\CMS\Redirects\Service; +use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Domain\Repository\PageRepository; +use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\LinkHandling\LinkService; use TYPO3\CMS\Core\Resource\Exception\InvalidPathException; @@ -33,7 +35,6 @@ use TYPO3\CMS\Core\Site\Entity\SiteInterface; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\HttpUtility; -use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Service\TypoLinkCodecService; use TYPO3\CMS\Frontend\Typolink\AbstractTypolinkBuilder; @@ -184,8 +185,11 @@ class RedirectService implements LoggerAwareInterface return $linkDetails; } - public function getTargetUrl(array $matchedRedirect, array $queryParams, FrontendUserAuthentication $frontendUserAuthentication, UriInterface $uri, ?SiteInterface $site = null): ?UriInterface + public function getTargetUrl(array $matchedRedirect, ServerRequestInterface $request): ?UriInterface { + $site = $request->getAttribute('site'); + $uri = $request->getUri(); + $queryParams = $request->getQueryParams(); $this->logger->debug('Found a redirect to process', $matchedRedirect); $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode((string)$matchedRedirect['target']); $redirectTarget = $linkParameterParts['url']; @@ -212,11 +216,30 @@ class RedirectService implements LoggerAwareInterface } return $url; } - if (($site === null || $site instanceof NullSite) && $linkDetails['type'] === 'page') { - $site = $this->siteFinder->getSiteByPageId((int)$linkDetails['pageuid']); - } + $site = $this->resolveSite($linkDetails, $site); // If it's a record or page, then boot up TSFE and use typolink - return $this->getUriFromCustomLinkDetails($matchedRedirect, $frontendUserAuthentication, $site, $linkDetails, $queryParams); + return $this->getUriFromCustomLinkDetails( + $matchedRedirect, + $site, + $linkDetails, + $queryParams, + $request + ); + } + + /** + * If no site is given, try to find a valid site for the target page + */ + protected function resolveSite(array $linkDetails, ?SiteInterface $site): ?SiteInterface + { + if (($site === null || $site instanceof NullSite) && ($linkDetails['type'] ?? '') === 'page') { + try { + return $this->siteFinder->getSiteByPageId((int)$linkDetails['pageuid']); + } catch (SiteNotFoundException $e) { + return new NullSite(); + } + } + return $site; } /** @@ -241,12 +264,15 @@ class RedirectService implements LoggerAwareInterface /** * Called when TypoScript/TSFE is available, so typolink is used to generate the URL */ - protected function getUriFromCustomLinkDetails(array $redirectRecord, FrontendUserAuthentication $frontendUserAuthentication, ?SiteInterface $site, array $linkDetails, array $queryParams): ?UriInterface + protected function getUriFromCustomLinkDetails(array $redirectRecord, ?SiteInterface $site, array $linkDetails, array $queryParams, ServerRequestInterface $originalRequest): ?UriInterface { if (!isset($linkDetails['type'], $GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']])) { return null; } - $controller = $this->bootFrontendController($frontendUserAuthentication, $site, $queryParams); + if ($site === null || $site instanceof NullSite) { + return null; + } + $controller = $this->bootFrontendController($site, $queryParams, $originalRequest); /** @var AbstractTypolinkBuilder $linkBuilder */ $linkBuilder = GeneralUtility::makeInstance( $GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']], @@ -289,19 +315,17 @@ class RedirectService implements LoggerAwareInterface * * So a link to a page can be generated. */ - protected function bootFrontendController(FrontendUserAuthentication $frontendUserAuthentication, ?SiteInterface $site, array $queryParams): TypoScriptFrontendController + protected function bootFrontendController(SiteInterface $site, array $queryParams, ServerRequestInterface $originalRequest): TypoScriptFrontendController { - $pageId = $site ? $site->getRootPageId() : ($GLOBALS['TSFE'] ? $GLOBALS['TSFE']->id : 0); $controller = GeneralUtility::makeInstance( TypoScriptFrontendController::class, GeneralUtility::makeInstance(Context::class), $site, $site->getDefaultLanguage(), - new PageArguments((int)$pageId, '0', []), - $frontendUserAuthentication + new PageArguments($site->getRootPageId(), '0', []), + $frontendUserAuthentication = $originalRequest->getAttribute('frontend.user') ); - $controller->fetch_the_id(); - $controller->settingLanguage(); + $controller->determineId($originalRequest); $controller->calculateLinkVars($queryParams); $controller->getConfigArray(); $controller->newCObj(); diff --git a/typo3/sysext/redirects/Tests/Unit/Service/RedirectServiceTest.php b/typo3/sysext/redirects/Tests/Unit/Service/RedirectServiceTest.php index b9cd10fad2421c49a4d7e8d2737ab09bee647f84..e1e2f4ff612a67310c152de663d5553ab07fdeb7 100644 --- a/typo3/sysext/redirects/Tests/Unit/Service/RedirectServiceTest.php +++ b/typo3/sysext/redirects/Tests/Unit/Service/RedirectServiceTest.php @@ -20,6 +20,7 @@ namespace TYPO3\CMS\Redirects\Tests\Unit\Service; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use Psr\Log\LoggerInterface; +use TYPO3\CMS\Core\Http\ServerRequest; use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\LinkHandling\LinkService; use TYPO3\CMS\Core\Resource\Exception\InvalidPathException; @@ -451,7 +452,7 @@ class RedirectServiceTest extends UnitTestCase { $this->linkServiceProphecy->resolve(Argument::any())->willThrow(new InvalidPathException('', 1516531195)); - $result = $this->redirectService->getTargetUrl(['target' => 'invalid'], [], new FrontendUserAuthentication(), new Uri(), new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl(['target' => 'invalid'], new ServerRequest(new Uri())); self::assertNull($result); } @@ -473,7 +474,9 @@ class RedirectServiceTest extends UnitTestCase $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails); $source = new Uri('https://example.com'); - $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new FrontendUserAuthentication(), $source, new Site('dummy', 13, [])); + $request = new ServerRequest($source); + $request = $request->withAttribute('site', new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request); $uri = new Uri('https://example.com/'); self::assertEquals($uri, $result); @@ -498,7 +501,9 @@ class RedirectServiceTest extends UnitTestCase $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails); $source = new Uri('https://example.com'); - $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new FrontendUserAuthentication(), $source, new Site('dummy', 13, [])); + $request = new ServerRequest($source); + $request = $request->withAttribute('site', new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request); $uri = new Uri('https://example.com/file.txt'); self::assertEquals($uri, $result); @@ -524,7 +529,9 @@ class RedirectServiceTest extends UnitTestCase $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails); $source = new Uri('https://example.com/'); - $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new FrontendUserAuthentication(), $source, new Site('dummy', 13, [])); + $request = new ServerRequest($source); + $request = $request->withAttribute('site', new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request); $uri = new Uri('https://example.com/folder/'); self::assertEquals($uri, $result); @@ -547,7 +554,9 @@ class RedirectServiceTest extends UnitTestCase $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails); $source = new Uri('https://example.com'); - $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new FrontendUserAuthentication(), $source, new Site('dummy', 13, [])); + $request = new ServerRequest($source); + $request = $request->withAttribute('site', new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request); $uri = new Uri('https://example.com'); self::assertEquals($uri, $result); @@ -570,7 +579,10 @@ class RedirectServiceTest extends UnitTestCase $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails); $source = new Uri('https://example.com/?bar=2&baz=4&foo=1'); - $result = $this->redirectService->getTargetUrl($redirectTargetMatch, ['bar' => 3, 'baz' => 4], new FrontendUserAuthentication(), $source, new Site('dummy', 13, [])); + $request = new ServerRequest($source); + $request = $request->withQueryParams(['bar' => 3, 'baz' => 4]); + $request = $request->withAttribute('site', new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request); $uri = new Uri('https://example.com/?bar=2&baz=4&foo=1'); self::assertEquals($uri, $result); @@ -612,10 +624,14 @@ class RedirectServiceTest extends UnitTestCase $frontendUserAuthentication = new FrontendUserAuthentication(); $site = new Site('dummy', 13, []); - $redirectService->expects(self::once())->method('getUriFromCustomLinkDetails') - ->with($redirectTargetMatch, $frontendUserAuthentication, $site, $linkDetails, $queryParams) + $request = new ServerRequest($uri); + $request = $request->withQueryParams($queryParams); + $request = $request->withAttribute('site', $site); + $request = $request->withAttribute('frontend.user', $frontendUserAuthentication); + $redirectService->expects(self::any())->method('getUriFromCustomLinkDetails') + ->with($redirectTargetMatch, $site, $linkDetails, $queryParams, $request) ->willReturn($uri); - $result = $redirectService->getTargetUrl($redirectTargetMatch, [], $frontendUserAuthentication, $uri, $site); + $result = $redirectService->getTargetUrl($redirectTargetMatch, $request); self::assertEquals($uri, $result); } @@ -639,7 +655,9 @@ class RedirectServiceTest extends UnitTestCase $this->linkServiceProphecy->resolve($redirectTargetMatch['target'])->willReturn($linkDetails); $source = new Uri('https://example.com/foo/bar'); - $result = $this->redirectService->getTargetUrl($redirectTargetMatch, [], new FrontendUserAuthentication(), $source, new Site('dummy', 13, [])); + $request = new ServerRequest($source); + $request = $request->withAttribute('site', new Site('dummy', 13, [])); + $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request); $uri = new Uri('https://anotherdomain.com/bar'); self::assertEquals($uri, $result);