Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
22 / 22 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
1 / 1 |
UserIpReader | |
100.00% |
22 / 22 |
|
100.00% |
2 / 2 |
10 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getUserIp | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
9 |
1 | <?php |
2 | |
3 | /** |
4 | * Service to retrieve user IP address. |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2020. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package Net |
25 | * @author Demian Katz <demian.katz@villanova.edu> |
26 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
27 | * @link https://vufind.org Main Page |
28 | */ |
29 | |
30 | namespace VuFind\Net; |
31 | |
32 | use Laminas\Stdlib\Parameters; |
33 | |
34 | use function count; |
35 | |
36 | /** |
37 | * Service to retrieve user IP address. |
38 | * |
39 | * @category VuFind |
40 | * @package Net |
41 | * @author Demian Katz <demian.katz@villanova.edu> |
42 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
43 | * @link https://vufind.org Main Page |
44 | */ |
45 | class UserIpReader |
46 | { |
47 | /** |
48 | * Server parameters |
49 | * |
50 | * @var Parameters |
51 | */ |
52 | protected $server; |
53 | |
54 | /** |
55 | * Configuration specifying allowed HTTP headers containing IPs (false for none). |
56 | * See [Proxy] allow_forwarded_ips setting in config.ini for more details. |
57 | * |
58 | * @var string|bool |
59 | */ |
60 | protected $allowForwardedIps; |
61 | |
62 | /** |
63 | * IP addresses to exclude from consideration |
64 | * |
65 | * @var array |
66 | */ |
67 | protected $ipFilter; |
68 | |
69 | /** |
70 | * Constructor |
71 | * |
72 | * @param Parameters $server Server parameters |
73 | * @param string|bool $allowForwardedIps Forwarded header configuration string |
74 | * (false to disable checking IP-related X- headers) |
75 | * @param array $ipFilter IP addresses to exclude from |
76 | * consideration |
77 | */ |
78 | public function __construct( |
79 | Parameters $server, |
80 | $allowForwardedIps = false, |
81 | array $ipFilter = [] |
82 | ) { |
83 | $this->server = $server; |
84 | $this->allowForwardedIps = $allowForwardedIps; |
85 | $this->ipFilter = array_map('trim', $ipFilter); |
86 | } |
87 | |
88 | /** |
89 | * Get the active user's IP address. Returns null if no address can be found. |
90 | * |
91 | * @return string |
92 | */ |
93 | public function getUserIp() |
94 | { |
95 | if ($this->allowForwardedIps) { |
96 | foreach (explode(',', $this->allowForwardedIps) as $chunk) { |
97 | // Extract field and behavior from chunk: |
98 | [$field, $behavior] = explode(':', $chunk . ':', 2); |
99 | |
100 | // Look up field value; skip if empty: |
101 | $fieldValue = $this->server->get($field); |
102 | if (empty($fieldValue)) { |
103 | continue; |
104 | } |
105 | |
106 | // Split up the field value, if it is delimited, then filter it: |
107 | $parts = array_diff( |
108 | array_map('trim', explode(',', $fieldValue)), |
109 | $this->ipFilter |
110 | ); |
111 | |
112 | // Apply the appropriate behavior (note that we trim any trailing |
113 | // colon off the behavior, since we may have added one above to |
114 | // prevent warnings in the explode operation): |
115 | // |
116 | // Also note that we need to use array_shift/array_pop/current here |
117 | // in place of specific indexes, because the filtering above may have |
118 | // left non-consecutive keys in place. |
119 | $finalBehavior = strtolower(rtrim($behavior, ':')); |
120 | $partCount = count($parts); |
121 | if ($finalBehavior === 'first' && $partCount > 0) { |
122 | return array_shift($parts); |
123 | } elseif ($finalBehavior === 'last' && $partCount > 0) { |
124 | return array_pop($parts); |
125 | } elseif ($partCount === 1) { |
126 | return current($parts); |
127 | } |
128 | } |
129 | } |
130 | // Default case: use REMOTE_ADDR directly. |
131 | return $this->server->get('REMOTE_ADDR'); |
132 | } |
133 | } |