Skip to content

Commit

Permalink
ensure complete test coverage, fix cs
Browse files Browse the repository at this point in the history
  • Loading branch information
bshaffer committed Oct 5, 2024
1 parent 19ae608 commit a70d236
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 20 deletions.
4 changes: 2 additions & 2 deletions src/Credentials/ImpersonatedServiceAccountCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public function fetchAuthToken(callable $httpHandler = null)
'Authorization' => sprintf('Bearer %s', $authToken['access_token'] ?? $authToken['id_token']),
], $this->isIdTokenRequest() ? 'it' : 'at');

$body = match($this->isIdTokenRequest()) {
$body = match ($this->isIdTokenRequest()) {
true => [
'audience' => $this->targetAudience,
'includeEmail' => true,
Expand All @@ -210,7 +210,7 @@ public function fetchAuthToken(callable $httpHandler = null)
$response = $httpHandler($request);
$body = json_decode((string) $response->getBody(), true);

return match($this->isIdTokenRequest()) {
return match ($this->isIdTokenRequest()) {
true => ['id_token' => $body['token']],
false => [
'access_token' => $body['accessToken'],
Expand Down
124 changes: 106 additions & 18 deletions tests/Credentials/ImpersonatedServiceAccountCredentialsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
use Google\Auth\Credentials\ImpersonatedServiceAccountCredentials;
use Google\Auth\Credentials\ServiceAccountCredentials;
use Google\Auth\Credentials\UserRefreshCredentials;
use Google\Auth\Middleware\AuthTokenMiddleware;
use Google\Auth\FetchAuthTokenInterface;
use Google\Auth\Middleware\AuthTokenMiddleware;
use Google\Auth\OAuth2;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use LogicException;
use PHPUnit\Framework\TestCase;
use Prophecy\PHPUnit\ProphecyTrait;
use Prophecy\Argument;
use Prophecy\PHPUnit\ProphecyTrait;
use Psr\Http\Message\RequestInterface;
use ReflectionClass;

Expand Down Expand Up @@ -77,9 +77,9 @@ public function testMissingSourceCredentialTypeThrowsException()
}

/**
* @dataProvider provideServiceAccountImpersonationJson
* @dataProvider provideSourceCredentialsClass
*/
public function testSourceCredentialsFromJsonFiles(array $json, string $credClass)
public function testSourceCredentialsClass(array $json, string $credClass)
{
$creds = new ImpersonatedServiceAccountCredentials(['scope/1', 'scope/2'], $json);

Expand All @@ -88,27 +88,67 @@ public function testSourceCredentialsFromJsonFiles(array $json, string $credClas
$this->assertInstanceOf($credClass, $sourceCredentialsProperty->getValue($creds));
}

public function provideServiceAccountImpersonationJson()
public function provideSourceCredentialsClass()
{
return [
[self::USER_TO_SERVICE_ACCOUNT_JSON, UserRefreshCredentials::class],
[self::SERVICE_ACCOUNT_TO_SERVICE_ACCOUNT_JSON, ServiceAccountCredentials::class],
[self::EXTERNAL_ACCOUNT_TO_SERVICE_ACCOUNT_JSON, ExternalAccountCredentials::class],
];
}

/**
* @dataProvider provideServiceAccountImpersonationIdTokenJson
* Test access token impersonation for Service Account and User Refresh Credentials.
*
* @dataProvider provideAuthTokenJson
*/
public function testGetIdTokenWithServiceAccountImpersonationCredentials($json, $grantType)
public function testGetAccessTokenWithServiceAccountAndUserRefreshCredentials($json, $grantType)
{
$requestCount = 0;
// getting an id token will take two requests
$httpHandler = function (RequestInterface $request) use (&$requestCount, $json, $grantType) {
if (++$requestCount == 1) {
// the call to swap the refresh token for an access token
$this->assertEquals(UserRefreshCredentials::TOKEN_CREDENTIAL_URI, (string) $request->getUri());
$body = (string) $request->getBody();
parse_str($body, $result);
parse_str((string) $request->getBody(), $result);
$this->assertEquals($grantType, $result['grant_type']);
} elseif ($requestCount == 2) {
// the call to swap the access token for an id token
$this->assertEquals($json['service_account_impersonation_url'], (string) $request->getUri());
$this->assertEquals(self::SCOPE, json_decode($request->getBody(), true)['scope'] ?? '');
$this->assertEquals('Bearer test-access-token', $request->getHeader('authorization')[0] ?? null);
}

return new Response(
200,
['Content-Type' => 'application/json'],
json_encode(match ($requestCount) {
1 => ['access_token' => 'test-access-token'],
2 => ['accessToken' => 'test-impersonated-access-token', 'expireTime' => 123]
})
);
};

$creds = new ImpersonatedServiceAccountCredentials(self::SCOPE, $json);
$token = $creds->fetchAuthToken($httpHandler);
$this->assertEquals('test-impersonated-access-token', $token['access_token']);
$this->assertEquals(2, $requestCount);
}

/**
* Test ID token impersonation for Service Account and User Refresh Credentials.
*
* @dataProvider provideAuthTokenJson
*/
public function testGetIdTokenWithServiceAccountAndUserRefreshCredentials($json, $grantType)
{
$requestCount = 0;
// getting an id token will take two requests
$httpHandler = function (RequestInterface $request) use (&$requestCount, $json, $grantType) {
if (++$requestCount == 1) {
// the call to swap the refresh token for an access token
$this->assertEquals(UserRefreshCredentials::TOKEN_CREDENTIAL_URI, (string) $request->getUri());
parse_str((string) $request->getBody(), $result);
$this->assertEquals($grantType, $result['grant_type']);
} elseif ($requestCount == 2) {
// the call to swap the access token for an id token
Expand All @@ -133,7 +173,57 @@ public function testGetIdTokenWithServiceAccountImpersonationCredentials($json,
$this->assertEquals(2, $requestCount);
}

public function testGetIdTokenWithExternalAccountToServiceAccountImpersonationCredentials()
public function provideAuthTokenJson()
{
return [
[self::USER_TO_SERVICE_ACCOUNT_JSON, 'refresh_token'],
[self::SERVICE_ACCOUNT_TO_SERVICE_ACCOUNT_JSON, OAuth2::JWT_URN],
];
}

/**
* Test access token impersonation for Exernal Account Credentials.
*/
public function testGetAccessTokenWithExternalAccountCredentials()
{
$json = self::EXTERNAL_ACCOUNT_TO_SERVICE_ACCOUNT_JSON;
$httpHandler = function (RequestInterface $request) use (&$requestCount, $json) {
if (++$requestCount == 1) {
// the call to swap the refresh token for an access token
$this->assertEquals(
$json['source_credentials']['credential_source']['url'],
(string) $request->getUri()
);
} elseif ($requestCount == 2) {
$this->assertEquals($json['source_credentials']['token_url'], (string) $request->getUri());
} elseif ($requestCount == 3) {
// the call to swap the access token for an id token
$this->assertEquals($json['service_account_impersonation_url'], (string) $request->getUri());
$this->assertEquals(self::SCOPE, json_decode($request->getBody(), true)['scope'] ?? '');
$this->assertEquals('Bearer test-access-token', $request->getHeader('authorization')[0] ?? null);
}

return new Response(
200,
['Content-Type' => 'application/json'],
json_encode(match ($requestCount) {
1 => ['access_token' => 'test-access-token'],
2 => ['access_token' => 'test-access-token'],
3 => ['accessToken' => 'test-impersonated-access-token', 'expireTime' => 123]
})
);
};

$creds = new ImpersonatedServiceAccountCredentials(self::SCOPE, $json);
$token = $creds->fetchAuthToken($httpHandler);
$this->assertEquals('test-impersonated-access-token', $token['access_token']);
$this->assertEquals(3, $requestCount);
}

/**
* Test ID token impersonation for Exernal Account Credentials.
*/
public function testGetIdTokenWithExternalAccountCredentials()
{
$json = self::EXTERNAL_ACCOUNT_TO_SERVICE_ACCOUNT_JSON;
$httpHandler = function (RequestInterface $request) use (&$requestCount, $json) {
Expand Down Expand Up @@ -169,6 +259,9 @@ public function testGetIdTokenWithExternalAccountToServiceAccountImpersonationCr
$this->assertEquals(3, $requestCount);
}

/**
* Test ID token impersonation for an arbitrary credential fetcher.
*/
public function testGetIdTokenWithArbitraryCredentials()
{
$httpHandler = function (RequestInterface $request) {
Expand All @@ -193,6 +286,9 @@ public function testGetIdTokenWithArbitraryCredentials()
$this->assertEquals('test-impersonated-id-token', $token['id_token']);
}

/**
* Test access token impersonation for an arbitrary credential fetcher.
*/
public function testGetAccessTokenWithArbitraryCredentials()
{
$httpHandler = function (RequestInterface $request) {
Expand Down Expand Up @@ -221,14 +317,6 @@ public function testGetAccessTokenWithArbitraryCredentials()
$this->assertEquals('test-impersonated-access-token', $token['access_token']);
}

public function provideServiceAccountImpersonationIdTokenJson()
{
return [
[self::USER_TO_SERVICE_ACCOUNT_JSON, 'refresh_token'],
[self::SERVICE_ACCOUNT_TO_SERVICE_ACCOUNT_JSON, OAuth2::JWT_URN],
];
}

public function testIdTokenWithAuthTokenMiddleware()
{
$targetAudience = 'test-target-audience';
Expand Down

0 comments on commit a70d236

Please sign in to comment.