Completed
Push — master ( 0728e2...7bf439 )
by Tomas
10:58 queued 10s
created

TokenAuthorization::ipInRange()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Tomaj\NetteApi\Authorization;
6
7
use Tomaj\NetteApi\Misc\BearerTokenRepositoryInterface;
8
use Tomaj\NetteApi\Misc\IpDetectorInterface;
9
use Tomaj\NetteApi\Misc\TokenRepositoryInterface;
10
11
abstract class TokenAuthorization implements ApiAuthorizationInterface
12
{
13
    /**
14
     * @var TokenRepositoryInterface
15
     */
16
    protected $tokenRepository;
17
18
    /**
19
     * @var string|null
20
     */
21
    protected $errorMessage = null;
22
23
    /**
24
     * @var IpDetectorInterface
25
     */
26
    protected $ipDetector;
27
28
    /**
29
     * @param TokenRepositoryInterface $tokenRepository
30
     * @param IpDetectorInterface            $ipDetector
31
     */
32
    public function __construct(TokenRepositoryInterface $tokenRepository, IpDetectorInterface $ipDetector)
33
    {
34
        $this->tokenRepository = $tokenRepository;
35
        $this->ipDetector = $ipDetector;
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function authorized(): bool
42
    {
43
        $token = $this->readAuthorizationToken();
44
        if ($token === null) {
45
            return false;
46
        }
47
48
        $result = $this->tokenRepository->validToken($token);
49
        if (!$result) {
50
            $this->errorMessage = 'Token doesn\'t exists or isn\'t active';
51
            return false;
52
        }
53
54
        if (!$this->isValidIp($this->tokenRepository->ipRestrictions($token))) {
55
            $this->errorMessage = 'Invalid IP';
56
            return false;
57
        }
58
59
        return true;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function getErrorMessage(): ?string
66
    {
67
        return $this->errorMessage;
68
    }
69
70
    /**
71
     * Check if actual IP from detector satisfies @ipRestristions
72
     * $ipRestrictions should contains multiple formats:
73
     *   '*'                  - accessible from anywhare
74
     *   '127.0.0.1'          - accessible from single IP
75
     *   '127.0.0.1,127.0.02' - accessible from multiple IP, separator could be new line or space
76
     *   '127.0.0.1/32'       - accessible from ip range
77
     *   null                 - disabled access
78
     *
79
     * @return boolean
80
     */
81
    private function isValidIp(?string $ipRestrictions): bool
82
    {
83
        if ($ipRestrictions === null) {
84
            return false;
85
        }
86
        if ($ipRestrictions === '*' || $ipRestrictions === '') {
87
            return true;
88
        }
89
        $ip = $this->ipDetector->getRequestIp();
90
91
        $ipWhiteList = str_replace([',', ' ', "\n"], '#', $ipRestrictions);
92
        $ipWhiteList = explode('#', $ipWhiteList);
93
        foreach ($ipWhiteList as $whiteIp) {
94
            if ($whiteIp === $ip) {
95
                return true;
96
            }
97
            if (strpos($whiteIp, '/') !== false) {
98
                return $this->ipInRange($ip, $whiteIp);
99
            }
100
        }
101
102
        return false;
103
    }
104
105
    /**
106
     * Check if IP is in $range
107
     *
108
     * @param string $ip     this ip will be verified
109
     * @param string $range  is in IP/CIDR format eg 127.0.0.1/24
110
     * @return boolean
111
     */
112
    private function ipInRange(string $ip, string $range): bool
113
    {
114
        list($range, $netmask) = explode('/', $range, 2);
115
        $range_decimal = ip2long($range);
116
        $ipDecimal = ip2long($ip);
117
        $wildcard_decimal = pow(2, (32 - (int)$netmask)) - 1;
118
        $netmask_decimal = ~ $wildcard_decimal;
119
        return (($ipDecimal & $netmask_decimal) === ($range_decimal & $netmask_decimal));
120
    }
121
122
    abstract protected function readAuthorizationToken(): ?string;
123
}
124