[!!!][TASK] Remove sys_domain and LegacyDomains
[Packages/TYPO3.CMS.git] / typo3 / sysext / felogin / Tests / Unit / Controller / FrontendLoginControllerTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Felogin\Tests\Unit\Controller;
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
18 use Psr\Log\NullLogger;
19 use TYPO3\CMS\Core\Authentication\LoginType;
20 use TYPO3\CMS\Core\Site\Entity\Site;
21 use TYPO3\CMS\Core\Site\SiteFinder;
22 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
23 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
24 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
25
26 /**
27 * Test case
28 */
29 class FrontendLoginControllerTest extends UnitTestCase
30 {
31 /**
32 * @var \TYPO3\CMS\Felogin\Controller\FrontendLoginController|\TYPO3\TestingFramework\Core\AccessibleObjectInterface
33 */
34 protected $accessibleFixture;
35
36 /**
37 * @var string
38 */
39 protected $testHostName;
40
41 /**
42 * @var string
43 */
44 protected $testSitePath;
45
46 protected $resetSingletonInstances = true;
47
48 /**
49 * Set up
50 */
51 protected function setUp()
52 {
53 $GLOBALS['TSFE'] = new \stdClass();
54 $this->testHostName = 'hostname.tld';
55 $this->testSitePath = '/';
56 $this->accessibleFixture = $this->getAccessibleMock(\TYPO3\CMS\Felogin\Controller\FrontendLoginController::class, ['dummy']);
57 $this->accessibleFixture->cObj = $this->createMock(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
58 $this->accessibleFixture->_set('frontendController', $this->createMock(\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::class));
59 $this->accessibleFixture->setLogger(new NullLogger());
60
61 $site = new Site('dummy', 1, ['base' => 'http://sub.domainhostname.tld/path/']);
62 $mockedSiteFinder = $this->getAccessibleMock(SiteFinder::class, ['getSiteByPageId'], [], '', false, false);
63 $mockedSiteFinder->method('getSiteByPageId')->willReturn($site);
64 $this->accessibleFixture->_set('siteFinder', $mockedSiteFinder);
65
66 $this->setUpFakeSitePathAndHost();
67 }
68
69 /**
70 * Set up a fake site path and host
71 */
72 protected function setUpFakeSitePathAndHost()
73 {
74 $_SERVER['ORIG_PATH_INFO'] = $_SERVER['PATH_INFO'] = $_SERVER['ORIG_SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME'] = $this->testSitePath . TYPO3_mainDir;
75 $_SERVER['HTTP_HOST'] = $this->testHostName;
76 }
77
78 /**
79 * @test
80 */
81 public function typo3SitePathEqualsStubSitePath()
82 {
83 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), $this->testSitePath);
84 }
85
86 /**
87 * @test
88 */
89 public function typo3SiteUrlEqualsStubSiteUrl()
90 {
91 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), ('http://' . $this->testHostName) . $this->testSitePath);
92 }
93
94 /**
95 * @test
96 */
97 public function typo3SitePathEqualsStubSitePathAfterChangingInTest()
98 {
99 $this->testHostName = 'somenewhostname.com';
100 $this->testSitePath = '/somenewpath/';
101 $this->setUpFakeSitePathAndHost();
102 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), $this->testSitePath);
103 }
104
105 /**
106 * @test
107 */
108 public function typo3SiteUrlEqualsStubSiteUrlAfterChangingInTest()
109 {
110 $this->testHostName = 'somenewhostname.com';
111 $this->testSitePath = '/somenewpath/';
112 $this->setUpFakeSitePathAndHost();
113 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), ('http://' . $this->testHostName) . $this->testSitePath);
114 }
115
116 /**
117 * Data provider for validateRedirectUrlClearsUrl
118 *
119 * @return array
120 */
121 public function validateRedirectUrlClearsUrlDataProvider()
122 {
123 return [
124 'absolute URL, hostname not in site, trailing slash' => ['http://badhost.tld/'],
125 'absolute URL, hostname not in site, no trailing slash' => ['http://badhost.tld'],
126 'absolute URL, subdomain in site, but main domain not, trailing slash' => ['http://domainhostname.tld.badhost.tld/'],
127 'absolute URL, subdomain in site, but main domain not, no trailing slash' => ['http://domainhostname.tld.badhost.tld'],
128 'non http absolute URL 1' => ['its://domainhostname.tld/itunes/'],
129 'non http absolute URL 2' => ['ftp://domainhostname.tld/download/'],
130 'XSS attempt 1' => ['javascript:alert(123)'],
131 'XSS attempt 2' => ['" onmouseover="alert(123)"'],
132 'invalid URL, HTML break out attempt' => ['" >blabuubb'],
133 'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
134 'invalid URL, backslashes in path' => ['http://domainhostname.tld\\bla\\blupp'],
135 'invalid URL, linefeed in path' => ['http://domainhostname.tld/bla/blupp' . LF],
136 'invalid URL, only one slash after scheme' => ['http:/domainhostname.tld/bla/blupp'],
137 'invalid URL, illegal chars' => ['http://(<>domainhostname).tld/bla/blupp'],
138 ];
139 }
140
141 /**
142 * @test
143 * @dataProvider validateRedirectUrlClearsUrlDataProvider
144 * @param string $url Invalid Url
145 */
146 public function validateRedirectUrlClearsUrl($url)
147 {
148 $this->assertEquals('', $this->accessibleFixture->_call('validateRedirectUrl', $url));
149 }
150
151 /**
152 * Data provider for validateRedirectUrlKeepsCleanUrl
153 *
154 * @return array
155 */
156 public function validateRedirectUrlKeepsCleanUrlDataProvider()
157 {
158 return [
159 'sane absolute URL' => ['http://sub.domainhostname.tld/path/'],
160 'sane absolute URL with script' => ['http://sub.domainhostname.tld/path/index.php?id=1'],
161 'sane absolute URL with realurl' => ['http://sub.domainhostname.tld/path/foo/bar/foo.html'],
162 'sane absolute URL with homedir' => ['http://sub.domainhostname.tld/path/~user/'],
163 'sane absolute URL with some strange chars encoded' => ['http://sub.domainhostname.tld/path/~user/a%cc%88o%cc%88%c3%9fa%cc%82/foo.html'],
164 'relative URL, no leading slash 1' => ['index.php?id=1'],
165 'relative URL, no leading slash 2' => ['foo/bar/index.php?id=2'],
166 'relative URL, leading slash, no realurl' => ['/index.php?id=1'],
167 'relative URL, leading slash, realurl' => ['/de/service/imprint.html'],
168 ];
169 }
170
171 /**
172 * @test
173 * @dataProvider validateRedirectUrlKeepsCleanUrlDataProvider
174 * @param string $url Clean URL to test
175 */
176 public function validateRedirectUrlKeepsCleanUrl($url)
177 {
178 $this->assertEquals($url, $this->accessibleFixture->_call('validateRedirectUrl', $url));
179 }
180
181 /**
182 * Data provider for validateRedirectUrlClearsInvalidUrlInSubdirectory
183 *
184 * @return array
185 */
186 public function validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider()
187 {
188 return [
189 'absolute URL, missing subdirectory' => ['http://hostname.tld/'],
190 'absolute URL, wrong subdirectory' => ['http://hostname.tld/hacker/index.php'],
191 'absolute URL, correct subdirectory, no trailing slash' => ['http://hostname.tld/subdir'],
192 'relative URL, leading slash, no path' => ['/index.php?id=1'],
193 'relative URL, leading slash, wrong path' => ['/de/sub/site.html'],
194 'relative URL, leading slash, slash only' => ['/'],
195 ];
196 }
197
198 /**
199 * @test
200 * @dataProvider validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider
201 * @param string $url Invalid Url
202 */
203 public function validateRedirectUrlClearsInvalidUrlInSubdirectory($url)
204 {
205 $this->testSitePath = '/subdir/';
206 $this->setUpFakeSitePathAndHost();
207 $this->assertEquals('', $this->accessibleFixture->_call('validateRedirectUrl', $url));
208 }
209
210 /**
211 * Data provider for validateRedirectUrlKeepsCleanUrlInSubdirectory
212 *
213 * @return array
214 */
215 public function validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider()
216 {
217 return [
218 'absolute URL, correct subdirectory' => ['http://hostname.tld/subdir/'],
219 'absolute URL, correct subdirectory, realurl' => ['http://hostname.tld/subdir/de/imprint.html'],
220 'absolute URL, correct subdirectory, no realurl' => ['http://hostname.tld/subdir/index.php?id=10'],
221 'absolute URL, correct subdirectory of site base' => ['http://sub.domainhostname.tld/path/'],
222 'relative URL, no leading slash, realurl' => ['de/service/imprint.html'],
223 'relative URL, no leading slash, no realurl' => ['index.php?id=1'],
224 'relative nested URL, no leading slash, no realurl' => ['foo/bar/index.php?id=2']
225 ];
226 }
227
228 /**
229 * @test
230 * @dataProvider validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider
231 * @param string $url Invalid Url
232 */
233 public function validateRedirectUrlKeepsCleanUrlInSubdirectory($url)
234 {
235 $this->testSitePath = '/subdir/';
236 $this->setUpFakeSitePathAndHost();
237 $this->assertEquals($url, $this->accessibleFixture->_call('validateRedirectUrl', $url));
238 }
239
240 /*************************
241 * Test concerning getPreverveGetVars
242 *************************/
243
244 /**
245 * @return array
246 */
247 public function getPreserveGetVarsReturnsCorrectResultDataProvider()
248 {
249 return [
250 'special get var id is not preserved' => [
251 [
252 'id' => 42,
253 ],
254 '',
255 [],
256 ],
257 'simple additional parameter is not preserved if not specified in preservedGETvars' => [
258 [
259 'id' => 42,
260 'special' => 23,
261 ],
262 '',
263 [],
264 ],
265 'all params except ignored ones are preserved if preservedGETvars is set to "all"' => [
266 [
267 'id' => 42,
268 'special1' => 23,
269 'special2' => [
270 'foo' => 'bar',
271 ],
272 'tx_felogin_pi1' => [
273 'forgot' => 1,
274 ],
275 ],
276 'all',
277 [
278 'special1' => 23,
279 'special2' => [
280 'foo' => 'bar',
281 ],
282 ]
283 ],
284 'preserve single parameter' => [
285 [
286 'L' => 42,
287 ],
288 'L',
289 [
290 'L' => 42,
291 ],
292 ],
293 'preserve whole parameter array' => [
294 [
295 'L' => 3,
296 'tx_someext' => [
297 'foo' => 'simple',
298 'bar' => [
299 'baz' => 'simple',
300 ],
301 ],
302 ],
303 'L,tx_someext',
304 [
305 'L' => 3,
306 'tx_someext' => [
307 'foo' => 'simple',
308 'bar' => [
309 'baz' => 'simple',
310 ],
311 ],
312 ],
313 ],
314 'preserve part of sub array' => [
315 [
316 'L' => 3,
317 'tx_someext' => [
318 'foo' => 'simple',
319 'bar' => [
320 'baz' => 'simple',
321 ],
322 ],
323 ],
324 'L,tx_someext[bar]',
325 [
326 'L' => 3,
327 'tx_someext' => [
328 'bar' => [
329 'baz' => 'simple',
330 ],
331 ],
332 ],
333 ],
334 'preserve keys on different levels' => [
335 [
336 'L' => 3,
337 'no-preserve' => 'whatever',
338 'tx_ext2' => [
339 'foo' => 'simple',
340 ],
341 'tx_ext3' => [
342 'bar' => [
343 'baz' => 'simple',
344 ],
345 'go-away' => '',
346 ],
347 ],
348 'L,tx_ext2,tx_ext3[bar]',
349 [
350 'L' => 3,
351 'tx_ext2' => [
352 'foo' => 'simple',
353 ],
354 'tx_ext3' => [
355 'bar' => [
356 'baz' => 'simple',
357 ],
358 ],
359 ],
360 ],
361 'preserved value that does not exist in get' => [
362 [],
363 'L,foo%5Bbar%5D',
364 [],
365 ],
366 ];
367 }
368
369 /**
370 * @test
371 * @dataProvider getPreserveGetVarsReturnsCorrectResultDataProvider
372 * @param array $getArray
373 * @param string $preserveVars
374 * @param string $expected
375 */
376 public function getPreserveGetVarsReturnsCorrectResult(array $getArray, $preserveVars, $expected)
377 {
378 $_GET = $getArray;
379 $this->accessibleFixture->conf['preserveGETvars'] = $preserveVars;
380 $this->assertSame($expected, $this->accessibleFixture->_call('getPreserveGetVars'));
381 }
382
383 /**************************************************
384 * Tests concerning isInLocalDomain
385 **************************************************/
386
387 /**
388 * Dataprovider for isInCurrentDomainIgnoresScheme
389 *
390 * @return array
391 */
392 public function isInCurrentDomainIgnoresSchemeDataProvider()
393 {
394 return [
395 'url https, current host http' => [
396 'example.com', // HTTP_HOST
397 '0', // HTTPS
398 'https://example.com/foo.html' // URL
399 ],
400 'url http, current host https' => [
401 'example.com',
402 '1',
403 'http://example.com/foo.html'
404 ],
405 'url https, current host https' => [
406 'example.com',
407 '1',
408 'https://example.com/foo.html'
409 ],
410 'url http, current host http' => [
411 'example.com',
412 '0',
413 'http://example.com/foo.html'
414 ]
415 ];
416 }
417
418 /**
419 * @test
420 * @dataProvider isInCurrentDomainIgnoresSchemeDataProvider
421 * @param string $host $_SERVER['HTTP_HOST']
422 * @param string $https $_SERVER['HTTPS']
423 * @param string $url The url to test
424 */
425 public function isInCurrentDomainIgnoresScheme($host, $https, $url)
426 {
427 $_SERVER['HTTP_HOST'] = $host;
428 $_SERVER['HTTPS'] = $https;
429 $this->assertTrue($this->accessibleFixture->_call('isInCurrentDomain', $url));
430 }
431
432 /**
433 * @return array
434 */
435 public function isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider()
436 {
437 return [
438 'simple difference' => [
439 'example.com', // HTTP_HOST
440 'http://typo3.org/foo.html' // URL
441 ],
442 'subdomain different' => [
443 'example.com',
444 'http://foo.example.com/bar.html'
445 ]
446 ];
447 }
448
449 /**
450 * @test
451 * @dataProvider isInCurrentDomainReturnsFalseIfDomainsAreDifferentDataProvider
452 * @param string $host $_SERVER['HTTP_HOST']
453 * @param string $url The url to test
454 */
455 public function isInCurrentDomainReturnsFalseIfDomainsAreDifferent($host, $url)
456 {
457 $_SERVER['HTTP_HOST'] = $host;
458 $this->assertFalse($this->accessibleFixture->_call('isInCurrentDomain', $url));
459 }
460
461 /**
462 * @test
463 */
464 public function processRedirectReferrerDomainsMatchesDomains()
465 {
466 $conf = [
467 'redirectMode' => 'refererDomains',
468 'domains' => 'example.com'
469 ];
470
471 $this->accessibleFixture->_set('conf', $conf);
472 $this->accessibleFixture->_set('logintype', LoginType::LOGIN);
473 $this->accessibleFixture->_set('referer', 'http://www.example.com/snafu');
474 /** @var TypoScriptFrontendController $tsfe */
475 $tsfe = $this->accessibleFixture->_get('frontendController');
476 $this->accessibleFixture->_set('userIsLoggedIn', true);
477 $this->assertSame(['http://www.example.com/snafu'], $this->accessibleFixture->_call('processRedirect'));
478 }
479
480 /**
481 *
482 */
483 public function processUserFieldsRespectsDefaultConfigurationForStdWrapDataProvider()
484 {
485 return [
486 'Simple casing' => [
487 [
488 'username' => 'Holy',
489 'lastname' => 'Wood',
490 ],
491 [
492 'username.' => [
493 'case' => 'upper'
494 ]
495 ],
496 [
497 '###FEUSER_USERNAME###' => 'HOLY',
498 '###FEUSER_LASTNAME###' => 'Wood',
499 '###USER###' => 'HOLY'
500 ]
501 ],
502 'Default config applies' => [
503 [
504 'username' => 'Holy',
505 'lastname' => 'O" Mally',
506 ],
507 [
508 'username.' => [
509 'case' => 'upper'
510 ]
511 ],
512 [
513 '###FEUSER_USERNAME###' => 'HOLY',
514 '###FEUSER_LASTNAME###' => 'O&quot; Mally',
515 '###USER###' => 'HOLY'
516 ]
517 ],
518 'Specific config overrides default config' => [
519 [
520 'username' => 'Holy',
521 'lastname' => 'O" Mally',
522 ],
523 [
524 'username.' => [
525 'case' => 'upper'
526 ],
527 'lastname.' => [
528 'htmlSpecialChars' => '0'
529 ]
530 ],
531 [
532 '###FEUSER_USERNAME###' => 'HOLY',
533 '###FEUSER_LASTNAME###' => 'O" Mally',
534 '###USER###' => 'HOLY'
535 ]
536 ],
537 'No given user returns empty array' => [
538 null,
539 [
540 'username.' => [
541 'case' => 'upper'
542 ],
543 'lastname.' => [
544 'htmlSpecialChars' => '0'
545 ]
546 ],
547 []
548 ],
549 ];
550 }
551
552 /**
553 * @test
554 * @dataProvider processUserFieldsRespectsDefaultConfigurationForStdWrapDataProvider
555 */
556 public function processUserFieldsRespectsDefaultConfigurationForStdWrap($userRecord, $fieldConf, $expectedMarkers)
557 {
558 $tsfe = new \stdClass();
559 $tsfe->fe_user = new \stdClass();
560 $tsfe->fe_user->user = $userRecord;
561 $conf = ['userfields.' => $fieldConf];
562 $this->accessibleFixture->_set('cObj', new ContentObjectRenderer());
563 $this->accessibleFixture->_set('frontendController', $tsfe);
564 $this->accessibleFixture->_set('conf', $conf);
565 $actualResult = $this->accessibleFixture->_call('getUserFieldMarkers');
566 $this->assertEquals($expectedMarkers, $actualResult);
567 }
568 }