PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
← All changes | includes/sdk/google/google/auth/src/Credentials/GCECredentials.php +95 -50 1.2.61.4.1 View file →
@@ -14,23 +14,25 @@
14 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 15 * See the License for the specific language governing permissions and
16 16 * limitations under the License.
17 17 */
18 -namespace Dudlewebs\WPMCS\Google\Auth\Credentials;
18 +namespace Dudlewebs\WPMCS\GCP\Google\Auth\Credentials;
19 19
20 -use Dudlewebs\WPMCS\Google\Auth\CredentialsLoader;
21 -use Dudlewebs\WPMCS\Google\Auth\GetQuotaProjectInterface;
22 -use Dudlewebs\WPMCS\Google\Auth\HttpHandler\HttpClientCache;
23 -use Dudlewebs\WPMCS\Google\Auth\HttpHandler\HttpHandlerFactory;
24 -use Dudlewebs\WPMCS\Google\Auth\Iam;
25 -use Dudlewebs\WPMCS\Google\Auth\IamSignerTrait;
26 -use Dudlewebs\WPMCS\Google\Auth\ProjectIdProviderInterface;
27 -use Dudlewebs\WPMCS\Google\Auth\SignBlobInterface;
28 -use Dudlewebs\WPMCS\GuzzleHttp\Exception\ClientException;
29 -use Dudlewebs\WPMCS\GuzzleHttp\Exception\ConnectException;
30 -use Dudlewebs\WPMCS\GuzzleHttp\Exception\RequestException;
31 -use Dudlewebs\WPMCS\GuzzleHttp\Exception\ServerException;
32 -use Dudlewebs\WPMCS\GuzzleHttp\Psr7\Request;
20 +use COM;
21 +use com_exception;
22 +use Dudlewebs\WPMCS\GCP\Google\Auth\CredentialsLoader;
23 +use Dudlewebs\WPMCS\GCP\Google\Auth\GetQuotaProjectInterface;
24 +use Dudlewebs\WPMCS\GCP\Google\Auth\HttpHandler\HttpClientCache;
25 +use Dudlewebs\WPMCS\GCP\Google\Auth\HttpHandler\HttpHandlerFactory;
26 +use Dudlewebs\WPMCS\GCP\Google\Auth\Iam;
27 +use Dudlewebs\WPMCS\GCP\Google\Auth\IamSignerTrait;
28 +use Dudlewebs\WPMCS\GCP\Google\Auth\ProjectIdProviderInterface;
29 +use Dudlewebs\WPMCS\GCP\Google\Auth\SignBlobInterface;
30 +use Dudlewebs\WPMCS\GCP\GuzzleHttp\Exception\ClientException;
31 +use Dudlewebs\WPMCS\GCP\GuzzleHttp\Exception\ConnectException;
32 +use Dudlewebs\WPMCS\GCP\GuzzleHttp\Exception\RequestException;
33 +use Dudlewebs\WPMCS\GCP\GuzzleHttp\Exception\ServerException;
34 +use Dudlewebs\WPMCS\GCP\GuzzleHttp\Psr7\Request;
33 35 use InvalidArgumentException;
34 36 /**
35 37 * GCECredentials supports authorization on Google Compute Engine.
36 38 *
@@ -86,9 +88,9 @@
86 88 const PROJECT_ID_URI_PATH = 'v1/project/project-id';
87 89 /**
88 90 * The metadata path of the project ID.
89 91 */
90 - const UNIVERSE_DOMAIN_URI_PATH = 'v1/universe/universe_domain';
92 + const UNIVERSE_DOMAIN_URI_PATH = 'v1/universe/universe-domain';
91 93 /**
92 94 * The header whose presence indicates GCE presence.
93 95 */
94 96 const FLAVOR_HEADER = 'Metadata-Flavor';
@@ -96,8 +98,21 @@
96 98 * The Linux file which contains the product name.
97 99 */
98 100 private const GKE_PRODUCT_NAME_FILE = '/sys/class/dmi/id/product_name';
99 101 /**
102 + * The Windows Registry key path to the product name
103 + */
104 + private const WINDOWS_REGISTRY_KEY_PATH = 'HKEY_LOCAL_MACHINE\\SYSTEM\\HardwareConfig\\Current\\';
105 + /**
106 + * The Windows registry key name for the product name
107 + */
108 + private const WINDOWS_REGISTRY_KEY_NAME = 'SystemProductName';
109 + /**
110 + * The Name of the product expected from the windows registry
111 + */
112 + private const PRODUCT_NAME = 'Google';
113 + private const CRED_TYPE = 'mds';
114 + /**
100 115 * Note: the explicit `timeout` and `tries` below is a workaround. The underlying
101 116 * issue is that resolving an unknown host on some networks will take
102 117 * 20-30 seconds; making this timeout short fixes the issue, but
103 118 * could lead to false negatives in the event that we are on GCE, but
@@ -154,9 +169,9 @@
154 169 * @var string
155 170 */
156 171 private ?string $universeDomain;
157 172 /**
158 - * @param Iam $iam [optional] An IAM instance.
173 + * @param Iam|null $iam [optional] An IAM instance.
159 174 * @param string|string[] $scope [optional] the scope of the access request,
160 175 * expressed either as an array or as a space-delimited string.
161 176 * @param string $targetAudience [optional] The audience for the ID token.
162 177 * @param string $quotaProject [optional] Specifies a project to bill for access
@@ -162,12 +177,12 @@
162 177 * @param string $quotaProject [optional] Specifies a project to bill for access
163 178 * charges associated with the request.
164 179 * @param string $serviceAccountIdentity [optional] Specify a service
165 180 * account identity name to use instead of "default".
166 - * @param string $universeDomain [optional] Specify a universe domain to use
181 + * @param string|null $universeDomain [optional] Specify a universe domain to use
167 182 * instead of fetching one from the metadata server.
168 183 */
169 - public function __construct(Iam $iam = null, $scope = null, $targetAudience = null, $quotaProject = null, $serviceAccountIdentity = null, string $universeDomain = null)
184 + public function __construct(?Iam $iam = null, $scope = null, $targetAudience = null, $quotaProject = null, $serviceAccountIdentity = null, ?string $universeDomain = null)
170 185 {
171 186 $this->iam = $iam;
172 187 if ($scope && $targetAudience) {
173 188 throw new InvalidArgumentException('Scope and targetAudience cannot both be supplied');
@@ -173,12 +188,12 @@
173 188 throw new InvalidArgumentException('Scope and targetAudience cannot both be supplied');
174 189 }
175 190 $tokenUri = self::getTokenUri($serviceAccountIdentity);
176 191 if ($scope) {
177 - if (is_string($scope)) {
178 - $scope = explode(' ', $scope);
192 + if (\is_string($scope)) {
193 + $scope = \explode(' ', $scope);
179 194 }
180 - $scope = implode(',', $scope);
195 + $scope = \implode(',', $scope);
181 196 $tokenUri = $tokenUri . '?scopes=' . $scope;
182 197 } elseif ($targetAudience) {
183 198 $tokenUri = self::getIdTokenUri($serviceAccountIdentity);
184 199 $tokenUri = $tokenUri . '?audience=' . $targetAudience;
@@ -200,9 +215,9 @@
200 215 {
201 216 $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
202 217 $base .= self::TOKEN_URI_PATH;
203 218 if ($serviceAccountIdentity) {
204 - return str_replace('/default/', '/' . $serviceAccountIdentity . '/', $base);
219 + return \str_replace('/default/', '/' . $serviceAccountIdentity . '/', $base);
205 220 }
206 221 return $base;
207 222 }
208 223 /**
@@ -216,9 +231,9 @@
216 231 {
217 232 $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
218 233 $base .= self::CLIENT_ID_URI_PATH;
219 234 if ($serviceAccountIdentity) {
220 - return str_replace('/default/', '/' . $serviceAccountIdentity . '/', $base);
235 + return \str_replace('/default/', '/' . $serviceAccountIdentity . '/', $base);
221 236 }
222 237 return $base;
223 238 }
224 239 /**
@@ -232,9 +247,9 @@
232 247 {
233 248 $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
234 249 $base .= self::ID_TOKEN_URI_PATH;
235 250 if ($serviceAccountIdentity) {
236 - return str_replace('/default/', '/' . $serviceAccountIdentity . '/', $base);
251 + return \str_replace('/default/', '/' . $serviceAccountIdentity . '/', $base);
237 252 }
238 253 return $base;
239 254 }
240 255 /**
@@ -264,9 +279,9 @@
264 279 * @return bool true if this an App Engine Flexible Instance, false otherwise
265 280 */
266 281 public static function onAppEngineFlexible()
267 282 {
268 - return substr((string) getenv('GAE_INSTANCE'), 0, 4) === 'aef-';
283 + return \substr((string) \getenv('GAE_INSTANCE'), 0, 4) === 'aef-';
269 284 }
270 285 /**
271 286 * Determines if this a GCE instance, by accessing the expected metadata
272 287 * host.
@@ -271,12 +286,12 @@
271 286 * Determines if this a GCE instance, by accessing the expected metadata
272 287 * host.
273 288 * If $httpHandler is not specified a the default HttpHandler is used.
274 289 *
275 - * @param callable $httpHandler callback which delivers psr7 request
290 + * @param callable|null $httpHandler callback which delivers psr7 request
276 291 * @return bool True if this a GCEInstance, false otherwise
277 292 */
278 - public static function onGce(callable $httpHandler = null)
293 + public static function onGce(?callable $httpHandler = null)
279 294 {
280 295 $httpHandler = $httpHandler ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
281 296 $checkUri = 'http://' . self::METADATA_IP;
282 297 for ($i = 1; $i <= self::MAX_COMPUTE_PING_TRIES; $i++) {
@@ -288,9 +303,9 @@
288 303 // 20-30 seconds; making this timeout short fixes the issue, but
289 304 // could lead to false negatives in the event that we are on GCE, but
290 305 // the metadata resolution was particularly slow. The latter case is
291 306 // "unlikely".
292 - $resp = $httpHandler(new Request('GET', $checkUri, [self::FLAVOR_HEADER => 'Google']), ['timeout' => self::COMPUTE_PING_CONNECTION_TIMEOUT_S]);
307 + $resp = $httpHandler(new Request('GET', $checkUri, [self::FLAVOR_HEADER => 'Google', self::$metricMetadataKey => self::getMetricsHeader('', 'mds')]), ['timeout' => self::COMPUTE_PING_CONNECTION_TIMEOUT_S]);
293 308 return $resp->getHeaderLine(self::FLAVOR_HEADER) == 'Google';
294 309 } catch (ClientException $e) {
295 310 } catch (ServerException $e) {
296 311 } catch (RequestException $e) {
@@ -296,23 +311,40 @@
296 311 } catch (RequestException $e) {
297 312 } catch (ConnectException $e) {
298 313 }
299 314 }
300 - if (\PHP_OS === 'Windows') {
301 - // @TODO: implement GCE residency detection on Windows
302 - return \false;
315 + if (\PHP_OS === 'Windows' || \PHP_OS === 'WINNT') {
316 + return self::detectResidencyWindows(self::WINDOWS_REGISTRY_KEY_PATH . self::WINDOWS_REGISTRY_KEY_NAME);
303 317 }
304 318 // Detect GCE residency on Linux
305 319 return self::detectResidencyLinux(self::GKE_PRODUCT_NAME_FILE);
306 320 }
307 - private static function detectResidencyLinux(string $productNameFile): bool
321 + private static function detectResidencyLinux(string $productNameFile) : bool
308 322 {
309 - if (file_exists($productNameFile)) {
310 - $productName = trim((string) file_get_contents($productNameFile));
311 - return 0 === strpos($productName, 'Google');
323 + if (\file_exists($productNameFile)) {
324 + $productName = \trim((string) \file_get_contents($productNameFile));
325 + return 0 === \strpos($productName, self::PRODUCT_NAME);
312 326 }
313 327 return \false;
314 328 }
329 + private static function detectResidencyWindows(string $registryProductKey) : bool
330 + {
331 + if (!\class_exists(COM::class)) {
332 + // the COM extension must be installed and enabled to detect Windows residency
333 + // see https://www.php.net/manual/en/book.com.php
334 + return \false;
335 + }
336 + $shell = new COM('WScript.Shell');
337 + $productName = null;
338 + try {
339 + $productName = $shell->regRead($registryProductKey);
340 + } catch (com_exception) {
341 + // This means that we tried to read a key that doesn't exist on the registry
342 + // which might mean that it is a windows instance that is not on GCE
343 + return \false;
344 + }
345 + return 0 === \strpos($productName, self::PRODUCT_NAME);
346 + }
315 347 /**
316 348 * Implements FetchAuthTokenInterface#fetchAuthToken.
317 349 *
318 350 * Fetches the auth tokens from the GCE metadata host if it is available.
@@ -317,9 +349,11 @@
317 349 *
318 350 * Fetches the auth tokens from the GCE metadata host if it is available.
319 351 * If $httpHandler is not specified a the default HttpHandler is used.
320 352 *
321 - * @param callable $httpHandler callback which delivers psr7 request
353 + * @param callable|null $httpHandler callback which delivers psr7 request
354 + * @param array<mixed> $headers [optional] Headers to be inserted
355 + * into the token endpoint request present.
322 356 *
323 357 * @return array<mixed> {
324 358 * A set of auth related metadata, based on the token type.
325 359 *
@@ -329,9 +363,9 @@
329 363 * @type string $id_token for ID tokens
330 364 * }
331 365 * @throws \Exception
332 366 */
333 - public function fetchAuthToken(callable $httpHandler = null)
367 + public function fetchAuthToken(?callable $httpHandler = null, array $headers = [])
334 368 {
335 369 $httpHandler = $httpHandler ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
336 370 if (!$this->hasCheckedOnGce) {
337 371 $this->isOnGce = self::onGce($httpHandler);
@@ -340,26 +374,30 @@
340 374 if (!$this->isOnGce) {
341 375 return [];
342 376 // return an empty array with no access token
343 377 }
344 - $response = $this->getFromMetadata($httpHandler, $this->tokenUri);
378 + $response = $this->getFromMetadata($httpHandler, $this->tokenUri, $this->applyTokenEndpointMetrics($headers, $this->targetAudience ? 'it' : 'at'));
345 379 if ($this->targetAudience) {
346 380 return $this->lastReceivedToken = ['id_token' => $response];
347 381 }
348 - if (null === $json = json_decode($response, \true)) {
382 + if (null === ($json = \json_decode($response, \true))) {
349 383 throw new \Exception('Invalid JSON response');
350 384 }
351 - $json['expires_at'] = time() + $json['expires_in'];
385 + $json['expires_at'] = \time() + $json['expires_in'];
352 386 // store this so we can retrieve it later
353 387 $this->lastReceivedToken = $json;
354 388 return $json;
355 389 }
356 390 /**
391 + * Returns the Cache Key for the credential token.
392 + * The format for the cache key is:
393 + * TokenURI
394 + *
357 395 * @return string
358 396 */
359 397 public function getCacheKey()
360 398 {
361 - return self::cacheKey;
399 + return $this->tokenUri;
362 400 }
363 401 /**
364 402 * @return array<mixed>|null
365 403 */
@@ -365,9 +403,9 @@
365 403 */
366 404 public function getLastReceivedToken()
367 405 {
368 406 if ($this->lastReceivedToken) {
369 - if (array_key_exists('id_token', $this->lastReceivedToken)) {
407 + if (\array_key_exists('id_token', $this->lastReceivedToken)) {
370 408 return $this->lastReceivedToken;
371 409 }
372 410 return ['access_token' => $this->lastReceivedToken['access_token'], 'expires_at' => $this->lastReceivedToken['expires_at']];
373 411 }
@@ -377,12 +415,12 @@
377 415 * Get the client name from GCE metadata.
378 416 *
379 417 * Subsequent calls will return a cached value.
380 418 *
381 - * @param callable $httpHandler callback which delivers psr7 request
419 + * @param callable|null $httpHandler callback which delivers psr7 request
382 420 * @return string
383 421 */
384 - public function getClientName(callable $httpHandler = null)
422 + public function getClientName(?callable $httpHandler = null)
385 423 {
386 424 if ($this->clientName) {
387 425 return $this->clientName;
388 426 }
@@ -401,12 +439,12 @@
401 439 * Fetch the default Project ID from compute engine.
402 440 *
403 441 * Returns null if called outside GCE.
404 442 *
405 - * @param callable $httpHandler Callback which delivers psr7 request
443 + * @param callable|null $httpHandler Callback which delivers psr7 request
406 444 * @return string|null
407 445 */
408 - public function getProjectId(callable $httpHandler = null)
446 + public function getProjectId(?callable $httpHandler = null)
409 447 {
410 448 if ($this->projectId) {
411 449 return $this->projectId;
412 450 }
@@ -423,12 +461,12 @@
423 461 }
424 462 /**
425 463 * Fetch the default universe domain from the metadata server.
426 464 *
427 - * @param callable $httpHandler Callback which delivers psr7 request
465 + * @param callable|null $httpHandler Callback which delivers psr7 request
428 466 * @return string
429 467 */
430 - public function getUniverseDomain(callable $httpHandler = null): string
468 + public function getUniverseDomain(?callable $httpHandler = null) : string
431 469 {
432 470 if (null !== $this->universeDomain) {
433 471 return $this->universeDomain;
434 472 }
@@ -459,13 +497,16 @@
459 497 * Fetch the value of a GCE metadata server URI.
460 498 *
461 499 * @param callable $httpHandler An HTTP Handler to deliver PSR7 requests.
462 500 * @param string $uri The metadata URI.
501 + * @param array<mixed> $headers [optional] If present, add these headers to the token
502 + * endpoint request.
503 + *
463 504 * @return string
464 505 */
465 - private function getFromMetadata(callable $httpHandler, $uri)
506 + private function getFromMetadata(callable $httpHandler, $uri, array $headers = [])
466 507 {
467 - $resp = $httpHandler(new Request('GET', $uri, [self::FLAVOR_HEADER => 'Google']));
508 + $resp = $httpHandler(new Request('GET', $uri, [self::FLAVOR_HEADER => 'Google'] + $headers));
468 509 return (string) $resp->getBody();
469 510 }
470 511 /**
471 512 * Get the quota project used for this API request
@@ -488,6 +529,10 @@
488 529 // Implicitly set hasCheckedGce to true
489 530 $this->hasCheckedOnGce = \true;
490 531 // Set isOnGce
491 532 $this->isOnGce = $isOnGce;
533 + }
534 + protected function getCredType() : string
535 + {
536 + return self::CRED_TYPE;
492 537 }
493 538 }