‪TYPO3CMS  ‪main
RedirectServiceTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
21 use Psr\Log\NullLogger;
22 use Symfony\Component\DependencyInjection\Container;
38 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
39 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
40 
41 class ‪RedirectServiceTest extends FunctionalTestCase
42 {
44 
45  protected const ‪LANGUAGE_PRESETS = [];
46 
47  protected array ‪$coreExtensionsToLoad = ['redirects'];
48 
49  protected array ‪$testFilesToDelete = [];
50 
51  protected array ‪$configurationToUseInTestInstance = [
52  'FE' => [
53  'cacheHash' => [
54  'excludedParameters' => ['L', 'pk_campaign', 'pk_kwd', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'fbclid', 'msclkid'],
55  // @todo this should be tested explicitly - enabled and disabled
56  'enforceValidation' => false,
57  ],
58  ],
59  ];
60 
61  protected function ‪setUp(): void
62  {
63  parent::setUp();
64  $this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv');
65  $this->setUpBackendUser(1);
66  }
67 
68  protected function ‪tearDown(): void
69  {
70  foreach ($this->testFilesToDelete as $filename) {
71  if (@is_file($filename)) {
72  unlink($filename);
73  }
74  }
75  parent::tearDown();
76  }
77 
82  {
83  $this->importCSVDataSet(__DIR__ . '/Fixtures/RedirectToAccessRestrictedPages.csv');
84 
86  'acme-com',
87  $this->‪buildSiteConfiguration(1, 'https://acme.com/')
88  );
89 
90  $typoscriptFile = ‪Environment::getVarPath() . '/transient/setup.typoscript';
91  file_put_contents($typoscriptFile, 'page = PAGE' . PHP_EOL . 'page.typeNum = 0');
92  $this->testFilesToDelete[] = $typoscriptFile;
93  $this->setUpFrontendRootPage(1, [$typoscriptFile]);
94 
95  $logger = new NullLogger();
96  $frontendUserAuthentication = new ‪FrontendUserAuthentication();
97  $frontendUserAuthentication->setLogger($logger);
98 
99  $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
100  $uri = new ‪Uri('https://acme.com/redirect-to-access-restricted-site');
101  $request = ‪$GLOBALS['TYPO3_REQUEST'] = (new ‪ServerRequest($uri))
102  ->withAttribute('site', $siteFinder->getSiteByRootPageId(1))
103  ->withAttribute('frontend.user', $frontendUserAuthentication)
104  ->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
105 
106  $linkServiceMock = $this->getMockBuilder(LinkService::class)->disableOriginalConstructor()->getMock();
107  $linkServiceMock->method('resolve')->with('t3://page?uid=2')->willReturn(
108  [
109  'pageuid' => 2,
110  'type' => ‪LinkService::TYPE_PAGE,
111  ]
112  );
113 
114  $redirectService = new ‪RedirectService(
116  $linkServiceMock,
117  $siteFinder,
119  );
120  $redirectService->setLogger($logger);
121 
122  // Assert correct redirect is matched
123  $redirectMatch = $redirectService->matchRedirect($uri->getHost(), $uri->getPath(), $uri->getQuery());
124  self::assertEquals(1, $redirectMatch['uid']);
125  self::assertEquals('t3://page?uid=2', $redirectMatch['target']);
126 
127  // Ensure we deal with an unauthorized request!
128  self::assertFalse(GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.user', 'isLoggedIn'));
129 
130  // Assert link to access restricted page is build
131  ‪$targetUrl = $redirectService->getTargetUrl($redirectMatch, $request);
132  self::assertEquals(new ‪Uri('https://acme.com/access-restricted'), ‪$targetUrl);
133  }
134 
135  public static function ‪redirectsDataProvider(): array
136  {
137  return [
138  [
139  'https://acme.com/redirect-301',
140  301,
141  'https://acme.com/',
142  1,
143  ],
144  [
145  'https://acme.com/redirect-308',
146  308,
147  'https://acme.com/page2',
148  2,
149  ],
150  [
151  'https://acme.com/redirect-302',
152  302,
153  'https://www.typo3.org',
154  3,
155  ],
156  ];
157  }
158 
163  public function ‪checkReponseCodeOnRedirect(‪$url, ‪$statusCode, ‪$targetUrl, $redirectUid): void
164  {
165  $this->importCSVDataSet(__DIR__ . '/Fixtures/RedirectToPages.csv');
166 
168  'acme-com',
169  $this->‪buildSiteConfiguration(1, 'https://acme.com/')
170  );
171 
172  $this->setUpFrontendRootPage(
173  1,
174  ['typo3/sysext/redirects/Tests/Functional/Service/Fixtures/Redirects.typoscript']
175  );
176 
177  $response = $this->executeFrontendSubRequest(
178  new InternalRequest(‪$url)
179  );
180  self::assertEquals(‪$statusCode, $response->getStatusCode());
181  self::assertIsArray($response->getHeader('X-Redirect-By'));
182  self::assertIsArray($response->getHeader('location'));
183  self::assertEquals('TYPO3 Redirect ' . $redirectUid, $response->getHeader('X-Redirect-By')[0]);
184  self::assertEquals(‪$targetUrl, $response->getHeader('location')[0]);
185  }
186 
187  public static function ‪checkRegExpRedirectsDataProvider(): array
188  {
189  return [
190  'regexp redirect respecting query parameter but not keeping them' => [
191  'https://acme.com/index.php?option=com_content&page=some_page',
192  301,
193  'https://anotherdomain.com/some_page',
194  1,
195  ],
196  'regexp redirect respecting query parameter and keeping them' => [
197  'https://acme.com/index.php?option=com_content2&page=some_page',
198  301,
199  'https://anotherdomain.com/some_page?option=com_content2&page=some_page',
200  2,
201  ],
202  'regexp redirect not respecting query parameters and not keeping them' => [
203  'https://acme.com/some-old-page-others?option=com_content',
204  301,
205  'https://anotherdomain.com/others',
206  3,
207  ],
208  'regexp redirect not respecting query parameters but keeping them' => [
209  'https://acme.com/some-page-others',
210  301,
211  'https://anotherdomain.com/others',
212  4,
213  ],
214  'regexp redirect not respecting query parameters and not keeping them, with query parameter in request' => [
215  'https://acme.com/some-old-page-others?option=com_content',
216  301,
217  'https://anotherdomain.com/others',
218  3,
219  ],
220  'regexp redirect not respecting query parameters but keeping them, without query parameter in request' => [
221  'https://acme.com/some-page-others',
222  301,
223  'https://anotherdomain.com/others',
224  4,
225  ],
226  // check against unsafe regexp captching group
227  'regexp redirect with unsafe captching group, respecting query parameters and not keeping them, with query parameter in request' => [
228  'https://acme.com/unsafe-captchinggroup-matching-queryparameters-others?option=com_content',
229  301,
230  'https://anotherdomain.com/others',
231  5,
232  ],
233  // checks against unsafe regexp captching group, but as keeping query parameters this may be undetected,
234  // and as such this test acts as counterpart to tests above
235  'regexp redirect with unsafe captching group, respecting query parameters but keeping them, with query parameter in request' => [
236  'https://acme.com/another-unsafe-captchinggroup-matching-queryparameters-others?option=com_content',
237  301,
238  'https://anotherdomain.com/others?option=com_content',
239  6,
240  ],
241  // check against safe regexp captching group
242  'regexp redirect safe captching group, respecting query parameters and not keeping them, with query parameter in request' => [
243  'https://acme.com/safe-captchinggroup-not-matching-queryparameters-others?option=com_content',
244  301,
245  'https://anotherdomain.com/others',
246  7,
247  ],
248  // checks against safe regexp captching group
249  'regexp redirect safe captching group, respecting query parameters but keeping them, with query parameter in request' => [
250  'https://acme.com/another-safe-captchinggroup-not-matching-queryparameters-others?option=com_content',
251  301,
252  'https://anotherdomain.com/others?option=com_content',
253  8,
254  ],
255  // check against more safe regexp captching group - this tests path fallback even with queryparameters in
256  // request for non query regexp with $ as end matching in regexp
257  'regexp redirect safe captching group, not respecting query parameters and not keeping them, with query parameter in request' => [
258  'https://acme.com/more-safe-captchinggroup-not-matching-queryparameters-others?option=com_content',
259  301,
260  'https://anotherdomain.com/others',
261  9,
262  ],
263  'regexp redirect safe captching group, not respecting query parameters but keeping them, with query parameter in request' => [
264  'https://acme.com/another-more-safe-captchinggroup-not-matching-queryparameters-others?option=com_content',
265  301,
266  'https://anotherdomain.com/others?option=com_content',
267  10,
268  ],
269  'regexp capture group with relative target' => [
270  'https://acme.com/relative-target-page2',
271  301,
272  '/page2',
273  11,
274  ],
275  'regexp capture group with relative target - keep query params' => [
276  'https://acme.com/relative-target-keep-page2?param1=value1',
277  301,
278  '/page2?param1=value1',
279  12,
280  ],
281  'regexp capture group with relative target - respect query param' => [
282  'https://acme.com/respect-relative-target-page2?param1=subpage',
283  301,
284  '/page2/subpage',
285  13,
286  ],
287  'regexp capture group with relative target - respect query param and keep them' => [
288  'https://acme.com/respect-keep-relative-target-page2?param1=subpage',
289  301,
290  '/page2/subpage?param1=subpage',
291  14,
292  ],
293  // test for https://forge.typo3.org/issues/89799#note-14
294  'regexp relative target redirect with unsafe regexp and without ending $' => [
295  'https://acme.com/other-relative-target-with-unsafe-capture-group-new',
296  301,
297  '/offer-new',
298  15,
299  ],
300  // test for https://forge.typo3.org/issues/89799#note-14
301  'regexp redirect with unsafe regexp and without ending $' => [
302  'https://acme.com/other-redirect-with-unsafe-capture-group-new',
303  301,
304  'https://anotherdomain.com/offernew',
305  16,
306  ],
307  ];
308  }
309 
314  public function ‪checkRegExpRedirects(string ‪$url, int $expectedStatusCode, string $expectedRedirectUri, int $expectedRedirectUid)
315  {
316  $this->importCSVDataSet(__DIR__ . '/Fixtures/RedirectService_regexp.csv');
318  'acme-com',
319  $this->‪buildSiteConfiguration(1, 'https://acme.com/')
320  );
321  $this->setUpFrontendRootPage(
322  1,
323  ['typo3/sysext/redirects/Tests/Functional/Service/Fixtures/Redirects.typoscript']
324  );
325 
326  $response = $this->executeFrontendSubRequest(
327  new InternalRequest(‪$url),
328  null,
329  false
330  );
331  self::assertEquals($expectedStatusCode, $response->getStatusCode());
332  self::assertIsArray($response->getHeader('X-Redirect-By'));
333  self::assertIsArray($response->getHeader('location'));
334  self::assertEquals('TYPO3 Redirect ' . $expectedRedirectUid, $response->getHeader('X-Redirect-By')[0]);
335  self::assertEquals($expectedRedirectUri, $response->getHeader('location')[0]);
336  }
337 
338  public static function ‪samePathWithSameDomainT3TargetDataProvider(): array
339  {
340  return [
341  'flat' => [
342  'https://acme.com/flat-samehost-1',
343  'https://acme.com/',
344  200,
345  null,
346  null,
347  ],
348  // this should redirect and not pass through
349  'flat - with query parameters' => [
350  'https://acme.com/flat-samehost-1?param1=value1&cHash=e0527192caa60a6dac1e30af7cfeaf64',
351  'https://acme.com/',
352  301,
353  'https://acme.com/flat-samehost-1',
354  1,
355  ],
356  'flat keep_query_parameters' => [
357  'https://acme.com/flat-samehost-2',
358  'https://acme.com/',
359  200,
360  null,
361  null,
362  ],
363  'flat keep_query_parameters - with query parameters' => [
364  'https://acme.com/flat-samehost-2?param1=value1&cHash=e0527192caa60a6dac1e30af7cfeaf64',
365  'https://acme.com/',
366  200,
367  null,
368  null,
369  ],
370  'flat respect_query_parameters' => [
371  'https://acme.com/flat-samehost-3',
372  'https://acme.com/',
373  200,
374  null,
375  null,
376  ],
377  // this should redirect and not pass through
378  'flat respect_query_parameters - with query parameters' => [
379  'https://acme.com/flat-samehost-3?param1=value1',
380  'https://acme.com/',
381  301,
382  'https://acme.com/flat-samehost-3',
383  3,
384  ],
385  'flat respect_query_parameters and keep_query_parameters' => [
386  'https://acme.com/flat-samehost-4',
387  'https://acme.com/',
388  200,
389  null,
390  null,
391  ],
392  'flat respect_query_parameters and keep_query_parameters - with query parameters' => [
393  'https://acme.com/flat-samehost-4?param1=value1&cHash=caa2156411affc2d7c8c5169652c6e13',
394  'https://acme.com/',
395  200,
396  null,
397  null,
398  ],
399  'regexp' => [
400  'https://acme.com/regexp-samehost-1',
401  'https://acme.com/',
402  200,
403  null,
404  null,
405  ],
406  // this should redirect and not pass through
407  'regexp - with query parameters' => [
408  'https://acme.com/regexp-samehost-1?param1=value1',
409  'https://acme.com/',
410  301,
411  'https://acme.com/regexp-samehost-1',
412  5,
413  ],
414  'regexp keep_query_parameters' => [
415  'https://acme.com/regexp-samehost-2',
416  'https://acme.com/',
417  200,
418  null,
419  null,
420  ],
421  'regexp keep_query_parameters - with query parameters' => [
422  'https://acme.com/regexp-samehost-2?param1=value1&cHash=feced69fa13ce7d3bf0483c21ff03064',
423  'https://acme.com/',
424  200,
425  null,
426  null,
427  ],
428  // this should redirect and not pass through
429  'regexp keep_query_parameters - with query parameters but without cHash' => [
430  'https://acme.com/regexp-samehost-2?param1=value1',
431  'https://acme.com/',
432  301,
433  'https://acme.com/regexp-samehost-2?param1=value1&cHash=feced69fa13ce7d3bf0483c21ff03064',
434  6,
435  ],
436  'regexp respect_query_parameters' => [
437  'https://acme.com/regexp-samehost-3',
438  'https://acme.com/',
439  200,
440  null,
441  null,
442  ],
443  // this should redirect and not pass through
444  'regexp respect_query_parameters - with query parameters but without cHash' => [
445  'https://acme.com/regexp-samehost-3?param1=value1',
446  'https://acme.com/',
447  301,
448  'https://acme.com/regexp-samehost-3',
449  7,
450  ],
451  'same host as external target with query arguments in another order than target should pass instead of redirect' => [
452  'https://acme.com/sanatize-samehost-3?param1=value1&param2=value2&param3=&cHash=69f1b01feb7ed14b95b85cbc66ee2a3a',
453  'https://acme.com/',
454  200,
455  null,
456  null,
457  ],
458  'same host as external target with fragment should pass instead of redirect' => [
459  'https://acme.com/sanatize-samehost-4',
460  'https://acme.com/',
461  200,
462  null,
463  null,
464  ],
465  ];
466  }
467 
472  public function ‪samePathWithSameDomainT3Target(string ‪$url, string $baseUri, int $expectedStatusCode, ?string $expectedRedirectUri, ?int $expectedRedirectUid): void
473  {
474  $this->importCSVDataSet(__DIR__ . '/Fixtures/RedirectService_samePathWithSameDomainT3Target.csv');
476  'acme-com',
477  $this->‪buildSiteConfiguration(1, $baseUri)
478  );
479  $this->setUpFrontendRootPage(
480  1,
481  ['typo3/sysext/redirects/Tests/Functional/Service/Fixtures/Redirects.typoscript']
482  );
483 
484  $response = $this->executeFrontendSubRequest(
485  new InternalRequest(‪$url),
486  null,
487  false
488  );
489  self::assertEquals($expectedStatusCode, $response->getStatusCode());
490  if ($expectedRedirectUri) {
491  self::assertIsArray($response->getHeader('X-Redirect-By'));
492  self::assertIsArray($response->getHeader('location'));
493  self::assertEquals('TYPO3 Redirect ' . $expectedRedirectUid, $response->getHeader('X-Redirect-By')[0]);
494  self::assertEquals($expectedRedirectUri, $response->getHeader('location')[0]);
495  }
496  }
497 
498  public static function ‪samePathWithSameDomainAndRelativeTargetDataProvider(): array
499  {
500  return [
501  'flat' => [
502  'https://acme.com/flat-samehost-1',
503  'https://acme.com/',
504  200,
505  null,
506  null,
507  ],
508  // this should redirect and not pass through
509  'flat - with query parameters' => [
510  'https://acme.com/flat-samehost-1?param1=value1&cHash=e0527192caa60a6dac1e30af7cfeaf64',
511  'https://acme.com/',
512  301,
513  '/flat-samehost-1',
514  1,
515  ],
516  'flat keep_query_parameters' => [
517  'https://acme.com/flat-samehost-2',
518  'https://acme.com/',
519  200,
520  null,
521  null,
522  ],
523  'flat keep_query_parameters - with query parameters' => [
524  'https://acme.com/flat-samehost-2?param1=value1&cHash=e0527192caa60a6dac1e30af7cfeaf64',
525  'https://acme.com/',
526  200,
527  null,
528  null,
529  ],
530  'flat respect_query_parameters' => [
531  'https://acme.com/flat-samehost-3',
532  'https://acme.com/',
533  200,
534  null,
535  null,
536  ],
537  // this should redirect and not pass through
538  'flat respect_query_parameters - with query parameters' => [
539  'https://acme.com/flat-samehost-3?param1=value1',
540  'https://acme.com/',
541  301,
542  '/flat-samehost-3',
543  3,
544  ],
545  'flat respect_query_parameters and keep_query_parameters' => [
546  'https://acme.com/flat-samehost-4',
547  'https://acme.com/',
548  200,
549  null,
550  null,
551  ],
552  'flat respect_query_parameters and keep_query_parameters - with query parameters' => [
553  'https://acme.com/flat-samehost-4?param1=value1&cHash=caa2156411affc2d7c8c5169652c6e13',
554  'https://acme.com/',
555  200,
556  null,
557  null,
558  ],
559  'regexp' => [
560  'https://acme.com/regexp-samehost-1',
561  'https://acme.com/',
562  200,
563  null,
564  null,
565  ],
566  // this should redirect and not pass through
567  'regexp - with query parameters' => [
568  'https://acme.com/regexp-samehost-1?param1=value1',
569  'https://acme.com/',
570  301,
571  '/regexp-samehost-1',
572  5,
573  ],
574  'regexp keep_query_parameters' => [
575  'https://acme.com/regexp-samehost-2',
576  'https://acme.com/',
577  200,
578  null,
579  null,
580  ],
581  'regexp keep_query_parameters - with query parameters' => [
582  'https://acme.com/regexp-samehost-2?param1=value1&cHash=feced69fa13ce7d3bf0483c21ff03064',
583  'https://acme.com/',
584  200,
585  null,
586  null,
587  ],
588  'regexp keep_query_parameters - with query parameters but without cHash' => [
589  'https://acme.com/regexp-samehost-2?param1=value1',
590  'https://acme.com/',
591  200,
592  null,
593  null,
594  ],
595  'regexp respect_query_parameters' => [
596  'https://acme.com/regexp-samehost-3',
597  'https://acme.com/',
598  200,
599  null,
600  null,
601  ],
602  // this should redirect and not pass through
603  'regexp respect_query_parameters - with query parameters but without cHash' => [
604  'https://acme.com/regexp-samehost-3?param1=value1',
605  'https://acme.com/',
606  301,
607  '/regexp-samehost-3',
608  7,
609  ],
610  ];
611  }
612 
617  public function ‪samePathWithSameDomainAndRelativeTarget(string ‪$url, string $baseUri, int $expectedStatusCode, ?string $expectedRedirectUri, ?int $expectedRedirectUid): void
618  {
619  $this->importCSVDataSet(__DIR__ . '/Fixtures/RedirectService_samePathWithSameDomainAndRelativeTarget.csv');
621  'acme-com',
622  $this->‪buildSiteConfiguration(1, $baseUri)
623  );
624  $this->setUpFrontendRootPage(
625  1,
626  ['typo3/sysext/redirects/Tests/Functional/Service/Fixtures/Redirects.typoscript']
627  );
628 
629  $response = $this->executeFrontendSubRequest(
630  new InternalRequest(‪$url),
631  null,
632  false
633  );
634  self::assertEquals($expectedStatusCode, $response->getStatusCode());
635  if ($expectedRedirectUri) {
636  self::assertIsArray($response->getHeader('X-Redirect-By'));
637  self::assertIsArray($response->getHeader('location'));
638  self::assertEquals('TYPO3 Redirect ' . $expectedRedirectUid, $response->getHeader('X-Redirect-By')[0]);
639  self::assertEquals($expectedRedirectUri, $response->getHeader('location')[0]);
640  }
641  }
642 
643  public static function ‪samePathRedirectsWithExternalTargetDataProvider(): array
644  {
645  return [
646  'flat' => [
647  'https://acme.com/flat-samehost-1',
648  'https://acme.com/',
649  301,
650  'https://external.acme.com/flat-samehost-1',
651  1,
652  ],
653  'flat - with query parameters' => [
654  'https://acme.com/flat-samehost-1?param1=value1&cHash=e0527192caa60a6dac1e30af7cfeaf64',
655  'https://acme.com/',
656  301,
657  'https://external.acme.com/flat-samehost-1',
658  1,
659  ],
660  'flat keep_query_parameters' => [
661  'https://acme.com/flat-samehost-2',
662  'https://acme.com/',
663  301,
664  'https://external.acme.com/flat-samehost-2',
665  2,
666  ],
667  'flat keep_query_parameters - with query parameters' => [
668  'https://acme.com/flat-samehost-2?param1=value1',
669  'https://acme.com/',
670  301,
671  'https://external.acme.com/flat-samehost-2?param1=value1',
672  2,
673  ],
674  // following will not match at all, so it is expected to be resolved with 200
675  'flat respect_query_parameters' => [
676  'https://acme.com/flat-samehost-3',
677  'https://acme.com/',
678  200,
679  null,
680  null,
681  ],
682  'flat respect_query_parameters - with query parameters' => [
683  'https://acme.com/flat-samehost-3?param1=value1',
684  'https://acme.com/',
685  301,
686  'https://external.acme.com/flat-samehost-3',
687  3,
688  ],
689  // following will not match at all, so it is expected to be resolved with 200
690  'flat respect_query_parameters and keep_query_parameters' => [
691  'https://acme.com/flat-samehost-4',
692  'https://acme.com/',
693  200,
694  null,
695  null,
696  ],
697  'flat respect_query_parameters and keep_query_parameters - with query parameters' => [
698  'https://acme.com/flat-samehost-4?param1=value1',
699  'https://acme.com/',
700  301,
701  'https://external.acme.com/flat-samehost-4?param1=value1',
702  4,
703  ],
704  'regexp' => [
705  'https://acme.com/regexp-samehost-1',
706  'https://acme.com/',
707  301,
708  'https://external.acme.com/regexp-samehost-1',
709  5,
710  ],
711  'regexp - with query parameters' => [
712  'https://acme.com/regexp-samehost-1?param1=value1',
713  'https://acme.com/',
714  301,
715  'https://external.acme.com/regexp-samehost-1',
716  5,
717  ],
718  'regexp keep_query_parameters' => [
719  'https://acme.com/regexp-samehost-2',
720  'https://acme.com/',
721  301,
722  'https://external.acme.com/regexp-samehost-2',
723  6,
724  ],
725  'regexp keep_query_parameters - with query parameters' => [
726  'https://acme.com/regexp-samehost-2?param1=value1',
727  'https://acme.com/',
728  301,
729  'https://external.acme.com/regexp-samehost-2?param1=value1',
730  6,
731  ],
732  'regexp respect_query_parameters' => [
733  'https://acme.com/regexp-samehost-3',
734  'https://acme.com/',
735  301,
736  'https://external.acme.com/regexp-samehost-3',
737  7,
738  ],
739  // this should redirect and not pass through
740  'regexp respect_query_parameters - with query parameters but without cHash' => [
741  'https://acme.com/regexp-samehost-3?param1=value1',
742  'https://acme.com/',
743  301,
744  'https://external.acme.com/regexp-samehost-3',
745  7,
746  ],
747  'same host as external target with port should pass instead of redirect' => [
748  'https://acme.com/sanatize-samehost-1',
749  'https://acme.com/',
750  200,
751  null,
752  null,
753  ],
754  'same host as external target with userinfo should pass instead of redirect' => [
755  'https://acme.com/sanatize-samehost-2',
756  'https://acme.com/',
757  200,
758  null,
759  null,
760  ],
761  'same host as external target with query arguments in another order than target should pass instead of redirect' => [
762  'https://acme.com/sanatize-samehost-3?param1=value1&param2=value2&param3=',
763  'https://acme.com/',
764  200,
765  null,
766  null,
767  ],
768  'same host as external target with fragment should pass instead of redirect' => [
769  'https://acme.com/sanatize-samehost-4',
770  'https://acme.com/',
771  200,
772  null,
773  null,
774  ],
775  ];
776  }
777 
782  public function ‪samePathRedirectsWithExternalTarget(string ‪$url, string $baseUri, int $expectedStatusCode, ?string $expectedRedirectUri, ?int $expectedRedirectUid): void
783  {
784  $this->importCSVDataSet(__DIR__ . '/Fixtures/RedirectService_samePathRedirectsWithExternalTarget.csv');
786  'acme-com',
787  $this->‪buildSiteConfiguration(1, $baseUri)
788  );
789  $this->setUpFrontendRootPage(
790  1,
791  ['typo3/sysext/redirects/Tests/Functional/Service/Fixtures/Redirects.typoscript']
792  );
793 
794  $response = $this->executeFrontendSubRequest(
795  new InternalRequest(‪$url),
796  null,
797  false
798  );
799  self::assertEquals($expectedStatusCode, $response->getStatusCode());
800  if ($expectedRedirectUri) {
801  self::assertIsArray($response->getHeader('X-Redirect-By'));
802  self::assertIsArray($response->getHeader('location'));
803  self::assertEquals('TYPO3 Redirect ' . $expectedRedirectUid, $response->getHeader('X-Redirect-By')[0]);
804  self::assertEquals($expectedRedirectUri, $response->getHeader('location')[0]);
805  }
806  }
807 
811  public function ‪beforeRedirectMatchDomainEventIsTriggered(): void
812  {
813  $this->importCSVDataSet(__DIR__ . '/Fixtures/BeforeRedirectMatchDomainEventIsTriggered.csv');
815  'acme-com',
816  $this->‪buildSiteConfiguration(1, 'https://acme.com/')
817  );
818  $typoscriptFile = ‪Environment::getVarPath() . '/transient/setup.typoscript';
819  file_put_contents($typoscriptFile, 'page = PAGE' . PHP_EOL . 'page.typeNum = 0');
820  $this->testFilesToDelete[] = $typoscriptFile;
821  $this->setUpFrontendRootPage(1, [$typoscriptFile]);
822 
823  $logger = new NullLogger();
824  $frontendUserAuthentication = new FrontendUserAuthentication();
825 
826  $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
827  $uri = new Uri('https://acme.com/non-existing-page');
828  $request = ‪$GLOBALS['TYPO3_REQUEST'] = (new ServerRequest($uri))
829  ->withAttribute('site', $siteFinder->getSiteByRootPageId(1))
830  ->withAttribute('frontend.user', $frontendUserAuthentication)
831  ->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_FE);
832 
833  $dispatchedEvents = [];
835  $container = $this->getContainer();
836  $container->set(
837  'before-redirect-match-domain-event-is-triggered',
838  static function (BeforeRedirectMatchDomainEvent $event) use (
839  &$dispatchedEvents
840  ): void {
841  $dispatchedEvents[] = $event;
842  if ($event->getMatchDomainName() === '*') {
843  $event->setMatchedRedirect(['wildcard-manual-matched' => $event->getPath()]);
844  }
845  }
846  );
847 
848  $eventListener = $container->get(ListenerProvider::class);
849  $eventListener->addListener(BeforeRedirectMatchDomainEvent::class, 'before-redirect-match-domain-event-is-triggered');
850 
851  $redirectService = new RedirectService(
852  new RedirectCacheService(),
853  new LinkService(),
854  $siteFinder,
855  $this->get(EventDispatcherInterface::class),
856  );
857  $redirectService->setLogger($logger);
858 
859  $redirectMatch = $redirectService->matchRedirect($uri->getHost(), $uri->getPath(), $uri->getQuery());
860 
861  self::assertCount(2, $dispatchedEvents);
862  self::assertNull($dispatchedEvents[0]->getMatchedRedirect());
863  self::assertEquals(['wildcard-manual-matched' => $uri->getPath()], $dispatchedEvents[1]->getMatchedRedirect());
864  self::assertSame(['wildcard-manual-matched' => $uri->getPath()], $redirectMatch);
865  }
866 }
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\samePathRedirectsWithExternalTargetDataProvider
‪static samePathRedirectsWithExternalTargetDataProvider()
Definition: RedirectServiceTest.php:642
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\redirectsDataProvider
‪static redirectsDataProvider()
Definition: RedirectServiceTest.php:134
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\samePathWithSameDomainT3TargetDataProvider
‪static samePathWithSameDomainT3TargetDataProvider()
Definition: RedirectServiceTest.php:337
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\linkForRedirectToAccessRestrictedPageIsBuild
‪linkForRedirectToAccessRestrictedPageIsBuild()
Definition: RedirectServiceTest.php:80
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\samePathWithSameDomainAndRelativeTargetDataProvider
‪static samePathWithSameDomainAndRelativeTargetDataProvider()
Definition: RedirectServiceTest.php:497
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\checkRegExpRedirects
‪checkRegExpRedirects(string $url, int $expectedStatusCode, string $expectedRedirectUri, int $expectedRedirectUid)
Definition: RedirectServiceTest.php:313
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder
Definition: SystemEnvironmentBuilder.php:41
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait
Definition: SiteBasedTestTrait.php:37
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\beforeRedirectMatchDomainEventIsTriggered
‪beforeRedirectMatchDomainEventIsTriggered()
Definition: RedirectServiceTest.php:810
‪TYPO3\CMS\Redirects\Event\BeforeRedirectMatchDomainEvent\getMatchDomainName
‪string getMatchDomainName()
Definition: BeforeRedirectMatchDomainEvent.php:66
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\writeSiteConfiguration
‪writeSiteConfiguration(string $identifier, array $site=[], array $languages=[], array $errorHandling=[])
Definition: SiteBasedTestTrait.php:50
‪TYPO3\CMS\Redirects\Service\RedirectService
Definition: RedirectService.php:51
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\$testFilesToDelete
‪array $testFilesToDelete
Definition: RedirectServiceTest.php:48
‪TYPO3\CMS\Redirects\Message\$targetUrl
‪identifier readonly UriInterface $targetUrl
Definition: RedirectWasHitMessage.php:33
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildSiteConfiguration
‪buildSiteConfiguration(int $rootPageId, string $base='')
Definition: SiteBasedTestTrait.php:96
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\samePathRedirectsWithExternalTarget
‪samePathRedirectsWithExternalTarget(string $url, string $baseUri, int $expectedStatusCode, ?string $expectedRedirectUri, ?int $expectedRedirectUid)
Definition: RedirectServiceTest.php:781
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\$configurationToUseInTestInstance
‪array $configurationToUseInTestInstance
Definition: RedirectServiceTest.php:50
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:55
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:29
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\samePathWithSameDomainT3Target
‪samePathWithSameDomainT3Target(string $url, string $baseUri, int $expectedStatusCode, ?string $expectedRedirectUri, ?int $expectedRedirectUid)
Definition: RedirectServiceTest.php:471
‪TYPO3\CMS\Redirects\Service\RedirectCacheService
Definition: RedirectCacheService.php:34
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\LANGUAGE_PRESETS
‪const LANGUAGE_PRESETS
Definition: RedirectServiceTest.php:44
‪TYPO3\CMS\Redirects\Message\$statusCode
‪identifier readonly UriInterface readonly int $statusCode
Definition: RedirectWasHitMessage.php:34
‪TYPO3\CMS\Redirects\Tests\Functional\Service
Definition: IntegrityServiceTest.php:18
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\samePathWithSameDomainAndRelativeTarget
‪samePathWithSameDomainAndRelativeTarget(string $url, string $baseUri, int $expectedStatusCode, ?string $expectedRedirectUri, ?int $expectedRedirectUid)
Definition: RedirectServiceTest.php:616
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\checkRegExpRedirectsDataProvider
‪static checkRegExpRedirectsDataProvider()
Definition: RedirectServiceTest.php:186
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest
Definition: RedirectServiceTest.php:42
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:37
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\checkReponseCodeOnRedirect
‪checkReponseCodeOnRedirect($url, $statusCode, $targetUrl, $redirectUid)
Definition: RedirectServiceTest.php:162
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\setUp
‪setUp()
Definition: RedirectServiceTest.php:60
‪TYPO3\CMS\Core\EventDispatcher\NoopEventDispatcher
Definition: NoopEventDispatcher.php:29
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
Definition: FrontendUserAuthentication.php:33
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\$coreExtensionsToLoad
‪array $coreExtensionsToLoad
Definition: RedirectServiceTest.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder\REQUESTTYPE_FE
‪const REQUESTTYPE_FE
Definition: SystemEnvironmentBuilder.php:43
‪TYPO3\CMS\Core\EventDispatcher\ListenerProvider
Definition: ListenerProvider.php:30
‪TYPO3\CMS\Redirects\Tests\Functional\Service\RedirectServiceTest\tearDown
‪tearDown()
Definition: RedirectServiceTest.php:67
‪TYPO3\CMS\Redirects\Event\BeforeRedirectMatchDomainEvent
Definition: BeforeRedirectMatchDomainEvent.php:28