Passed
Push — master ( ec7431...354ad9 )
by mcfog
03:00
created

IpAddressMiddleware::parseForwarded()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
crap 3.0416
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Lit\Middleware\IpAddress;
6
7
use Lit\Nimo\AbstractMiddleware;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\ServerRequestInterface;
10
11
class IpAddressMiddleware extends AbstractMiddleware
12
{
13
    /**
14
     * @var array
15
     */
16
    protected $trustedProxies;
17
    /**
18
     * @var array
19
     */
20
    protected $headers;
21
    /**
22
     * @var string
23
     */
24
    protected $ipAddress;
25
26 13
    public function __construct(
27
        array $trustedProxies = [],
28
        array $headers = []
29
    ) {
30 13
        $this->trustedProxies = $trustedProxies;
31 13
        $this->headers = $headers;
32 13
    }
33
34
    /**
35
     * @return string
36
     */
37 13
    public function getIpAddress(): ?string
38
    {
39 13
        return $this->ipAddress;
40
    }
41
42 13
    public static function getIpAddressFromRequest(
43
        ServerRequestInterface $request,
44
        array $trustedProxies = [],
45
        array $headers = []
46
    ): ?string {
47 13
        $headers = $headers ?: [
48 13
            'Forwarded',
49
            'X-Forwarded-For',
50
            'X-Forwarded',
51
            'X-Cluster-Client-Ip',
52
            'Client-Ip',
53
        ];
54
55 13
        $params = $request->getServerParams();
56
57 13
        $remoteAddr = $params['REMOTE_ADDR'] ?? '';
58 13
        if (!self::isValidIpAddress($remoteAddr)) {
59 1
            return null;
60
        }
61
62 12
        if (empty($trustedProxies) || !in_array($remoteAddr, $trustedProxies)) {
63 3
            return $remoteAddr;
64
        }
65
66 9
        $ip = self::getIpAddressFromHeaders($request, $headers);
67 9
        if (!empty($ip)) {
68 8
            return $ip;
69
        }
70
71 1
        return $remoteAddr;
72
    }
73
74 13
    protected function main(): ResponseInterface
75
    {
76 13
        $this->attachToRequest();
77 13
        $this->ipAddress = static::getIpAddressFromRequest($this->request, $this->trustedProxies, $this->headers);
78
79 13
        return $this->delegate();
80
    }
81
82 13
    protected static function isValidIpAddress(string $ip): bool
83
    {
84 13
        return false !== filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6);
85
    }
86
87
    /**
88
     * @param ServerRequestInterface $request
89
     * @param string[] $headers
90
     * @return null|string
91
     */
92 9
    protected static function getIpAddressFromHeaders(ServerRequestInterface $request, array $headers): ?string
93
    {
94 9
        foreach ($headers as $headerName) {
95 9
            $headerValue = trim(explode(',', $request->getHeaderLine($headerName))[0]);
96 9
            if (empty($headerValue)) {
97 5
                continue;
98
            }
99
100 9
            if (strtolower($headerName) == 'forwarded') {
101 3
                $headerValue = static::parseForwarded($headerValue);
102
            }
103
104 9
            if (static::isValidIpAddress($headerValue)) {
105 9
                return $headerValue;
106
            }
107
        }
108
109 1
        return null;
110
    }
111
112 3
    protected static function parseForwarded($headerValue): ?string
113
    {
114 3
        foreach (explode(';', $headerValue) as $headerPart) {
115 3
            if (strtolower(substr($headerPart, 0, 4)) == 'for=') {
116 3
                $for = explode(']', $headerPart);
117 3
                return trim(substr($for[0], 4), " \t\n\r\0\x0B\"[]");
118
            }
119
        }
120
121
        return null;
122
    }
123
}
124