Failed Conditions
Push — master ( 516359...a8b39a )
by Florent
04:00
created

IpAddressMiddleware::determineClientIpAddress()   D

Complexity

Conditions 10
Paths 30

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 17
nc 30
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Component\Server\Tests\Stub;
15
16
use Interop\Http\ServerMiddleware\DelegateInterface;
17
use Interop\Http\ServerMiddleware\MiddlewareInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
20
/**
21
 * Class IpAddress.
22
 *
23
 * @see https://github.com/akrabat/rka-ip-address-middleware
24
 */
25
class IpAddressMiddleware implements MiddlewareInterface
26
{
27
    /**
28
     * Enable checking of proxy headers (X-Forwarded-For to determined client IP.
29
     *
30
     * Defaults to false as only $_SERVER['REMOTE_ADDR'] is a trustworthy source
31
     * of IP address.
32
     *
33
     * @var bool
34
     */
35
    protected $checkProxyHeaders;
36
37
    /**
38
     * List of trusted proxy IP addresses.
39
     *
40
     * If not empty, then one of these IP addresses must be in $_SERVER['REMOTE_ADDR']
41
     * in order for the proxy headers to be looked at.
42
     *
43
     * @var array
44
     */
45
    protected $trustedProxies;
46
47
    /**
48
     * Name of the attribute added to the ServerRequest object.
49
     *
50
     * @var string
51
     */
52
    protected $attributeName = 'ip_address';
53
54
    /**
55
     * List of proxy headers inspected for the client IP address.
56
     *
57
     * @var array
58
     */
59
    protected $headersToInspect = [
60
        'Forwarded',
61
        'X-Forwarded-For',
62
        'X-Forwarded',
63
        'X-Cluster-Client-Ip',
64
        'Client-Ip',
65
    ];
66
67
    /**
68
     * Constructor.
69
     *
70
     * @param bool        $checkProxyHeaders Whether to use proxy headers to determine client IP
71
     * @param array       $trustedProxies    List of IP addresses of trusted proxies
72
     * @param string|null $attributeName     Name of attribute added to ServerRequest object
73
     * @param array       $headersToInspect  List of headers to inspect
74
     */
75
    public function __construct(bool $checkProxyHeaders = false, array $trustedProxies = [], string $attributeName = null, array $headersToInspect = [])
76
    {
77
        $this->checkProxyHeaders = $checkProxyHeaders;
78
        $this->trustedProxies = $trustedProxies;
79
80
        if (null !== $attributeName) {
81
            $this->attributeName = $attributeName;
82
        }
83
        if (!empty($headersToInspect)) {
84
            $this->headersToInspect = $headersToInspect;
85
        }
86
    }
87
88
    /**
89
     * Set the "$attributeName" attribute to the client's IP address as determined from
90
     * the proxy header (X-Forwarded-For or from $_SERVER['REMOTE_ADDR'].
91
     *
92
     * {@inheritdoc}
93
     */
94
    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
95
    {
96
        $ipAddress = $this->determineClientIpAddress($request);
97
        $request = $request->withAttribute($this->attributeName, $ipAddress);
98
99
        return $delegate->process($request);
100
    }
101
102
    /**
103
     * Find out the client's IP address from the headers available to us.
104
     *
105
     * @param ServerRequestInterface $request PSR-7 Request
106
     *
107
     * @return string
108
     */
109
    protected function determineClientIpAddress(ServerRequestInterface $request): string
110
    {
111
        $ipAddress = null;
112
113
        $serverParams = $request->getServerParams();
114
        if (isset($serverParams['REMOTE_ADDR']) && $this->isValidIpAddress($serverParams['REMOTE_ADDR'])) {
115
            $ipAddress = $serverParams['REMOTE_ADDR'];
116
        }
117
118
        $checkProxyHeaders = $this->checkProxyHeaders;
119
        if ($checkProxyHeaders && !empty($this->trustedProxies)) {
120
            if (!in_array($ipAddress, $this->trustedProxies)) {
121
                $checkProxyHeaders = false;
122
            }
123
        }
124
125
        if ($checkProxyHeaders) {
126
            foreach ($this->headersToInspect as $header) {
127
                if ($request->hasHeader($header)) {
128
                    $ip = $this->getFirstIpAddressFromHeader($request, $header);
129
                    if ($this->isValidIpAddress($ip)) {
130
                        $ipAddress = $ip;
131
                        break;
132
                    }
133
                }
134
            }
135
        }
136
137
        return $ipAddress;
138
    }
139
140
    /**
141
     * Check that a given string is a valid IP address.
142
     *
143
     * @param string $ip
144
     *
145
     * @return bool
146
     */
147
    protected function isValidIpAddress(string $ip): bool
148
    {
149
        $flags = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
150
        if (filter_var($ip, FILTER_VALIDATE_IP, $flags) === false) {
151
            return false;
152
        }
153
154
        return true;
155
    }
156
157
    /**
158
     * Find out the client's IP address from the headers available to us.
159
     *
160
     * @param ServerRequestInterface $request PSR-7 Request
161
     * @param string                 $header  Header name
162
     *
163
     * @return string
164
     */
165
    private function getFirstIpAddressFromHeader(ServerRequestInterface $request, string $header): string
166
    {
167
        $items = explode(',', $request->getHeaderLine($header));
168
        $headerValue = trim(reset($items));
169
170
        if (ucfirst($header) == 'Forwarded') {
171
            foreach (explode(';', $headerValue) as $headerPart) {
172
                if (strtolower(substr($headerPart, 0, 4)) == 'for=') {
173
                    $for = explode(']', $headerPart);
174
                    $headerValue = trim(substr(reset($for), 4), " \t\n\r\0\x0B".'"[]');
175
                    break;
176
                }
177
            }
178
        }
179
180
        return $headerValue;
181
    }
182
}
183