IP::__construct()   C
last analyzed

Complexity

Conditions 9
Paths 24

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 6.412
c 0
b 0
f 0
cc 9
eloc 11
nc 24
nop 2
1
<?php
2
3
/*
4
 * janitor (http://juliangut.com/janitor).
5
 * Effortless maintenance management.
6
 *
7
 * @license BSD-3-Clause
8
 * @link https://github.com/juliangut/janitor
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
namespace Janitor\Excluder;
13
14
use Psr\Http\Message\ServerRequestInterface;
15
16
/**
17
 * IP based maintenance excluder.
18
 */
19
class IP implements ExcluderInterface
20
{
21
    /**
22
     * List of IPs to be excluded.
23
     *
24
     * @var array
25
     */
26
    protected $ips = [];
27
28
    /**
29
     * Allowed proxies.
30
     *
31
     * @var array
32
     */
33
    protected $trustedProxies = [];
34
35
    /**
36
     * IP constructor.
37
     *
38
     * @param string|array $ips
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ips not be string|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
39
     * @param string|array $trustedProxies
0 ignored issues
show
Documentation introduced by
Should the type for parameter $trustedProxies not be string|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
40
     *
41
     * @throws \InvalidArgumentException
42
     */
43
    public function __construct($ips = null, $trustedProxies = null)
44
    {
45
        if ($ips !== null && !is_array($ips)) {
46
            $ips = [$ips];
47
        }
48
49
        if (is_array($ips)) {
50
            foreach ($ips as $ipAddress) {
51
                $this->addIP($ipAddress);
52
            }
53
        }
54
55
        if ($trustedProxies !== null && !is_array($trustedProxies)) {
56
            $trustedProxies = [$trustedProxies];
57
        }
58
59
        if (is_array($trustedProxies)) {
60
            foreach ($trustedProxies as $ipAddress) {
61
                $this->addTrustedProxy($ipAddress);
62
            }
63
        }
64
    }
65
66
    /**
67
     * Add IP.
68
     *
69
     * @param string $ipAddress
70
     *
71
     * @throws \InvalidArgumentException
72
     *
73
     * @return $this
74
     */
75
    public function addIP($ipAddress)
76
    {
77
        if (trim($ipAddress) !== '') {
78
            if (!$this->isValidIP($ipAddress)) {
79
                throw new \InvalidArgumentException(sprintf('"%s" is not a valid IP address', $ipAddress));
80
            }
81
82
            $this->ips[] = $ipAddress;
83
        }
84
85
        return $this;
86
    }
87
88
    /**
89
     * Add Trusted proxy.
90
     *
91
     * @param string $ipAddress
92
     *
93
     * @throws \InvalidArgumentException
94
     *
95
     * @return $this
96
     */
97
    public function addTrustedProxy($ipAddress)
98
    {
99
        if (trim($ipAddress) !== '') {
100
            if (!$this->isValidIP($ipAddress)) {
101
                throw new \InvalidArgumentException(sprintf('"%s" is not a valid IP address', $ipAddress));
102
            }
103
104
            $this->trustedProxies[] = $ipAddress;
105
        }
106
107
        return $this;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     *
113
     * @throws \RuntimeException
114
     */
115
    public function isExcluded(ServerRequestInterface $request)
116
    {
117
        if (!count($this->ips)) {
118
            throw new \RuntimeException('No IPs defined in IP excluder');
119
        }
120
121
        $currentIP = $this->determineCurrentIP($request);
122
123
        foreach ($this->ips as $ipAddress) {
124
            if ($ipAddress === $currentIP) {
125
                return true;
126
            }
127
        }
128
129
        return false;
130
    }
131
132
    /**
133
     * Find client's IP.
134
     *
135
     * @param ServerRequestInterface $request
136
     *
137
     * @return string
138
     */
139
    protected function determineCurrentIP(ServerRequestInterface $request)
140
    {
141
        $inspectionHeaders = [
142
            'X-Forwarded-For',
143
            'X-Forwarded',
144
            'X-Cluster-Client-Ip',
145
            'Client-Ip',
146
        ];
147
148
        $currentIp = $this->getIPFromServerParams($request);
149
150
        if (!count($this->trustedProxies) || in_array($currentIp, $this->trustedProxies, true)) {
151
            $trustedIp = null;
152
153
            $headers = $inspectionHeaders;
154
            while ($trustedIp === null && $header = array_shift($headers)) {
155
                if ($request->hasHeader($header)) {
156
                    $ipAddress = trim(current(explode(',', $request->getHeaderLine($header))));
157
158
                    if ($this->isValidIP($ipAddress)) {
159
                        $trustedIp = $ipAddress;
160
                    }
161
                }
162
            }
163
164
            if ($trustedIp !== null) {
165
                $currentIp = $trustedIp;
166
            }
167
        }
168
169
        return $currentIp;
170
    }
171
172
    /**
173
     * Return current IP retrieved from server parameters.
174
     *
175
     * @param ServerRequestInterface $request
176
     *
177
     * @return string
178
     */
179
    private function getIPFromServerParams(ServerRequestInterface $request)
180
    {
181
        $currentIp = null;
182
183
        $serverParams = $request->getServerParams();
184
        if (isset($serverParams['REMOTE_ADDR']) && $this->isValidIP($serverParams['REMOTE_ADDR'])) {
185
            $currentIp = $serverParams['REMOTE_ADDR'];
186
        }
187
188
        return $currentIp;
189
    }
190
191
    /**
192
     * Check IP validity.
193
     *
194
     * @param string $ipAddress
195
     *
196
     * @return bool
197
     */
198
    private function isValidIP($ipAddress)
199
    {
200
        return filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6) !== false;
201
    }
202
}
203