Completed
Push — scrutinizer ( 83b4bd )
by Jonathan
04:29
created

WhipTest   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 409
Duplicated Lines 30.56 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 22
c 3
b 1
f 0
lcom 2
cbo 3
dl 125
loc 409
rs 10

22 Methods

Rating   Name   Duplication   Size   Complexity  
A testInvalidSource() 0 4 1
A testSuperglobal() 0 6 1
A testEmptySuperglobal() 0 6 1
A getHttpMessageMock() 0 12 1
A testPsr7Request() 0 16 1
A testNoAddresFoundDueToBitmask() 0 6 1
A testRemoteAddrMethod() 0 6 1
A testInvalidIPv4Address() 0 6 1
A testValidIPv6Address() 0 6 1
B testValidWhitelistedProxyMethod() 0 28 1
A testValidWhitelistedProxyMethodWithDashNotation() 21 21 1
A testValidWhitelistedProxyMethodWithWildcardNotation() 21 21 1
A testValidWhitelistedProxyMethodWithCIDRdNotation() 21 21 1
A testValidIpRejectedDueToWhitelist() 21 21 1
A testIPv6AddressRejectedDueToWhitelist() 0 18 1
A testIPv6AddressFoundInWhitelist() 0 18 1
A testIPv4AddressRejectedDueToEmptyWhitelist() 0 18 1
A testIPv6AddressRejectedDueToEmptyWhitelist() 0 18 1
A testCustomHeader() 22 22 1
A testHttpXRealIpHeader() 0 12 1
A testSourceArrayOverridesServerSuperglobal() 9 9 1
A testSetSourceArrayOverridesServerSuperglobal() 10 10 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 Vectorface\Whip\IpRange\IpWhitelist;
30
use Psr\Http\Message\ServerRequestInterface;
31
32
/**
33
 * Test class for testing Whip.
34
 * @backupGlobals enabled
35
 * @copyright Vectorface, Inc 2015
36
 * @author Daniel Bruce <[email protected]>
37
 */
38
class WhipTest extends PHPUnit_Framework_TestCase
39
{
40
    /**
41
     * Tests that an invalid source format is rejected.
42
     * @expectedException \InvalidArgumentException
43
     */
44
    public function testInvalidSource()
45
    {
46
        new Whip(Whip::REMOTE_ADDR, [], new \stdClass());
47
    }
48
    /**
49
     * Tests that we get back the right IP when there using superglobals.
50
     */
51
    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...
52
    {
53
        $_SERVER = array('REMOTE_ADDR' => '24.24.24.24');
54
        $lookup = new Whip(Whip::REMOTE_ADDR);
55
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
56
    }
57
58
    /**
59
     * Tests that we get back 127.0.0.1 when there is no superglobal information
60
     * at all.
61
     */
62
    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...
63
    {
64
        $_SERVER = array();
65
        $lookup = new Whip();
66
        $this->assertFalse($lookup->getIpAddress());
67
    }
68
69
    /**
70
     * Helper to get a mocked PSR-7 instance.
71
     *
72
     * @param string $remoteAddr The remote address to mock.
73
     * @param string[] $headers The headers, in the format expected by Psr-7.
74
     */
75
    private function getHttpMessageMock($remoteAddr, array $headers = [])
76
    {
77
        $stub = $this->getMockBuilder("Psr\Http\Message\ServerRequestInterface")
78
            ->getMock();
79
80
        $stub->method('getServerParams')
81
            ->willReturn(array('REMOTE_ADDR' => $remoteAddr));
82
        $stub->method('getHeaders')
83
            ->willReturn($headers);
84
85
        return $stub;
86
    }
87
    /**
88
     * Tests that we can use a PSR-7 ServerRequestInterface compatible class.
89
     */
90
    public function testPsr7Request()
91
    {
92
        $lookup = new Whip(
93
            Whip::PROXY_HEADERS,
94
            array(
95
                Whip::PROXY_HEADERS => array(
96
                    IpWhitelist::IPV4 => array(
97
                        '127.0.0.1'
98
                    )
99
                )
100
            ),
101
            $this->getHttpMessageMock("127.0.0.1", array('X-Forwarded-For' => array('192.168.1.1,32.32.32.32')))
0 ignored issues
show
Documentation introduced by
array('X-Forwarded-For' ....168.1.1,32.32.32.32')) is of type array<string,array<integ...,{\"0\":\"string\"}>"}>, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
102
        );
103
104
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
105
    }
106
107
    /**
108
     * Tests that we get false when no valid IP address could be found.
109
     */
110
    public function testNoAddresFoundDueToBitmask()
111
    {
112
        $lookup = new Whip(Whip::PROXY_HEADERS);
113
        $lookup->setSource(array('REMOTE_ADDR' => '127.0.0.1'));
114
        $this->assertFalse($lookup->getIpAddress());
115
    }
116
117
    /**
118
     * Tests the standard REMOTE_ADDR method.
119
     */
120
    public function testRemoteAddrMethod()
121
    {
122
        $lookup = new Whip(Whip::REMOTE_ADDR);
123
        $lookup->setSOurce(array('REMOTE_ADDR' => '24.24.24.24'));
124
        $this->assertEquals('24.24.24.24', $lookup->getValidIpAddress());
125
    }
126
127
    /**
128
     * Tests that an invalid IPv4 address returns false.
129
     */
130
    public function testInvalidIPv4Address()
131
    {
132
        $lookup = new Whip(Whip::REMOTE_ADDR);
133
        $lookup->setSource(array('REMOTE_ADDR' => '127.0.0.256'));
134
        $this->assertFalse($lookup->getValidIpAddress());
135
    }
136
137
    /**
138
     * Tests a valid IPv6 address.
139
     */
140
    public function testValidIPv6Address()
141
    {
142
        $lookup = new Whip(Whip::REMOTE_ADDR);
143
        $lookup->setSource(array('REMOTE_ADDR' => '::1'));
144
        $this->assertEquals('::1', $lookup->getValidIpAddress());
145
    }
146
147
    /**
148
     * Tests that we accept whitelisted proxy methods when the IP matches, even
149
     * if the IP listed is a comma separated list.
150
     */
151
    public function testValidWhitelistedProxyMethod()
152
    {
153
        $lookup = new Whip(
154
            Whip::PROXY_HEADERS,
155
            array(
156
                Whip::PROXY_HEADERS => array(
157
                    IpWhitelist::IPV4 => array(
158
                        '127.0.0.1'
159
                    ),
160
                    IpWhitelist::IPV6 => array(
161
                        '::1'
162
                    )
163
                )
164
            ),
165
            array(
166
                'REMOTE_ADDR' => '127.0.0.1',
167
                'HTTP_X_FORWARDED_FOR' => '192.168.1.1,32.32.32.32'
168
            )
169
        );
170
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
171
172
         /* Repeat test for IPv6 */
173
        $lookup->setSource(array(
174
            'REMOTE_ADDR' => '::1',
175
            'HTTP_X_FORWARDED_FOR' => '192.168.1.1,32.32.32.32'
176
        ));
177
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
178
    }
179
180
    /**
181
     * Tests that we accept proxy method based on a whitelisted IP using the
182
     * dashed range notation.
183
     */
184 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...
185
    {
186
        $lookup = new Whip(
187
            Whip::PROXY_HEADERS,
188
            array(
189
                Whip::PROXY_HEADERS => array(
190
                    IpWhitelist::IPV4 => array(
191
                        '127.0.0.0-127.0.255.255',
192
                    ),
193
                    IpWhitelist::IPV6 => array(
194
                        '::1'
195
                    )
196
                )
197
            ),
198
            array(
199
                'REMOTE_ADDR' => '127.0.0.1',
200
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
201
            )
202
        );
203
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
204
    }
205
206
    /**
207
     * Tests that we accept proxy method based on a whitelisted IP using the
208
     * wildcard asterix notation.
209
     */
210 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...
211
    {
212
        $lookup = new Whip(
213
            Whip::PROXY_HEADERS,
214
            array(
215
                Whip::PROXY_HEADERS => array(
216
                    IpWhitelist::IPV4 => array(
217
                        '127.0.*'
218
                    ),
219
                    IpWhitelist::IPV6 => array(
220
                        '::1'
221
                    )
222
                )
223
            ),
224
            array(
225
                'REMOTE_ADDR' => '127.0.0.1',
226
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
227
            )
228
        );
229
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
230
    }
231
232
    /**
233
     * Tests that we accept proxy method based on a whitelisted IP using the
234
     * CIDR address notation.
235
     */
236 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...
237
    {
238
        $lookup = new Whip(
239
            Whip::PROXY_HEADERS,
240
            array(
241
                Whip::PROXY_HEADERS => array(
242
                    IpWhitelist::IPV4 => array(
243
                        '127.0.0.0/24'
244
                    ),
245
                    IpWhitelist::IPV6 => array(
246
                        '::1'
247
                    )
248
                )
249
            ),
250
            array(
251
                'REMOTE_ADDR' => '127.0.0.1',
252
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
253
            )
254
        );
255
        $this->assertEquals('32.32.32.32', $lookup->getIpAddress());
256
    }
257
258
    /**
259
     * Tests that we get false if there is a valid IP in a proxy header but
260
     * we reject it due to REMOTE_ADDR not being in the whitelist.
261
     */
262 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...
263
    {
264
        $lookup = new Whip(
265
            Whip::PROXY_HEADERS,
266
            array(
267
                Whip::PROXY_HEADERS => array(
268
                    IpWhitelist::IPV4 => array(
269
                        '127.0.0.1/24'
270
                    ),
271
                    IpWhitelist::IPV6 => array(
272
                        '::1'
273
                    )
274
                )
275
            ),
276
            array(
277
                'REMOTE_ADDR' => '24.24.24.24',
278
                'HTTP_X_FORWARDED_FOR' => '32.32.32.32'
279
            )
280
        );
281
        $this->assertFalse($lookup->getIpAddress());
282
    }
283
284
    /**
285
     * Tests that we reject a proxy listed IPv6 address that does not fall within
286
     * the allowed subnet.
287
     */
288
    public function testIPv6AddressRejectedDueToWhitelist()
289
    {
290
        $lookup = new Whip(
291
            Whip::PROXY_HEADERS,
292
            array(
293
                Whip::PROXY_HEADERS => array(
294
                    IpWhitelist::IPV6 => array(
295
                        '2400:cb00::/32'
296
                    )
297
                )
298
            ),
299
            array(
300
                'REMOTE_ADDR' => '::1',
301
                'HTTP_X_FORWARDED_FOR' => '::1'
302
            )
303
        );
304
        $this->assertFalse($lookup->getIpAddress());
305
    }
306
307
    /**
308
     * Tests that we reject a proxy listed IPv6 address that does not fall within
309
     * the allowed subnet.
310
     */
311
    public function testIPv6AddressFoundInWhitelist()
312
    {
313
        $lookup = new Whip(
314
            Whip::PROXY_HEADERS,
315
            array(
316
                Whip::PROXY_HEADERS => array(
317
                    IpWhitelist::IPV6 => array(
318
                        '::1/32'
319
                    )
320
                )
321
            ),
322
            array(
323
                'REMOTE_ADDR' => '::1',
324
                'HTTP_X_FORWARDED_FOR' => '::1'
325
            )
326
        );
327
        $this->assertEquals('::1', $lookup->getIpAddress());
328
    }
329
330
    /**
331
     * Test that an IPv4 address is rejected because the whitelist is empty for
332
     * IPv4.
333
     */
334
    public function testIPv4AddressRejectedDueToEmptyWhitelist()
335
    {
336
        $lookup = new Whip(
337
            Whip::PROXY_HEADERS,
338
            array(
339
                Whip::PROXY_HEADERS => array(
340
                    IpWhitelist::IPV6 => array(
341
                        '::1/32'
342
                    )
343
                )
344
            ),
345
            array(
346
                'REMOTE_ADDR' => '127.0.0.1',
347
                'HTTP_X_FORWARDED_FOR' => '24.24.24.24'
348
            )
349
        );
350
        $this->assertFalse($lookup->getIpAddress());
351
    }
352
353
    /**
354
     * Test that an IPv6 address is rejected because the whitelist is empty for
355
     * IPv6.
356
     */
357
    public function testIPv6AddressRejectedDueToEmptyWhitelist()
358
    {
359
        $lookup = new Whip(
360
            Whip::PROXY_HEADERS,
361
            array(
362
                Whip::PROXY_HEADERS => array(
363
                    IpWhitelist::IPV4 => array(
364
                        '127.0.0.0/24'
365
                    )
366
                )
367
            ),
368
            array(
369
                'REMOTE_ADDR' => '::1',
370
                'HTTP_X_FORWARDED_FOR' => '::1'
371
            )
372
        );
373
        $this->assertFalse($lookup->getIpAddress());
374
    }
375
376
    /**
377
     * Test a custom header with a whitelisted IP.
378
     */
379 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...
380
    {
381
        $lookup = new Whip(
382
            Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR,
383
            array(
384
                Whip::CUSTOM_HEADERS => array(
385
                    IpWhitelist::IPV4 => array(
386
                        '127.0.0.1',
387
                        '::1'
388
                    )
389
                )
390
            ),
391
            array(
392
                'REMOTE_ADDR' => '127.0.0.1',
393
                'HTTP_CUSTOM_SECRET_HEADER' => '32.32.32.32'
394
            )
395
        );
396
        $this->assertEquals(
397
            '32.32.32.32',
398
            $lookup->addCustomHeader('HTTP_CUSTOM_SECRET_HEADER')->getIpAddress()
399
        );
400
    }
401
402
    /**
403
     * Test HTTP_X_REAL_IP header.
404
     */
405
    public function testHttpXRealIpHeader()
406
    {
407
        $lookup = new Whip(
408
            Whip::PROXY_HEADERS | Whip::REMOTE_ADDR,
409
            array(),
410
            array(
411
                'REMOTE_ADDR' => '127.0.0.1',
412
                'HTTP_X_REAL_IP' => '24.24.24.24'
413
            )
414
        );
415
        $this->assertEquals('24.24.24.24', $lookup->getIpAddress());
416
    }
417
418
    /**
419
     * Tests that if we specify the source array, it overrides any values found
420
     * in the $_SERVER array.
421
     */
422 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...
423
    {
424
        $source = array(
425
            'REMOTE_ADDR' => '24.24.24.24'
426
        );
427
        $lookup = new Whip(Whip::REMOTE_ADDR, [], array('REMOTE_ADDR' => '127.0.0.1'));
428
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
429
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress($source));
430
    }
431
432
    /**
433
     * Tests that if we specify the source array through Whip::setSource, the
434
     * class will override any values found in $_SERVER.
435
     */
436 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...
437
    {
438
        $source = array(
439
            'REMOTE_ADDR' => '24.24.24.24'
440
        );
441
        $lookup = new Whip(Whip::REMOTE_ADDR, [], array('REMOTE_ADDR' => '127.0.0.1'));
442
        $this->assertNotEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
443
        $lookup->setSource($source);
444
        $this->assertEquals($source['REMOTE_ADDR'], $lookup->getIpAddress());
445
    }
446
}
447