Completed
Push — dev ( 3aadaf...6a5b86 )
by Jonathan
13s
created

WhipTest::testFallbackToRemoteAddr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 5
nc 1
nop 0
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 PHPUnit_Framework_TestCase;
28
use Vectorface\Whip\Whip;
29
use Psr\Http\Message\ServerRequestInterface;
30
31
/**
32
 * Test class for testing Whip.
33
 * @backupGlobals enabled
34
 * @copyright Vectorface, Inc 2015
35
 * @author Daniel Bruce <[email protected]>
36
 */
37
class WhipTest extends PHPUnit_Framework_TestCase
38
{
39
    /**
40
     * Tests that an invalid source format is rejected.
41
     * @expectedException \InvalidArgumentException
42
     */
43
    public function testInvalidSource()
44
    {
45
        new Whip(Whip::REMOTE_ADDR, array(), new \stdClass());
46
    }
47
    /**
48
     * Tests that we get back the right IP when there using superglobals.
49
     */
50
    public function testSuperglobal()
0 ignored issues
show
Coding Style introduced by
testSuperglobal uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
51
    {
52
        $_SERVER = array('REMOTE_ADDR' => '24.24.24.24');
53
        $lookup = new Whip(Whip::REMOTE_ADDR);
54
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
55
    }
56
57
    /**
58
     * Tests that we get back 127.0.0.1 when there is no superglobal information
59
     * at all.
60
     */
61
    public function testEmptySuperglobal()
0 ignored issues
show
Coding Style introduced by
testEmptySuperglobal uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
62
    {
63
        $_SERVER = array();
64
        $lookup = new Whip();
65
        $this->assertFalse($lookup->getIpAddress());
66
    }
67
68
    /**
69
     * Helper to get a mocked PSR-7 instance.
70
     *
71
     * @param string $remoteAddr The remote address to mock.
72
     * @param string[][] $headers The headers, in the format expected by Psr-7.
73
     */
74
    private function getHttpMessageMock($remoteAddr, array $headers = array())
75
    {
76
        $stub = $this->getMockBuilder("Psr\Http\Message\ServerRequestInterface")
77
            ->getMock();
78
79
        $stub->method('getServerParams')
80
            ->willReturn(array('REMOTE_ADDR' => $remoteAddr));
81
        $stub->method('getHeaders')
82
            ->willReturn($headers);
83
84
        return $stub;
85
    }
86
    /**
87
     * Tests that we can use a PSR-7 ServerRequestInterface compatible class.
88
     */
89
    public function testPsr7Request()
90
    {
91
        $lookup = new Whip(
92
            Whip::PROXY_HEADERS,
93
            array(
94
                Whip::PROXY_HEADERS => array(
95
                    Whip::IPV4 => array(
96
                        '127.0.0.1'
97
                    )
98
                )
99
            ),
100
            $this->getHttpMessageMock("127.0.0.1", array('X-Forwarded-For' => array('192.168.1.1,32.32.32.32')))
101
        );
102
103
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
104
    }
105
106
    /**
107
     * Tests that we get false when no valid IP address could be found.
108
     */
109
    public function testNoAddresFoundDueToBitmask()
110
    {
111
        $lookup = new Whip(Whip::PROXY_HEADERS);
112
        $lookup->setSource(array('REMOTE_ADDR' => '127.0.0.1'));
113
        $this->assertFalse($lookup->getIpAddress());
114
    }
115
116
    /**
117
     * Tests the standard REMOTE_ADDR method.
118
     */
119
    public function testRemoteAddrMethod()
120
    {
121
        $lookup = new Whip(Whip::REMOTE_ADDR);
122
        $lookup->setSource(array('REMOTE_ADDR' => '24.24.24.24'));
123
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
124
    }
125
126
    /**
127
     * Tests that an invalid IPv4 address returns false.
128
     */
129
    public function testInvalidIPv4Address()
130
    {
131
        $lookup = new Whip(Whip::REMOTE_ADDR);
132
        $lookup->setSource(array('REMOTE_ADDR' => '127.0.0.256'));
133
        $this->assertFalse($lookup->getValidIpAddress());
134
    }
135
136
    /**
137
     * Tests a valid IPv6 address.
138
     */
139
    public function testValidIPv6Address()
140
    {
141
        $lookup = new Whip(Whip::REMOTE_ADDR);
142
        $lookup->setSource(array('REMOTE_ADDR' => '::1'));
143
        $this->assertEquals('::1', $lookup->getValidIpAddress());
144
    }
145
146
    /**
147
     * Tests that we accept whitelisted proxy methods when the IP matches, even
148
     * if the IP listed is a comma separated list.
149
     *
150
     * @dataProvider proxyMethodWhitelistProvider
151
     */
152 View Code Duplication
    public function testValidWhitelistedProxyMethod($remoteAddr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
    {
154
        $lookup = new Whip(
155
            Whip::PROXY_HEADERS,
156
            array(
157
                Whip::PROXY_HEADERS => array(
158
                    Whip::IPV4 => array('127.0.0.1'),
159
                    Whip::IPV6 => array('::1')
160
                )
161
            ),
162
            array(
163
                'REMOTE_ADDR' => $remoteAddr,
164
                'HTTP_X_FORWARDED_FOR' => '192.168.1.1,32.32.32.32'
165
            )
166
        );
167
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
168
    }
169
170
    /**
171
     * Repeats the above test twice for ipv4 and ipv6
172
     */
173
    public function proxyMethodWhitelistProvider()
174
    {
175
        return array(
176
            array('127.0.0.1'),
177
            array('::1'),
178
        );
179
    }
180
181
    /**
182
     * Tests that we accept proxy method based on a whitelisted IP using the
183
     * dashed range notation.
184
     */
185 View Code Duplication
    public function testValidWhitelistedProxyMethodWithDashNotation()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
    {
187
        $lookup = new Whip(
188
            Whip::PROXY_HEADERS,
189
            array(
190
                Whip::PROXY_HEADERS => array(
191
                    Whip::IPV4 => array(
192
                        '127.0.0.0-127.0.255.255',
193
                    ),
194
                    Whip::IPV6 => array(
195
                        '::1'
196
                    )
197
                )
198
            ),
199
            array(
200
                'REMOTE_ADDR' => '127.0.0.1',
201
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
202
            )
203
        );
204
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
205
    }
206
207
    /**
208
     * Tests that we accept proxy method based on a whitelisted IP using the
209
     * wildcard asterix notation.
210
     */
211 View Code Duplication
    public function testValidWhitelistedProxyMethodWithWildcardNotation()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
212
    {
213
        $lookup = new Whip(
214
            Whip::PROXY_HEADERS,
215
            array(
216
                Whip::PROXY_HEADERS => array(
217
                    Whip::IPV4 => array(
218
                        '127.0.*'
219
                    ),
220
                    Whip::IPV6 => array(
221
                        '::1'
222
                    )
223
                )
224
            ),
225
            array(
226
                'REMOTE_ADDR' => '127.0.0.1',
227
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
228
            )
229
        );
230
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
231
    }
232
233
    /**
234
     * Tests that we accept proxy method based on a whitelisted IP using the
235
     * CIDR address notation.
236
     */
237 View Code Duplication
    public function testValidWhitelistedProxyMethodWithCIDRdNotation()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
    {
239
        $lookup = new Whip(
240
            Whip::PROXY_HEADERS,
241
            array(
242
                Whip::PROXY_HEADERS => array(
243
                    Whip::IPV4 => array(
244
                        '127.0.0.0/24'
245
                    ),
246
                    Whip::IPV6 => array(
247
                        '::1'
248
                    )
249
                )
250
            ),
251
            array(
252
                'REMOTE_ADDR' => '127.0.0.1',
253
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
254
            )
255
        );
256
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
257
    }
258
259
    /**
260
     * Tests that we get false if there is a valid IP in a proxy header but
261
     * we reject it due to REMOTE_ADDR not being in the whitelist.
262
     */
263 View Code Duplication
    public function testValidIpRejectedDueToWhitelist()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
264
    {
265
        $lookup = new Whip(
266
            Whip::PROXY_HEADERS,
267
            array(
268
                Whip::PROXY_HEADERS => array(
269
                    Whip::IPV4 => array(
270
                        '127.0.0.1/24'
271
                    ),
272
                    Whip::IPV6 => array(
273
                        '::1'
274
                    )
275
                )
276
            ),
277
            array(
278
                'REMOTE_ADDR' => '24.24.24.24',
279
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
280
            )
281
        );
282
        $this->assertFalse($lookup->getIpAddress());
283
    }
284
285
    /**
286
     * Tests that we reject a proxy listed IPv6 address that does not fall within
287
     * the allowed subnet.
288
     */
289
    public function testIPv6AddressRejectedDueToWhitelist()
290
    {
291
        $lookup = new Whip(
292
            Whip::PROXY_HEADERS,
293
            array(
294
                Whip::PROXY_HEADERS => array(
295
                    Whip::IPV6 => array(
296
                        '2400:cb00::/32'
297
                    )
298
                )
299
            ),
300
            array(
301
                'REMOTE_ADDR' => '::1',
302
                'HTTP_X_FORWARDED_FOR' => '::1'
303
            )
304
        );
305
        $this->assertFalse($lookup->getIpAddress());
306
    }
307
308
    /**
309
     * Tests that we reject a proxy listed IPv6 address that does not fall within
310
     * the allowed subnet.
311
     */
312
    public function testIPv6AddressFoundInWhitelist()
313
    {
314
        $lookup = new Whip(
315
            Whip::PROXY_HEADERS,
316
            array(
317
                Whip::PROXY_HEADERS => array(
318
                    Whip::IPV6 => array(
319
                        '::1/32'
320
                    )
321
                )
322
            ),
323
            array(
324
                'REMOTE_ADDR' => '::1',
325
                'HTTP_X_FORWARDED_FOR' => '::1'
326
            )
327
        );
328
        $this->assertEquals('::1', $lookup->getIpAddress());
329
    }
330
331
    /**
332
     * Test that an IPv4 address is rejected because the whitelist is empty for
333
     * IPv4.
334
     */
335
    public function testIPv4AddressRejectedDueToEmptyWhitelist()
336
    {
337
        $lookup = new Whip(
338
            Whip::PROXY_HEADERS,
339
            array(
340
                Whip::PROXY_HEADERS => array(
341
                    Whip::IPV6 => array(
342
                        '::1/32'
343
                    )
344
                )
345
            ),
346
            array(
347
                'REMOTE_ADDR' => '127.0.0.1',
348
                'HTTP_X_FORWARDED_FOR' => '24.24.24.24'
349
            )
350
        );
351
        $this->assertFalse($lookup->getIpAddress());
352
    }
353
354
    /**
355
     * Test that an IPv6 address is rejected because the whitelist is empty for
356
     * IPv6.
357
     */
358
    public function testIPv6AddressRejectedDueToEmptyWhitelist()
359
    {
360
        $lookup = new Whip(
361
            Whip::PROXY_HEADERS,
362
            array(
363
                Whip::PROXY_HEADERS => array(
364
                    Whip::IPV4 => array(
365
                        '127.0.0.0/24'
366
                    )
367
                )
368
            ),
369
            array(
370
                'REMOTE_ADDR' => '::1',
371
                'HTTP_X_FORWARDED_FOR' => '::1'
372
            )
373
        );
374
        $this->assertFalse($lookup->getIpAddress());
375
    }
376
377
    /**
378
     * Test a custom header with a whitelisted IP.
379
     */
380 View Code Duplication
    public function testCustomHeader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
381
    {
382
        $lookup = new Whip(
383
            Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR,
384
            array(
385
                Whip::CUSTOM_HEADERS => array(
386
                    Whip::IPV4 => array(
387
                        '127.0.0.1',
388
                        '::1'
389
                    )
390
                )
391
            ),
392
            array(
393
                'REMOTE_ADDR' => '127.0.0.1',
394
                'HTTP_CUSTOM_SECRET_HEADER' => '32.32.32.32'
395
            )
396
        );
397
        $this->assertEquals(
398
            '32.32.32.32',
399
            $lookup->addCustomHeader('HTTP_CUSTOM_SECRET_HEADER')->getIpAddress()
400
        );
401
    }
402
403
    /**
404
     * Test HTTP_X_REAL_IP header.
405
     */
406
    public function testHttpXRealIpHeader()
407
    {
408
        $lookup = new Whip(
409
            Whip::PROXY_HEADERS | Whip::REMOTE_ADDR,
410
            array(),
411
            array(
412
                'REMOTE_ADDR' => '127.0.0.1',
413
                'HTTP_X_REAL_IP' => '24.24.24.24'
414
            )
415
        );
416
        $this->assertEquals('24.24.24.24', $lookup->getIpAddress());
417
    }
418
419
    /**
420
     * Tests that if we specify the source array, it overrides any values found
421
     * in the $_SERVER array.
422
     */
423 View Code Duplication
    public function testSourceArrayOverridesServerSuperglobal()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
424
    {
425
        $source = array(
426
            'REMOTE_ADDR' => '24.24.24.24'
427
        );
428
        $lookup = new Whip(Whip::REMOTE_ADDR, array(), array('REMOTE_ADDR' => '127.0.0.1'));
429
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
430
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress($source));
431
    }
432
433
    /**
434
     * Tests that if we specify the source array through Whip::setSource, the
435
     * class will override any values found in $_SERVER.
436
     */
437 View Code Duplication
    public function testSetSourceArrayOverridesServerSuperglobal()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
438
    {
439
        $source = array(
440
            'REMOTE_ADDR' => '24.24.24.24'
441
        );
442
        $lookup = new Whip(Whip::REMOTE_ADDR, array(), array('REMOTE_ADDR' => '127.0.0.1'));
443
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
444
        $lookup->setSource($source);
445
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
446
    }
447
448
    /**
449
     * Tests that we fallback to REMOTE_ADDR if the custom header was not found
450
     */
451
    public function testFallbackToRemoteAddr()
452
    {
453
        $source = array(
454
            'REMOTE_ADDR' => '24.24.24.24'
455
        );
456
        $lookup = new Whip(Whip::PROXY_HEADERS | Whip::REMOTE_ADDR, array(), $source);
457
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
458
    }
459
}
460