PluginProbe ʕ •ᴥ•ʔ
ShareThis Dashboard for Google Analytics / 3.1.3
ShareThis Dashboard for Google Analytics v3.1.3
3.3.2 trunk 1.0.7 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.5 2.3.5 2.3.6 2.3.7 2.3.8 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.0.0 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3.0 3.3.1
googleanalytics / lib / analytics-admin / vendor / firebase / php-jwt / src / CachedKeySet.php
googleanalytics / lib / analytics-admin / vendor / firebase / php-jwt / src Last commit date
BeforeValidException.php 3 years ago CachedKeySet.php 3 years ago ExpiredException.php 3 years ago JWK.php 3 years ago JWT.php 3 years ago Key.php 3 years ago SignatureInvalidException.php 3 years ago
CachedKeySet.php
230 lines
1 <?php
2
3 namespace Firebase\JWT;
4
5 use ArrayAccess;
6 use LogicException;
7 use OutOfBoundsException;
8 use Psr\Cache\CacheItemInterface;
9 use Psr\Cache\CacheItemPoolInterface;
10 use Psr\Http\Client\ClientInterface;
11 use Psr\Http\Message\RequestFactoryInterface;
12 use RuntimeException;
13
14 /**
15 * @implements ArrayAccess<string, Key>
16 */
17 class CachedKeySet implements ArrayAccess
18 {
19 /**
20 * @var string
21 */
22 private $jwksUri;
23 /**
24 * @var ClientInterface
25 */
26 private $httpClient;
27 /**
28 * @var RequestFactoryInterface
29 */
30 private $httpFactory;
31 /**
32 * @var CacheItemPoolInterface
33 */
34 private $cache;
35 /**
36 * @var ?int
37 */
38 private $expiresAfter;
39 /**
40 * @var ?CacheItemInterface
41 */
42 private $cacheItem;
43 /**
44 * @var array<string, Key>
45 */
46 private $keySet;
47 /**
48 * @var string
49 */
50 private $cacheKey;
51 /**
52 * @var string
53 */
54 private $cacheKeyPrefix = 'jwks';
55 /**
56 * @var int
57 */
58 private $maxKeyLength = 64;
59 /**
60 * @var bool
61 */
62 private $rateLimit;
63 /**
64 * @var string
65 */
66 private $rateLimitCacheKey;
67 /**
68 * @var int
69 */
70 private $maxCallsPerMinute = 10;
71 /**
72 * @var string|null
73 */
74 private $defaultAlg;
75
76 public function __construct(
77 string $jwksUri,
78 ClientInterface $httpClient,
79 RequestFactoryInterface $httpFactory,
80 CacheItemPoolInterface $cache,
81 int $expiresAfter = null,
82 bool $rateLimit = false,
83 string $defaultAlg = null
84 ) {
85 $this->jwksUri = $jwksUri;
86 $this->httpClient = $httpClient;
87 $this->httpFactory = $httpFactory;
88 $this->cache = $cache;
89 $this->expiresAfter = $expiresAfter;
90 $this->rateLimit = $rateLimit;
91 $this->defaultAlg = $defaultAlg;
92 $this->setCacheKeys();
93 }
94
95 /**
96 * @param string $keyId
97 * @return Key
98 */
99 public function offsetGet($keyId): Key
100 {
101 if (!$this->keyIdExists($keyId)) {
102 throw new OutOfBoundsException('Key ID not found');
103 }
104 return $this->keySet[$keyId];
105 }
106
107 /**
108 * @param string $keyId
109 * @return bool
110 */
111 public function offsetExists($keyId): bool
112 {
113 return $this->keyIdExists($keyId);
114 }
115
116 /**
117 * @param string $offset
118 * @param Key $value
119 */
120 public function offsetSet($offset, $value): void
121 {
122 throw new LogicException('Method not implemented');
123 }
124
125 /**
126 * @param string $offset
127 */
128 public function offsetUnset($offset): void
129 {
130 throw new LogicException('Method not implemented');
131 }
132
133 private function keyIdExists(string $keyId): bool
134 {
135 if (null === $this->keySet) {
136 $item = $this->getCacheItem();
137 // Try to load keys from cache
138 if ($item->isHit()) {
139 // item found! Return it
140 $jwks = $item->get();
141 $this->keySet = JWK::parseKeySet(json_decode($jwks, true), $this->defaultAlg);
142 }
143 }
144
145 if (!isset($this->keySet[$keyId])) {
146 if ($this->rateLimitExceeded()) {
147 return false;
148 }
149 $request = $this->httpFactory->createRequest('get', $this->jwksUri);
150 $jwksResponse = $this->httpClient->sendRequest($request);
151 $jwks = (string) $jwksResponse->getBody();
152 $this->keySet = JWK::parseKeySet(json_decode($jwks, true), $this->defaultAlg);
153
154 if (!isset($this->keySet[$keyId])) {
155 return false;
156 }
157
158 $item = $this->getCacheItem();
159 $item->set($jwks);
160 if ($this->expiresAfter) {
161 $item->expiresAfter($this->expiresAfter);
162 }
163 $this->cache->save($item);
164 }
165
166 return true;
167 }
168
169 private function rateLimitExceeded(): bool
170 {
171 if (!$this->rateLimit) {
172 return false;
173 }
174
175 $cacheItem = $this->cache->getItem($this->rateLimitCacheKey);
176 if (!$cacheItem->isHit()) {
177 $cacheItem->expiresAfter(1); // # of calls are cached each minute
178 }
179
180 $callsPerMinute = (int) $cacheItem->get();
181 if (++$callsPerMinute > $this->maxCallsPerMinute) {
182 return true;
183 }
184 $cacheItem->set($callsPerMinute);
185 $this->cache->save($cacheItem);
186 return false;
187 }
188
189 private function getCacheItem(): CacheItemInterface
190 {
191 if (\is_null($this->cacheItem)) {
192 $this->cacheItem = $this->cache->getItem($this->cacheKey);
193 }
194
195 return $this->cacheItem;
196 }
197
198 private function setCacheKeys(): void
199 {
200 if (empty($this->jwksUri)) {
201 throw new RuntimeException('JWKS URI is empty');
202 }
203
204 // ensure we do not have illegal characters
205 $key = preg_replace('|[^a-zA-Z0-9_\.!]|', '', $this->jwksUri);
206
207 // add prefix
208 $key = $this->cacheKeyPrefix . $key;
209
210 // Hash keys if they exceed $maxKeyLength of 64
211 if (\strlen($key) > $this->maxKeyLength) {
212 $key = substr(hash('sha256', $key), 0, $this->maxKeyLength);
213 }
214
215 $this->cacheKey = $key;
216
217 if ($this->rateLimit) {
218 // add prefix
219 $rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key;
220
221 // Hash keys if they exceed $maxKeyLength of 64
222 if (\strlen($rateLimitKey) > $this->maxKeyLength) {
223 $rateLimitKey = substr(hash('sha256', $rateLimitKey), 0, $this->maxKeyLength);
224 }
225
226 $this->rateLimitCacheKey = $rateLimitKey;
227 }
228 }
229 }
230