Passed
Pull Request — master (#27)
by
unknown
11:39
created

WhipTest::getSymfonyRequest()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 10
rs 10
1
<?php
2
/*
3
The MIT License (MIT)
4
5
Copyright (c) 2015 Vectorface, Inc.
6
7
Permission is hereby granted, free of charge, to any person obtaining a copy
8
of this software and associated documentation files (the "Software"), to deal
9
in the Software without restriction, including without limitation the rights
10
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
copies of the Software, and to permit persons to whom the Software is
12
furnished to do so, subject to the following conditions:
13
14
The above copyright notice and this permission notice shall be included in
15
all copies or substantial portions of the Software.
16
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
THE SOFTWARE.
24
*/
25
namespace Vectorface\WhipTests;
26
27
use InvalidArgumentException;
28
use PHPUnit\Framework\TestCase;
29
use Psr\Http\Message\ServerRequestInterface;
30
use stdClass;
31
use Symfony\Component\HttpFoundation\Request;
32
use Vectorface\Whip\Whip;
33
34
/**
35
 * Test class for testing Whip.
36
 * @backupGlobals enabled
37
 * @copyright Vectorface, Inc 2015
38
 * @author Daniel Bruce <[email protected]>
39
 */
40
class WhipTest extends TestCase
41
{
42
    /**
43
     * Tests that an invalid source format is rejected.
44
     */
45
    public function testInvalidSource()
46
    {
47
        $this->expectException(InvalidArgumentException::class);
48
        new Whip(Whip::REMOTE_ADDR, [], new stdClass());
49
    }
50
51
    /**
52
     * Tests that we get back the right IP when they are using superglobals.
53
     */
54
    public function testSuperglobal()
55
    {
56
        $_SERVER['REMOTE_ADDR'] = '24.24.24.24';
57
        $lookup = new Whip(Whip::REMOTE_ADDR);
58
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
59
    }
60
61
    /**
62
     * Tests that we get back 127.0.0.1 when there is no superglobal information
63
     * at all.
64
     */
65
    public function testEmptySuperglobal()
66
    {
67
        $_SERVER = [];
68
        $lookup = new Whip();
69
        $this->assertFalse($lookup->getIpAddress());
70
    }
71
72
    /**
73
     * Helper to get a mocked PSR-7 instance.
74
     *
75
     * @param string $remoteAddr The remote address to mock.
76
     * @param string[][] $headers The headers, in the format expected by Psr-7.
77
     */
78
    private function getHttpMessageMock(string $remoteAddr, array $headers = [])
79
    {
80
        $stub = $this->getMockBuilder(ServerRequestInterface::class)
81
            ->getMock();
82
83
        $stub->method('getServerParams')
84
            ->willReturn(['REMOTE_ADDR' => $remoteAddr]);
85
        $stub->method('getHeaders')
86
            ->willReturn($headers);
87
88
        return $stub;
89
    }
90
    /**
91
     * Tests that we can use a PSR-7 ServerRequestInterface compatible class.
92
     */
93
    public function testPsr7Request()
94
    {
95
        $lookup = new Whip(
96
            Whip::PROXY_HEADERS,
97
            [
98
                Whip::PROXY_HEADERS => [
99
                    Whip::IPV4 => [
100
                        '127.0.0.1'
101
                    ]
102
                ]
103
            ],
104
            $this->getHttpMessageMock("127.0.0.1", ['X-Forwarded-For' => ['32.32.32.32,192.168.1.1']])
105
        );
106
107
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
108
    }
109
110
    /**
111
     * Helper to get a Symfony request instance.
112
     *
113
     * @param string $remoteAddr The remote address to mock.
114
     * @param string[][] $headers The headers, in the format expected by Psr-7.
115
     */
116
    private function getSymfonyRequest(string $remoteAddr, array $headers = []): Request
117
    {
118
        $serverVariables = [
119
            'REMOTE_ADDR' => $remoteAddr,
120
        ];
121
        foreach($headers as $key => $value) {
122
            $serverVariables['HTTP_' . strtoupper($key)] = implode(',', $value);
123
        }
124
        return new Request(
125
            server: $serverVariables,
126
        );
127
    }
128
    public function testSymfonyRequest()
129
    {
130
        $lookup = new Whip(
131
            Whip::PROXY_HEADERS,
132
            [
133
                Whip::PROXY_HEADERS => [
134
                    Whip::IPV4 => [
135
                        '127.0.0.1'
136
                    ]
137
                ]
138
            ],
139
            $this->getSymfonyRequest("127.0.0.1", ['X-Forwarded-For' => ['32.32.32.32,192.168.1.1']])
140
        );
141
142
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
143
    }
144
145
    /**
146
     * Tests that we get false when no valid IP address could be found.
147
     */
148
    public function testNoAddresFoundDueToBitmask()
149
    {
150
        $lookup = new Whip(Whip::PROXY_HEADERS);
151
        $lookup->setSource(['REMOTE_ADDR' => '127.0.0.1']);
152
        $this->assertFalse($lookup->getIpAddress());
153
    }
154
155
    /**
156
     * Tests the standard REMOTE_ADDR method.
157
     */
158
    public function testRemoteAddrMethod()
159
    {
160
        $lookup = new Whip(Whip::REMOTE_ADDR);
161
        $lookup->setSource(['REMOTE_ADDR' => '24.24.24.24']);
162
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
163
    }
164
165
    /**
166
     * Tests that an invalid IPv4 address returns false.
167
     */
168
    public function testInvalidIPv4Address()
169
    {
170
        $lookup = new Whip(Whip::REMOTE_ADDR);
171
        $lookup->setSource(['REMOTE_ADDR' => '127.0.0.256']);
172
        $this->assertFalse($lookup->getValidIpAddress());
173
    }
174
175
    /**
176
     * Tests a valid IPv6 address.
177
     */
178
    public function testValidIPv6Address()
179
    {
180
        $lookup = new Whip(Whip::REMOTE_ADDR);
181
        $lookup->setSource(['REMOTE_ADDR' => '::1']);
182
        $this->assertEquals('::1', $lookup->getValidIpAddress());
183
    }
184
185
    /**
186
     * Tests that we accept whitelisted proxy methods when the IP matches, even
187
     * if the IP listed is a comma separated list.
188
     *
189
     * @dataProvider proxyMethodWhitelistProvider
190
     */
191
    public function testValidWhitelistedProxyMethod($remoteAddr)
192
    {
193
        $lookup = new Whip(
194
            Whip::PROXY_HEADERS,
195
            [
196
                Whip::PROXY_HEADERS => [
197
                    Whip::IPV4 => ['127.0.0.1'],
198
                    Whip::IPV6 => ['::1']
199
                ]
200
            ],
201
            [
202
                'REMOTE_ADDR' => $remoteAddr,
203
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32,192.168.1.1'
204
            ]
205
        );
206
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
207
    }
208
209
    /**
210
     * Repeats the above test twice for ipv4 and ipv6
211
     */
212
    public function proxyMethodWhitelistProvider() : array
213
    {
214
        return [
215
            ['127.0.0.1'],
216
            ['::1'],
217
        ];
218
    }
219
220
    /**
221
     * Tests that we accept proxy method based on a whitelisted IP using the
222
     * dashed range notation.
223
     */
224
    public function testValidWhitelistedProxyMethodWithDashNotation()
225
    {
226
        $lookup = new Whip(
227
            Whip::PROXY_HEADERS,
228
            [
229
                Whip::PROXY_HEADERS => [
230
                    Whip::IPV4 => [
231
                        '127.0.0.0-127.0.255.255',
232
                    ],
233
                    Whip::IPV6 => [
234
                        '::1'
235
                    ]
236
                ]
237
            ],
238
            [
239
                'REMOTE_ADDR' => '127.0.0.1',
240
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
241
            ]
242
        );
243
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
244
    }
245
246
    /**
247
     * Tests that we accept proxy method based on a whitelisted IP using the
248
     * wildcard asterix notation.
249
     */
250
    public function testValidWhitelistedProxyMethodWithWildcardNotation()
251
    {
252
        $lookup = new Whip(
253
            Whip::PROXY_HEADERS,
254
            [
255
                Whip::PROXY_HEADERS => [
256
                    Whip::IPV4 => [
257
                        '127.0.*'
258
                    ],
259
                    Whip::IPV6 => [
260
                        '::1'
261
                    ]
262
                ]
263
            ],
264
            [
265
                'REMOTE_ADDR' => '127.0.0.1',
266
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
267
            ]
268
        );
269
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
270
    }
271
272
    /**
273
     * Tests that we accept proxy method based on a whitelisted IP using the
274
     * CIDR address notation.
275
     */
276
    public function testValidWhitelistedProxyMethodWithCIDRdNotation()
277
    {
278
        $lookup = new Whip(
279
            Whip::PROXY_HEADERS,
280
            [
281
                Whip::PROXY_HEADERS => [
282
                    Whip::IPV4 => [
283
                        '127.0.0.0/24'
284
                    ],
285
                    Whip::IPV6 => [
286
                        '::1'
287
                    ]
288
                ]
289
            ],
290
            [
291
                'REMOTE_ADDR' => '127.0.0.1',
292
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
293
            ]
294
        );
295
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
296
    }
297
298
    /**
299
     * Tests that we get false if there is a valid IP in a proxy header but
300
     * we reject it due to REMOTE_ADDR not being in the whitelist.
301
     */
302
    public function testValidIpRejectedDueToWhitelist()
303
    {
304
        $lookup = new Whip(
305
            Whip::PROXY_HEADERS,
306
            [
307
                Whip::PROXY_HEADERS => [
308
                    Whip::IPV4 => [
309
                        '127.0.0.1/24'
310
                    ],
311
                    Whip::IPV6 => [
312
                        '::1'
313
                    ]
314
                ]
315
            ],
316
            [
317
                'REMOTE_ADDR' => '24.24.24.24',
318
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
319
            ]
320
        );
321
        $this->assertFalse($lookup->getIpAddress());
322
    }
323
324
    /**
325
     * Tests that we reject a proxy listed IPv6 address that does not fall within
326
     * the allowed subnet.
327
     */
328
    public function testIPv6AddressRejectedDueToWhitelist()
329
    {
330
        $lookup = new Whip(
331
            Whip::PROXY_HEADERS,
332
            [
333
                Whip::PROXY_HEADERS => [
334
                    Whip::IPV6 => [
335
                        '2400:cb00::/32'
336
                    ]
337
                ]
338
            ],
339
            [
340
                'REMOTE_ADDR' => '::1',
341
                'HTTP_X_FORWARDED_FOR' => '::1'
342
            ]
343
        );
344
        $this->assertFalse($lookup->getIpAddress());
345
    }
346
347
    /**
348
     * Tests that we reject a proxy listed IPv6 address that does not fall within
349
     * the allowed subnet.
350
     */
351
    public function testIPv6AddressFoundInWhitelist()
352
    {
353
        $lookup = new Whip(
354
            Whip::PROXY_HEADERS,
355
            [
356
                Whip::PROXY_HEADERS => [
357
                    Whip::IPV6 => [
358
                        '::1/32'
359
                    ]
360
                ]
361
            ],
362
            [
363
                'REMOTE_ADDR' => '::1',
364
                'HTTP_X_FORWARDED_FOR' => '::1'
365
            ]
366
        );
367
        $this->assertEquals('::1', $lookup->getIpAddress());
368
    }
369
370
    /**
371
     * Test that an IPv4 address is rejected because the whitelist is empty for
372
     * IPv4.
373
     */
374
    public function testIPv4AddressRejectedDueToEmptyWhitelist()
375
    {
376
        $lookup = new Whip(
377
            Whip::PROXY_HEADERS,
378
            [
379
                Whip::PROXY_HEADERS => [
380
                    Whip::IPV6 => [
381
                        '::1/32'
382
                    ]
383
                ]
384
            ],
385
            [
386
                'REMOTE_ADDR' => '127.0.0.1',
387
                'HTTP_X_FORWARDED_FOR' => '24.24.24.24'
388
            ]
389
        );
390
        $this->assertFalse($lookup->getIpAddress());
391
    }
392
393
    /**
394
     * Test that an IPv6 address is rejected because the whitelist is empty for
395
     * IPv6.
396
     */
397
    public function testIPv6AddressRejectedDueToEmptyWhitelist()
398
    {
399
        $lookup = new Whip(
400
            Whip::PROXY_HEADERS,
401
            [
402
                Whip::PROXY_HEADERS => [
403
                    Whip::IPV4 => [
404
                        '127.0.0.0/24'
405
                    ]
406
                ]
407
            ],
408
            [
409
                'REMOTE_ADDR' => '::1',
410
                'HTTP_X_FORWARDED_FOR' => '::1'
411
            ]
412
        );
413
        $this->assertFalse($lookup->getIpAddress());
414
    }
415
416
    /**
417
     * Test a custom header with a whitelisted IP.
418
     */
419
    public function testCustomHeader()
420
    {
421
        $lookup = new Whip(
422
            Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR,
423
            [
424
                Whip::CUSTOM_HEADERS => [
425
                    Whip::IPV4 => [
426
                        '127.0.0.1',
427
                        '::1'
428
                    ]
429
                ]
430
            ],
431
            [
432
                'REMOTE_ADDR' => '127.0.0.1',
433
                'HTTP_CUSTOM_SECRET_HEADER' => '32.32.32.32'
434
            ]
435
        );
436
        $this->assertEquals(
437
            '32.32.32.32',
438
            $lookup->addCustomHeader('HTTP_CUSTOM_SECRET_HEADER')->getIpAddress()
439
        );
440
    }
441
442
    /**
443
     * Test HTTP_X_REAL_IP header.
444
     */
445
    public function testHttpXRealIpHeader()
446
    {
447
        $lookup = new Whip(
448
            Whip::PROXY_HEADERS | Whip::REMOTE_ADDR,
449
            [],
450
            [
451
                'REMOTE_ADDR' => '127.0.0.1',
452
                'HTTP_X_REAL_IP' => '24.24.24.24'
453
            ]
454
        );
455
        $this->assertEquals('24.24.24.24', $lookup->getIpAddress());
456
    }
457
458
    /**
459
     * Tests that if we specify the source array, it overrides any values found
460
     * in the $_SERVER array.
461
     */
462
    public function testSourceArrayOverridesServerSuperglobal()
463
    {
464
        $source = [
465
            'REMOTE_ADDR' => '24.24.24.24'
466
        ];
467
        $lookup = new Whip(Whip::REMOTE_ADDR, [], ['REMOTE_ADDR' => '127.0.0.1']);
468
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
469
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress($source));
470
    }
471
472
    /**
473
     * Tests that if we specify the source array through Whip::setSource, the
474
     * class will override any values found in $_SERVER.
475
     */
476
    public function testSetSourceArrayOverridesServerSuperglobal()
477
    {
478
        $source = [
479
            'REMOTE_ADDR' => '24.24.24.24'
480
        ];
481
        $lookup = new Whip(Whip::REMOTE_ADDR, [], ['REMOTE_ADDR' => '127.0.0.1']);
482
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
483
        $lookup->setSource($source);
484
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
485
    }
486
487
    /**
488
     * Tests that we fallback to REMOTE_ADDR if the custom header was not found
489
     */
490
    public function testFallbackToRemoteAddr()
491
    {
492
        $source = [
493
            'REMOTE_ADDR' => '24.24.24.24'
494
        ];
495
        $lookup = new Whip(Whip::PROXY_HEADERS | Whip::REMOTE_ADDR, [], $source);
496
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
497
    }
498
}
499