IpMatchingService   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 96.72%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 1
dl 0
loc 140
rs 10
c 0
b 0
f 0
ccs 59
cts 61
cp 0.9672

5 Methods

Rating   Name   Duplication   Size   Complexity  
A isValidIp() 0 5 1
A isValidWildcardIp() 0 13 3
A isValidDashRange() 0 11 3
A isValidCidrRange() 0 8 1
C isIpAllowed() 0 53 11
1
<?php
2
namespace AOE\AoeIpauth\Service;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2019 AOE GmbH <[email protected]>
8
 *
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
30
/**
31
 * Class IpMatchingService
32
 *
33
 * @package AOE\AoeIpauth\Service
34
 */
35
class IpMatchingService implements \TYPO3\CMS\Core\SingletonInterface
36
{
37
38
    const NORMAL_IP_TYPE = 0;
39
    const WILDCARD_IP_TYPE = 2;
40
    const CIDR_IP_TYPE = 1;
41
    const DASHRANGE_IP_TYPE = 3;
42
43
    /**
44
     * Checks if an IP is valid
45
     * Allows IPv4 and IPv6
46
     *
47
     * @param string $possibleIp
48
     * @return bool
49
     */
50 28
    public function isValidIp($possibleIp)
51
    {
52 28
        $isValid = GeneralUtility::validIP($possibleIp);
53 28
        return $isValid;
54
    }
55
56
    /**
57
     * Checks if an IP is valid (containing wildcards)
58
     * Only works for IPv4
59
     *
60
     * @param string $possibleIp
61
     * @return bool
62
     */
63 9
    public function isValidWildcardIp($possibleIp)
64
    {
65 9
        $numberOfWildcards = substr_count($possibleIp, '*');
66
        // Minimum 1, Maximum 4 wildcards
67 9
        if (0 >= $numberOfWildcards || 4 < $numberOfWildcards) {
68 4
            return false;
69
        }
70
        // Replace wildcards and simply validate the IP
71 5
        $normalizedPossibleIp = str_replace('*', '50', $possibleIp);
72
73 5
        $isValid = GeneralUtility::validIPv4($normalizedPossibleIp);
74 5
        return $isValid;
75
    }
76
77
    /**
78
     * Checks if an IP range is valid (containing dash notation)
79
     * E.g. 192.168.1.0-192.168.1.200
80
     *
81
     * @param string $possibleRange
82
     * @return bool
83
     */
84 6
    public function isValidDashRange($possibleRange)
85
    {
86 6
        $ips = explode('-', $possibleRange);
87 6
        if (2 !== count($ips)) {
88 3
            return false;
89
        }
90 3
        $lower = $ips[0];
91 3
        $upper = $ips[1];
92 3
        $isValid = ($this->isValidIp($lower) && $this->isValidIp($upper));
93 3
        return $isValid;
94
    }
95
96
    /**
97
     * Checks if a string is a
98
     *
99
     * @param string $possibleRange
100
     * @return bool
101
     */
102 21
    public function isValidCidrRange($possibleRange)
103
    {
104 21
        $doesMatch = preg_match(
105 21
            '#^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)/(3[0-2]|[1-2]?[0-9])$#i',
106
            $possibleRange
107 21
        );
108 21
        return (1 === $doesMatch);
109
    }
110
111
    /**
112
     * Checks if an IP is allowed when matched against a whitelist
113
     *
114
     * @see http://pgregg.com/blog/2009/04/php-algorithms-determining-if-an-ip-is-within-a-specific-range/
115
     * @see http://pgregg.com/projects/php/ip_in_range/ip_in_range.phps
116
     *
117
     * @param string $givenIp The IP to check
118
     * @param string $givenWhitelist The ip/range the given ip needs to match;
119
     * @return bool
120
     */
121 15
    public function isIpAllowed($givenIp, $givenWhitelist)
122
    {
123 15
        $isAllowed = false;
124
125 15
        if ($this->isValidIp($givenWhitelist)) {
126
            // Simple IP
127 2
            $isAllowed = ($givenIp == $givenWhitelist);
128 15
        } elseif ($this->isValidCidrRange($givenWhitelist)) {
129
            // CIDR
130 5
            list($range, $netmask) = explode('/', $givenWhitelist, 2);
131 5
            $x = explode('.', $range);
132 5
            while (count($x) < 4) {
133
                $x[] = '0';
134
            }
135 5
            list($a, $b, $c, $d) = $x;
136 5
            $range = sprintf(
137 5
                '%u.%u.%u.%u',
138 5
                empty($a) ? '0' : $a,
139 5
                empty($b) ?' 0' : $b,
140 5
                empty($c) ? '0' : $c,
141 5
                empty($d) ? '0' : $d
142 5
            );
143 5
            $rangeDec = ip2long($range);
144 5
            $ipDec = ip2long($givenIp);
145
146
            // Create netmask
147 5
            $wildcardDec = pow(2, (32 - $netmask)) - 1;
148 5
            $netmaskDec = ~ $wildcardDec;
149
150 5
            $isAllowed = (($ipDec & $netmaskDec) == ($rangeDec & $netmaskDec));
151 5
        } else {
152
            // Wildcard or dash range. Both get matched very similarly.
153 8
            $dashRange = $givenWhitelist;
154
155
            // Transform wildcard to dash range
156 8
            if (false !== strpos($givenWhitelist, '*')) {
157
                // a.b.*.* format, converts to A-B format by setting * to 0 for A and 255 for B
158 2
                $lower = str_replace('*', '0', $givenWhitelist);
159 2
                $upper = str_replace('*', '255', $givenWhitelist);
160 2
                $dashRange = $lower . '-' . $upper;
161 2
            }
162
163
            // Validate dash range
164 8
            if (false !== strpos($dashRange, '-')) {
165 8
                list($lower, $upper) = explode('-', $dashRange, 2);
166 8
                $lowerDec = (float)sprintf('%u', ip2long($lower));
167 8
                $upperDec = (float)sprintf('%u', ip2long($upper));
168 8
                $givenIpDec = (float)sprintf('%u', ip2long($givenIp));
169 8
                $isAllowed =  (($givenIpDec >= $lowerDec) && ($givenIpDec <= $upperDec));
170 8
            }
171
        }
172 15
        return $isAllowed;
173
    }
174
}
175