Skip to content

Commit

Permalink
Merge branch 'main' into drop-php80
Browse files Browse the repository at this point in the history
  • Loading branch information
bshaffer authored Apr 12, 2024
2 parents d6eba00 + 3bcd1fc commit c8cbdf6
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 8 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

* [feat]: add support for Firebase v6.0 (#391)

## [1.37.1](https://github.com/googleapis/google-auth-library-php/compare/v1.37.0...v1.37.1) (2024-03-07)


### Bug Fixes

* Use gmdate to format x-amz-date with UTC irrespective of timezone ([#540](https://github.com/googleapis/google-auth-library-php/issues/540)) ([3031d2c](https://github.com/googleapis/google-auth-library-php/commit/3031d2c616902d514867953ede8688d2914d5b11))

## [1.37.0](https://github.com/googleapis/google-auth-library-php/compare/v1.36.0...v1.37.0) (2024-02-21)


### Features

* Add caching for universe domain ([#533](https://github.com/googleapis/google-auth-library-php/issues/533)) ([69249ab](https://github.com/googleapis/google-auth-library-php/commit/69249ab03d4852e55377962752bdda5253f3d574))

## [1.36.0](https://github.com/googleapis/google-auth-library-php/compare/v1.35.0...v1.36.0) (2024-02-20)


### Features

* Universe domain for Iam ([#531](https://github.com/googleapis/google-auth-library-php/issues/531)) ([b905a56](https://github.com/googleapis/google-auth-library-php/commit/b905a561ac8913420d4b3c0a24734ded48687028))

## [1.35.0](https://github.com/googleapis/google-auth-library-php/compare/v1.34.0...v1.35.0) (2024-02-01)


Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.33.0
1.37.1
4 changes: 2 additions & 2 deletions src/CredentialSource/AwsNativeSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ public static function getSignedRequestHeaders(
$service = 'sts';

# Create a date for headers and the credential string in ISO-8601 format
$amzdate = date('Ymd\THis\Z');
$datestamp = date('Ymd'); # Date w/o time, used in credential scope
$amzdate = gmdate('Ymd\THis\Z');
$datestamp = gmdate('Ymd'); # Date w/o time, used in credential scope

# Create the canonical headers and signed headers. Header names
# must be trimmed and lowercase, and sorted in code point order from
Expand Down
2 changes: 1 addition & 1 deletion src/Credentials/ExternalAccountCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ExternalAccountCredentials implements
use UpdateMetadataTrait;

private const EXTERNAL_ACCOUNT_TYPE = 'external_account';
private const CLOUD_RESOURCE_MANAGER_URL='https://cloudresourcemanager.UNIVERSE_DOMAIN/v1/projects/%s';
private const CLOUD_RESOURCE_MANAGER_URL = 'https://cloudresourcemanager.UNIVERSE_DOMAIN/v1/projects/%s';

private OAuth2 $auth;
private ?string $quotaProject;
Expand Down
16 changes: 16 additions & 0 deletions src/FetchAuthTokenCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public function __construct(
$this->cacheConfig = array_merge([
'lifetime' => 1500,
'prefix' => '',
'cacheUniverseDomain' => $fetcher instanceof Credentials\GCECredentials,
], (array) $cacheConfig);
}

Expand Down Expand Up @@ -212,6 +213,9 @@ public function getProjectId(callable $httpHandler = null)
public function getUniverseDomain(): string
{
if ($this->fetcher instanceof GetUniverseDomainInterface) {
if ($this->cacheConfig['cacheUniverseDomain']) {
return $this->getCachedUniverseDomain($this->fetcher);
}
return $this->fetcher->getUniverseDomain();
}

Expand Down Expand Up @@ -320,4 +324,16 @@ private function saveAuthTokenInCache($authToken, $authUri = null)
$this->setCachedValue($cacheKey, $authToken);
}
}

private function getCachedUniverseDomain(GetUniverseDomainInterface $fetcher): string
{
$cacheKey = $this->getFullCacheKey($fetcher->getCacheKey() . 'universe_domain'); // @phpstan-ignore-line
if ($universeDomain = $this->getCachedValue($cacheKey)) {
return $universeDomain;
}

$universeDomain = $fetcher->getUniverseDomain();
$this->setCachedValue($cacheKey, $universeDomain);
return $universeDomain;
}
}
16 changes: 13 additions & 3 deletions src/Iam.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,31 @@
*/
class Iam
{
/**
* @deprecated
*/
const IAM_API_ROOT = 'https://iamcredentials.googleapis.com/v1';
const SIGN_BLOB_PATH = '%s:signBlob?alt=json';
const SERVICE_ACCOUNT_NAME = 'projects/-/serviceAccounts/%s';
private const IAM_API_ROOT_TEMPLATE = 'https://iamcredentials.UNIVERSE_DOMAIN/v1';

/**
* @var callable
*/
private $httpHandler;

private string $universeDomain;

/**
* @param callable $httpHandler [optional] The HTTP Handler to send requests.
*/
public function __construct(callable $httpHandler = null)
{
public function __construct(
callable $httpHandler = null,
string $universeDomain = GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN
) {
$this->httpHandler = $httpHandler
?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
$this->universeDomain = $universeDomain;
}

/**
Expand All @@ -66,7 +75,8 @@ public function signBlob($email, $accessToken, $stringToSign, array $delegates =
{
$httpHandler = $this->httpHandler;
$name = sprintf(self::SERVICE_ACCOUNT_NAME, $email);
$uri = self::IAM_API_ROOT . '/' . sprintf(self::SIGN_BLOB_PATH, $name);
$apiRoot = str_replace('UNIVERSE_DOMAIN', $this->universeDomain, self::IAM_API_ROOT_TEMPLATE);
$uri = $apiRoot . '/' . sprintf(self::SIGN_BLOB_PATH, $name);

if ($delegates) {
foreach ($delegates as &$delegate) {
Expand Down
7 changes: 6 additions & 1 deletion src/IamSignerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ public function signBlob($stringToSign, $forceOpenSsl = false, $accessToken = nu

// Providing a signer is useful for testing, but it's undocumented
// because it's not something a user would generally need to do.
$signer = $this->iam ?: new Iam($httpHandler);
$signer = $this->iam;
if (!$signer) {
$signer = $this instanceof GetUniverseDomainInterface
? new Iam($httpHandler, $this->getUniverseDomain())
: new Iam($httpHandler);
}

$email = $this->getClientName($httpHandler);

Expand Down
39 changes: 39 additions & 0 deletions tests/Credentials/GCECredentialsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Google\Auth\Tests\BaseTest;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Utils;
use InvalidArgumentException;
Expand Down Expand Up @@ -380,6 +381,44 @@ public function testSignBlobWithLastReceivedAccessToken()
$signature = $creds->signBlob($stringToSign);
}

public function testSignBlobWithUniverseDomain()
{
$token = [
'access_token' => 'token',
'expires_in' => '57',
'token_type' => 'Bearer',
];
$signedBlob = ['signedBlob' => 'abc123'];
$client = $this->prophesize('GuzzleHttp\ClientInterface');
$client->send(Argument::any(), Argument::any())
->willReturn(
new Response(200, [], Utils::streamFor('[email protected]')),
new Response(200, [], Utils::streamFor(json_encode($token)))
);
$client->send(
Argument::that(
fn (Request $request) => $request->getUri()->getHost() === 'iamcredentials.example-universe.com'
),
Argument::any()
)
->shouldBeCalledOnce()
->willReturn(new Response(200, [], Utils::streamFor(json_encode($signedBlob))));

HttpClientCache::setHttpClient($client->reveal());

$creds = new GCECredentials(
null,
null,
null,
null,
null,
'example-universe.com'
);
$creds->setIsOnGce(true);
$signature = $creds->signBlob('inputString');
$this->assertEquals('abc123', $signature);
}

public function testGetProjectId()
{
$expected = 'foobar';
Expand Down
97 changes: 97 additions & 0 deletions tests/FetchAuthTokenCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\CredentialsLoader;
use Google\Auth\FetchAuthTokenCache;
use Google\Auth\FetchAuthTokenInterface;
use Google\Auth\GetUniverseDomainInterface;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Utils;
Expand All @@ -37,6 +38,7 @@ class FetchAuthTokenCacheTest extends BaseTest
private $mockCacheItem;
private $mockCache;
private $mockSigner;
private static string $cacheKey;

protected function setUp(): void
{
Expand Down Expand Up @@ -700,4 +702,99 @@ public function testGetFetcher()

$this->assertSame($mockFetcher, $fetcher->getFetcher());
}

public function testCacheUniverseDomain()
{
$mockFetcher = $this->prophesize(FetchAuthTokenInterface::class);
$mockFetcher->willImplement(GetUniverseDomainInterface::class);
$mockFetcher->getUniverseDomain()
->shouldBeCalledTimes(2)
->willReturn('example-universe.domain');
$mockFetcher->getCacheKey()
->shouldNotBeCalled();

$fetcher = new FetchAuthTokenCache(
$mockFetcher->reveal(),
['cacheUniverseDomain' => false],
new MemoryCacheItemPool()
);

// Call it twice
$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());
$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());

// Now set the cache option and ensure it's only called once
$mockFetcher = $this->prophesize(FetchAuthTokenInterface::class);
$mockFetcher->willImplement(GetUniverseDomainInterface::class);
$mockFetcher->getUniverseDomain()
->shouldBeCalledOnce()
->willReturn('example-universe.domain');
$mockFetcher->getCacheKey()
->shouldBeCalledTimes(2)
->willReturn('my-cache-key');

$fetcher = new FetchAuthTokenCache(
$mockFetcher->reveal(),
['cacheUniverseDomain' => true],
new MemoryCacheItemPool()
);
$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());
$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());
}

public function testCacheUniverseDomainByDefaultForGCECredentials()
{
$mockFetcher = $this->prophesize(GCECredentials::class);
$mockFetcher->getUniverseDomain()
->shouldBeCalledOnce()
->willReturn('example-universe.domain');
$mockFetcher->getCacheKey()
->shouldBeCalledTimes(2)
->willReturn('my-cache-key');

$fetcher = new FetchAuthTokenCache(
$mockFetcher->reveal(),
[], // don't set cacheUniverseDomain, it will be true by default
new MemoryCacheItemPool()
);

$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());
$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());
}

public function testUniverseDomainWithFileCache()
{
require_once __DIR__ . '/mocks/TestFileCacheItemPool.php';
self::$cacheKey = 'universe-domain-check-' . time() . rand();

$cache = new TestFileCacheItemPool(sys_get_temp_dir() . '/google-auth-test');

$mockFetcher = $this->prophesize(FetchAuthTokenInterface::class);
$mockFetcher->willImplement(GetUniverseDomainInterface::class);
$mockFetcher->getUniverseDomain()
->shouldBeCalledOnce()
->willReturn('example-universe.domain');
$mockFetcher->getCacheKey()
->shouldBeCalledOnce()
->willReturn(self::$cacheKey);

$fetcher = new FetchAuthTokenCache(
$mockFetcher->reveal(),
['cacheUniverseDomain' => true],
$cache
);
$this->assertEquals('example-universe.domain', $fetcher->getUniverseDomain());
}

/**
* @depends testUniverseDomainWithFileCache
*/
public function testUniverseDomainWithFileCacheProcess2()
{
$cmd = sprintf('php %s/mocks/test_file_cache_separate_process.php %s', __DIR__, self::$cacheKey);
exec($cmd, $output, $retVar);

$this->assertEquals(0, $retVar);
$this->assertEquals('example-universe.domain', implode('', $output));
}
}
Loading

0 comments on commit c8cbdf6

Please sign in to comment.