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 ( de0c2e...06730c )
by Sam
01:56
created

UrlSigner::signParams()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 10
cts 10
cp 1
rs 9.568
c 0
b 0
f 0
cc 4
nc 4
nop 3
crap 4
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
     * @var string
37
     */
38
    public $secret;
39
40 16
    public function init(): void
41
    {
42 16
        parent::init();
43 16
        $this->setDefaultExpirationInterval('P7D');
44 16
        if (empty($this->secret)
45 16
            || empty($this->hmacParam)
46 16
            || empty($this->paramsParam)
47 16
            || empty($this->expirationParam)
48
        ) {
49 2
            throw new InvalidConfigException('The following configuration params are required: secret, hmacParam, paramsParam and expirationParam');
50
        }
51
52
53
    }
54
55 16
    public function setDefaultExpirationInterval(string $interval): void
56
    {
57 16
        $this->_defaultExpirationInterval = new \DateInterval($interval);
58
59
    }
60
    /**
61
     * Calculates the HMAC for a URL.
62
     **/
63 10
    public function calculateHMAC(
64
        array $params,
65
        string $route
66
    ): string {
67 10
        if (isset($params[0])) {
68 10
            unset($params[0]);
69
        }
70
71 10
        \ksort($params);
72
73 10
        $hash = \hash_hmac('sha256',
74 10
            \trim($route, '/') . '|' . \implode('#', $params),
75 10
            $this->secret,
76 10
            true
77
        );
78
79 10
        return $this->urlEncode($hash);
80
    }
81
82
    /**
83
     * This adds an HMAC to a list of query params.
84
     * If
85
     * @param array $queryParams List of query parameters
86
     * @param bool $allowAddition Whether to allow extra parameters to be added.
87
     * @throws \Exception
88
     * @return void
89
     */
90 12
    public function signParams(
91
        array &$queryParams,
92
        $allowAddition = true,
93
        ?\DateTimeInterface $expiration = null
94
    ): void {
95 12
        if (isset($queryParams[$this->hmacParam])) {
96 2
            throw new \RuntimeException("HMAC param is already present");
97
        }
98
99 12
        $route = $queryParams[0];
100
101 12
        if (\strncmp($route, '/', 1) !== 0) {
102 2
            throw new \RuntimeException("Route must be absolute (start with /)");
103
        }
104
105 10
        $this->addExpiration($queryParams, $expiration);
106 10
        if ($allowAddition) {
107 4
            $this->addParamKeys($queryParams);
108
        }
109
110 10
        $queryParams[$this->hmacParam] = $this->calculateHMAC($queryParams, $route);
111
    }
112
113
    /**
114
     * Adds the expiration param if needed.
115
     */
116 10
    private function addExpiration(array &$params, ?\DateTimeInterface $expiration = null): void
117
    {
118 10
        if (!empty($this->expirationParam)) {
119 10
            if (!isset($expiration)) {
120 10
                $expiration = (new \DateTime())->add($this->_defaultExpirationInterval);
121
            }
122 10
            $params[$this->expirationParam] = $expiration->getTimestamp();
123
        }
124
    }
125
126 6
    private function checkExpiration(array $params): bool
127
    {
128
        // Check expiration date.
129 6
        return $params[$this->expirationParam] > \time();
130
    }
131
132
    /**
133
     * Adds the keys of all params to the param array so it is included for signing.
134
     * @param array $params
135
     */
136 4
    private function addParamKeys(array &$params): void
137
    {
138 4
        $keys = \array_keys($params);
139 4
        if ($keys[0] === 0) {
140 4
            unset($keys[0]);
141
        }
142 4
        $params[$this->paramsParam] = implode(',', $keys);
143
    }
144
145
    /**
146
     * Extracts the signed params from an array of params.
147
     * @param array $params
148
     * @return array
149
     */
150 8
    private function getSignedParams(array $params): array
151
    {
152 8
        if (empty($params[$this->paramsParam])) {
153
            // HMAC itself is never signed.
154 6
            unset($params[$this->hmacParam]);
155 6
            return $params;
156
        }
157
158 2
        $signedParams = [];
159 2
        $signedParams[$this->paramsParam] = $params[$this->paramsParam];
160
161 2
        foreach(\explode(',', $params[$this->paramsParam]) as $signedParam) {
162 2
            $signedParams[$signedParam] = $params[$signedParam] ?? null;
163
        }
164
165 2
        return $signedParams;
166
    }
167
168
    /**
169
     * Verifies the params for a specific route.
170
     * Checks that the HMAC is present and valid.
171
     * Checks that the HMAC is not expired.
172
     * @param array $params
173
     * @throws \Exception
174
     * @return bool
175
     */
176 8
    public function verify(array $params, string $route):bool
177
    {
178 8
        if (!isset($params[$this->hmacParam])) {
179 2
           return false;
180
        }
181 8
        $hmac = $params[$this->hmacParam];
182
183 8
        $signedParams = $this->getSignedParams($params);
184
185 8
        $calculated = $this->calculateHMAC($signedParams, $route);
186 8
        if (!\hash_equals($calculated, $hmac)) {
187 2
            return false;
188
        }
189
190 6
        return $this->checkExpiration($params);
191
    }
192
193 10
    private function urlEncode(string $bytes): string
194
    {
195 10
        return StringHelper::base64UrlEncode($bytes);
196
    }
197
}
198