GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 881994...60bec7 )
by Sam
02:07
created

UrlSigner::time()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types=1);
3
4
5
namespace SamIT\Yii2\UrlSigner;
6
7
8
use yii\base\Component;
9
use yii\base\InvalidConfigException;
10
use yii\helpers\StringHelper;
11
12
class UrlSigner extends Component
13
{
14
    /**
15
     * @var string The name of the URL param for the HMAC
16
     */
17
    public $hmacParam = 'hmac';
18
19
    /**
20
     * @var string The name of the URL param for the parameters
21
     */
22
    public $paramsParam = 'params';
23
24
    /**
25
     * @var string The name of the URL param for the expiration date time
26
     */
27
    public $expirationParam = 'expires';
28
29
    /**
30
     * Note that expiration dates cannot be disabled. If you really need to you can set a longer duration for the links.
31
     * @var \DateInterval The default interval for link validity (default: 1 week)
32
     */
33
    private $_defaultExpirationInterval;
34
35
    /**
36
     * Stores the current timestamp, primarily used for testing.
37
     * @var int
38
     */
39
    private $_currentTimestamp;
40
41
    /**
42
     * @var string
43
     */
44
    public $secret;
45
46 24
    public function init(): void
47
    {
48 24
        parent::init();
49 24
        if (!isset($this->_defaultExpirationInterval)) {
50 20
            $this->setDefaultExpirationInterval('P7D');
51
        }
52 24
        if (empty($this->secret)
53 24
            || empty($this->hmacParam)
54 24
            || empty($this->paramsParam)
55 24
            || empty($this->expirationParam)
56
        ) {
57 2
            throw new InvalidConfigException('The following configuration params are required: secret, hmacParam, paramsParam and expirationParam');
58
        }
59
60
61
    }
62
63 24
    public function setDefaultExpirationInterval(string $interval): void
64
    {
65 24
        $this->_defaultExpirationInterval = new \DateInterval($interval);
66
    }
67
68 2
    public function setCurrentTimestamp(?int $time): void
69
    {
70 2
        $this->_currentTimestamp = $time;
71
    }
72
73
    /**
74
     * Calculates the HMAC for a URL.
75
     **/
76 18
    public function calculateHMAC(
77
        array $params,
78
        string $route
79
    ): string {
80 18
        if (isset($params[0])) {
81 18
            unset($params[0]);
82
        }
83
84 18
        \ksort($params);
85
86 18
        $hash = \hash_hmac('sha256',
87 18
            \trim($route, '/') . '|' . \implode('#', $params),
88 18
            $this->secret,
89 18
            true
90
        );
91
92 18
        return $this->urlEncode($hash);
93
    }
94
95
    /**
96
     * This adds an HMAC to a list of query params.
97
     * If
98
     * @param array $queryParams List of query parameters
99
     * @param bool $allowAddition Whether to allow extra parameters to be added.
100
     * @throws \Exception
101
     * @return void
102
     */
103 20
    public function signParams(
104
        array &$queryParams,
105
        $allowAddition = true,
106
        ?\DateTimeInterface $expiration = null
107
    ): void {
108 20
        if (isset($queryParams[$this->hmacParam])) {
109 2
            throw new \RuntimeException("HMAC param is already present");
110
        }
111
112 20
        $route = $queryParams[0];
113
114 20
        if (\strncmp($route, '/', 1) !== 0) {
115 2
            throw new \RuntimeException("Route must be absolute (start with /)");
116
        }
117
118 18
        $this->addExpiration($queryParams, $expiration);
119 18
        if ($allowAddition) {
120 12
            $this->addParamKeys($queryParams);
121
        }
122
123 18
        $queryParams[$this->hmacParam] = $this->calculateHMAC($queryParams, $route);
124
    }
125
126
    /**
127
     * Adds the expiration param if needed.
128
     */
129 18
    private function addExpiration(array &$params, ?\DateTimeInterface $expiration = null): void
130
    {
131 18
        if (!empty($this->expirationParam)) {
132 16
            if (!isset($expiration)) {
133 16
                $expiration = (new \DateTime('@' . $this->time()))->add($this->_defaultExpirationInterval);
134
            }
135 16
            $params[$this->expirationParam] = $expiration->getTimestamp();
136
        }
137
    }
138
139 16
    private function time(): int
140
    {
141 16
        return $this->_currentTimestamp ?? \time();
142
    }
143 10
    private function checkExpiration(array $params): bool
144
    {
145
        // Check expiration date.
146 10
        return !isset($params[$this->expirationParam]) || $params[$this->expirationParam] > $this->time();
147
    }
148
149
    /**
150
     * Adds the keys of all params to the param array so it is included for signing.
151
     * @param array $params
152
     */
153 12
    private function addParamKeys(array &$params): void
154
    {
155 12
        $keys = \array_keys($params);
156 12
        if ($keys[0] === 0) {
157 12
            unset($keys[0]);
158
        }
159 12
        $params[$this->paramsParam] = \implode(',', $keys);
160
    }
161
162
    /**
163
     * Extracts the signed params from an array of params.
164
     * @param array $params
165
     * @return array
166
     */
167 14
    private function getSignedParams(array $params): array
168
    {
169 14
        if (empty($params[$this->paramsParam])) {
170
            // HMAC itself is never signed.
171 6
            unset($params[$this->hmacParam]);
172 6
            return $params;
173
        }
174
175 8
        $signedParams = [];
176 8
        $signedParams[$this->paramsParam] = $params[$this->paramsParam];
177
178 8
        foreach(\explode(',', $params[$this->paramsParam]) as $signedParam) {
179 8
            $signedParams[$signedParam] = $params[$signedParam] ?? null;
180
        }
181
182 8
        return $signedParams;
183
    }
184
185
    /**
186
     * Verifies the params for a specific route.
187
     * Checks that the HMAC is present and valid.
188
     * Checks that the HMAC is not expired.
189
     * @param array $params
190
     * @throws \Exception
191
     * @return bool
192
     */
193 14
    public function verify(array $params, string $route):bool
194
    {
195 14
        if (!isset($params[$this->hmacParam])) {
196 2
           return false;
197
        }
198 14
        $hmac = $params[$this->hmacParam];
199
200 14
        $signedParams = $this->getSignedParams($params);
201
202 14
        $calculated = $this->calculateHMAC($signedParams, $route);
203 14
        if (!\hash_equals($calculated, $hmac)) {
204 4
            return false;
205
        }
206
207 10
        return $this->checkExpiration($params);
208
    }
209
210 18
    private function urlEncode(string $bytes): string
211
    {
212 18
        return \trim(StringHelper::base64UrlEncode($bytes), '=');
213
    }
214
}
215