source: vendor/google/auth/src/FetchAuthTokenCache.php@ f9c482b

Last change on this file since f9c482b was f9c482b, checked in by Vlado 222039 <vlado.popovski@…>, 7 days ago

Upload new project files

  • Property mode set to 100644
File size: 10.6 KB
Line 
1<?php
2/*
3 * Copyright 2010 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18namespace Google\Auth;
19
20use Psr\Cache\CacheItemPoolInterface;
21
22/**
23 * A class to implement caching for any object implementing
24 * FetchAuthTokenInterface
25 */
26class FetchAuthTokenCache implements
27 FetchAuthTokenInterface,
28 GetQuotaProjectInterface,
29 GetUniverseDomainInterface,
30 SignBlobInterface,
31 ProjectIdProviderInterface,
32 UpdateMetadataInterface
33{
34 use CacheTrait;
35
36 /**
37 * @var FetchAuthTokenInterface
38 */
39 private $fetcher;
40
41 /**
42 * @var int
43 */
44 private $eagerRefreshThresholdSeconds = 10;
45
46 /**
47 * @param FetchAuthTokenInterface $fetcher A credentials fetcher
48 * @param array<mixed>|null $cacheConfig Configuration for the cache
49 * @param CacheItemPoolInterface $cache
50 */
51 public function __construct(
52 FetchAuthTokenInterface $fetcher,
53 ?array $cacheConfig = null,
54 ?CacheItemPoolInterface $cache = null
55 ) {
56 $this->fetcher = $fetcher;
57 $this->cache = $cache;
58 $this->cacheConfig = array_merge([
59 'lifetime' => 1500,
60 'prefix' => '',
61 'cacheUniverseDomain' => $fetcher instanceof Credentials\GCECredentials,
62 ], (array) $cacheConfig);
63 }
64
65 /**
66 * @return FetchAuthTokenInterface
67 */
68 public function getFetcher()
69 {
70 return $this->fetcher;
71 }
72
73 /**
74 * Implements FetchAuthTokenInterface#fetchAuthToken.
75 *
76 * Checks the cache for a valid auth token and fetches the auth tokens
77 * from the supplied fetcher.
78 *
79 * @param callable|null $httpHandler callback which delivers psr7 request
80 * @return array<mixed> the response
81 * @throws \Exception
82 */
83 public function fetchAuthToken(?callable $httpHandler = null)
84 {
85 if ($cached = $this->fetchAuthTokenFromCache()) {
86 return $cached;
87 }
88
89 $auth_token = $this->fetcher->fetchAuthToken($httpHandler);
90
91 $this->saveAuthTokenInCache($auth_token);
92
93 return $auth_token;
94 }
95
96 /**
97 * @return string
98 */
99 public function getCacheKey()
100 {
101 return $this->getFullCacheKey($this->fetcher->getCacheKey());
102 }
103
104 /**
105 * @return array<mixed>|null
106 */
107 public function getLastReceivedToken()
108 {
109 return $this->fetcher->getLastReceivedToken();
110 }
111
112 /**
113 * Get the client name from the fetcher.
114 *
115 * @param callable|null $httpHandler An HTTP handler to deliver PSR7 requests.
116 * @return string
117 */
118 public function getClientName(?callable $httpHandler = null)
119 {
120 if (!$this->fetcher instanceof SignBlobInterface) {
121 throw new \RuntimeException(
122 'Credentials fetcher does not implement ' .
123 'Google\Auth\SignBlobInterface'
124 );
125 }
126
127 return $this->fetcher->getClientName($httpHandler);
128 }
129
130 /**
131 * Sign a blob using the fetcher.
132 *
133 * @param string $stringToSign The string to sign.
134 * @param bool $forceOpenSsl Require use of OpenSSL for local signing. Does
135 * not apply to signing done using external services. **Defaults to**
136 * `false`.
137 * @return string The resulting signature.
138 * @throws \RuntimeException If the fetcher does not implement
139 * `Google\Auth\SignBlobInterface`.
140 */
141 public function signBlob($stringToSign, $forceOpenSsl = false)
142 {
143 if (!$this->fetcher instanceof SignBlobInterface) {
144 throw new \RuntimeException(
145 'Credentials fetcher does not implement ' .
146 'Google\Auth\SignBlobInterface'
147 );
148 }
149
150 // Pass the access token from cache for credentials that sign blobs
151 // using the IAM API. This saves a call to fetch an access token when a
152 // cached token exists.
153 if ($this->fetcher instanceof Credentials\GCECredentials
154 || $this->fetcher instanceof Credentials\ImpersonatedServiceAccountCredentials
155 ) {
156 $cached = $this->fetchAuthTokenFromCache();
157 $accessToken = $cached['access_token'] ?? null;
158 return $this->fetcher->signBlob($stringToSign, $forceOpenSsl, $accessToken);
159 }
160
161 return $this->fetcher->signBlob($stringToSign, $forceOpenSsl);
162 }
163
164 /**
165 * Get the quota project used for this API request from the credentials
166 * fetcher.
167 *
168 * @return string|null
169 */
170 public function getQuotaProject()
171 {
172 if ($this->fetcher instanceof GetQuotaProjectInterface) {
173 return $this->fetcher->getQuotaProject();
174 }
175
176 return null;
177 }
178
179 /**
180 * Get the Project ID from the fetcher.
181 *
182 * @param callable|null $httpHandler Callback which delivers psr7 request
183 * @return string|null
184 * @throws \RuntimeException If the fetcher does not implement
185 * `Google\Auth\ProvidesProjectIdInterface`.
186 */
187 public function getProjectId(?callable $httpHandler = null)
188 {
189 if (!$this->fetcher instanceof ProjectIdProviderInterface) {
190 throw new \RuntimeException(
191 'Credentials fetcher does not implement ' .
192 'Google\Auth\ProvidesProjectIdInterface'
193 );
194 }
195
196 // Pass the access token from cache for credentials that require an
197 // access token to fetch the project ID. This saves a call to fetch an
198 // access token when a cached token exists.
199 if ($this->fetcher instanceof Credentials\ExternalAccountCredentials) {
200 $cached = $this->fetchAuthTokenFromCache();
201 $accessToken = $cached['access_token'] ?? null;
202 return $this->fetcher->getProjectId($httpHandler, $accessToken);
203 }
204
205 return $this->fetcher->getProjectId($httpHandler);
206 }
207
208 /*
209 * Get the Universe Domain from the fetcher.
210 *
211 * @return string
212 */
213 public function getUniverseDomain(): string
214 {
215 if ($this->fetcher instanceof GetUniverseDomainInterface) {
216 if ($this->cacheConfig['cacheUniverseDomain']) {
217 return $this->getCachedUniverseDomain($this->fetcher);
218 }
219 return $this->fetcher->getUniverseDomain();
220 }
221
222 return GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN;
223 }
224
225 /**
226 * Updates metadata with the authorization token.
227 *
228 * @param array<mixed> $metadata metadata hashmap
229 * @param string $authUri optional auth uri
230 * @param callable|null $httpHandler callback which delivers psr7 request
231 * @return array<mixed> updated metadata hashmap
232 * @throws \RuntimeException If the fetcher does not implement
233 * `Google\Auth\UpdateMetadataInterface`.
234 */
235 public function updateMetadata(
236 $metadata,
237 $authUri = null,
238 ?callable $httpHandler = null
239 ) {
240 if (!$this->fetcher instanceof UpdateMetadataInterface) {
241 throw new \RuntimeException(
242 'Credentials fetcher does not implement ' .
243 'Google\Auth\UpdateMetadataInterface'
244 );
245 }
246
247 $cached = $this->fetchAuthTokenFromCache($authUri);
248 if ($cached) {
249 // Set the access token in the `Authorization` metadata header so
250 // the downstream call to updateMetadata know they don't need to
251 // fetch another token.
252 if (isset($cached['access_token'])) {
253 $metadata[self::AUTH_METADATA_KEY] = [
254 'Bearer ' . $cached['access_token']
255 ];
256 } elseif (isset($cached['id_token'])) {
257 $metadata[self::AUTH_METADATA_KEY] = [
258 'Bearer ' . $cached['id_token']
259 ];
260 }
261 }
262
263 $newMetadata = $this->fetcher->updateMetadata(
264 $metadata,
265 $authUri,
266 $httpHandler
267 );
268
269 if (!$cached && $token = $this->fetcher->getLastReceivedToken()) {
270 $this->saveAuthTokenInCache($token, $authUri);
271 }
272
273 return $newMetadata;
274 }
275
276 /**
277 * @param string|null $authUri
278 * @return array<mixed>|null
279 */
280 private function fetchAuthTokenFromCache($authUri = null)
281 {
282 // Use the cached value if its available.
283 //
284 // TODO: correct caching; update the call to setCachedValue to set the expiry
285 // to the value returned with the auth token.
286 //
287 // TODO: correct caching; enable the cache to be cleared.
288
289 // if $authUri is set, use it as the cache key
290 $cacheKey = $authUri
291 ? $this->getFullCacheKey($authUri)
292 : $this->fetcher->getCacheKey();
293
294 $cached = $this->getCachedValue($cacheKey);
295 if (is_array($cached)) {
296 if (empty($cached['expires_at'])) {
297 // If there is no expiration data, assume token is not expired.
298 // (for JwtAccess and ID tokens)
299 return $cached;
300 }
301 if ((time() + $this->eagerRefreshThresholdSeconds) < $cached['expires_at']) {
302 // access token is not expired
303 return $cached;
304 }
305 }
306
307 return null;
308 }
309
310 /**
311 * @param array<mixed> $authToken
312 * @param string|null $authUri
313 * @return void
314 */
315 private function saveAuthTokenInCache($authToken, $authUri = null)
316 {
317 if (isset($authToken['access_token']) ||
318 isset($authToken['id_token'])) {
319 // if $authUri is set, use it as the cache key
320 $cacheKey = $authUri
321 ? $this->getFullCacheKey($authUri)
322 : $this->fetcher->getCacheKey();
323
324 $this->setCachedValue($cacheKey, $authToken);
325 }
326 }
327
328 private function getCachedUniverseDomain(GetUniverseDomainInterface $fetcher): string
329 {
330 $cacheKey = $this->getFullCacheKey($fetcher->getCacheKey() . 'universe_domain'); // @phpstan-ignore-line
331 if ($universeDomain = $this->getCachedValue($cacheKey)) {
332 return $universeDomain;
333 }
334
335 $universeDomain = $fetcher->getUniverseDomain();
336 $this->setCachedValue($cacheKey, $universeDomain);
337 return $universeDomain;
338 }
339}
Note: See TracBrowser for help on using the repository browser.