XffTrustProvider::isTrusted()   A
last analyzed

Complexity

Conditions 6
Paths 8

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 34
rs 9.0777
c 0
b 0
f 0
cc 6
nc 8
nop 1
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Providers;
10
11
use PDOStatement;
12
use Waca\PdoDatabase;
13
use Waca\Providers\Interfaces\IXffTrustProvider;
14
15
/**
16
 * XffTrustProvider short summary.
17
 *
18
 * XffTrustProvider description.
19
 *
20
 * @version 1.0
21
 * @author  stwalkerster
22
 */
23
class XffTrustProvider implements IXffTrustProvider
24
{
25
    /**
26
     * Array of IP addresses which are TRUSTED proxies
27
     * @var string[]
28
     */
29
    private $trustedCache;
30
    /**
31
     * Array of IP addresses which are UNTRUSTED proxies
32
     * @var string[]
33
     */
34
    private $untrustedCache = array();
35
    /** @var PDOStatement */
36
    private $trustedQuery;
37
    /**
38
     * @var PdoDatabase
39
     */
40
    private $database;
41
42
    /**
43
     * Creates a new instance of the trust provider
44
     *
45
     * @param string[]    $squidIpList List of IP addresses to pre-approve
46
     * @param PdoDatabase $database
47
     */
48
    public function __construct($squidIpList, PdoDatabase $database)
49
    {
50
        $this->trustedCache = $squidIpList;
51
        $this->database = $database;
52
    }
53
54
    /**
55
     * Returns a value if the IP address is a trusted proxy
56
     *
57
     * @param string $ip
58
     *
59
     * @return bool
60
     */
61
    public function isTrusted($ip)
62
    {
63
        if (in_array($ip, $this->trustedCache)) {
64
            return true;
65
        }
66
67
        if (in_array($ip, $this->untrustedCache)) {
68
            return false;
69
        }
70
71
        if ($this->trustedQuery === null) {
72
            $query = "SELECT COUNT(id) FROM xfftrustcache WHERE ip = :ip;";
73
            $this->trustedQuery = $this->database->prepare($query);
74
        }
75
76
        $this->trustedQuery->execute(array(":ip" => $ip));
77
        $result = $this->trustedQuery->fetchColumn();
78
        $this->trustedQuery->closeCursor();
79
80
        if ($result == 0) {
81
            $this->untrustedCache[] = $ip;
82
83
            return false;
84
        }
85
86
        if ($result >= 1) {
87
            $this->trustedCache[] = $ip;
88
89
            return true;
90
        }
91
92
        // something weird has happened if we've got here.
93
        // default to untrusted.
94
        return false;
95
    }
96
97
    /**
98
     * Gets the last trusted IP in the proxy chain.
99
     *
100
     * @param string $ip      The IP address from REMOTE_ADDR
101
     * @param string $proxyIp The contents of the XFF header.
102
     *
103
     * @return string Trusted source IP address
104
     */
105
    public function getTrustedClientIp($ip, $proxyIp)
106
    {
107
        $clientIpAddress = $ip;
108
        if ($proxyIp) {
109
            $ipList = explode(",", $proxyIp);
110
            $ipList[] = $clientIpAddress;
111
            $ipList = array_reverse($ipList);
112
113
            foreach ($ipList as $ipNumber => $ipAddress) {
114
                if ($this->isTrusted(trim($ipAddress)) && $ipNumber < (count($ipList) - 1)) {
115
                    continue;
116
                }
117
118
                $clientIpAddress = $ipAddress;
119
                break;
120
            }
121
        }
122
123
        return trim($clientIpAddress);
124
    }
125
126
    /**
127
     * Takes an array( "low" => "high" ) values, and returns true if $needle is in at least one of them.
128
     *
129
     * @param array  $haystack
130
     * @param string $ip
131
     *
132
     * @return bool
133
     */
134
    public function ipInRange($haystack, $ip)
135
    {
136
        $needle = ip2long($ip);
137
138
        foreach ($haystack as $low => $high) {
139
            if (ip2long($low) <= $needle && ip2long($high) >= $needle) {
140
                return true;
141
            }
142
        }
143
144
        return false;
145
    }
146
147
    /**
148
     * Explodes a CIDR range into an array of addresses
149
     *
150
     * @param string $range A CIDR-format range
151
     *
152
     * @return array An array containing every IP address in the range
153
     */
154
    public function explodeCidr($range)
155
    {
156
        $cidrData = explode('/', $range);
157
158
        if (!isset($cidrData[1])) {
159
            return array($range);
160
        }
161
162
        $blow = (
163
            str_pad(decbin(ip2long($cidrData[0])), 32, "0", STR_PAD_LEFT) &
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
164
            str_pad(str_pad("", $cidrData[1], "1"), 32, "0")
0 ignored issues
show
Bug introduced by
$cidrData[1] of type string is incompatible with the type integer expected by parameter $length of str_pad(). ( Ignorable by Annotation )

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

164
            str_pad(str_pad("", /** @scrutinizer ignore-type */ $cidrData[1], "1"), 32, "0")
Loading history...
165
        );
166
        $bhigh = ($blow | str_pad(str_pad("", $cidrData[1], "0"), 32, "1"));
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise | or did you mean ||?
Loading history...
167
168
        $list = array();
169
170
        $bindecBHigh = bindec($bhigh);
171
        for ($x = bindec($blow); $x <= $bindecBHigh; $x++) {
172
            $list[] = long2ip($x);
0 ignored issues
show
Bug introduced by
It seems like $x can also be of type double; however, parameter $ip of long2ip() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

172
            $list[] = long2ip(/** @scrutinizer ignore-type */ $x);
Loading history...
173
        }
174
175
        return $list;
176
    }
177
}
178