71ee6fd4b4f589849a0ee8187378d7408ee9b388
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Unit / Controller / ErrorControllerTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\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 TYPO3\CMS\Core\Http\HtmlResponse;
19 use TYPO3\CMS\Core\Http\RedirectResponse;
20 use TYPO3\CMS\Frontend\Controller\ErrorController;
21
22 /**
23 * Testcase for \TYPO3\CMS\Frontend\Controller\ErrorController
24 */
25 class ErrorControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
26 {
27
28 /**
29 * Tests concerning pageNotFound handling
30 */
31
32 /**
33 * @test
34 */
35 public function pageNotFoundHandlingThrowsExceptionIfNotConfigured()
36 {
37 $this->expectExceptionMessage('This test page was not found!');
38 $this->expectExceptionCode(1518472189);
39 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'] = false;
40 $subject = new ErrorController();
41 $subject->pageNotFoundAction('This test page was not found!');
42 }
43
44 /**
45 * Data Provider for 404
46 *
47 * @return array
48 */
49 public function errorPageHandlingDataProvider()
50 {
51 return [
52 '404 with default errorpage' => [
53 'handler' => true,
54 'header' => 'HTTP/1.0 404 Not Found',
55 'message' => 'Custom message',
56 'response' => [
57 'type' => HtmlResponse::class,
58 'statusCode' => 404,
59 'reasonPhrase' => 'Not Found',
60 'content' => 'Reason: Custom message',
61 'headers' => [
62 'Content-Type' => ['text/html; charset=utf-8']
63 ]
64 ]
65 ],
66 '404 with default errorpage setting the handler to legacy value' => [
67 'handler' => '1',
68 'header' => 'HTTP/1.0 404 This is a dead end',
69 'message' => 'Come back tomorrow',
70 'response' => [
71 'type' => HtmlResponse::class,
72 'statusCode' => 404,
73 'reasonPhrase' => 'This is a dead end',
74 'content' => 'Reason: Come back tomorrow',
75 'headers' => [
76 'Content-Type' => ['text/html; charset=utf-8']
77 ]
78 ]
79 ],
80 '404 with custom userfunction' => [
81 'handler' => 'USER_FUNCTION:' . ErrorControllerTest::class . '->mockedUserFunctionCall',
82 'header' => 'HTTP/1.0 404 Not Found',
83 'message' => 'Custom message',
84 'response' => [
85 'type' => HtmlResponse::class,
86 'statusCode' => 404,
87 'reasonPhrase' => 'Not Found',
88 'content' => 'It\'s magic, Michael: Custom message',
89 'headers' => [
90 'Content-Type' => ['text/html; charset=utf-8']
91 ]
92 ]
93 ],
94 '404 with a readfile functionality' => [
95 'handler' => 'READFILE:LICENSE.txt',
96 'header' => 'HTTP/1.0 404 Not Found',
97 'message' => 'Custom message',
98 'response' => [
99 'type' => HtmlResponse::class,
100 'statusCode' => 404,
101 'reasonPhrase' => 'Not Found',
102 'content' => 'GNU GENERAL PUBLIC LICENSE',
103 'headers' => [
104 'Content-Type' => ['text/html; charset=utf-8']
105 ]
106 ]
107 ],
108 '404 with a readfile functionality with an invalid file' => [
109 'handler' => 'READFILE:does_not_exist.php6',
110 'header' => 'HTTP/1.0 404 Not Found',
111 'message' => 'Custom message',
112 'response' => null,
113 'exceptionCode' => 1518472245,
114 ],
115 '404 with a redirect - never do that in production - it is bad for SEO. But with custom headers as well...' => [
116 'handler' => 'REDIRECT:www.typo3.org',
117 'header' => 'HTTP/1.0 404 Not Found
118 X-TYPO3-Additional-Header: Banana Stand',
119 'message' => 'Custom message',
120 'response' => [
121 'type' => RedirectResponse::class,
122 'statusCode' => 404,
123 'reasonPhrase' => 'Not Found',
124 'headers' => [
125 'location' => ['www.typo3.org'],
126 'X-TYPO3-Additional-Header' => ['Banana Stand'],
127 ]
128 ]
129 ],
130 'Custom path, no prefix' => [
131 'handler' => '/404/',
132 'header' => 'HTTP/1.0 404 Not Found
133 X-TYPO3-Additional-Header: Banana Stand',
134 'message' => 'Custom message',
135 'response' => [
136 'type' => RedirectResponse::class,
137 'statusCode' => 404,
138 'reasonPhrase' => 'Not Found',
139 'headers' => [
140 'location' => ['https://localhost/404/'],
141 'X-TYPO3-Additional-Header' => ['Banana Stand'],
142 ]
143 ]
144 ],
145 ];
146 }
147
148 /**
149 * @test
150 * @dataProvider errorPageHandlingDataProvider
151 */
152 public function pageNotFoundHandlingReturnsConfiguredResponseObject($handler, $header, $message, $expectedResponseDetails, $expectedExceptionCode = null)
153 {
154 if ($expectedExceptionCode !== null) {
155 $this->expectExceptionCode($expectedExceptionCode);
156 }
157 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'] = $handler;
158 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling_statheader'] = $header;
159 // faking getIndpEnv() variables
160 $_SERVER['REQUEST_URI'] = '/unit-test/';
161 $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
162 $_SERVER['HTTP_HOST'] = 'localhost';
163 $_SERVER['SSL_SESSION_ID'] = true;
164 $subject = new ErrorController();
165 $response = $subject->pageNotFoundAction($message);
166 if (is_array($expectedResponseDetails)) {
167 $this->assertInstanceOf($expectedResponseDetails['type'], $response);
168 $this->assertEquals($expectedResponseDetails['statusCode'], $response->getStatusCode());
169 $this->assertEquals($expectedResponseDetails['reasonPhrase'], $response->getReasonPhrase());
170 if (isset($expectedResponseDetails['content'])) {
171 $this->assertContains($expectedResponseDetails['content'], $response->getBody()->getContents());
172 }
173 $this->assertEquals($expectedResponseDetails['headers'], $response->getHeaders());
174 }
175 }
176
177 /**
178 * Tests concerning accessDenied handling
179 */
180
181 /**
182 * Data Provider for 403
183 *
184 * @return array
185 */
186 public function accessDeniedDataProvider()
187 {
188 return [
189 '403 with default errorpage' => [
190 'handler' => true,
191 'header' => 'HTTP/1.0 403 Who are you',
192 'message' => 'Be nice, do good',
193 'response' => [
194 'type' => HtmlResponse::class,
195 'statusCode' => 403,
196 'reasonPhrase' => 'Who are you',
197 'content' => 'Reason: Be nice, do good',
198 'headers' => [
199 'Content-Type' => ['text/html; charset=utf-8']
200 ]
201 ]
202 ],
203 ];
204 }
205
206 /**
207 * @test
208 * @dataProvider accessDeniedDataProvider
209 */
210 public function accessDeniedReturnsProperHeaders($handler, $header, $message, $expectedResponseDetails)
211 {
212 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'] = $handler;
213 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling_accessdeniedheader'] = $header;
214 // faking getIndpEnv() variables
215 $_SERVER['REQUEST_URI'] = '/unit-test/';
216 $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
217 $_SERVER['HTTP_HOST'] = 'localhost';
218 $_SERVER['SSL_SESSION_ID'] = true;
219 $subject = new ErrorController();
220 $response = $subject->accessDeniedAction($message);
221 if (is_array($expectedResponseDetails)) {
222 $this->assertInstanceOf($expectedResponseDetails['type'], $response);
223 $this->assertEquals($expectedResponseDetails['statusCode'], $response->getStatusCode());
224 $this->assertEquals($expectedResponseDetails['reasonPhrase'], $response->getReasonPhrase());
225 if (isset($expectedResponseDetails['content'])) {
226 $this->assertContains($expectedResponseDetails['content'], $response->getBody()->getContents());
227 }
228 $this->assertEquals($expectedResponseDetails['headers'], $response->getHeaders());
229 }
230 }
231
232 /**
233 * Tests concerning unavailable handling
234 */
235
236 /**
237 * @test
238 */
239 public function unavailableHandlingThrowsExceptionIfNotConfigured()
240 {
241 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*';
242 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling'] = true;
243 $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
244 $this->expectExceptionMessage('All your system are belong to us!');
245 $this->expectExceptionCode(1518472181);
246 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling'] = false;
247 $subject = new ErrorController();
248 $subject->unavailableAction('All your system are belong to us!');
249 }
250
251 /**
252 * @test
253 */
254 public function unavailableHandlingDoesNotTriggerDueToDevIpMask()
255 {
256 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*';
257 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling'] = true;
258 $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
259
260 $this->expectExceptionMessage('All your system are belong to us!');
261 $this->expectExceptionCode(1518472181);
262 $subject = new ErrorController();
263 $subject->unavailableAction('All your system are belong to us!');
264 }
265 /**
266 * Data Provider for 503
267 *
268 * @return array
269 */
270 public function unavailableHandlingDataProvider()
271 {
272 return [
273 '503 with default errorpage' => [
274 'handler' => true,
275 'header' => 'HTTP/1.0 503 Service Temporarily Unavailable',
276 'message' => 'Custom message',
277 'response' => [
278 'type' => HtmlResponse::class,
279 'statusCode' => 503,
280 'reasonPhrase' => 'Not Found',
281 'content' => 'Reason: Custom message',
282 'headers' => [
283 'Content-Type' => ['text/html; charset=utf-8']
284 ]
285 ]
286 ],
287 '503 with default errorpage setting the handler to legacy value' => [
288 'handler' => '1',
289 'header' => 'HTTP/1.0 503 This is a dead end',
290 'message' => 'Come back tomorrow',
291 'response' => [
292 'type' => HtmlResponse::class,
293 'statusCode' => 503,
294 'reasonPhrase' => 'This is a dead end',
295 'content' => 'Reason: Come back tomorrow',
296 'headers' => [
297 'Content-Type' => ['text/html; charset=utf-8']
298 ]
299 ]
300 ],
301 '503 with custom userfunction' => [
302 'handler' => 'USER_FUNCTION:' . ErrorControllerTest::class . '->mockedUserFunctionCall',
303 'header' => 'HTTP/1.0 503 Service Temporarily Unavailable',
304 'message' => 'Custom message',
305 'response' => [
306 'type' => HtmlResponse::class,
307 'statusCode' => 503,
308 'reasonPhrase' => 'Not Found',
309 'content' => 'It\'s magic, Michael: Custom message',
310 'headers' => [
311 'Content-Type' => ['text/html; charset=utf-8']
312 ]
313 ]
314 ],
315 '503 with a readfile functionality' => [
316 'handler' => 'READFILE:LICENSE.txt',
317 'header' => 'HTTP/1.0 503 Service Temporarily Unavailable',
318 'message' => 'Custom message',
319 'response' => [
320 'type' => HtmlResponse::class,
321 'statusCode' => 503,
322 'reasonPhrase' => 'Not Found',
323 'content' => 'GNU GENERAL PUBLIC LICENSE',
324 'headers' => [
325 'Content-Type' => ['text/html; charset=utf-8']
326 ]
327 ]
328 ],
329 '503 with a readfile functionality with an invalid file' => [
330 'handler' => 'READFILE:does_not_exist.php6',
331 'header' => 'HTTP/1.0 503 Service Temporarily Unavailable',
332 'message' => 'Custom message',
333 'response' => null,
334 'exceptionCode' => 1518472245,
335 ],
336 '503 with a redirect - never do that in production - it is bad for SEO. But with custom headers as well...' => [
337 'handler' => 'REDIRECT:www.typo3.org',
338 'header' => 'HTTP/1.0 503 Service Temporarily Unavailable
339 X-TYPO3-Additional-Header: Banana Stand',
340 'message' => 'Custom message',
341 'response' => [
342 'type' => RedirectResponse::class,
343 'statusCode' => 503,
344 'reasonPhrase' => 'Not Found',
345 'headers' => [
346 'location' => ['www.typo3.org'],
347 'X-TYPO3-Additional-Header' => ['Banana Stand'],
348 ]
349 ]
350 ],
351 'Custom path, no prefix' => [
352 'handler' => '/fail/',
353 'header' => 'HTTP/1.0 503 Service Temporarily Unavailable
354 X-TYPO3-Additional-Header: Banana Stand',
355 'message' => 'Custom message',
356 'response' => [
357 'type' => RedirectResponse::class,
358 'statusCode' => 503,
359 'reasonPhrase' => 'Not Found',
360 'headers' => [
361 'location' => ['https://localhost/fail/'],
362 'X-TYPO3-Additional-Header' => ['Banana Stand'],
363 ]
364 ]
365 ],
366 ];
367 }
368
369 /**
370 * @test
371 * @dataProvider errorPageHandlingDataProvider
372 */
373 public function pageUnavailableHandlingReturnsConfiguredResponseObject($handler, $header, $message, $expectedResponseDetails, $expectedExceptionCode = null)
374 {
375 if ($expectedExceptionCode !== null) {
376 $this->expectExceptionCode($expectedExceptionCode);
377 }
378 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '-1';
379 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling'] = $handler;
380 $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling_statheader'] = $header;
381 // faking getIndpEnv() variables
382 $_SERVER['REQUEST_URI'] = '/unit-test/';
383 $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
384 $_SERVER['HTTP_HOST'] = 'localhost';
385 $_SERVER['SSL_SESSION_ID'] = true;
386 $subject = new ErrorController();
387 $response = $subject->unavailableAction($message);
388 if (is_array($expectedResponseDetails)) {
389 $this->assertInstanceOf($expectedResponseDetails['type'], $response);
390 $this->assertEquals($expectedResponseDetails['statusCode'], $response->getStatusCode());
391 $this->assertEquals($expectedResponseDetails['reasonPhrase'], $response->getReasonPhrase());
392 if (isset($expectedResponseDetails['content'])) {
393 $this->assertContains($expectedResponseDetails['content'], $response->getBody()->getContents());
394 }
395 $this->assertEquals($expectedResponseDetails['headers'], $response->getHeaders());
396 }
397 }
398
399 /**
400 * Callback function when testing "USER_FUNCTION:" prefix
401 */
402 public function mockedUserFunctionCall($params)
403 {
404 return '<p>It\'s magic, Michael: ' . $params['reasonText'] . '</p>';
405 }
406 }