9334d3993036a522d14dd91a11aef3ac358042b7
[Packages/TYPO3.CMS.git] / typo3 / sysext / felogin / Tests / Unit / Controller / FrontendLoginControllerTest.php
1 <?php
2 namespace TYPO3\CMS\Felogin\Tests\Unit\Controller;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2012 Helmut Hummel <helmut@typo3.org>
8 *
9 * All rights reserved
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 *
27 * The code was adapted from newloginbox, see manual for detailed description
28 ***************************************************************/
29
30 /**
31 * Testcase for URL validation in class FrontendLoginController
32 *
33 * @author Helmut Hummel <helmut@typo3.org>
34 * @package TYPO3
35 * @subpackage felogin
36 */
37 class FrontendLoginTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
38
39 /**
40 * @var \TYPO3\CMS\Core\Database\DatabaseConnection
41 */
42 protected $typo3DbBackup;
43
44 /**
45 * @var \TYPO3\CMS\Felogin\Controller\FrontendLoginController|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
46 */
47 protected $accessibleFixture;
48
49 /**
50 * @var string
51 */
52 protected $testHostName;
53
54 /**
55 * @var string
56 */
57 protected $testSitePath;
58
59 /**
60 * @var string
61 */
62 private $testTableName;
63
64 /**
65 * Set up
66 */
67 public function setUp() {
68 $this->typo3DbBackup = $GLOBALS['TYPO3_DB'];
69 $this->testTableName = 'sys_domain';
70 $this->testHostName = 'hostname.tld';
71 $this->testSitePath = '/';
72 $this->accessibleFixture = $this->getAccessibleMock('TYPO3\\CMS\\Felogin\\Controller\\FrontendLoginController', array('dummy'));
73 $this->accessibleFixture->cObj = $this->getMock('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
74 $GLOBALS['TSFE'] = $this->getMock('TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController', array(), array(), '', FALSE);
75 $this->setUpFakeSitePathAndHost();
76 }
77
78 /**
79 * Tear down
80 */
81 public function tearDown() {
82 $GLOBALS['TYPO3_DB'] = $this->typo3DbBackup;
83 $this->accessibleFixture = NULL;
84 }
85
86 /**
87 * Set up a fake site path and host
88 */
89 protected function setUpFakeSitePathAndHost() {
90 $_SERVER['ORIG_PATH_INFO'] = $_SERVER['PATH_INFO'] = $_SERVER['ORIG_SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME'] = $this->testSitePath . TYPO3_mainDir;
91 $_SERVER['HTTP_HOST'] = $this->testHostName;
92 }
93
94 /**
95 * Mock database
96 */
97 protected function setUpDatabaseMock() {
98 $GLOBALS['TYPO3_DB'] = $this->getMock('TYPO3\\CMS\\Core\\Database\\DatabaseConnection', array('exec_SELECTgetRows'));
99 $GLOBALS['TYPO3_DB']
100 ->expects($this->any())
101 ->method('exec_SELECTgetRows')
102 ->will($this->returnCallback(array($this, 'getDomainRecordsCallback')));
103 }
104
105 /**
106 * Callback method for pageIdCanBeDetermined test cases.
107 * Simulates TYPO3_DB->exec_SELECTgetRows().
108 *
109 * @param string $fields
110 * @param string $table
111 * @param string $where
112 * @return mixed
113 * @see setUpDatabaseMock
114 */
115 public function getDomainRecordsCallback($fields, $table, $where) {
116 if ($table !== $this->testTableName) {
117 return FALSE;
118 }
119 return array(
120 array('domainName' => 'domainhostname.tld'),
121 array('domainName' => 'otherhostname.tld/path'),
122 array('domainName' => 'sub.domainhostname.tld/path/')
123 );
124 }
125
126 /**
127 * @test
128 */
129 public function typo3SitePathEqualsStubSitePath() {
130 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), $this->testSitePath);
131 }
132
133 /**
134 * @test
135 */
136 public function typo3SiteUrlEqualsStubSiteUrl() {
137 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), ('http://' . $this->testHostName) . $this->testSitePath);
138 }
139
140 /**
141 * @test
142 */
143 public function typo3SitePathEqualsStubSitePathAfterChangingInTest() {
144 $this->testHostName = 'somenewhostname.com';
145 $this->testSitePath = '/somenewpath/';
146 $this->setUpFakeSitePathAndHost();
147 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_PATH'), $this->testSitePath);
148 }
149
150 /**
151 * @test
152 */
153 public function typo3SiteUrlEqualsStubSiteUrlAfterChangingInTest() {
154 $this->testHostName = 'somenewhostname.com';
155 $this->testSitePath = '/somenewpath/';
156 $this->setUpFakeSitePathAndHost();
157 $this->assertEquals(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'), ('http://' . $this->testHostName) . $this->testSitePath);
158 }
159
160 /**
161 * Data provider for validateRedirectUrlClearsUrl
162 *
163 * @return array
164 */
165 public function validateRedirectUrlClearsUrlDataProvider() {
166 return array(
167 'absolute URL, hostname not in sys_domain, trailing slash' => array('http://badhost.tld/'),
168 'absolute URL, hostname not in sys_domain, no trailing slash' => array('http://badhost.tld'),
169 'absolute URL, subdomain in sys_domain, but main domain not, trailing slash' => array('http://domainhostname.tld.badhost.tld/'),
170 'absolute URL, subdomain in sys_domain, but main domain not, no trailing slash' => array('http://domainhostname.tld.badhost.tld'),
171 'non http absolute URL 1' => array('its://domainhostname.tld/itunes/'),
172 'non http absolute URL 2' => array('ftp://domainhostname.tld/download/'),
173 'XSS attempt 1' => array('javascript:alert(123)'),
174 'XSS attempt 2' => array('" onmouseover="alert(123)"'),
175 'invalid URL, HTML break out attempt' => array('" >blabuubb'),
176 'invalid URL, UNC path' => array('\\\\foo\\bar\\'),
177 'invalid URL, backslashes in path' => array('http://domainhostname.tld\\bla\\blupp'),
178 'invalid URL, linefeed in path' => array('http://domainhostname.tld/bla/blupp' . LF),
179 'invalid URL, only one slash after scheme' => array('http:/domainhostname.tld/bla/blupp'),
180 'invalid URL, illegal chars' => array('http://(<>domainhostname).tld/bla/blupp'),
181 );
182 }
183
184 /**
185 * @test
186 * @dataProvider validateRedirectUrlClearsUrlDataProvider
187 * @param string $url Invalid Url
188 */
189 public function validateRedirectUrlClearsUrl($url) {
190 $this->setUpDatabaseMock();
191 $this->assertEquals('', $this->accessibleFixture->_call('validateRedirectUrl', $url));
192 }
193
194 /**
195 * Data provider for validateRedirectUrlKeepsCleanUrl
196 *
197 * @return array
198 */
199 public function validateRedirectUrlKeepsCleanUrlDataProvider() {
200 return array(
201 'sane absolute URL' => array('http://domainhostname.tld/'),
202 'sane absolute URL with script' => array('http://domainhostname.tld/index.php?id=1'),
203 'sane absolute URL with realurl' => array('http://domainhostname.tld/foo/bar/foo.html'),
204 'sane absolute URL with homedir' => array('http://domainhostname.tld/~user/'),
205 'sane absolute URL with some strange chars encoded' => array('http://domainhostname.tld/~user/a%cc%88o%cc%88%c3%9fa%cc%82/foo.html'),
206 'sane absolute URL (domain record with path)' => array('http://otherhostname.tld/path/'),
207 'sane absolute URL with script (domain record with path)' => array('http://otherhostname.tld/path/index.php?id=1'),
208 'sane absolute URL with realurl (domain record with path)' => array('http://otherhostname.tld/path/foo/bar/foo.html'),
209 'sane absolute URL (domain record with path and slash)' => array('http://sub.domainhostname.tld/path/'),
210 'sane absolute URL with script (domain record with path slash)' => array('http://sub.domainhostname.tld/path/index.php?id=1'),
211 'sane absolute URL with realurl (domain record with path slash)' => array('http://sub.domainhostname.tld/path/foo/bar/foo.html'),
212 'relative URL, no leading slash 1' => array('index.php?id=1'),
213 'relative URL, no leading slash 2' => array('foo/bar/index.php?id=2'),
214 'relative URL, leading slash, no realurl' => array('/index.php?id=1'),
215 'relative URL, leading slash, realurl' => array('/de/service/imprint.html'),
216 );
217 }
218
219 /**
220 * @test
221 * @dataProvider validateRedirectUrlKeepsCleanUrlDataProvider
222 * @param string $url Clean URL to test
223 */
224 public function validateRedirectUrlKeepsCleanUrl($url) {
225 $this->setUpDatabaseMock();
226 $this->assertEquals($url, $this->accessibleFixture->_call('validateRedirectUrl', $url));
227 }
228
229 /**
230 * Data provider for validateRedirectUrlClearsInvalidUrlInSubdirectory
231 *
232 * @return array
233 */
234 public function validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider() {
235 return array(
236 'absolute URL, missing subdirectory' => array('http://hostname.tld/'),
237 'absolute URL, wrong subdirectory' => array('http://hostname.tld/hacker/index.php'),
238 'absolute URL, correct subdirectory, no trailing slash' => array('http://hostname.tld/subdir'),
239 'absolute URL, correct subdirectory of sys_domain record, no trailing slash' => array('http://otherhostname.tld/path'),
240 'absolute URL, correct subdirectory of sys_domain record, no trailing slash, subdomain' => array('http://sub.domainhostname.tld/path'),
241 'relative URL, leading slash, no path' => array('/index.php?id=1'),
242 'relative URL, leading slash, wrong path' => array('/de/sub/site.html'),
243 'relative URL, leading slash, slash only' => array('/'),
244 );
245 }
246
247 /**
248 * @test
249 * @dataProvider validateRedirectUrlClearsInvalidUrlInSubdirectoryDataProvider
250 * @param string $url Invalid Url
251 */
252 public function validateRedirectUrlClearsInvalidUrlInSubdirectory($url) {
253 $this->testSitePath = '/subdir/';
254 $this->setUpFakeSitePathAndHost();
255 $this->setUpDatabaseMock();
256 $this->assertEquals('', $this->accessibleFixture->_call('validateRedirectUrl', $url));
257 }
258
259 /**
260 * Data provider for validateRedirectUrlKeepsCleanUrlInSubdirectory
261 *
262 * @return array
263 */
264 public function validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider() {
265 return array(
266 'absolute URL, correct subdirectory' => array('http://hostname.tld/subdir/'),
267 'absolute URL, correct subdirectory, realurl' => array('http://hostname.tld/subdir/de/imprint.html'),
268 'absolute URL, correct subdirectory, no realurl' => array('http://hostname.tld/subdir/index.php?id=10'),
269 'absolute URL, correct subdirectory of sys_domain record' => array('http://otherhostname.tld/path/'),
270 'absolute URL, correct subdirectory of sys_domain record, subdomain' => array('http://sub.domainhostname.tld/path/'),
271 'relative URL, no leading slash, realurl' => array('de/service/imprint.html'),
272 'relative URL, no leading slash, no realurl' => array('index.php?id=1'),
273 'relative nested URL, no leading slash, no realurl' => array('foo/bar/index.php?id=2')
274 );
275 }
276
277 /**
278 * @test
279 * @dataProvider validateRedirectUrlKeepsCleanUrlInSubdirectoryDataProvider
280 * @param string $url Invalid Url
281 */
282 public function validateRedirectUrlKeepsCleanUrlInSubdirectory($url) {
283 $this->testSitePath = '/subdir/';
284 $this->setUpFakeSitePathAndHost();
285 $this->setUpDatabaseMock();
286 $this->assertEquals($url, $this->accessibleFixture->_call('validateRedirectUrl', $url));
287 }
288
289
290 /*************************
291 * Test concerning getPreverveGetVars
292 *************************/
293
294 /**
295 * @return array
296 */
297 public function getPreserveGetVarsReturnsCorrectResultDataProvider() {
298 $getArray = array(
299 'id' => '10',
300 'L' => '3',
301 'tx_ext2' => 'ext2value',
302 'tx_ext3' => array('ext3key' => 44),
303 'tx_someext' => array(
304 '@widget_0' => array('currentPage' => '3', 'perPage' => '8'),
305 'controller' => 'controller1',
306 'action' => 'action1'
307 ),
308 'no_cache' => 1,
309 'logintype' => 'login',
310 'redirect_url' => 'someurl',
311 'cHash' => '1c9b08081c416bada560b4cac62ec64d',
312 );
313
314 return array(
315 'special get var id is not preserved' => array(
316 array(
317 'id' => 42,
318 ),
319 '',
320 '',
321 ),
322 'simple additional parameter is not preserved if not specified in preservedGETvars' => array(
323 array(
324 'id' => 42,
325 'special' => 23,
326 ),
327 '',
328 '',
329 ),
330 'all params except ignored ones are preserved if preservedGETvars is set to "all"' => array(
331 array(
332 'id' => 42,
333 'special1' => 23,
334 'special2' => array(
335 'foo' => 'bar',
336 ),
337 ),
338 'all',
339 '&special1=23&special2[foo]=bar',
340 ),
341 'preserve single parameter' => array(
342 array(
343 'L' => 42,
344 ),
345 'L',
346 '&L=42'
347 ),
348 'preserve whole parameter array' => array(
349 array(
350 'L' => 3,
351 'tx_someext' => array(
352 'foo' => 'simple',
353 'bar' => array(
354 'baz' => 'simple',
355 ),
356 ),
357 ),
358 'L,tx_someext',
359 '&L=3&tx_someext[foo]=simple&tx_someext[bar][baz]=simple',
360 ),
361 'preserve part of sub array' => array(
362 array(
363 'L' => 3,
364 'tx_someext' => array(
365 'foo' => 'simple',
366 'bar' => array(
367 'baz' => 'simple',
368 ),
369 ),
370 ),
371 'L,tx_someext[bar]',
372 '&L=3&tx_someext[bar][baz]=simple',
373 ),
374 'preserve keys on different levels' => array(
375 array(
376 'L' => 3,
377 'no-preserve' => 'whatever',
378 'tx_ext2' => array(
379 'foo' => 'simple',
380 ),
381 'tx_ext3' => array(
382 'bar' => array(
383 'baz' => 'simple',
384 ),
385 'go-away' => '',
386 ),
387 ),
388 'L,tx_ext2,tx_ext3[bar]',
389 '&L=3&tx_ext2[foo]=simple&tx_ext3[bar][baz]=simple',
390 ),
391 'preserved value that does not exist in get' => array(
392 array(),
393 'L,foo[bar]',
394 ''
395 ),
396 'url params are encoded' => array(
397 array('tx_ext1' => 'param with spaces and \\ %<>& /'),
398 'L,tx_ext1',
399 '&tx_ext1=param%20with%20spaces%20and%20%20%25%3C%3E%26%20%2F'
400 ),
401 );
402 }
403
404 /**
405 * @test
406 * @dataProvider getPreserveGetVarsReturnsCorrectResultDataProvider
407 * @param array $getArray
408 * @param string $preserveVars
409 * @param string $expected
410 * @return void
411 */
412 public function getPreserveGetVarsReturnsCorrectResult(array $getArray, $preserveVars, $expected) {
413 $_GET = $getArray;
414 $this->accessibleFixture->conf['preserveGETvars'] = $preserveVars;
415 $this->assertSame($expected, $this->accessibleFixture->_call('getPreserveGetVars'));
416 }
417 }
418
419 ?>