From 6d4884b137ff016990faed8530ff863ade84a516 Mon Sep 17 00:00:00 2001 From: Jeremy Lindblom Date: Mon, 11 May 2015 15:34:07 -0700 Subject: [PATCH] Updates credential chain so that it fails over to instance profile credentials when ini credentials are invalid. --- src/Aws/Common/Credentials/Credentials.php | 25 +++--- .../Tests/Common/Credentials/.aws/credentials | 3 +- .../Common/Credentials/CredentialsTest.php | 81 ++++++++++++++++--- 3 files changed, 86 insertions(+), 23 deletions(-) diff --git a/src/Aws/Common/Credentials/Credentials.php b/src/Aws/Common/Credentials/Credentials.php index 73b8ffa000..f8ab2f7d79 100644 --- a/src/Aws/Common/Credentials/Credentials.php +++ b/src/Aws/Common/Credentials/Credentials.php @@ -143,11 +143,11 @@ public static function fromIni($profile = null, $filename = null) $profile = self::getEnvVar(self::ENV_PROFILE) ?: 'default'; } - if (!file_exists($filename) || !($data = parse_ini_file($filename, true))) { + if (!is_readable($filename) || ($data = parse_ini_file($filename, true)) === false) { throw new \RuntimeException("Invalid AWS credentials file: {$filename}."); } - if (empty($data[$profile])) { + if (!isset($data[$profile]['aws_access_key_id']) || !isset($data[$profile]['aws_secret_access_key'])) { throw new \RuntimeException("Invalid AWS credentials profile {$profile} in {$filename}."); } @@ -262,7 +262,7 @@ private static function createFromEnvironment($config) // Get key and secret from ENV variables $envKey = self::getEnvVar(self::ENV_KEY); if (!($envSecret = self::getEnvVar(self::ENV_SECRET))) { - // Use AWS_SECRET_ACCESS_KEY if AWS_SECRET_KEY was not set. + // Use AWS_SECRET_ACCESS_KEY if AWS_SECRET_KEY was not set $envSecret = self::getEnvVar(self::ENV_SECRET_ACCESS_KEY); } @@ -271,17 +271,16 @@ private static function createFromEnvironment($config) return new static($envKey, $envSecret); } - // Use credentials from the ini file in HOME directory if available - $home = self::getHomeDir(); - if ($home && file_exists("{$home}/.aws/credentials")) { - return self::fromIni($config[Options::PROFILE], "{$home}/.aws/credentials"); + try { + // Use credentials from the INI file in HOME directory if available + return self::fromIni($config[Options::PROFILE]); + } catch (\RuntimeException $e) { + // Otherwise, try using instance profile credentials (available on EC2 instances) + return new RefreshableInstanceProfileCredentials( + new static('', '', '', 1), + $config[Options::CREDENTIALS_CLIENT] + ); } - - // Use instance profile credentials (available on EC2 instances) - return new RefreshableInstanceProfileCredentials( - new static('', '', '', 1), - $config[Options::CREDENTIALS_CLIENT] - ); } private static function createCache(CredentialsInterface $credentials, $cache, $cacheKey) diff --git a/tests/Aws/Tests/Common/Credentials/.aws/credentials b/tests/Aws/Tests/Common/Credentials/.aws/credentials index 2c08c0b399..f7c8114c1a 100644 --- a/tests/Aws/Tests/Common/Credentials/.aws/credentials +++ b/tests/Aws/Tests/Common/Credentials/.aws/credentials @@ -3,4 +3,5 @@ aws_access_key_id=foo aws_secret_access_key=bar [test] aws_access_key_id=fizz -aws_secret_access_key=buzz \ No newline at end of file +aws_secret_access_key=buzz +[other] diff --git a/tests/Aws/Tests/Common/Credentials/CredentialsTest.php b/tests/Aws/Tests/Common/Credentials/CredentialsTest.php index 95af7515d7..583521887f 100644 --- a/tests/Aws/Tests/Common/Credentials/CredentialsTest.php +++ b/tests/Aws/Tests/Common/Credentials/CredentialsTest.php @@ -147,6 +147,19 @@ public function testFactoryCreatesInstanceProfileWhenNoKeysAreProvided() $this->assertInstanceOf('Aws\Common\Credentials\RefreshableInstanceProfileCredentials', $credentials); } + /** + * @covers Aws\Common\Credentials\Credentials::factory + * @covers Aws\Common\Credentials\Credentials::createFromEnvironment + */ + public function testFallsbackToInstanceProfileWhenCredentialsFileFails() + { + unset($_SERVER[Credentials::ENV_KEY], $_SERVER[Credentials::ENV_SECRET]); + putenv('AWS_ACCESS_KEY_ID='); + putenv('AWS_SECRET_KEY='); + $credentials = Credentials::factory(array('profile' => 'other')); + $this->assertInstanceOf('Aws\Common\Credentials\RefreshableInstanceProfileCredentials', $credentials); + } + /** * @covers Aws\Common\Credentials\Credentials::factory * @covers Aws\Common\Credentials\Credentials::createFromEnvironment @@ -215,16 +228,20 @@ public function testFactoryBailsWhenCacheCannotBeDetermined() */ public function testFactoryCreatesCredentialsFromCredentialFile( array $envVars = array(), + $profile = null, $expKey = null, $expSecret = null, - $profile = null + $expExMessage = null ) { foreach ($envVars as $key => $value) { $_SERVER[$key] = $value; + putenv("{$key}="); } - if (!$expKey && !$expSecret) { - $this->setExpectedException('RuntimeException'); + if ($expExMessage) { + $this->setExpectedExceptionRegExp( + 'RuntimeException', "/Invalid AWS credentials {$expExMessage}/" + ); } $credentials = Credentials::fromIni($profile); @@ -235,12 +252,58 @@ public function testFactoryCreatesCredentialsFromCredentialFile( public function getDataForCredentialFileTest() { return array( - array(array('HOME' => __DIR__), 'foo', 'bar'), - array(array('HOMEDRIVE' => '/', 'HOMEPATH' => __DIR__), 'foo', 'bar'), - array(), - array(array('HOME' => __DIR__), null, null, 'invalid'), - array(array('HOME' => __DIR__), 'fizz', 'buzz', 'test'), - array(array('HOME' => __DIR__, Credentials::ENV_PROFILE => 'test'), 'fizz', 'buzz'), + array( + array('HOME' => __DIR__), + null, + 'foo', + 'bar', + ), + array( + array('HOMEDRIVE' => '/', 'HOMEPATH' => __DIR__), + null, + 'foo', + 'bar', + ), + array( + array(), + null, + null, + null, + 'file' + ), + array( + array('HOME' => __DIR__), + 'invalid', + null, + null, + 'profile', + ), + array( + array('HOME' => __DIR__), + 'test', + 'fizz', + 'buzz', + ), + array( + array('HOME' => __DIR__, Credentials::ENV_PROFILE => 'test'), + null, + 'fizz', + 'buzz' + ), + array( + array('HOME' => __DIR__), + 'other', + null, + null, + 'profile' + ), + array( + array('HOME' => __DIR__ . '/invalid'), + 'other', + null, + null, + 'file' + ), ); } }