Passed
Push — fix-8121 ( 0fc06e )
by Sam
08:32
created

DirectorTest::testResetGlobalsAfterTestRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 26
nc 1
nop 0
dl 0
loc 35
rs 9.504
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Tests;
4
5
use SilverStripe\Control\Cookie_Backend;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Control\HTTPResponse;
9
use SilverStripe\Control\HTTPResponse_Exception;
10
use SilverStripe\Control\Middleware\CanonicalURLMiddleware;
11
use SilverStripe\Control\Middleware\RequestHandlerMiddlewareAdapter;
12
use SilverStripe\Control\Middleware\TrustedProxyMiddleware;
13
use SilverStripe\Control\RequestProcessor;
14
use SilverStripe\Control\Tests\DirectorTest\TestController;
15
use SilverStripe\Core\Config\Config;
16
use SilverStripe\Core\Injector\Injector;
17
use SilverStripe\Core\Kernel;
18
use SilverStripe\Dev\SapphireTest;
19
20
/**
21
 * @todo test Director::alternateBaseFolder()
22
 */
23
class DirectorTest extends SapphireTest
24
{
25
    protected static $extra_controllers = [
26
        TestController::class,
27
    ];
28
29
    protected function setUp()
30
    {
31
        parent::setUp();
32
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/');
33
34
        // Ensure redirects enabled on all environments and global state doesn't affect the tests
35
        CanonicalURLMiddleware::singleton()
36
            ->setForceSSLDomain(null)
37
            ->setForceSSLPatterns([])
38
            ->setEnabledEnvs(true);
39
        $this->expectedRedirect = null;
40
    }
41
42
    protected function getExtraRoutes()
43
    {
44
        $rules = parent::getExtraRoutes();
45
46
        $rules['DirectorTestRule/$Action/$ID/$OtherID'] = TestController::class;
47
        $rules['en-nz/$Action/$ID/$OtherID'] = [
48
            'Controller' => TestController::class,
49
            'Locale' => 'en_NZ',
50
        ];
51
        return $rules;
52
    }
53
54
    protected function setUpRoutes()
55
    {
56
        // Don't merge with any existing rules
57
        Director::config()->set('rules', $this->getExtraRoutes());
58
    }
59
60
    public function testFileExists()
61
    {
62
        $tempFileName = 'DirectorTest_testFileExists.tmp';
63
        $tempFilePath = TEMP_PATH . DIRECTORY_SEPARATOR . $tempFileName;
64
65
        // create temp file
66
        file_put_contents($tempFilePath, '');
67
68
        $this->assertTrue(
69
            Director::fileExists($tempFilePath),
70
            'File exist check with absolute path'
71
        );
72
73
        $this->assertTrue(
74
            Director::fileExists($tempFilePath . '?queryparams=1&foo[bar]=bar'),
75
            'File exist check with query params ignored'
76
        );
77
78
        unlink($tempFilePath);
79
    }
80
81
    public function testAbsoluteURL()
82
    {
83
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/mysite/');
84
        $_SERVER['REQUEST_URI'] = "http://www.mysite.com:9090/mysite/sub-page/";
85
86
        //test empty / local urls
87
        foreach (array('', './', '.') as $url) {
88
            $this->assertEquals("http://www.mysite.com:9090/mysite/", Director::absoluteURL($url, Director::BASE));
89
            $this->assertEquals("http://www.mysite.com:9090/", Director::absoluteURL($url, Director::ROOT));
90
            $this->assertEquals("http://www.mysite.com:9090/mysite/sub-page/", Director::absoluteURL($url, Director::REQUEST));
91
        }
92
93
        // Test site root url
94
        $this->assertEquals("http://www.mysite.com:9090/", Director::absoluteURL('/'));
95
96
        // Test Director::BASE
97
        $this->assertEquals('http://www.mysite.com:9090/', Director::absoluteURL('http://www.mysite.com:9090/', Director::BASE));
98
        $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', Director::BASE));
99
        $this->assertEquals("http://www.mysite.com:9090/test", Director::absoluteURL("http://www.mysite.com:9090/test", Director::BASE));
100
        $this->assertEquals("http://www.mysite.com:9090/root", Director::absoluteURL("/root", Director::BASE));
101
        $this->assertEquals("http://www.mysite.com:9090/root/url", Director::absoluteURL("/root/url", Director::BASE));
102
103
        // Test Director::ROOT
104
        $this->assertEquals('http://www.mysite.com:9090/', Director::absoluteURL('http://www.mysite.com:9090/', Director::ROOT));
105
        $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', Director::ROOT));
106
        $this->assertEquals("http://www.mysite.com:9090/test", Director::absoluteURL("http://www.mysite.com:9090/test", Director::ROOT));
107
        $this->assertEquals("http://www.mysite.com:9090/root", Director::absoluteURL("/root", Director::ROOT));
108
        $this->assertEquals("http://www.mysite.com:9090/root/url", Director::absoluteURL("/root/url", Director::ROOT));
109
110
        // Test Director::REQUEST
111
        $this->assertEquals('http://www.mysite.com:9090/', Director::absoluteURL('http://www.mysite.com:9090/', Director::REQUEST));
112
        $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', Director::REQUEST));
113
        $this->assertEquals("http://www.mysite.com:9090/test", Director::absoluteURL("http://www.mysite.com:9090/test", Director::REQUEST));
114
        $this->assertEquals("http://www.mysite.com:9090/root", Director::absoluteURL("/root", Director::REQUEST));
115
        $this->assertEquals("http://www.mysite.com:9090/root/url", Director::absoluteURL("/root/url", Director::REQUEST));
116
117
        // Test evaluating relative urls relative to base (default)
118
        $this->assertEquals("http://www.mysite.com:9090/mysite/test", Director::absoluteURL("test"));
119
        $this->assertEquals("http://www.mysite.com:9090/mysite/test/url", Director::absoluteURL("test/url"));
120
        $this->assertEquals("http://www.mysite.com:9090/mysite/test", Director::absoluteURL("test", Director::BASE));
121
        $this->assertEquals("http://www.mysite.com:9090/mysite/test/url", Director::absoluteURL("test/url", Director::BASE));
122
123
        // Test evaluting relative urls relative to root
124
        $this->assertEquals("http://www.mysite.com:9090/test", Director::absoluteURL("test", Director::ROOT));
125
        $this->assertEquals("http://www.mysite.com:9090/test/url", Director::absoluteURL("test/url", Director::ROOT));
126
127
        // Test relative to requested page
128
        $this->assertEquals("http://www.mysite.com:9090/mysite/sub-page/test", Director::absoluteURL("test", Director::REQUEST));
129
        $this->assertEquals("http://www.mysite.com:9090/mysite/sub-page/test/url", Director::absoluteURL("test/url", Director::REQUEST));
130
131
        // Test that javascript links are not left intact
132
        $this->assertStringStartsNotWith('javascript', Director::absoluteURL('javascript:alert("attack")'));
133
        $this->assertStringStartsNotWith('alert', Director::absoluteURL('javascript:alert("attack")'));
134
        $this->assertStringStartsNotWith('javascript', Director::absoluteURL('alert("attack")'));
135
        $this->assertStringStartsNotWith('alert', Director::absoluteURL('alert("attack")'));
136
    }
137
138
    public function testAlternativeBaseURL()
139
    {
140
        // relative base URLs - you should end them in a /
141
        Director::config()->set('alternate_base_url', '/relativebase/');
142
        $_SERVER['HTTP_HOST'] = 'www.somesite.com';
143
        $_SERVER['REQUEST_URI'] = "/relativebase/sub-page/";
144
145
        $this->assertEquals('/relativebase/', Director::baseURL());
146
        $this->assertEquals('http://www.somesite.com/relativebase/', Director::absoluteBaseURL());
147
        $this->assertEquals(
148
            'http://www.somesite.com/relativebase/subfolder/test',
149
            Director::absoluteURL('subfolder/test')
150
        );
151
152
        // absolute base URLS with subdirectory - You should end them in a /
153
        Director::config()->set('alternate_base_url', 'http://www.example.org/relativebase/');
154
        $_SERVER['REQUEST_URI'] = "http://www.example.org/relativebase/sub-page/";
155
        $this->assertEquals('/relativebase/', Director::baseURL()); // Non-absolute url
156
        $this->assertEquals('http://www.example.org/relativebase/', Director::absoluteBaseURL());
157
        $this->assertEquals('http://www.example.org/relativebase/sub-page/', Director::absoluteURL('', Director::REQUEST));
158
        $this->assertEquals('http://www.example.org/relativebase/', Director::absoluteURL('', Director::BASE));
159
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::ROOT));
160
        $this->assertEquals(
161
            'http://www.example.org/relativebase/sub-page/subfolder/test',
162
            Director::absoluteURL('subfolder/test', Director::REQUEST)
163
        );
164
        $this->assertEquals(
165
            'http://www.example.org/subfolder/test',
166
            Director::absoluteURL('subfolder/test', Director::ROOT)
167
        );
168
        $this->assertEquals(
169
            'http://www.example.org/relativebase/subfolder/test',
170
            Director::absoluteURL('subfolder/test', Director::BASE)
171
        );
172
173
        // absolute base URLs - you should end them in a /
174
        Director::config()->set('alternate_base_url', 'http://www.example.org/');
175
        $_SERVER['REQUEST_URI'] = "http://www.example.org/sub-page/";
176
        $this->assertEquals('/', Director::baseURL()); // Non-absolute url
177
        $this->assertEquals('http://www.example.org/', Director::absoluteBaseURL());
178
        $this->assertEquals('http://www.example.org/sub-page/', Director::absoluteURL('', Director::REQUEST));
179
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::BASE));
180
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::ROOT));
181
        $this->assertEquals(
182
            'http://www.example.org/sub-page/subfolder/test',
183
            Director::absoluteURL('subfolder/test', Director::REQUEST)
184
        );
185
        $this->assertEquals(
186
            'http://www.example.org/subfolder/test',
187
            Director::absoluteURL('subfolder/test', Director::ROOT)
188
        );
189
        $this->assertEquals(
190
            'http://www.example.org/subfolder/test',
191
            Director::absoluteURL('subfolder/test', Director::BASE)
192
        );
193
    }
194
195
    /**
196
     * Tests that {@link Director::is_absolute()} works under different environment types
197
     * @dataProvider provideAbsolutePaths
198
     */
199
    public function testIsAbsolute($path, $result)
200
    {
201
        $this->assertEquals($result, Director::is_absolute($path));
202
    }
203
204
    public function provideAbsolutePaths()
205
    {
206
        return [
207
            ['C:/something', true],
208
            ['d:\\', true],
209
            ['e/', false],
210
            ['s:/directory', true],
211
            ['/var/www', true],
212
            ['\\Something', true],
213
            ['something/c:', false],
214
            ['folder', false],
215
            ['a/c:/', false],
216
        ];
217
    }
218
219
    public function testIsAbsoluteUrl()
220
    {
221
        $this->assertTrue(Director::is_absolute_url('http://test.com/testpage'));
222
        $this->assertTrue(Director::is_absolute_url('ftp://test.com'));
223
        $this->assertFalse(Director::is_absolute_url('test.com/testpage'));
224
        $this->assertFalse(Director::is_absolute_url('/relative'));
225
        $this->assertFalse(Director::is_absolute_url('relative'));
226
        $this->assertFalse(Director::is_absolute_url("/relative/?url=http://foo.com"));
227
        $this->assertFalse(Director::is_absolute_url("/relative/#http://foo.com"));
228
        $this->assertTrue(Director::is_absolute_url("https://test.com/?url=http://foo.com"));
229
        $this->assertTrue(Director::is_absolute_url("trickparseurl:http://test.com"));
230
        $this->assertTrue(Director::is_absolute_url('//test.com'));
231
        $this->assertTrue(Director::is_absolute_url('/////test.com'));
232
        $this->assertTrue(Director::is_absolute_url('  ///test.com'));
233
        $this->assertTrue(Director::is_absolute_url('http:test.com'));
234
        $this->assertTrue(Director::is_absolute_url('//http://test.com'));
235
    }
236
237
    public function testHostPortParts()
238
    {
239
        $this->assertEquals('www.mysite.com:9090', Director::host());
240
        $this->assertEquals('www.mysite.com', Director::hostName());
241
        $this->assertEquals('9090', Director::port());
242
    }
243
244
    public function testIsRelativeUrl()
245
    {
246
        $this->assertFalse(Director::is_relative_url('http://test.com'));
247
        $this->assertFalse(Director::is_relative_url('https://test.com'));
248
        $this->assertFalse(Director::is_relative_url('   https://test.com/testpage   '));
249
        $this->assertTrue(Director::is_relative_url('test.com/testpage'));
250
        $this->assertFalse(Director::is_relative_url('ftp://test.com'));
251
        $this->assertTrue(Director::is_relative_url('/relative'));
252
        $this->assertTrue(Director::is_relative_url('relative'));
253
        $this->assertTrue(Director::is_relative_url('/relative/?url=http://test.com'));
254
        $this->assertTrue(Director::is_relative_url('/relative/#=http://test.com'));
255
    }
256
257
    /**
258
     * @return array
259
     */
260
    public function providerMakeRelative()
261
    {
262
        return [
263
            // Resilience to slash position
264
            [
265
                'http://www.mysite.com:9090/base/folder',
266
                'http://www.mysite.com:9090/base/folder',
267
                ''
268
            ],
269
            [
270
                'http://www.mysite.com:9090/base/folder',
271
                'http://www.mysite.com:9090/base/folder/',
272
                ''
273
            ],
274
            [
275
                'http://www.mysite.com:9090/base/folder/',
276
                'http://www.mysite.com:9090/base/folder',
277
                ''
278
            ],
279
            [
280
                'http://www.mysite.com:9090/',
281
                'http://www.mysite.com:9090/',
282
                ''
283
            ],
284
            [
285
                'http://www.mysite.com:9090/',
286
                'http://www.mysite.com',
287
                ''
288
            ],
289
            [
290
                'http://www.mysite.com',
291
                'http://www.mysite.com:9090/',
292
                ''
293
            ],
294
            [
295
                'http://www.mysite.com:9090/base/folder',
296
                'http://www.mysite.com:9090/base/folder/page',
297
                'page'
298
            ],
299
            [
300
                'http://www.mysite.com:9090/',
301
                'http://www.mysite.com:9090/page/',
302
                'page/'
303
            ],
304
            // Parsing protocol safely
305
            [
306
                'http://www.mysite.com:9090/base/folder',
307
                'https://www.mysite.com:9090/base/folder',
308
                ''
309
            ],
310
            [
311
                'https://www.mysite.com:9090/base/folder',
312
                'http://www.mysite.com:9090/base/folder/testpage',
313
                'testpage'
314
            ],
315
            [
316
                'http://www.mysite.com:9090/base/folder',
317
                '//www.mysite.com:9090/base/folder/testpage',
318
                'testpage'
319
            ],
320
            // Dirty input
321
            [
322
                'http://www.mysite.com:9090/base/folder',
323
                '    https://www.mysite.com:9090/base/folder/testpage    ',
324
                'testpage'
325
            ],
326
            [
327
                'http://www.mysite.com:9090/base/folder',
328
                '//www.mysite.com:9090/base//folder/testpage//subpage',
329
                'testpage/subpage'
330
            ],
331
            // Non-http protocol isn't modified
332
            [
333
                'http://www.mysite.com:9090/base/folder',
334
                'ftp://test.com',
335
                'ftp://test.com'
336
            ],
337
            // Alternate hostnames are redirected
338
            [
339
                'https://www.mysite.com:9090/base/folder',
340
                'http://mysite.com:9090/base/folder/testpage',
341
                'testpage'
342
            ],
343
            [
344
                'http://www.otherdomain.com/base/folder',
345
                '//www.mysite.com:9090/base/folder/testpage',
346
                'testpage'
347
            ],
348
            // Base folder is found
349
            [
350
                'http://www.mysite.com:9090/base/folder',
351
                BASE_PATH . '/some/file.txt',
352
                'some/file.txt',
353
            ],
354
            // public folder is found
355
            [
356
                'http://www.mysite.com:9090/base/folder',
357
                PUBLIC_PATH . '/some/file.txt',
358
                'some/file.txt',
359
            ],
360
            // querystring is protected
361
            [
362
                'http://www.mysite.com:9090/base/folder',
363
                '//www.mysite.com:9090/base//folder/testpage//subpage?args=hello',
364
                'testpage/subpage?args=hello'
365
            ],
366
            [
367
                'http://www.mysite.com:9090/base/folder',
368
                '//www.mysite.com:9090/base//folder/?args=hello',
369
                '?args=hello'
370
            ],
371
        ];
372
    }
373
374
    /**
375
     * @dataProvider providerMakeRelative
376
     * @param string $baseURL Site base URL
377
     * @param string $requestURL Request URL
378
     * @param string $relativeURL Expected relative URL
379
     */
380
    public function testMakeRelative($baseURL, $requestURL, $relativeURL)
381
    {
382
        Director::config()->set('alternate_base_url', $baseURL);
383
        $actualRelative = Director::makeRelative($requestURL);
384
        $this->assertEquals(
385
            $relativeURL,
386
            $actualRelative,
387
            "Expected relativeURL of {$requestURL} to be {$relativeURL}"
388
        );
389
    }
390
391
    /**
392
     * Mostly tested by {@link testIsRelativeUrl()},
393
     * just adding the host name matching aspect here.
394
     */
395
    public function testIsSiteUrl()
396
    {
397
        $this->assertFalse(Director::is_site_url("http://test.com"));
398
        $this->assertTrue(Director::is_site_url(Director::absoluteBaseURL()));
399
        $this->assertFalse(Director::is_site_url("http://test.com?url=" . Director::absoluteBaseURL()));
400
        $this->assertFalse(Director::is_site_url("http://test.com?url=" . urlencode(Director::absoluteBaseURL())));
401
        $this->assertFalse(Director::is_site_url("//test.com?url=" . Director::absoluteBaseURL()));
402
        $this->assertFalse(Director::is_site_url('http://google.com\@test.com'));
403
        $this->assertFalse(Director::is_site_url('http://google.com/@test.com'));
404
        $this->assertFalse(Director::is_site_url('http://google.com:pass\@test.com'));
405
        $this->assertFalse(Director::is_site_url('http://google.com:pass/@test.com'));
406
    }
407
408
    /**
409
     * Tests isDev, isTest, isLive set from querystring
410
     */
411
    public function testQueryIsEnvironment()
412
    {
413
        if (!isset($_SESSION)) {
414
            $_SESSION = [];
415
        }
416
        // Reset
417
        unset($_SESSION['isDev']);
418
        unset($_SESSION['isLive']);
419
        unset($_GET['isTest']);
420
        unset($_GET['isDev']);
421
422
        /** @var Kernel $kernel */
423
        $kernel = Injector::inst()->get(Kernel::class);
424
        $kernel->setEnvironment(null);
425
426
        // Test isDev=1
427
        $_GET['isDev'] = '1';
428
        $this->assertTrue(Director::isDev());
429
        $this->assertFalse(Director::isTest());
430
        $this->assertFalse(Director::isLive());
431
432
        // Test persistence
433
        unset($_GET['isDev']);
434
        $this->assertTrue(Director::isDev());
435
        $this->assertFalse(Director::isTest());
436
        $this->assertFalse(Director::isLive());
437
438
        // Test change to isTest
439
        $_GET['isTest'] = '1';
440
        $this->assertFalse(Director::isDev());
441
        $this->assertTrue(Director::isTest());
442
        $this->assertFalse(Director::isLive());
443
444
        // Test persistence
445
        unset($_GET['isTest']);
446
        $this->assertFalse(Director::isDev());
447
        $this->assertTrue(Director::isTest());
448
        $this->assertFalse(Director::isLive());
449
    }
450
451
    public function testResetGlobalsAfterTestRequest()
452
    {
453
        $_GET = array('somekey' => 'getvalue');
454
        $_POST = array('somekey' => 'postvalue');
455
        $_COOKIE = array('somekey' => 'cookievalue');
456
457
        $cookies = Injector::inst()->createWithArgs(
458
            Cookie_Backend::class,
459
            array(array('somekey' => 'sometestcookievalue'))
460
        );
461
462
        Director::test(
463
            'errorpage?somekey=sometestgetvalue',
464
            array('somekey' => 'sometestpostvalue'),
465
            null,
466
            null,
467
            null,
468
            null,
469
            $cookies
470
        );
471
472
        $this->assertEquals(
473
            'getvalue',
474
            $_GET['somekey'],
475
            '$_GET reset to original value after Director::test()'
476
        );
477
        $this->assertEquals(
478
            'postvalue',
479
            $_POST['somekey'],
480
            '$_POST reset to original value after Director::test()'
481
        );
482
        $this->assertEquals(
483
            'cookievalue',
484
            $_COOKIE['somekey'],
485
            '$_COOKIE reset to original value after Director::test()'
486
        );
487
    }
488
489
    public function providerTestTestRequestCarriesGlobals()
490
    {
491
        $tests = [];
492
        $fixture = ['somekey' => 'sometestvalue'];
493
        foreach (array('get', 'post') as $method) {
494
            foreach (array('return%sValue', 'returnRequestValue', 'returnCookieValue') as $testfunction) {
495
                $url = 'TestController/' . sprintf($testfunction, ucfirst($method))
496
                    . '?' . http_build_query($fixture);
497
                $tests[] = [$url, $fixture, $method];
498
            }
499
        }
500
        return $tests;
501
    }
502
503
    /**
504
     * @dataProvider providerTestTestRequestCarriesGlobals
505
     * @param $url
506
     * @param $fixture
507
     * @param $method
508
     */
509
    public function testTestRequestCarriesGlobals($url, $fixture, $method)
510
    {
511
        $getresponse = Director::test(
512
            $url,
513
            $fixture,
514
            null,
515
            strtoupper($method),
516
            null,
517
            null,
518
            Injector::inst()->createWithArgs(Cookie_Backend::class, array($fixture))
519
        );
520
521
        $this->assertInstanceOf(HTTPResponse::class, $getresponse, 'Director::test() returns HTTPResponse');
522
        $this->assertEquals($fixture['somekey'], $getresponse->getBody(), "Director::test({$url}, {$method})");
523
    }
524
525
    /**
526
     * Tests that additional parameters specified in the routing table are
527
     * saved in the request
528
     */
529
    public function testRouteParams()
530
    {
531
        /** @var HTTPRequest $request */
532
        Director::test('en-nz/myaction/myid/myotherid', null, null, null, null, null, null, $request);
533
534
        $this->assertEquals(
535
            array(
536
                'Controller' => TestController::class,
537
                'Action' => 'myaction',
538
                'ID' => 'myid',
539
                'OtherID' => 'myotherid',
540
                'Locale' => 'en_NZ'
541
            ),
542
            $request->params()
543
        );
544
    }
545
546
    public function testForceWWW()
547
    {
548
        $this->expectExceptionRedirect('http://www.mysite.com:9090/some-url');
549
        Director::mockRequest(function ($request) {
550
            Injector::inst()->registerService($request, HTTPRequest::class);
551
            Director::forceWWW();
552
        }, 'http://mysite.com:9090/some-url');
553
    }
554
555
    public function testPromisedForceWWW()
556
    {
557
        Director::forceWWW();
558
559
        // Flag is set but not redirected yet
560
        $middleware = CanonicalURLMiddleware::singleton();
561
        $this->assertTrue($middleware->getForceWWW());
562
563
        // Middleware forces the redirection eventually
564
        /** @var HTTPResponse $response */
565
        $response = Director::mockRequest(function ($request) use ($middleware) {
566
            return $middleware->process($request, function ($request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

566
            return $middleware->process($request, function (/** @scrutinizer ignore-unused */ $request) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
567
                return null;
568
            });
569
        }, 'http://mysite.com:9090/some-url');
570
571
        // Middleware returns non-exception redirect
572
        $this->assertEquals('http://www.mysite.com:9090/some-url', $response->getHeader('Location'));
573
        $this->assertEquals(301, $response->getStatusCode());
574
    }
575
576
    public function testForceSSLProtectsEntireSite()
577
    {
578
        $this->expectExceptionRedirect('https://www.mysite.com:9090/some-url');
579
        Director::mockRequest(function ($request) {
580
            Injector::inst()->registerService($request, HTTPRequest::class);
581
            Director::forceSSL();
582
        }, 'http://www.mysite.com:9090/some-url');
583
    }
584
585
    public function testPromisedForceSSL()
586
    {
587
        Director::forceSSL();
588
589
        // Flag is set but not redirected yet
590
        $middleware = CanonicalURLMiddleware::singleton();
591
        $this->assertTrue($middleware->getForceSSL());
592
593
        // Middleware forces the redirection eventually
594
        /** @var HTTPResponse $response */
595
        $response = Director::mockRequest(function ($request) use ($middleware) {
596
            return $middleware->process($request, function ($request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

596
            return $middleware->process($request, function (/** @scrutinizer ignore-unused */ $request) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
597
                return null;
598
            });
599
        }, 'http://www.mysite.com:9090/some-url');
600
601
        // Middleware returns non-exception redirect
602
        $this->assertInstanceOf(HTTPResponse::class, $response);
603
        $this->assertEquals('https://www.mysite.com:9090/some-url', $response->getHeader('Location'));
604
        $this->assertEquals(301, $response->getStatusCode());
605
    }
606
607
    public function testForceSSLOnTopLevelPagePattern()
608
    {
609
        // Expect admin to trigger redirect
610
        $this->expectExceptionRedirect('https://www.mysite.com:9090/admin');
611
        Director::mockRequest(function (HTTPRequest $request) {
612
            Injector::inst()->registerService($request, HTTPRequest::class);
613
            Director::forceSSL(array('/^admin/'));
614
        }, 'http://www.mysite.com:9090/admin');
615
    }
616
617
    public function testForceSSLOnSubPagesPattern()
618
    {
619
        // Expect to redirect to security login page
620
        $this->expectExceptionRedirect('https://www.mysite.com:9090/Security/login');
621
        Director::mockRequest(function (HTTPRequest $request) {
622
            Injector::inst()->registerService($request, HTTPRequest::class);
623
            Director::forceSSL(array('/^Security/'));
624
        }, 'http://www.mysite.com:9090/Security/login');
625
    }
626
627
    public function testForceSSLWithPatternDoesNotMatchOtherPages()
628
    {
629
        // Not on same url should not trigger redirect
630
        $response = Director::mockRequest(function (HTTPRequest $request) {
631
            Injector::inst()->registerService($request, HTTPRequest::class);
632
            Director::forceSSL(array('/^admin/'));
633
        }, 'http://www.mysite.com:9090/normal-page');
634
        $this->assertNull($response, 'Non-matching patterns do not trigger redirect');
635
636
        // nested url should not triger redirect either
637
        $response = Director::mockRequest(function (HTTPRequest $request) {
638
            Injector::inst()->registerService($request, HTTPRequest::class);
639
            Director::forceSSL(array('/^admin/', '/^Security/'));
640
        }, 'http://www.mysite.com:9090/just-another-page/sub-url');
641
        $this->assertNull($response, 'Non-matching patterns do not trigger redirect');
642
    }
643
644
    public function testForceSSLAlternateDomain()
645
    {
646
        // Ensure that forceSSL throws the appropriate exception
647
        $this->expectExceptionRedirect('https://secure.mysite.com/admin');
648
        Director::mockRequest(function (HTTPRequest $request) {
649
            Injector::inst()->registerService($request, HTTPRequest::class);
650
            return Director::forceSSL(array('/^admin/'), 'secure.mysite.com');
0 ignored issues
show
Bug introduced by
Are you sure the usage of SilverStripe\Control\Dir...), 'secure.mysite.com') targeting SilverStripe\Control\Director::forceSSL() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
651
        }, 'http://www.mysite.com:9090/admin');
652
    }
653
654
    public function testForceSSLAlternateDomainWithPort()
655
    {
656
        // Ensure that forceSSL throws the appropriate exception
657
        $this->expectExceptionRedirect('https://secure.mysite.com:81/admin');
658
        Director::mockRequest(function (HTTPRequest $request) {
659
            Injector::inst()->registerService($request, HTTPRequest::class);
660
            return Director::forceSSL(array('/^admin/'), 'secure.mysite.com:81');
0 ignored issues
show
Bug introduced by
Are you sure the usage of SilverStripe\Control\Dir...'secure.mysite.com:81') targeting SilverStripe\Control\Director::forceSSL() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
661
        }, 'http://www.mysite.com:9090/admin');
662
    }
663
664
    /**
665
     * Test that combined forceWWW and forceSSL combine safely
666
     */
667
    public function testForceSSLandForceWWW()
668
    {
669
        Director::forceWWW();
670
        Director::forceSSL();
671
672
        // Flag is set but not redirected yet
673
        $middleware = CanonicalURLMiddleware::singleton();
674
        $this->assertTrue($middleware->getForceWWW());
675
        $this->assertTrue($middleware->getForceSSL());
676
677
        // Middleware forces the redirection eventually
678
        /** @var HTTPResponse $response */
679
        $response = Director::mockRequest(function ($request) use ($middleware) {
680
            return $middleware->process($request, function ($request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

680
            return $middleware->process($request, function (/** @scrutinizer ignore-unused */ $request) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
681
                return null;
682
            });
683
        }, 'http://mysite.com:9090/some-url');
684
685
        // Middleware returns non-exception redirect
686
        $this->assertEquals('https://www.mysite.com:9090/some-url', $response->getHeader('Location'));
687
        $this->assertEquals(301, $response->getStatusCode());
688
    }
689
690
    /**
691
     * Set url to redirect to
692
     *
693
     * @var string
694
     */
695
    protected $expectedRedirect = null;
696
697
    /**
698
     * Expects this test to throw a HTTPResponse_Exception with the given redirect
699
     *
700
     * @param string $url
701
     */
702
    protected function expectExceptionRedirect($url)
703
    {
704
        $this->expectedRedirect = $url;
705
    }
706
707
    protected function runTest()
708
    {
709
        try {
710
            $result = parent::runTest();
711
            if ($this->expectedRedirect) {
712
                $this->fail("Expected to redirect to {$this->expectedRedirect} but no redirect found");
713
            }
714
            return $result;
715
        } catch (HTTPResponse_Exception $exception) {
716
            // Check URL
717
            if ($this->expectedRedirect) {
718
                $url = $exception->getResponse()->getHeader('Location');
719
                $this->assertEquals($this->expectedRedirect, $url, "Expected to redirect to {$this->expectedRedirect}");
720
                return null;
721
            } else {
722
                throw $exception;
723
            }
724
        }
725
    }
726
727
    public function testUnmatchedRequestReturns404()
728
    {
729
        // Remove non-tested rules
730
        $this->assertEquals(404, Director::test('no-route')->getStatusCode());
731
    }
732
733
    public function testIsHttps()
734
    {
735
        // Trust all IPs for this test
736
        /** @var TrustedProxyMiddleware $trustedProxyMiddleware */
737
        $trustedProxyMiddleware
738
            = Injector::inst()->get(TrustedProxyMiddleware::class);
739
        $trustedProxyMiddleware->setTrustedProxyIPs('*');
740
741
        // Clear alternate_base_url for this test
742
        Director::config()->remove('alternate_base_url');
743
744
        // nothing available
745
        $headers = array(
746
            'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'
747
        );
748
        foreach ($headers as $header) {
749
            if (isset($_SERVER[$header])) {
750
                unset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']);
751
            }
752
        }
753
754
        $this->assertEquals(
755
            'no',
756
            Director::test('TestController/returnIsSSL')->getBody()
757
        );
758
759
        $this->assertEquals(
760
            'yes',
761
            Director::test(
762
                'TestController/returnIsSSL',
763
                null,
764
                null,
765
                null,
766
                null,
767
                ['X-Forwarded-Protocol' => 'https']
768
            )->getBody()
769
        );
770
771
        $this->assertEquals(
772
            'no',
773
            Director::test(
774
                'TestController/returnIsSSL',
775
                null,
776
                null,
777
                null,
778
                null,
779
                ['X-Forwarded-Protocol' => 'http']
780
            )->getBody()
781
        );
782
783
        $this->assertEquals(
784
            'no',
785
            Director::test(
786
                'TestController/returnIsSSL',
787
                null,
788
                null,
789
                null,
790
                null,
791
                ['X-Forwarded-Protocol' => 'ftp']
792
            )->getBody()
793
        );
794
795
        // https via HTTPS
796
        $_SERVER['HTTPS'] = 'true';
797
        $this->assertEquals(
798
            'yes',
799
            Director::test('TestController/returnIsSSL')->getBody()
800
        );
801
802
        $_SERVER['HTTPS'] = '1';
803
        $this->assertEquals(
804
            'yes',
805
            Director::test('TestController/returnIsSSL')->getBody()
806
        );
807
808
        $_SERVER['HTTPS'] = 'off';
809
        $this->assertEquals(
810
            'no',
811
            Director::test('TestController/returnIsSSL')->getBody()
812
        );
813
814
        // https via SSL
815
        $_SERVER['SSL'] = '';
816
        $this->assertEquals(
817
            'yes',
818
            Director::test('TestController/returnIsSSL')->getBody()
819
        );
820
    }
821
822
    public function testTestIgnoresHashes()
823
    {
824
        //test that hashes are ignored
825
        $url = "TestController/returnGetValue?somekey=key";
826
        $hash = "#test";
827
        /** @var HTTPRequest $request */
828
        $response = Director::test($url . $hash, null, null, null, null, null, null, $request);
829
        $this->assertFalse($response->isError());
830
        $this->assertEquals('key', $response->getBody());
831
        $this->assertEquals($request->getURL(true), $url);
832
833
        //test encoded hashes are accepted
834
        $url = "TestController/returnGetValue?somekey=test%23key";
835
        $response = Director::test($url, null, null, null, null, null, null, $request);
836
        $this->assertFalse($response->isError());
837
        $this->assertEquals('test#key', $response->getBody());
838
        $this->assertEquals($request->getURL(true), $url);
839
    }
840
841
    public function testRequestFilterInDirectorTest()
842
    {
843
        $filter = new DirectorTest\TestRequestFilter;
844
845
        $processor = new RequestProcessor(array($filter));
0 ignored issues
show
Deprecated Code introduced by
The class SilverStripe\Control\RequestProcessor has been deprecated: 4.0.0:5.0.0 Use HTTPMiddleware directly instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

845
        $processor = /** @scrutinizer ignore-deprecated */ new RequestProcessor(array($filter));
Loading history...
846
847
        Injector::inst()->registerService($processor, RequestProcessor::class);
848
        $response = Director::test('some-dummy-url');
849
        $this->assertEquals(404, $response->getStatusCode());
850
851
        $this->assertEquals(1, $filter->preCalls);
852
        $this->assertEquals(1, $filter->postCalls);
853
854
        $filter->failPost = true;
855
856
        $response = Director::test('some-dummy-url');
857
        $this->assertEquals(500, $response->getStatusCode());
858
        $this->assertEquals(_t(Director::class . '.REQUEST_ABORTED', 'Request aborted'), $response->getBody());
859
860
        $this->assertEquals(2, $filter->preCalls);
861
        $this->assertEquals(2, $filter->postCalls);
862
863
        $filter->failPre = true;
864
865
        $response = Director::test('some-dummy-url');
866
        $this->assertEquals(400, $response->getStatusCode());
867
        $this->assertEquals(_t(Director::class . '.INVALID_REQUEST', 'Invalid request'), $response->getBody());
868
869
        $this->assertEquals(3, $filter->preCalls);
870
871
        // preCall 'true' will trigger an exception and prevent post call execution
872
        $this->assertEquals(2, $filter->postCalls);
873
    }
874
875
    public function testGlobalMiddleware()
876
    {
877
        $middleware = new DirectorTest\TestMiddleware;
878
        Director::singleton()->setMiddlewares([$middleware]);
879
880
        $response = Director::test('some-dummy-url');
881
        $this->assertEquals(404, $response->getStatusCode());
882
883
        // Both triggered
884
        $this->assertEquals(1, $middleware->preCalls);
885
        $this->assertEquals(1, $middleware->postCalls);
886
887
        $middleware->failPost = true;
888
889
        $response = Director::test('some-dummy-url');
890
        $this->assertEquals(500, $response->getStatusCode());
891
892
        // Both triggered
893
        $this->assertEquals(2, $middleware->preCalls);
894
        $this->assertEquals(2, $middleware->postCalls);
895
896
        $middleware->failPre = true;
897
898
        $response = Director::test('some-dummy-url');
899
        $this->assertEquals(400, $response->getStatusCode());
900
901
        // Pre triggered, post not
902
        $this->assertEquals(3, $middleware->preCalls);
903
        $this->assertEquals(2, $middleware->postCalls);
904
    }
905
906
    public function testRouteSpecificMiddleware()
907
    {
908
        // Inject adapter in place of controller
909
        $specificMiddleware = new DirectorTest\TestMiddleware;
910
        Injector::inst()->registerService($specificMiddleware, 'SpecificMiddleware');
911
912
        // Register adapter as factory for creating this controller
913
        Config::modify()->merge(
914
            Injector::class,
915
            'ControllerWithMiddleware',
916
            [
917
                'class' => RequestHandlerMiddlewareAdapter::class,
918
                'constructor' => [
919
                    '%$' . TestController::class
920
                ],
921
                'properties' => [
922
                    'Middlewares' => [
923
                        '%$SpecificMiddleware',
924
                    ],
925
                ],
926
            ]
927
        );
928
929
        // Global middleware
930
        $middleware = new DirectorTest\TestMiddleware;
931
        Director::singleton()->setMiddlewares([$middleware]);
932
933
        // URL rules, one of which has a specific middleware
934
        Config::modify()->set(
935
            Director::class,
936
            'rules',
937
            [
938
                'url-one' => TestController::class,
939
                'url-two' => [
940
                    'Controller' => 'ControllerWithMiddleware',
941
                ],
942
            ]
943
        );
944
945
        // URL without a route-specific middleware
946
        Director::test('url-one');
947
948
        // Only the global middleware triggered
949
        $this->assertEquals(1, $middleware->preCalls);
950
        $this->assertEquals(0, $specificMiddleware->postCalls);
951
952
        Director::test('url-two');
953
954
        // Both triggered on the url with the specific middleware applied
955
        $this->assertEquals(2, $middleware->preCalls);
956
        $this->assertEquals(1, $specificMiddleware->postCalls);
957
    }
958
959
    /**
960
     * If using phpdbg it returns itself instead of "cli" from php_sapi_name()
961
     */
962
    public function testIsCli()
963
    {
964
        $this->assertTrue(Director::is_cli(), 'is_cli should be true for PHP CLI and phpdbg');
965
    }
966
967
    public function testMockRequest()
968
    {
969
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/some-subdir/');
970
971
        // Can handle root-relative $url
972
        Director::mockRequest(function (HTTPRequest $request) {
973
            $this->assertEquals('some-page/nested', $request->getURL());
974
            $this->assertEquals(1, $request->getVar('query'));
975
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
976
        }, '/some-subdir/some-page/nested?query=1');
977
978
        // Can handle absolute $url
979
        Director::mockRequest(function (HTTPRequest $request) {
980
            $this->assertEquals('some-page/nested', $request->getURL());
981
            $this->assertEquals(1, $request->getVar('query'));
982
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
983
        }, 'http://www.mysite.com:9090/some-subdir/some-page/nested?query=1');
984
985
        // Can handle relative $url
986
        Director::mockRequest(function (HTTPRequest $request) {
987
            $this->assertEquals('some-page/nested', $request->getURL());
988
            $this->assertEquals(1, $request->getVar('query'));
989
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
990
        }, 'some-page/nested?query=1');
991
    }
992
}
993