Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add universe domain support #2563

Merged
merged 14 commits into from
Apr 24, 2024
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"license": "Apache-2.0",
"require": {
"php": "^7.4|^8.0",
"google/auth": "^1.33",
"google/apiclient-services": "~0.200",
"google/auth": "^1.37",
"google/apiclient-services": "dev-universe-domain as 0.340",
bshaffer marked this conversation as resolved.
Show resolved Hide resolved
"firebase/php-jwt": "~6.0",
"monolog/monolog": "^2.9||^3.0",
"phpseclib/phpseclib": "^3.0.34",
Expand Down
28 changes: 27 additions & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Google\Auth\Credentials\UserRefreshCredentials;
use Google\Auth\CredentialsLoader;
use Google\Auth\FetchAuthTokenCache;
use Google\Auth\GetUniverseDomainInterface;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google\Auth\OAuth2;
use Google\AuthHandler\AuthHandlerFactory;
Expand Down Expand Up @@ -176,7 +177,10 @@ public function __construct(array $config = [])

// Setting api_format_v2 will return more detailed error messages
// from certain APIs.
'api_format_v2' => false
'api_format_v2' => false,

// Setting the universe domain will change the
'universe_domain' => GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN,
], $config);

if (!is_null($this->config['credentials'])) {
Expand Down Expand Up @@ -428,6 +432,7 @@ public function authorize(ClientInterface $http = null)
// 3b. If access token exists but is expired, try to refresh it
// 4. Check for API Key
if ($this->credentials) {
$this->checkUniverseDomain($this->credentials);
return $authHandler->attachCredentials(
$http,
$this->credentials,
Expand All @@ -437,6 +442,7 @@ public function authorize(ClientInterface $http = null)

if ($this->isUsingApplicationDefaultCredentials()) {
$credentials = $this->createApplicationDefaultCredentials();
$this->checkUniverseDomain($credentials);
return $authHandler->attachCredentialsCache(
$http,
$credentials,
Expand All @@ -452,6 +458,7 @@ public function authorize(ClientInterface $http = null)
$scopes,
$token['refresh_token']
);
$this->checkUniverseDomain($credentials);
return $authHandler->attachCredentials(
$http,
$credentials,
Expand Down Expand Up @@ -1297,4 +1304,23 @@ private function createUserRefreshCredentials($scope, $refreshToken)

return new UserRefreshCredentials($scope, $creds);
}

private function checkUniverseDomain($credentials)
{
$credentialsUniverse = $credentials instanceof GetUniverseDomainInterface
? $credentials->getUniverseDomain()
: GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN;
if ($credentialsUniverse !== $this->getUniverseDomain()) {
throw new DomainException(sprintf(
'The configured universe domain (%s) does not match the credential universe domain (%s)',
$this->getUniverseDomain(),
$credentialsUniverse
));
}
}

public function getUniverseDomain()
{
return $this->config['universe_domain'];
}
}
4 changes: 4 additions & 0 deletions src/Service.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
class Service
{
public $batchPath;
/**
* @deprecated use $rootUrlTemplate instead
*/
public $rootUrl;
public $rootUrlTemplate;
public $version;
public $servicePath;
public $serviceName;
Expand Down
16 changes: 9 additions & 7 deletions src/Service/Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class Resource
'prettyPrint' => ['type' => 'string', 'location' => 'query'],
];

/** @var string $rootUrl */
private $rootUrl;
/** @var string $rootUrlTemplate */
private $rootUrlTemplate;

/** @var \Google\Client $client */
private $client;
Expand All @@ -65,7 +65,7 @@ class Resource

public function __construct($service, $serviceName, $resourceName, $resource)
{
$this->rootUrl = $service->rootUrl;
$this->rootUrlTemplate = $service->rootUrlTemplate ?? $service->rootUrl;
$this->client = $service->getClient();
$this->servicePath = $service->servicePath;
$this->serviceName = $serviceName;
Expand Down Expand Up @@ -268,12 +268,14 @@ public function createRequestUri($restPath, $params)
$requestUrl = $this->servicePath . $restPath;
}

// code for leading slash
if ($this->rootUrl) {
if ('/' !== substr($this->rootUrl, -1) && '/' !== substr($requestUrl, 0, 1)) {
if ($this->rootUrlTemplate) {
// code for universe domain
$rootUrl = str_replace('UNIVERSE_DOMAIN', $this->client->getUniverseDomain(), $this->rootUrlTemplate);
// code for leading slash
if ('/' !== substr($rootUrl, -1) && '/' !== substr($requestUrl, 0, 1)) {
$requestUrl = '/' . $requestUrl;
}
$requestUrl = $this->rootUrl . $requestUrl;
$requestUrl = $rootUrl . $requestUrl;
}
$uriTemplateVars = [];
$queryVars = [];
Expand Down
31 changes: 31 additions & 0 deletions tests/Google/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
use Google\Service\Drive;
use Google\AuthHandler\AuthHandlerFactory;
use Google\Auth\FetchAuthTokenCache;
use Google\Auth\CredentialsLoader;
use Google\Auth\GCECache;
use Google\Auth\Credentials\GCECredentials;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
Expand All @@ -37,6 +39,7 @@
use ReflectionMethod;
use InvalidArgumentException;
use Exception;
use DomainException;

class ClientTest extends BaseTest
{
Expand Down Expand Up @@ -689,11 +692,20 @@ public function testOnGceCacheAndCacheOptions()
$mockCacheItem->get()
->shouldBeCalledTimes(1)
->willReturn(true);
$mockUniverseDomainCacheItem = $this->prophesize(CacheItemInterface::class);
$mockUniverseDomainCacheItem->isHit()
->willReturn(true);
$mockUniverseDomainCacheItem->get()
->shouldBeCalledTimes(1)
->willReturn('googleapis.com');

$mockCache = $this->prophesize(CacheItemPoolInterface::class);
$mockCache->getItem($prefix . GCECache::GCE_CACHE_KEY)
->shouldBeCalledTimes(1)
->willReturn($mockCacheItem->reveal());
$mockCache->getItem(GCECredentials::cacheKey . 'universe_domain')
->shouldBeCalledTimes(1)
->willReturn($mockUniverseDomainCacheItem->reveal());

$client = new Client(['cache_config' => $cacheConfig]);
$client->setCache($mockCache->reveal());
Expand Down Expand Up @@ -849,6 +861,8 @@ public function testCredentialsOptionWithCredentialsLoader()
$credentials = $this->prophesize('Google\Auth\CredentialsLoader');
$credentials->getCacheKey()
->willReturn('cache-key');
$credentials->getUniverseDomain()
->willReturn('googleapis.com');

// Ensure the access token provided by our credentials loader is used
$credentials->updateMetadata([], null, Argument::any())
Expand Down Expand Up @@ -913,4 +927,21 @@ public function testQueryParamsForAuthUrl()
]);
$this->assertStringContainsString('&enable_serial_consent=true', $authUrl1);
}
public function testUniverseDomainMismatch()
{
$this->expectException(DomainException::class);
$this->expectExceptionMessage(
'The configured universe domain (example.com) does not match the credential universe domain (foo.com)'
);

$credentials = $this->prophesize(CredentialsLoader::class);
$credentials->getUniverseDomain()
->shouldBeCalledOnce()
->willReturn('foo.com');
$client = new Client([
'universe_domain' => 'example.com',
'credentials' => $credentials->reveal(),
]);
$client->authorize();
}
}
41 changes: 37 additions & 4 deletions tests/Google/Service/ResourceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@

class TestService extends \Google\Service
{
public function __construct(Client $client)
public function __construct(Client $client, $rootUrl = null)
{
parent::__construct($client);
$this->rootUrl = "https://test.example.com";
$this->rootUrl = $rootUrl ?: "https://test.example.com";
$this->rootUrlTemplate = $rootUrl ?: "https://test.UNIVERSE_DOMAIN";
$this->servicePath = "";
$this->version = "v1beta1";
$this->serviceName = "test";
Expand All @@ -59,6 +60,7 @@ public function setUp(): void
$this->client->getLogger()->willReturn($logger->reveal());
$this->client->shouldDefer()->willReturn(true);
$this->client->getHttpClient()->willReturn(new GuzzleClient());
$this->client->getUniverseDomain()->willReturn('example.com');

$this->service = new TestService($this->client->reveal());
}
Expand Down Expand Up @@ -106,6 +108,37 @@ public function testCall()
$this->assertFalse($request->hasHeader('Content-Type'));
}

public function testCallWithUniverseDomainTemplate()
{
$client = $this->prophesize(Client::class);
$logger = $this->prophesize("Monolog\Logger");
$this->client->getLogger()->willReturn($logger->reveal());
$this->client->shouldDefer()->willReturn(true);
$this->client->getHttpClient()->willReturn(new GuzzleClient());
$this->client->getUniverseDomain()->willReturn('example-universe-domain.com');

$this->service = new TestService($this->client->reveal());

$resource = new GoogleResource(
$this->service,
"test",
"testResource",
[
"methods" => [
"testMethod" => [
"parameters" => [],
"path" => "method/path",
"httpMethod" => "POST",
]
]
]
);
$request = $resource->call("testMethod", [[]]);
$this->assertEquals("https://test.example-universe-domain.com/method/path", (string) $request->getUri());
$this->assertEquals("POST", $request->getMethod());
$this->assertFalse($request->hasHeader('Content-Type'));
}

public function testCallWithPostBody()
{
$resource = new GoogleResource(
Expand All @@ -130,9 +163,9 @@ public function testCallWithPostBody()

public function testCallServiceDefinedRoot()
{
$this->service->rootUrl = "https://sample.example.com";
$service = new TestService($this->client->reveal(), "https://sample.example.com");
$resource = new GoogleResource(
$this->service,
$service,
"test",
"testResource",
[
Expand Down
Loading