From 0ac671ad2e9673c3d7373cdb2960e118142a239c Mon Sep 17 00:00:00 2001 From: Yenfry Herrera Feliz Date: Fri, 15 Nov 2024 07:12:53 -0800 Subject: [PATCH] chore: add credentials->source tests - Add tests cases to cover credentials sets the correct source when resolved. - Fix the sso, sso_legacy, and process metrics. They should be all a profile based metric. --- src/Credentials/CredentialProvider.php | 6 +- src/Credentials/CredentialSources.php | 6 +- src/Credentials/Credentials.php | 2 +- src/MetricsBuilder.php | 18 +- tests/Credentials/CredentialProviderTest.php | 649 +++++++++++++++++++ tests/UserAgentMiddlewareTest.php | 8 +- 6 files changed, 670 insertions(+), 19 deletions(-) diff --git a/src/Credentials/CredentialProvider.php b/src/Credentials/CredentialProvider.php index 65c0dccf10..ea438ea2b2 100644 --- a/src/Credentials/CredentialProvider.php +++ b/src/Credentials/CredentialProvider.php @@ -646,7 +646,7 @@ public static function process($profile = null, $filename = null) $processData['SessionToken'], $expires, $accountId, - CredentialSources::PROCESS + CredentialSources::PROFILE_PROCESS ) ); }; @@ -927,7 +927,7 @@ private static function getSsoCredentials($profiles, $ssoProfileName, $filename, $ssoCredentials['sessionToken'], $expiration, $ssoProfile['sso_account_id'], - CredentialSources::SSO + CredentialSources::PROFILE_SSO ) ); } @@ -988,7 +988,7 @@ private static function getSsoCredentialsLegacy($profiles, $ssoProfileName, $fil $ssoCredentials['sessionToken'], $expiration, $ssoProfile['sso_account_id'], - CredentialSources::SSO_LEGACY + CredentialSources::PROFILE_SSO_LEGACY ) ); } diff --git a/src/Credentials/CredentialSources.php b/src/Credentials/CredentialSources.php index 08dd678e0d..829aa919c1 100644 --- a/src/Credentials/CredentialSources.php +++ b/src/Credentials/CredentialSources.php @@ -16,7 +16,7 @@ final class CredentialSources const PROFILE = 'profile'; const IMDS = 'instance_profile_provider'; const ECS = 'ecs'; - const SSO = 'sso'; - const SSO_LEGACY = 'sso_legacy'; - const PROCESS = 'process'; + const PROFILE_SSO = 'profile_sso'; + const PROFILE_SSO_LEGACY = 'profile_sso_legacy'; + const PROFILE_PROCESS = 'profile_process'; } diff --git a/src/Credentials/Credentials.php b/src/Credentials/Credentials.php index 70066a745f..766de78cd6 100644 --- a/src/Credentials/Credentials.php +++ b/src/Credentials/Credentials.php @@ -41,7 +41,7 @@ public function __construct( $this->token = $token; $this->expires = $expires; $this->accountId = $accountId; - $this->source = $source; + $this->source = $source ?? CredentialSources::STATIC; } public static function __set_state(array $state) diff --git a/src/MetricsBuilder.php b/src/MetricsBuilder.php index d27634f290..38184450f0 100644 --- a/src/MetricsBuilder.php +++ b/src/MetricsBuilder.php @@ -43,9 +43,9 @@ final class MetricsBuilder const CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN = "q"; const CREDENTIALS_HTTP = "z"; const CREDENTIALS_IMDS = "0"; - const CREDENTIALS_PROCESS = "w"; - const CREDENTIALS_SSO = "s"; - const CREDENTIALS_SSO_LEGACY = "u"; + const CREDENTIALS_PROFILE_PROCESS = "v"; + const CREDENTIALS_PROFILE_SSO = "r"; + const CREDENTIALS_PROFILE_SSO_LEGACY = "t"; /** @var int */ private static $MAX_METRICS_SIZE = 1024; // 1KB or 1024 B @@ -229,12 +229,12 @@ private function appendCredentialsMetric( MetricsBuilder::CREDENTIALS_HTTP, CredentialSources::PROFILE_STS_WEB_ID_TOKEN => MetricsBuilder::CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN, - CredentialSources::PROCESS => - MetricsBuilder::CREDENTIALS_PROCESS, - CredentialSources::SSO => - MetricsBuilder::CREDENTIALS_SSO, - CredentialSources::SSO_LEGACY => - MetricsBuilder::CREDENTIALS_SSO_LEGACY, + CredentialSources::PROFILE_PROCESS => + MetricsBuilder::CREDENTIALS_PROFILE_PROCESS, + CredentialSources::PROFILE_SSO => + MetricsBuilder::CREDENTIALS_PROFILE_SSO, + CredentialSources::PROFILE_SSO_LEGACY => + MetricsBuilder::CREDENTIALS_PROFILE_SSO_LEGACY, ]; if (isset($credentialsMetricMapping[$source])) { $this->append($credentialsMetricMapping[$source]); diff --git a/tests/Credentials/CredentialProviderTest.php b/tests/Credentials/CredentialProviderTest.php index 73701f1b53..4ce4a2990f 100644 --- a/tests/Credentials/CredentialProviderTest.php +++ b/tests/Credentials/CredentialProviderTest.php @@ -2,6 +2,7 @@ namespace Aws\Test\Credentials; use Aws\Api\DateTimeResult; +use Aws\Credentials\AssumeRoleWithWebIdentityCredentialProvider; use Aws\Credentials\CredentialProvider; use Aws\Credentials\Credentials; use Aws\Credentials\CredentialSources; @@ -10,10 +11,16 @@ use Aws\History; use Aws\LruArrayCache; use Aws\Result; +use Aws\SSO\SSOClient; +use Aws\Sts\StsClient; use Aws\Token\SsoTokenProvider; use GuzzleHttp\Promise; use Aws\Test\UsesServiceTrait; +use GuzzleHttp\Promise\Create; +use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Utils; use Yoast\PHPUnitPolyfills\TestCases\TestCase; +use function Aws\dir_iterator; /** @@ -2066,4 +2073,646 @@ public function shouldUseEcsProvider() ['', '', '', '', false] ]; } + + /** + * Test credentials defaults source to `static`. + * + * @return void + */ + public function testCredentialsSourceFromStatic() + { + $credentials = new Credentials('foo', 'foo'); + + $this->assertEquals( + CredentialSources::STATIC, + $credentials->getSource() + ); + } + + /** + * Test credentials from environment, sets source to `env`. + * + * @return void + */ + public function testCredentialsSourceFromEnv() + { + $currentEnv = [ + 'AWS_ACCESS_KEY_ID' => getenv('AWS_ACCESS_KEY_ID'), + 'AWS_SECRET_ACCESS_KEY' => getenv('AWS_SECRET_ACCESS_KEY') + ]; + putenv('AWS_ACCESS_KEY_ID=foo'); + putenv('AWS_SECRET_ACCESS_KEY=bazz'); + try { + $credentialsProvider = CredentialProvider::env(); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::ENVIRONMENT, + $credentials->getSource() + ); + } finally { + foreach ($currentEnv as $key => $value) { + if ($value !== false) { + putenv("$key=$value"); + } else { + putenv("$key"); + } + } + } + } + + /** + * Test credentials from sts web id token, sets source to `sts_web_id_token`. + * + * @return void + */ + public function testCredentialsSourceFromStsWebIdToken() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $tokenPath = $awsDir . '/my-token.jwt'; + file_put_contents($tokenPath, 'token'); + $roleArn = 'arn:aws:iam::123456789012:role/role_name'; + $result = [ + 'Credentials' => [ + 'AccessKeyId' => 'foo', + 'SecretAccessKey' => 'bar', + 'SessionToken' => 'baz', + 'Expiration' => time() + 1000 + ], + 'AssumedRoleUser' => [ + 'AssumedRoleId' => 'test_user_621903f1f21f5.01530789', + 'Arn' => $roleArn + ] + ]; + try { + $stsClient = new StsClient([ + 'region' => 'us-east-1', + 'credentials' => false, + 'handler' => function ($command, $request) use ($result) { + return Create::promiseFor(new Result($result)); + } + ]); + $credentialsProvider = new AssumeRoleWithWebIdentityCredentialProvider([ + 'RoleArn' => $roleArn, + 'WebIdentityTokenFile' => $tokenPath, + 'client' => $stsClient + ]); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::STS_WEB_ID_TOKEN, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + } + } + + /** + * Test credentials from sts web id token defined by env, sets source to + * `env_sts_web_id_token`. + * + * @return void + */ + public function testCredentialsSourceFromEnvStsWebIdToken() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $tokenPath = $awsDir . '/my-token.jwt'; + file_put_contents($tokenPath, 'token'); + $roleArn = 'arn:aws:iam::123456789012:role/role_name'; + // Set temporary env values + $currentEnv = [ + CredentialProvider::ENV_ARN => getenv( + CredentialProvider::ENV_ARN + ), + CredentialProvider::ENV_TOKEN_FILE => getenv( + CredentialProvider::ENV_TOKEN_FILE + ), + CredentialProvider::ENV_ROLE_SESSION_NAME => getenv( + CredentialProvider::ENV_ROLE_SESSION_NAME + ) + ]; + putenv(CredentialProvider::ENV_ARN . "={$roleArn}"); + putenv(CredentialProvider::ENV_TOKEN_FILE . "={$tokenPath}"); + putenv( + CredentialProvider::ENV_ROLE_SESSION_NAME . "=TestSession" + ); + // End setting env values + $result = [ + 'Credentials' => [ + 'AccessKeyId' => 'foo', + 'SecretAccessKey' => 'bar', + 'SessionToken' => 'baz', + 'Expiration' => time() + 1000 + ], + 'AssumedRoleUser' => [ + 'AssumedRoleId' => 'test_user_621903f1f21f5.01530789', + 'Arn' => $roleArn + ] + ]; + try { + $stsClient = new StsClient([ + 'region' => 'us-east-1', + 'credentials' => false, + 'handler' => function ($command, $request) use ($result) { + return Create::promiseFor(new Result($result)); + } + ]); + $credentialsProvider = + CredentialProvider::assumeRoleWithWebIdentityCredentialProvider([ + 'stsClient' => $stsClient + ]); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::ENVIRONMENT_STS_WEB_ID_TOKEN, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + foreach ($currentEnv as $key => $value) { + if ($value !== false) { + putenv("$key=$value"); + } else { + putenv("$key"); + } + } + } + } + + /** + * Test credentials from sts web id token defined by profile, sets source to + * `profile_sts_web_id_token`. + * + * @return void + */ + public function testCredentialsSourceFromProfileStsWebIdToken() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $tokenPath = $awsDir . '/my-token.jwt'; + file_put_contents($tokenPath, 'token'); + $roleArn = 'arn:aws:iam::123456789012:role/role_name'; + $profile = "test-profile"; + $configPath = $awsDir . '/my-config'; + $configData = << [ + 'AccessKeyId' => 'foo', + 'SecretAccessKey' => 'bar', + 'SessionToken' => 'baz', + 'Expiration' => time() + 1000 + ], + 'AssumedRoleUser' => [ + 'AssumedRoleId' => 'test_user_621903f1f21f5.01530789', + 'Arn' => $roleArn + ] + ]; + try { + $stsClient = new StsClient([ + 'region' => 'us-east-1', + 'credentials' => false, + 'handler' => function ($command, $request) use ($result) { + return Create::promiseFor(new Result($result)); + } + ]); + $credentialsProvider = + CredentialProvider::assumeRoleWithWebIdentityCredentialProvider([ + 'stsClient' => $stsClient, + 'filename' => $configPath + ]); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::PROFILE_STS_WEB_ID_TOKEN, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + putenv(CredentialProvider::ENV_PROFILE); + } + } + + /** + * Test credentials from sts assume role, sets source to + * `sts_assume_role`. + * + * @return void + */ + public function testCredentialsSourceFromStsAssumeRole() + { + $stsClient = new StsClient([ + 'region' => 'us-east-2', + 'handler' => function ($command, $request) { + return Create::promiseFor( + new Result([ + 'Credentials' => [ + 'AccessKeyId' => 'foo', + 'SecretAccessKey' => 'foo' + ] + ]) + ); + } + ]); + $credentialsProvider = CredentialProvider::assumeRole([ + 'assume_role_params' => [ + 'RoleArn' => 'arn:aws:iam::account-id:role/role-name', + 'RoleSessionName' => 'foo_session' + ], + 'client' => $stsClient + ]); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::STS_ASSUME_ROLE, + $credentials->getSource() + ); + } + + /** + * Test credentials sourced from a profile, sets source to + * `profile`. + * + * @return void + */ + public function testCredentialsSourceFromProfile() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $profile = 'test-profile'; + $configPath = $awsDir . '/credentials'; + $configData = << getenv('AWS_ACCESS_KEY_ID'), + 'AWS_SECRET_ACCESS_KEY' => getenv('AWS_SECRET_ACCESS_KEY') + ]; + putenv("AWS_ACCESS_KEY_ID"); + putenv("AWS_SECRET_ACCESS_KEY"); + try { + $credentialsProvider = CredentialProvider::ini( + $profile, + $configPath + ); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::PROFILE, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + foreach ($currentEnv as $key => $value) { + if ($value !== false) { + putenv("$key=$value"); + } else { + putenv("$key"); + } + } + } + } + + /** + * Test credentials from IMDS, sets source to + * `instance_profile_provider`. + * + * @return void + */ + public function testCredentialsSourceFromIMDS() + { + $imdsHandler = function ($request) { + $path = $request->getUri()->getPath(); + if ($path === '/latest/api/token') { + return Create::promiseFor( + new Response(200, [], Utils::streamFor('')) + ); + } elseif ($path === '/latest/meta-data/iam/security-credentials/') { + return Create::promiseFor( + new Response(200, [], Utils::streamFor('testProfile')) + ); + } elseif ($path === '/latest/meta-data/iam/security-credentials/testProfile') { + $expiration = time() + 1000; + return Create::promiseFor( + new Response( + 200, + [], + Utils::streamFor( + << $imdsHandler, + ]); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::IMDS, + $credentials->getSource() + ); + } + + /** + * Test credentials from ECS, sets source to + * `ecs`. + * + * @return void + */ + public function testCredentialsSourceFromECS() + { + $ecsHandler = function ($request) { + $expiration = time() + 1000; + return Create::promiseFor( + new Response( + 200, + [], + << $ecsHandler, + ]); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::ECS, + $credentials->getSource() + ); + } + + /** + * Test credentials sourced from process, sets source to + * `profile_process`. + * + * @return void + */ + public function testCredentialsSourceFromProcess() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $profile = 'test-profile'; + $configData = <<wait(); + + $this->assertEquals( + CredentialSources::PROFILE_PROCESS, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + } + } + + /** + * Test credentials sourced from sso, sets source to + * `profile_sso`. + * + * @return void + */ + public function testCredentialsSourceFromSso() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $expiration = time() + 1000; + $ini = << [ + 'accessKeyId' => 'Foo', + 'secretAccessKey' => 'Bazz', + 'sessionToken' => null, + 'expiration' => $expiration + ], + ]; + $ssoClient = new SSOClient([ + 'region' => 'us-east-1', + 'credentials' => false, + 'handler' => function ($command, $request) use ($result) { + + return Create::promiseFor(new Result($result)); + } + ]); + try { + $credentialsProvider = CredentialProvider::sso( + 'default', + $configPath, + [ + 'ssoClient' => $ssoClient + ] + ); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::PROFILE_SSO, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + } + } + + /** + * Test credentials sourced from sso legacy, sets source to + * `profile_sso_legacy`. + * + * @return void + */ + public function testCredentialsSourceFromSsoLegacy() + { + $tempHomeDir = sys_get_temp_dir() . "/test_credentials_source"; + $awsDir = $tempHomeDir . "/.aws"; + if (!is_dir($awsDir)) { + mkdir($awsDir, 0777, true); + } + $expiration = time() + 1000; + $ini = << [ + 'accessKeyId' => 'Foo', + 'secretAccessKey' => 'Bazz', + 'sessionToken' => null, + 'expiration' => $expiration + ], + ]; + $ssoClient = new SSOClient([ + 'region' => 'us-east-1', + 'credentials' => false, + 'handler' => function ($command, $request) use ($result) { + + return Create::promiseFor(new Result($result)); + } + ]); + try { + $credentialsProvider = CredentialProvider::sso( + 'default', + $configPath, + [ + 'ssoClient' => $ssoClient + ] + ); + $credentials = $credentialsProvider()->wait(); + + $this->assertEquals( + CredentialSources::PROFILE_SSO_LEGACY, + $credentials->getSource() + ); + } finally { + $this->cleanUpDir($tempHomeDir); + } + } + + /** + * Helper method to clean up temporary dirs. + * + * @param $dirPath + * + * @return void + */ + private function cleanUpDir($dirPath): void + { + if (!is_dir($dirPath)) { + return; + } + + $files = dir_iterator($dirPath); + foreach ($files as $file) { + if (in_array($file, ['.', '..'])) { + continue; + } + + $filePath = $dirPath . '/' . $file; + if (is_file($filePath) || !is_dir($filePath)) { + unlink($filePath); + } elseif (is_dir($filePath)) { + $this->cleanUpDir($filePath); + } + } + + rmdir($dirPath); + } } diff --git a/tests/UserAgentMiddlewareTest.php b/tests/UserAgentMiddlewareTest.php index 2cc14c4396..4362d49fb4 100644 --- a/tests/UserAgentMiddlewareTest.php +++ b/tests/UserAgentMiddlewareTest.php @@ -1272,7 +1272,8 @@ public function testUserAgentCaptureCredentialsProcessMetric() $metrics = $this->getMetricsAsArray($request); $this->assertTrue( - in_array(MetricsBuilder::CREDENTIALS_PROCESS, $metrics) + in_array(MetricsBuilder::CREDENTIALS_PROFILE_PROCESS, + $metrics) ); return new Response( @@ -1357,7 +1358,7 @@ public function testUserAgentCaptureCredentialsSSOMetric() $metrics = $this->getMetricsAsArray($request); $this->assertTrue( - in_array(MetricsBuilder::CREDENTIALS_SSO, $metrics) + in_array(MetricsBuilder::CREDENTIALS_PROFILE_SSO, $metrics) ); return new Response( @@ -1437,7 +1438,8 @@ public function testUserAgentCaptureCredentialsSSOLegacyMetric() $metrics = $this->getMetricsAsArray($request); $this->assertTrue( - in_array(MetricsBuilder::CREDENTIALS_SSO_LEGACY, $metrics) + in_array(MetricsBuilder::CREDENTIALS_PROFILE_SSO_LEGACY, + $metrics) ); return new Response(