Completed
Push — master ( 6fa8c1...df30fc )
by Martijn van
02:49
created

IpAccess::matchRange()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
c 7
b 0
f 0
dl 0
loc 20
rs 9.4285
cc 3
eloc 11
nc 1
nop 0
1
<?php
2
3
/**
4
 * Check Access based on remote IP address
5
 *
6
 * @Example entries :
7
 * 192.168.178.8
8
 * 192.168.178.0/24
9
 * 192.168.178.0-50
10
 * 192.168.178.*
11
 * 192.168.*
12
 * @return false || string : entry the ip address was matched against
13
 */
14
class IpAccess extends Object
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
15
{
16
    /**
17
     * @var array
18
     */
19
    public $allowedIps = array();
20
21
    /**
22
     * @config
23
     * @var array
24
     */
25
    private static $allowed_ips = array();
26
27
    /**
28
     * @var string
29
     */
30
    private $ip = '';
31
32
    /**
33
     * IpAccess constructor.
34
     *
35
     * @param string $ip
36
     * @param array $allowedIps
37
     */
38
    public function __construct($ip = '', $allowedIps = array())
39
    {
40
        parent::__construct();
41
        $this->ip = $ip;
42
43
        self::config()->allowed_ips = $allowedIps;
44
    }
45
46
    /**
47
     * @param $ip
48
     */
49
    public function setIp($ip)
50
    {
51
        $this->ip = $ip;
52
    }
53
54
    /**
55
     * @return array
56
     */
57
    public function getAllowedIps()
58
    {
59
        if (!empty($this->allowedIps)) {
60
            Deprecation::notice('1.1', 'Use the "IpAccess.allowed_ips" config setting instead');
61
            self::config()->allowed_ips = $this->allowedIps;
62
        }
63
        return self::$allowed_ips ? self::$allowed_ips : (array)self::config()->allowed_ips;
64
    }
65
66
    /**
67
     * @return bool
68
     */
69
    public function isEnabled()
70
    {
71
        return (bool)Config::inst()->get('IpAccess', 'enabled');
72
    }
73
74
    /**
75
     * @return bool
76
     */
77
    public function hasAccess()
78
    {
79
        if (!$this->isEnabled() || !(bool)$this->getAllowedIps()) {
80
            return true;
81
        }
82
83
        return $this->matchIp();
84
    }
85
86
    /**
87
     * @return bool
88
     */
89
    public function matchIp()
90
    {
91
        return ($this->matchExact() || $this->matchRange() || $this->matchCIDR() || $this->matchWildCard());
92
    }
93
94
    /**
95
     * @param Controller $controller
96
     * @throws SS_HTTPResponse_Exception
97
     */
98
    public function respondNoAccess(Controller $controller)
99
    {
100
        $response = null;
101
        if (class_exists('ErrorPage', true)) {
102
            $response = ErrorPage::response_for(403);
103
        }
104
        $controller->httpError(403, $response ? $response : 'The requested page could not be found.');
105
    }
106
107
    /**
108
     * @return string
109
     */
110
    public function matchExact()
111
    {
112
        return in_array($this->ip, $this->getAllowedIps()) ? $this->ip : '';
113
    }
114
115
    /**
116
     * Try to match against a ip range
117
     * Example : 192.168.1.50-100
118
     *
119
     * @return string
120
     */
121
    public function matchRange()
122
    {
123
        $ranges = array_filter($this->getAllowedIps(), function ($ip) {
124
            return strstr($ip, '-');
125
        });
126
127
        $ip = $this->ip;
128
129
        $matches = array_filter($ranges, function ($range) use ($ip) {
130
            $ipFirstPart    = substr($ip, 0, strrpos($ip, '.') + 1);
131
            $ipLastPart     = substr(strrchr($ip, '.'), 1);
132
            $rangeFirstPart = substr($range, 0, strrpos($range, '.') + 1);
133
134
            list ($start, $end) = explode('-', substr(strrchr($range, '.'), 1));
135
136
            return $ipFirstPart === $rangeFirstPart && $ipLastPart >= $start && $ipLastPart <= $end;
137
        });
138
139
        return array_shift($matches);
140
    }
141
142
    /**
143
     * Try to match cidr range
144
     * Example : 192.168.1.0/24
145
     *
146
     * @return string
147
     */
148
    public function matchCIDR()
149
    {
150
        $ranges = array_filter($this->getAllowedIps(), function ($ip) {
151
            return strstr($ip, '/');
152
        });
153
154
        if (!empty($ranges)) foreach ($ranges as $range) {
155
            list ($net, $mask) = explode('/', $range);
156
            if ((ip2long($this->ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($net)) {
157
                return $range;
158
            }
159
        }
160
        return '';
161
    }
162
163
    /**
164
     * Try to match against a range that ends with a wildcard *
165
     * Example : 192.168.1.*
166
     * Example : 192.168.*
167
     *
168
     * @return string
169
     */
170
    public function matchWildCard()
171
    {
172
        $ranges = array_filter($this->getAllowedIps(), function ($ip) {
173
            return substr($ip, -1) === '*';
174
        });
175
176
        if (!empty($ranges)) foreach ($ranges as $range) {
177
            if (substr($this->ip, 0, strlen(substr($range, 0, -1))) === substr($range, 0, -1)) {
178
                return $range;
179
            }
180
        }
181
        return '';
182
    }
183
184
}
185