TYPO3 CMS  TYPO3_8-7
FrontendLoginControllerTest.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
27 
31 class FrontendLoginControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
32 {
36  protected $accessibleFixture;
37 
41  protected $testHostName;
42 
46  protected $testSitePath;
47 
51  protected $testTableName;
52 
57 
61  protected function setUp()
62  {
63  $this->singletonInstances = GeneralUtility::getSingletonInstances();
64  $GLOBALS['TSFE'] = new \stdClass();
65  $GLOBALS['TSFE']->gr_list = '0,-1';
66  $this->testTableName = 'sys_domain';
67  $this->testHostName = 'hostname.tld';
68  $this->testSitePath = '/';
69  $this->accessibleFixture = $this->getAccessibleMock(\TYPO3\CMS\Felogin\Controller\FrontendLoginController::class, ['dummy']);
70  $this->accessibleFixture->cObj = $this->createMock(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class);
71  $this->accessibleFixture->_set('frontendController', $this->createMock(\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::class));
72  $this->setUpFakeSitePathAndHost();
73  }
74 
78  protected function tearDown()
79  {
80  // setUpDatabaseMock() prepares some instances via addInstance(), but not all
81  // tests use that instance. purgeInstances() removes left overs and resets known
82  // singletons afterwards
84  GeneralUtility::resetSingletonInstances($this->singletonInstances);
85  parent::tearDown();
86  }
87 
91  protected function setUpFakeSitePathAndHost()
92  {
93  $_SERVER['ORIG_PATH_INFO'] = $_SERVER['PATH_INFO'] = $_SERVER['ORIG_SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME'] = $this->testSitePath . TYPO3_mainDir;
94  $_SERVER['HTTP_HOST'] = $this->testHostName;
95  }
96 
100  protected function setUpDatabaseMock()
101  {
103  $connection = $this->prophesize(Connection::class);
104  $connection->getDatabasePlatform()->willReturn(new MockPlatform());
105  $connection->getExpressionBuilder()->willReturn(new ExpressionBuilder($connection->reveal()));
106  $connection->quoteIdentifier(Argument::cetera())->willReturnArgument(0);
107 
108  // TODO: This should rather be a functional test if we need a query builder
109  // or we should clean up the code itself to not need to mock internal behavior here
110  $queryBuilder = new QueryBuilder(
111  $connection->reveal(),
112  null,
113  new \Doctrine\DBAL\Query\QueryBuilder($connection->reveal())
114  );
115 
117  $resultSet = $this->prophesize(\Doctrine\DBAL\Driver\Statement::class);
118  $resultSet->fetchAll()->willReturn([
119  ['domainName' => 'domainhostname.tld'],
120  ['domainName' => 'otherhostname.tld/path'],
121  ['domainName' => 'sub.domainhostname.tld/path/']
122  ]);
123 
125  $connectionPool = $this->prophesize(ConnectionPool::class);
126  $connectionPool->getQueryBuilderForTable('sys_domain')->willReturn($queryBuilder);
127  GeneralUtility::addInstance(ConnectionPool::class, $connectionPool->reveal());
128 
129  $connection->executeQuery('SELECT domainName FROM sys_domain', Argument::cetera())
130  ->willReturn($resultSet->reveal());
131  }
132 
137  {
138  $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), $this->testSitePath);
139  }
140 
145  {
146  $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), ('http://' . $this->testHostName) . $this->testSitePath);
147  }
148 
153  {
154  $this->testHostName = 'somenewhostname.com';
155  $this->testSitePath = '/somenewpath/';
156  $this->setUpFakeSitePathAndHost();
157  $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), $this->testSitePath);
158  }
159 
164  {
165  $this->testHostName = 'somenewhostname.com';
166  $this->testSitePath = '/somenewpath/';
167  $this->setUpFakeSitePathAndHost();
168  $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), ('http://' . $this->testHostName) . $this->testSitePath);
169  }
170 
177  {
178  return [
179  'absolute URL, hostname not in sys_domain, trailing slash' => ['http://badhost.tld/'],
180  'absolute URL, hostname not in sys_domain, no trailing slash' => ['http://badhost.tld'],
181  'absolute URL, subdomain in sys_domain, but main domain not, trailing slash' => ['http://domainhostname.tld.badhost.tld/'],
182  'absolute URL, subdomain in sys_domain, but main domain not, no trailing slash' => ['http://domainhostname.tld.badhost.tld'],
183  'non http absolute URL 1' => ['its://domainhostname.tld/itunes/'],
184  'non http absolute URL 2' => ['ftp://domainhostname.tld/download/'],
185  'XSS attempt 1' => ['javascript:alert(123)'],
186  'XSS attempt 2' => ['" onmouseover="alert(123)"'],
187  'invalid URL, HTML break out attempt' => ['" >blabuubb'],
188  'invalid URL, UNC path' => ['\\\\foo\\bar\\'],
189  'invalid URL, backslashes in path' => ['http://domainhostname.tld\\bla\\blupp'],
190  'invalid URL, linefeed in path' => ['http://domainhostname.tld/bla/blupp' . LF],
191  'invalid URL, only one slash after scheme' => ['http:/domainhostname.tld/bla/blupp'],
192  'invalid URL, illegal chars' => ['http://(<>domainhostname).tld/bla/blupp'],
193  ];
194  }
195 
201  public function validateRedirectUrlClearsUrl($url)
202  {
203  $this->setUpDatabaseMock();
204  $this->assertEquals('', $this->accessibleFixture->_call('validateRedirectUrl', $url));
205  }
206 
213  {
214  return [
215  'sane absolute URL' => ['http://domainhostname.tld/'],
216  'sane absolute URL with script' => ['http://domainhostname.tld/index.php?id=1'],
217  'sane absolute URL with realurl' => ['http://domainhostname.tld/foo/bar/foo.html'],
218  'sane absolute URL with homedir' => ['http://domainhostname.tld/~user/'],
219  'sane absolute URL with some strange chars encoded' => ['http://domainhostname.tld/~user/a%cc%88o%cc%88%c3%9fa%cc%82/foo.html'],
220  'sane absolute URL (domain record with path)' => ['http://otherhostname.tld/path/'],
221  'sane absolute URL with script (domain record with path)' => ['http://otherhostname.tld/path/index.php?id=1'],
222  'sane absolute URL with realurl (domain record with path)' => ['http://otherhostname.tld/path/foo/bar/foo.html'],
223  'sane absolute URL (domain record with path and slash)' => ['http://sub.domainhostname.tld/path/'],
224  'sane absolute URL with script (domain record with path slash)' => ['http://sub.domainhostname.tld/path/index.php?id=1'],
225  'sane absolute URL with realurl (domain record with path slash)' => ['http://sub.domainhostname.tld/path/foo/bar/foo.html'],
226  'relative URL, no leading slash 1' => ['index.php?id=1'],
227  'relative URL, no leading slash 2' => ['foo/bar/index.php?id=2'],
228  'relative URL, leading slash, no realurl' => ['/index.php?id=1'],
229  'relative URL, leading slash, realurl' => ['/de/service/imprint.html'],
230  ];
231  }
232 
238  public function validateRedirectUrlKeepsCleanUrl($url)
239  {
240  $this->setUpDatabaseMock();
241  $this->assertEquals($url, $this->accessibleFixture->_call('validateRedirectUrl', $url));
242  }
243 
250  {
251  return [
252  'absolute URL, missing subdirectory' => ['http://hostname.tld/'],
253  'absolute URL, wrong subdirectory' => ['http://hostname.tld/hacker/index.php'],
254  'absolute URL, correct subdirectory, no trailing slash' => ['http://hostname.tld/subdir'],
255  'absolute URL, correct subdirectory of sys_domain record, no trailing slash' => ['http://otherhostname.tld/path'],
256  'absolute URL, correct subdirectory of sys_domain record, no trailing slash, subdomain' => ['http://sub.domainhostname.tld/path'],
257  'relative URL, leading slash, no path' => ['/index.php?id=1'],
258  'relative URL, leading slash, wrong path' => ['/de/sub/site.html'],
259  'relative URL, leading slash, slash only' => ['/'],
260  ];
261  }
262 
269  {
270  $this->testSitePath = '/subdir/';
271  $this->setUpFakeSitePathAndHost();
272  $this->setUpDatabaseMock();
273  $this->assertEquals('', $this->accessibleFixture->_call('validateRedirectUrl', $url));
274  }
275 
282  {
283  return [
284  'absolute URL, correct subdirectory' => ['http://hostname.tld/subdir/'],
285  'absolute URL, correct subdirectory, realurl' => ['http://hostname.tld/subdir/de/imprint.html'],
286  'absolute URL, correct subdirectory, no realurl' => ['http://hostname.tld/subdir/index.php?id=10'],
287  'absolute URL, correct subdirectory of sys_domain record' => ['http://otherhostname.tld/path/'],
288  'absolute URL, correct subdirectory of sys_domain record, subdomain' => ['http://sub.domainhostname.tld/path/'],
289  'relative URL, no leading slash, realurl' => ['de/service/imprint.html'],
290  'relative URL, no leading slash, no realurl' => ['index.php?id=1'],
291  'relative nested URL, no leading slash, no realurl' => ['foo/bar/index.php?id=2']
292  ];
293  }
294 
301  {
302  $this->testSitePath = '/subdir/';
303  $this->setUpFakeSitePathAndHost();
304  $this->setUpDatabaseMock();
305  $this->assertEquals($url, $this->accessibleFixture->_call('validateRedirectUrl', $url));
306  }
307 
308  /*************************
309  * Test concerning getPreverveGetVars
310  *************************/
311 
316  {
317  return [
318  'special get var id is not preserved' => [
319  [
320  'id' => 42,
321  ],
322  '',
323  '',
324  ],
325  'simple additional parameter is not preserved if not specified in preservedGETvars' => [
326  [
327  'id' => 42,
328  'special' => 23,
329  ],
330  '',
331  '',
332  ],
333  'all params except ignored ones are preserved if preservedGETvars is set to "all"' => [
334  [
335  'id' => 42,
336  'special1' => 23,
337  'special2' => [
338  'foo' => 'bar',
339  ],
340  'tx_felogin_pi1' => [
341  'forgot' => 1,
342  ],
343  ],
344  'all',
345  '&special1=23&special2[foo]=bar',
346  ],
347  'preserve single parameter' => [
348  [
349  'L' => 42,
350  ],
351  'L',
352  '&L=42'
353  ],
354  'preserve whole parameter array' => [
355  [
356  'L' => 3,
357  'tx_someext' => [
358  'foo' => 'simple',
359  'bar' => [
360  'baz' => 'simple',
361  ],
362  ],
363  ],
364  'L,tx_someext',
365  '&L=3&tx_someext[foo]=simple&tx_someext[bar][baz]=simple',
366  ],
367  'preserve part of sub array' => [
368  [
369  'L' => 3,
370  'tx_someext' => [
371  'foo' => 'simple',
372  'bar' => [
373  'baz' => 'simple',
374  ],
375  ],
376  ],
377  'L,tx_someext[bar]',
378  '&L=3&tx_someext[bar][baz]=simple',
379  ],
380  'preserve keys on different levels' => [
381  [
382  'L' => 3,
383  'no-preserve' => 'whatever',
384  'tx_ext2' => [
385  'foo' => 'simple',
386  ],
387  'tx_ext3' => [
388  'bar' => [
389  'baz' => 'simple',
390  ],
391  'go-away' => '',
392  ],
393  ],
394  'L,tx_ext2,tx_ext3[bar]',
395  '&L=3&tx_ext2[foo]=simple&tx_ext3[bar][baz]=simple',
396  ],
397  'preserved value that does not exist in get' => [
398  [],
399  'L,foo[bar]',
400  ''
401  ],
402  'url params are encoded' => [
403  ['tx_ext1' => 'param with spaces and \\ %<>& /'],
404  'L,tx_ext1',
405  '&tx_ext1=param%20with%20spaces%20and%20%5C%20%25%3C%3E%26%20%2F'
406  ],
407  ];
408  }
409 
417  public function getPreserveGetVarsReturnsCorrectResult(array $getArray, $preserveVars, $expected)
418  {
419  $_GET = $getArray;
420  $this->accessibleFixture->conf['preserveGETvars'] = $preserveVars;
421  $this->assertSame($expected, $this->accessibleFixture->_call('getPreserveGetVars'));
422  }
423 
424  /**************************************************
425  * Tests concerning isInLocalDomain
426  **************************************************/
427 
434  {
435  return [
436  'url https, current host http' => [
437  'example.com', // HTTP_HOST
438  '0', // HTTPS
439  'https://example.com/foo.html' // URL
440  ],
441  'url http, current host https' => [
442  'example.com',
443  '1',
444  'http://example.com/foo.html'
445  ],
446  'url https, current host https' => [
447  'example.com',
448  '1',
449  'https://example.com/foo.html'
450  ],
451  'url http, current host http' => [
452  'example.com',
453  '0',
454  'http://example.com/foo.html'
455  ]
456  ];
457  }
458 
466  public function isInCurrentDomainIgnoresScheme($host, $https, $url)
467  {
468  $_SERVER['HTTP_HOST'] = $host;
469  $_SERVER['HTTPS'] = $https;
470  $this->assertTrue($this->accessibleFixture->_call('isInCurrentDomain', $url));
471  }
472 
477  {
478  return [
479  'simple difference' => [
480  'example.com', // HTTP_HOST
481  'http://typo3.org/foo.html' // URL
482  ],
483  'subdomain different' => [
484  'example.com',
485  'http://foo.example.com/bar.html'
486  ]
487  ];
488  }
489 
497  {
498  $_SERVER['HTTP_HOST'] = $host;
499  $this->assertFalse($this->accessibleFixture->_call('isInCurrentDomain', $url));
500  }
501 
505  public function processRedirectReferrerDomainsMatchesDomains()
506  {
507  $conf = [
508  'redirectMode' => 'refererDomains',
509  'domains' => 'example.com'
510  ];
511 
512  $this->accessibleFixture->_set('conf', $conf);
513  $this->accessibleFixture->_set('logintype', 'login');
514  $this->accessibleFixture->_set('referer', 'http://www.example.com/snafu');
516  $tsfe = $this->accessibleFixture->_get('frontendController');
517  $tsfe->loginUser = true;
518  $this->assertSame(['http://www.example.com/snafu'], $this->accessibleFixture->_call('processRedirect'));
519  }
520 
525  {
526  return [
527  'Simple casing' => [
528  [
529  'username' => 'Holy',
530  'lastname' => 'Wood',
531  ],
532  [
533  'username.' => [
534  'case' => 'upper'
535  ]
536  ],
537  [
538  '###FEUSER_USERNAME###' => 'HOLY',
539  '###FEUSER_LASTNAME###' => 'Wood',
540  '###USER###' => 'HOLY'
541  ]
542  ],
543  'Default config applies' => [
544  [
545  'username' => 'Holy',
546  'lastname' => 'O" Mally',
547  ],
548  [
549  'username.' => [
550  'case' => 'upper'
551  ]
552  ],
553  [
554  '###FEUSER_USERNAME###' => 'HOLY',
555  '###FEUSER_LASTNAME###' => 'O&quot; Mally',
556  '###USER###' => 'HOLY'
557  ]
558  ],
559  'Specific config overrides default config' => [
560  [
561  'username' => 'Holy',
562  'lastname' => 'O" Mally',
563  ],
564  [
565  'username.' => [
566  'case' => 'upper'
567  ],
568  'lastname.' => [
569  'htmlSpecialChars' => '0'
570  ]
571  ],
572  [
573  '###FEUSER_USERNAME###' => 'HOLY',
574  '###FEUSER_LASTNAME###' => 'O" Mally',
575  '###USER###' => 'HOLY'
576  ]
577  ],
578  'No given user returns empty array' => [
579  null,
580  [
581  'username.' => [
582  'case' => 'upper'
583  ],
584  'lastname.' => [
585  'htmlSpecialChars' => '0'
586  ]
587  ],
588  []
589  ],
590  ];
591  }
592 
597  public function processUserFieldsRespectsDefaultConfigurationForStdWrap($userRecord, $fieldConf, $expectedMarkers)
598  {
599  $tsfe = new \stdClass();
600  $tsfe->fe_user = new \stdClass();
601  $tsfe->fe_user->user = $userRecord;
602  $conf = ['userfields.' => $fieldConf];
603  $this->accessibleFixture->_set('cObj', new ContentObjectRenderer());
604  $this->accessibleFixture->_set('frontendController', $tsfe);
605  $this->accessibleFixture->_set('conf', $conf);
606  $actualResult = $this->accessibleFixture->_call('getUserFieldMarkers');
607  $this->assertEquals($expectedMarkers, $actualResult);
608  }
609 }
static addInstance($className, $instance)
static resetSingletonInstances(array $newSingletonInstances)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
processUserFieldsRespectsDefaultConfigurationForStdWrap($userRecord, $fieldConf, $expectedMarkers)