diff options
author | Andrew Dolgov <[email protected]> | 2022-11-23 21:14:33 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2022-11-23 21:14:33 +0300 |
commit | 0c8af4992cb0f7589dcafaad65ada12753c64594 (patch) | |
tree | 18e83d068c3e7dd2499331de977782b382279396 /vendor/aws/aws-sdk-php/src/Credentials |
initial
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Credentials')
7 files changed, 1736 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleCredentialProvider.php b/vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleCredentialProvider.php new file mode 100644 index 0000000..416d795 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleCredentialProvider.php @@ -0,0 +1,64 @@ +<?php +namespace Aws\Credentials; + +use Aws\Exception\CredentialsException; +use Aws\Result; +use Aws\Sts\StsClient; +use GuzzleHttp\Promise\PromiseInterface; + +/** + * Credential provider that provides credentials via assuming a role + * More Information, see: http://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sts-2011-06-15.html#assumerole + */ +class AssumeRoleCredentialProvider +{ + const ERROR_MSG = "Missing required 'AssumeRoleCredentialProvider' configuration option: "; + + /** @var StsClient */ + private $client; + + /** @var array */ + private $assumeRoleParams; + + /** + * The constructor requires following configure parameters: + * - client: a StsClient + * - assume_role_params: Parameters used to make assumeRole call + * + * @param array $config Configuration options + * @throws \InvalidArgumentException + */ + public function __construct(array $config = []) + { + if (!isset($config['assume_role_params'])) { + throw new \InvalidArgumentException(self::ERROR_MSG . "'assume_role_params'."); + } + + if (!isset($config['client'])) { + throw new \InvalidArgumentException(self::ERROR_MSG . "'client'."); + } + + $this->client = $config['client']; + $this->assumeRoleParams = $config['assume_role_params']; + } + + /** + * Loads assume role credentials. + * + * @return PromiseInterface + */ + public function __invoke() + { + $client = $this->client; + return $client->assumeRoleAsync($this->assumeRoleParams) + ->then(function (Result $result) { + return $this->client->createCredentials($result); + })->otherwise(function (\RuntimeException $exception) { + throw new CredentialsException( + "Error in retrieving assume role credentials.", + 0, + $exception + ); + }); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleWithWebIdentityCredentialProvider.php b/vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleWithWebIdentityCredentialProvider.php new file mode 100644 index 0000000..7e8057e --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleWithWebIdentityCredentialProvider.php @@ -0,0 +1,166 @@ +<?php +namespace Aws\Credentials; + +use Aws\Exception\AwsException; +use Aws\Exception\CredentialsException; +use Aws\Result; +use Aws\Sts\StsClient; +use GuzzleHttp\Promise; + +/** + * Credential provider that provides credentials via assuming a role with a web identity + * More Information, see: https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sts-2011-06-15.html#assumerolewithwebidentity + */ +class AssumeRoleWithWebIdentityCredentialProvider +{ + const ERROR_MSG = "Missing required 'AssumeRoleWithWebIdentityCredentialProvider' configuration option: "; + const ENV_RETRIES = 'AWS_METADATA_SERVICE_NUM_ATTEMPTS'; + + /** @var string */ + private $tokenFile; + + /** @var string */ + private $arn; + + /** @var string */ + private $session; + + /** @var StsClient */ + private $client; + + /** @var integer */ + private $retries; + + /** @var integer */ + private $authenticationAttempts; + + /** @var integer */ + private $tokenFileReadAttempts; + + /** + * The constructor attempts to load config from environment variables. + * If not set, the following config options are used: + * - WebIdentityTokenFile: full path of token filename + * - RoleArn: arn of role to be assumed + * - SessionName: (optional) set by SDK if not provided + * + * @param array $config Configuration options + * @throws \InvalidArgumentException + */ + public function __construct(array $config = []) + { + if (!isset($config['RoleArn'])) { + throw new \InvalidArgumentException(self::ERROR_MSG . "'RoleArn'."); + } + $this->arn = $config['RoleArn']; + + if (!isset($config['WebIdentityTokenFile'])) { + throw new \InvalidArgumentException(self::ERROR_MSG . "'WebIdentityTokenFile'."); + } + $this->tokenFile = $config['WebIdentityTokenFile']; + + if (!preg_match("/^\w\:|^\/|^\\\/", $this->tokenFile)) { + throw new \InvalidArgumentException("'WebIdentityTokenFile' must be an absolute path."); + } + + $this->retries = (int) getenv(self::ENV_RETRIES) ?: (isset($config['retries']) ? $config['retries'] : 3); + $this->authenticationAttempts = 0; + $this->tokenFileReadAttempts = 0; + + $this->session = isset($config['SessionName']) + ? $config['SessionName'] + : 'aws-sdk-php-' . round(microtime(true) * 1000); + + $region = isset($config['region']) + ? $config['region'] + : 'us-east-1'; + + if (isset($config['client'])) { + $this->client = $config['client']; + } else { + $this->client = new StsClient([ + 'credentials' => false, + 'region' => $region, + 'version' => 'latest' + ]); + } + } + + /** + * Loads assume role with web identity credentials. + * + * @return Promise\PromiseInterface + */ + public function __invoke() + { + return Promise\Coroutine::of(function () { + $client = $this->client; + $result = null; + while ($result == null) { + try { + $token = @file_get_contents($this->tokenFile); + if (false === $token) { + clearstatcache(true, dirname($this->tokenFile) . "/" . readlink($this->tokenFile)); + clearstatcache(true, dirname($this->tokenFile) . "/" . dirname(readlink($this->tokenFile))); + clearstatcache(true, $this->tokenFile); + if (!@is_readable($this->tokenFile)) { + throw new CredentialsException( + "Unreadable tokenfile at location {$this->tokenFile}" + ); + } + + $token = @file_get_contents($this->tokenFile); + } + if (empty($token)) { + if ($this->tokenFileReadAttempts < $this->retries) { + sleep((int) pow(1.2, $this->tokenFileReadAttempts)); + $this->tokenFileReadAttempts++; + continue; + } + throw new CredentialsException("InvalidIdentityToken from file: {$this->tokenFile}"); + } + } catch (\Exception $exception) { + throw new CredentialsException( + "Error reading WebIdentityTokenFile from " . $this->tokenFile, + 0, + $exception + ); + } + + $assumeParams = [ + 'RoleArn' => $this->arn, + 'RoleSessionName' => $this->session, + 'WebIdentityToken' => $token + ]; + + try { + $result = $client->assumeRoleWithWebIdentity($assumeParams); + } catch (AwsException $e) { + if ($e->getAwsErrorCode() == 'InvalidIdentityToken') { + if ($this->authenticationAttempts < $this->retries) { + sleep((int) pow(1.2, $this->authenticationAttempts)); + } else { + throw new CredentialsException( + "InvalidIdentityToken, retries exhausted" + ); + } + } else { + throw new CredentialsException( + "Error assuming role from web identity credentials", + 0, + $e + ); + } + } catch (\Exception $e) { + throw new CredentialsException( + "Error retrieving web identity credentials: " . $e->getMessage() + . " (" . $e->getCode() . ")" + ); + } + $this->authenticationAttempts++; + } + + yield $this->client->createCredentials($result); + }); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Credentials/CredentialProvider.php b/vendor/aws/aws-sdk-php/src/Credentials/CredentialProvider.php new file mode 100644 index 0000000..47735a5 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/CredentialProvider.php @@ -0,0 +1,908 @@ +<?php +namespace Aws\Credentials; + +use Aws; +use Aws\Api\DateTimeResult; +use Aws\CacheInterface; +use Aws\Exception\CredentialsException; +use Aws\Sts\StsClient; +use GuzzleHttp\Promise; +/** + * Credential providers are functions that accept no arguments and return a + * promise that is fulfilled with an {@see \Aws\Credentials\CredentialsInterface} + * or rejected with an {@see \Aws\Exception\CredentialsException}. + * + * <code> + * use Aws\Credentials\CredentialProvider; + * $provider = CredentialProvider::defaultProvider(); + * // Returns a CredentialsInterface or throws. + * $creds = $provider()->wait(); + * </code> + * + * Credential providers can be composed to create credentials using conditional + * logic that can create different credentials in different environments. You + * can compose multiple providers into a single provider using + * {@see Aws\Credentials\CredentialProvider::chain}. This function accepts + * providers as variadic arguments and returns a new function that will invoke + * each provider until a successful set of credentials is returned. + * + * <code> + * // First try an INI file at this location. + * $a = CredentialProvider::ini(null, '/path/to/file.ini'); + * // Then try an INI file at this location. + * $b = CredentialProvider::ini(null, '/path/to/other-file.ini'); + * // Then try loading from environment variables. + * $c = CredentialProvider::env(); + * // Combine the three providers together. + * $composed = CredentialProvider::chain($a, $b, $c); + * // Returns a promise that is fulfilled with credentials or throws. + * $promise = $composed(); + * // Wait on the credentials to resolve. + * $creds = $promise->wait(); + * </code> + */ +class CredentialProvider +{ + const ENV_ARN = 'AWS_ROLE_ARN'; + const ENV_KEY = 'AWS_ACCESS_KEY_ID'; + const ENV_PROFILE = 'AWS_PROFILE'; + const ENV_ROLE_SESSION_NAME = 'AWS_ROLE_SESSION_NAME'; + const ENV_SECRET = 'AWS_SECRET_ACCESS_KEY'; + const ENV_SESSION = 'AWS_SESSION_TOKEN'; + const ENV_TOKEN_FILE = 'AWS_WEB_IDENTITY_TOKEN_FILE'; + const ENV_SHARED_CREDENTIALS_FILE = 'AWS_SHARED_CREDENTIALS_FILE'; + + /** + * Create a default credential provider that + * first checks for environment variables, + * then checks for assumed role via web identity, + * then checks for cached SSO credentials from the CLI, + * then check for credential_process in the "default" profile in ~/.aws/credentials, + * then checks for the "default" profile in ~/.aws/credentials, + * then for credential_process in the "default profile" profile in ~/.aws/config, + * then checks for "profile default" profile in ~/.aws/config (which is + * the default profile of AWS CLI), + * then tries to make a GET Request to fetch credentials if ECS environment variable is presented, + * finally checks for EC2 instance profile credentials. + * + * This provider is automatically wrapped in a memoize function that caches + * previously provided credentials. + * + * @param array $config Optional array of ecs/instance profile credentials + * provider options. + * + * @return callable + */ + public static function defaultProvider(array $config = []) + { + $cacheable = [ + 'web_identity', + 'sso', + 'process_credentials', + 'process_config', + 'ecs', + 'instance' + ]; + + $profileName = getenv(self::ENV_PROFILE) ?: 'default'; + + $defaultChain = [ + 'env' => self::env(), + 'web_identity' => self::assumeRoleWithWebIdentityCredentialProvider($config), + ]; + if ( + !isset($config['use_aws_shared_config_files']) + || $config['use_aws_shared_config_files'] !== false + ) { + $defaultChain['sso'] = self::sso( + 'profile '. $profileName, + self::getHomeDir() . '/.aws/config', + $config + ); + $defaultChain['process_credentials'] = self::process(); + $defaultChain['ini'] = self::ini(); + $defaultChain['process_config'] = self::process( + 'profile ' . $profileName, + self::getHomeDir() . '/.aws/config' + ); + $defaultChain['ini_config'] = self::ini( + 'profile '. $profileName, + self::getHomeDir() . '/.aws/config' + ); + } + + if (self::shouldUseEcs()) { + $defaultChain['ecs'] = self::ecsCredentials($config); + } else { + $defaultChain['instance'] = self::instanceProfile($config); + } + + if (isset($config['credentials']) + && $config['credentials'] instanceof CacheInterface + ) { + foreach ($cacheable as $provider) { + if (isset($defaultChain[$provider])) { + $defaultChain[$provider] = self::cache( + $defaultChain[$provider], + $config['credentials'], + 'aws_cached_' . $provider . '_credentials' + ); + } + } + } + + return self::memoize( + call_user_func_array( + [CredentialProvider::class, 'chain'], + array_values($defaultChain) + ) + ); + } + + /** + * Create a credential provider function from a set of static credentials. + * + * @param CredentialsInterface $creds + * + * @return callable + */ + public static function fromCredentials(CredentialsInterface $creds) + { + $promise = Promise\Create::promiseFor($creds); + + return function () use ($promise) { + return $promise; + }; + } + + /** + * Creates an aggregate credentials provider that invokes the provided + * variadic providers one after the other until a provider returns + * credentials. + * + * @return callable + */ + public static function chain() + { + $links = func_get_args(); + if (empty($links)) { + throw new \InvalidArgumentException('No providers in chain'); + } + + return function ($previousCreds = null) use ($links) { + /** @var callable $parent */ + $parent = array_shift($links); + $promise = $parent(); + while ($next = array_shift($links)) { + if ($next instanceof InstanceProfileProvider + && $previousCreds instanceof Credentials + ) { + $promise = $promise->otherwise( + function () use ($next, $previousCreds) {return $next($previousCreds);} + ); + } else { + $promise = $promise->otherwise($next); + } + } + return $promise; + }; + } + + /** + * Wraps a credential provider and caches previously provided credentials. + * + * Ensures that cached credentials are refreshed when they expire. + * + * @param callable $provider Credentials provider function to wrap. + * + * @return callable + */ + public static function memoize(callable $provider) + { + return function () use ($provider) { + static $result; + static $isConstant; + + // Constant credentials will be returned constantly. + if ($isConstant) { + return $result; + } + + // Create the initial promise that will be used as the cached value + // until it expires. + if (null === $result) { + $result = $provider(); + } + + // Return credentials that could expire and refresh when needed. + return $result + ->then(function (CredentialsInterface $creds) use ($provider, &$isConstant, &$result) { + // Determine if these are constant credentials. + if (!$creds->getExpiration()) { + $isConstant = true; + return $creds; + } + + // Refresh expired credentials. + if (!$creds->isExpired()) { + return $creds; + } + // Refresh the result and forward the promise. + return $result = $provider($creds); + }) + ->otherwise(function($reason) use (&$result) { + // Cleanup rejected promise. + $result = null; + return new Promise\RejectedPromise($reason); + }); + }; + } + + /** + * Wraps a credential provider and saves provided credentials in an + * instance of Aws\CacheInterface. Forwards calls when no credentials found + * in cache and updates cache with the results. + * + * @param callable $provider Credentials provider function to wrap + * @param CacheInterface $cache Cache to store credentials + * @param string|null $cacheKey (optional) Cache key to use + * + * @return callable + */ + public static function cache( + callable $provider, + CacheInterface $cache, + $cacheKey = null + ) { + $cacheKey = $cacheKey ?: 'aws_cached_credentials'; + + return function () use ($provider, $cache, $cacheKey) { + $found = $cache->get($cacheKey); + if ($found instanceof CredentialsInterface && !$found->isExpired()) { + return Promise\Create::promiseFor($found); + } + + return $provider() + ->then(function (CredentialsInterface $creds) use ( + $cache, + $cacheKey + ) { + $cache->set( + $cacheKey, + $creds, + null === $creds->getExpiration() ? + 0 : $creds->getExpiration() - time() + ); + + return $creds; + }); + }; + } + + /** + * Provider that creates credentials from environment variables + * AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN. + * + * @return callable + */ + public static function env() + { + return function () { + // Use credentials from environment variables, if available + $key = getenv(self::ENV_KEY); + $secret = getenv(self::ENV_SECRET); + if ($key && $secret) { + return Promise\Create::promiseFor( + new Credentials($key, $secret, getenv(self::ENV_SESSION) ?: NULL) + ); + } + + return self::reject('Could not find environment variable ' + . 'credentials in ' . self::ENV_KEY . '/' . self::ENV_SECRET); + }; + } + + /** + * Credential provider that creates credentials using instance profile + * credentials. + * + * @param array $config Array of configuration data. + * + * @return InstanceProfileProvider + * @see Aws\Credentials\InstanceProfileProvider for $config details. + */ + public static function instanceProfile(array $config = []) + { + return new InstanceProfileProvider($config); + } + + /** + * Credential provider that retrieves cached SSO credentials from the CLI + * + * @return callable + */ + public static function sso($ssoProfileName, $filename = null, $config = []) + { + $filename = $filename ?: (self::getHomeDir() . '/.aws/config'); + + return function () use ($ssoProfileName, $filename, $config) { + if (!@is_readable($filename)) { + return self::reject("Cannot read credentials from $filename"); + } + $profiles = self::loadProfiles($filename); + if (!isset($profiles[$ssoProfileName])) { + return self::reject("Profile {$ssoProfileName} does not exist in {$filename}."); + } + $ssoProfile = $profiles[$ssoProfileName]; + if (!empty($ssoProfile['sso_session'])) { + return self::reject( + "Profile {$ssoProfileName} contains an sso_session and will rely on" + . " the token provider instead of the legacy sso credential provider." + ); + } + if (empty($ssoProfile['sso_start_url']) + || empty($ssoProfile['sso_region']) + || empty($ssoProfile['sso_account_id']) + || empty($ssoProfile['sso_role_name']) + ) { + return self::reject( + "Profile {$ssoProfileName} in {$filename} must contain the following keys: " + . "sso_start_url, sso_region, sso_account_id, and sso_role_name." + ); + } + + $tokenLocation = self::getHomeDir() + . '/.aws/sso/cache/' + . sha1($ssoProfile['sso_start_url']) + . ".json"; + + if (!@is_readable($tokenLocation)) { + return self::reject("Unable to read token file at $tokenLocation"); + } + + $tokenData = json_decode(file_get_contents($tokenLocation), true); + if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { + return self::reject( + "Token file at {$tokenLocation} must contain an access token and an expiration" + ); + } + try { + $expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp(); + } catch (\Exception $e) { + return self::reject("Cached SSO credentials returned an invalid expiration"); + } + $now = time(); + if ($expiration < $now) { + return self::reject("Cached SSO credentials returned expired credentials"); + } + + $ssoClient = null; + if (empty($config['ssoClient'])) { + $ssoClient = new Aws\SSO\SSOClient([ + 'region' => $ssoProfile['sso_region'], + 'version' => '2019-06-10', + 'credentials' => false + ]); + } else { + $ssoClient = $config['ssoClient']; + } + $ssoResponse = $ssoClient->getRoleCredentials([ + 'accessToken' => $tokenData['accessToken'], + 'accountId' => $ssoProfile['sso_account_id'], + 'roleName' => $ssoProfile['sso_role_name'] + ]); + + $ssoCredentials = $ssoResponse['roleCredentials']; + return Promise\Create::promiseFor( + new Credentials( + $ssoCredentials['accessKeyId'], + $ssoCredentials['secretAccessKey'], + $ssoCredentials['sessionToken'], + $expiration + ) + ); + }; + } + + /** + * Credential provider that creates credentials using + * ecs credentials by a GET request, whose uri is specified + * by environment variable + * + * @param array $config Array of configuration data. + * + * @return EcsCredentialProvider + * @see Aws\Credentials\EcsCredentialProvider for $config details. + */ + public static function ecsCredentials(array $config = []) + { + return new EcsCredentialProvider($config); + } + + /** + * Credential provider that creates credentials using assume role + * + * @param array $config Array of configuration data + * @return callable + * @see Aws\Credentials\AssumeRoleCredentialProvider for $config details. + */ + public static function assumeRole(array $config=[]) + { + return new AssumeRoleCredentialProvider($config); + } + + /** + * Credential provider that creates credentials by assuming role from a + * Web Identity Token + * + * @param array $config Array of configuration data + * @return callable + * @see Aws\Credentials\AssumeRoleWithWebIdentityCredentialProvider for + * $config details. + */ + public static function assumeRoleWithWebIdentityCredentialProvider(array $config = []) + { + return function () use ($config) { + $arnFromEnv = getenv(self::ENV_ARN); + $tokenFromEnv = getenv(self::ENV_TOKEN_FILE); + $stsClient = isset($config['stsClient']) + ? $config['stsClient'] + : null; + $region = isset($config['region']) + ? $config['region'] + : null; + + if ($tokenFromEnv && $arnFromEnv) { + $sessionName = getenv(self::ENV_ROLE_SESSION_NAME) + ? getenv(self::ENV_ROLE_SESSION_NAME) + : null; + $provider = new AssumeRoleWithWebIdentityCredentialProvider([ + 'RoleArn' => $arnFromEnv, + 'WebIdentityTokenFile' => $tokenFromEnv, + 'SessionName' => $sessionName, + 'client' => $stsClient, + 'region' => $region + ]); + + return $provider(); + } + + $profileName = getenv(self::ENV_PROFILE) ?: 'default'; + if (isset($config['filename'])) { + $profiles = self::loadProfiles($config['filename']); + } else { + $profiles = self::loadDefaultProfiles(); + } + + if (isset($profiles[$profileName])) { + $profile = $profiles[$profileName]; + if (isset($profile['region'])) { + $region = $profile['region']; + } + if (isset($profile['web_identity_token_file']) + && isset($profile['role_arn']) + ) { + $sessionName = isset($profile['role_session_name']) + ? $profile['role_session_name'] + : null; + $provider = new AssumeRoleWithWebIdentityCredentialProvider([ + 'RoleArn' => $profile['role_arn'], + 'WebIdentityTokenFile' => $profile['web_identity_token_file'], + 'SessionName' => $sessionName, + 'client' => $stsClient, + 'region' => $region + ]); + + return $provider(); + } + } else { + return self::reject("Unknown profile: $profileName"); + } + return self::reject("No RoleArn or WebIdentityTokenFile specified"); + }; + } + + /** + * Credentials provider that creates credentials using an ini file stored + * in the current user's home directory. A source can be provided + * in this file for assuming a role using the credential_source config option. + * + * @param string|null $profile Profile to use. If not specified will use + * the "default" profile in "~/.aws/credentials". + * @param string|null $filename If provided, uses a custom filename rather + * than looking in the home directory. + * @param array|null $config If provided, may contain the following: + * preferStaticCredentials: If true, prefer static + * credentials to role_arn if both are present + * disableAssumeRole: If true, disable support for + * roles that assume an IAM role. If true and role profile + * is selected, an error is raised. + * stsClient: StsClient used to assume role specified in profile + * + * @return callable + */ + public static function ini($profile = null, $filename = null, array $config = []) + { + $filename = self::getFileName($filename); + $profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default'); + + return function () use ($profile, $filename, $config) { + $preferStaticCredentials = isset($config['preferStaticCredentials']) + ? $config['preferStaticCredentials'] + : false; + $disableAssumeRole = isset($config['disableAssumeRole']) + ? $config['disableAssumeRole'] + : false; + $stsClient = isset($config['stsClient']) ? $config['stsClient'] : null; + + if (!@is_readable($filename)) { + return self::reject("Cannot read credentials from $filename"); + } + $data = self::loadProfiles($filename); + if ($data === false) { + return self::reject("Invalid credentials file: $filename"); + } + if (!isset($data[$profile])) { + return self::reject("'$profile' not found in credentials file"); + } + + /* + In the CLI, the presence of both a role_arn and static credentials have + different meanings depending on how many profiles have been visited. For + the first profile processed, role_arn takes precedence over any static + credentials, but for all subsequent profiles, static credentials are + used if present, and only in their absence will the profile's + source_profile and role_arn keys be used to load another set of + credentials. This bool is intended to yield compatible behaviour in this + sdk. + */ + $preferStaticCredentialsToRoleArn = ($preferStaticCredentials + && isset($data[$profile]['aws_access_key_id']) + && isset($data[$profile]['aws_secret_access_key'])); + + if (isset($data[$profile]['role_arn']) + && !$preferStaticCredentialsToRoleArn + ) { + if ($disableAssumeRole) { + return self::reject( + "Role assumption profiles are disabled. " + . "Failed to load profile " . $profile); + } + return self::loadRoleProfile( + $data, + $profile, + $filename, + $stsClient, + $config + ); + } + + if (!isset($data[$profile]['aws_access_key_id']) + || !isset($data[$profile]['aws_secret_access_key']) + ) { + return self::reject("No credentials present in INI profile " + . "'$profile' ($filename)"); + } + + if (empty($data[$profile]['aws_session_token'])) { + $data[$profile]['aws_session_token'] + = isset($data[$profile]['aws_security_token']) + ? $data[$profile]['aws_security_token'] + : null; + } + + return Promise\Create::promiseFor( + new Credentials( + $data[$profile]['aws_access_key_id'], + $data[$profile]['aws_secret_access_key'], + $data[$profile]['aws_session_token'] + ) + ); + }; + } + + /** + * Credentials provider that creates credentials using a process configured in + * ini file stored in the current user's home directory. + * + * @param string|null $profile Profile to use. If not specified will use + * the "default" profile in "~/.aws/credentials". + * @param string|null $filename If provided, uses a custom filename rather + * than looking in the home directory. + * + * @return callable + */ + public static function process($profile = null, $filename = null) + { + $filename = self::getFileName($filename); + $profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default'); + + return function () use ($profile, $filename) { + if (!@is_readable($filename)) { + return self::reject("Cannot read process credentials from $filename"); + } + $data = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW); + if ($data === false) { + return self::reject("Invalid credentials file: $filename"); + } + if (!isset($data[$profile])) { + return self::reject("'$profile' not found in credentials file"); + } + if (!isset($data[$profile]['credential_process'])) { + return self::reject("No credential_process present in INI profile " + . "'$profile' ($filename)"); + } + + $credentialProcess = $data[$profile]['credential_process']; + $json = shell_exec($credentialProcess); + + $processData = json_decode($json, true); + + // Only support version 1 + if (isset($processData['Version'])) { + if ($processData['Version'] !== 1) { + return self::reject("credential_process does not return Version == 1"); + } + } + + if (!isset($processData['AccessKeyId']) + || !isset($processData['SecretAccessKey'])) + { + return self::reject("credential_process does not return valid credentials"); + } + + if (isset($processData['Expiration'])) { + try { + $expiration = new DateTimeResult($processData['Expiration']); + } catch (\Exception $e) { + return self::reject("credential_process returned invalid expiration"); + } + $now = new DateTimeResult(); + if ($expiration < $now) { + return self::reject("credential_process returned expired credentials"); + } + $expires = $expiration->getTimestamp(); + } else { + $expires = null; + } + + if (empty($processData['SessionToken'])) { + $processData['SessionToken'] = null; + } + + return Promise\Create::promiseFor( + new Credentials( + $processData['AccessKeyId'], + $processData['SecretAccessKey'], + $processData['SessionToken'], + $expires + ) + ); + }; + } + + /** + * Assumes role for profile that includes role_arn + * + * @return callable + */ + private static function loadRoleProfile( + $profiles, + $profileName, + $filename, + $stsClient, + $config = [] + ) { + $roleProfile = $profiles[$profileName]; + $roleArn = isset($roleProfile['role_arn']) ? $roleProfile['role_arn'] : ''; + $roleSessionName = isset($roleProfile['role_session_name']) + ? $roleProfile['role_session_name'] + : 'aws-sdk-php-' . round(microtime(true) * 1000); + + if ( + empty($roleProfile['source_profile']) + == empty($roleProfile['credential_source']) + ) { + return self::reject("Either source_profile or credential_source must be set " . + "using profile " . $profileName . ", but not both." + ); + } + + $sourceProfileName = ""; + if (!empty($roleProfile['source_profile'])) { + $sourceProfileName = $roleProfile['source_profile']; + if (!isset($profiles[$sourceProfileName])) { + return self::reject("source_profile " . $sourceProfileName + . " using profile " . $profileName . " does not exist" + ); + } + if (isset($config['visited_profiles']) && + in_array($roleProfile['source_profile'], $config['visited_profiles']) + ) { + return self::reject("Circular source_profile reference found."); + } + $config['visited_profiles'] [] = $roleProfile['source_profile']; + } else { + if (empty($roleArn)) { + return self::reject( + "A role_arn must be provided with credential_source in " . + "file {$filename} under profile {$profileName} " + ); + } + } + + if (empty($stsClient)) { + $sourceRegion = isset($profiles[$sourceProfileName]['region']) + ? $profiles[$sourceProfileName]['region'] + : 'us-east-1'; + $config['preferStaticCredentials'] = true; + $sourceCredentials = null; + if (!empty($roleProfile['source_profile'])){ + $sourceCredentials = call_user_func( + CredentialProvider::ini($sourceProfileName, $filename, $config) + )->wait(); + } else { + $sourceCredentials = self::getCredentialsFromSource( + $profileName, + $filename + ); + } + $stsClient = new StsClient([ + 'credentials' => $sourceCredentials, + 'region' => $sourceRegion, + 'version' => '2011-06-15', + ]); + } + + $result = $stsClient->assumeRole([ + 'RoleArn' => $roleArn, + 'RoleSessionName' => $roleSessionName + ]); + + $credentials = $stsClient->createCredentials($result); + return Promise\Create::promiseFor($credentials); + } + + /** + * Gets the environment's HOME directory if available. + * + * @return null|string + */ + private static function getHomeDir() + { + // On Linux/Unix-like systems, use the HOME environment variable + if ($homeDir = getenv('HOME')) { + return $homeDir; + } + + // Get the HOMEDRIVE and HOMEPATH values for Windows hosts + $homeDrive = getenv('HOMEDRIVE'); + $homePath = getenv('HOMEPATH'); + + return ($homeDrive && $homePath) ? $homeDrive . $homePath : null; + } + + /** + * Gets profiles from specified $filename, or default ini files. + */ + private static function loadProfiles($filename) + { + $profileData = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW); + + // If loading .aws/credentials, also load .aws/config when AWS_SDK_LOAD_NONDEFAULT_CONFIG is set + if ($filename === self::getHomeDir() . '/.aws/credentials' + && getenv('AWS_SDK_LOAD_NONDEFAULT_CONFIG') + ) { + $configFilename = self::getHomeDir() . '/.aws/config'; + $configProfileData = \Aws\parse_ini_file($configFilename, true, INI_SCANNER_RAW); + foreach ($configProfileData as $name => $profile) { + // standardize config profile names + $name = str_replace('profile ', '', $name); + if (!isset($profileData[$name])) { + $profileData[$name] = $profile; + } + } + } + + return $profileData; + } + + /** + * Gets profiles from ~/.aws/credentials and ~/.aws/config ini files + */ + private static function loadDefaultProfiles() { + $profiles = []; + $credFile = self::getHomeDir() . '/.aws/credentials'; + $configFile = self::getHomeDir() . '/.aws/config'; + if (file_exists($credFile)) { + $profiles = \Aws\parse_ini_file($credFile, true, INI_SCANNER_RAW); + } + + if (file_exists($configFile)) { + $configProfileData = \Aws\parse_ini_file($configFile, true, INI_SCANNER_RAW); + foreach ($configProfileData as $name => $profile) { + // standardize config profile names + $name = str_replace('profile ', '', $name); + if (!isset($profiles[$name])) { + $profiles[$name] = $profile; + } + } + } + + return $profiles; + } + + public static function getCredentialsFromSource( + $profileName = '', + $filename = '', + $config = [] + ) { + $data = self::loadProfiles($filename); + $credentialSource = !empty($data[$profileName]['credential_source']) + ? $data[$profileName]['credential_source'] + : null; + $credentialsPromise = null; + + switch ($credentialSource) { + case 'Environment': + $credentialsPromise = self::env(); + break; + case 'Ec2InstanceMetadata': + $credentialsPromise = self::instanceProfile($config); + break; + case 'EcsContainer': + $credentialsPromise = self::ecsCredentials($config); + break; + default: + throw new CredentialsException( + "Invalid credential_source found in config file: {$credentialSource}. Valid inputs " + . "include Environment, Ec2InstanceMetadata, and EcsContainer." + ); + } + + $credentialsResult = null; + try { + $credentialsResult = $credentialsPromise()->wait(); + } catch (\Exception $reason) { + return self::reject( + "Unable to successfully retrieve credentials from the source specified in the" + . " credentials file: {$credentialSource}; failure message was: " + . $reason->getMessage() + ); + } + return function () use ($credentialsResult) { + return Promise\Create::promiseFor($credentialsResult); + }; + } + + private static function reject($msg) + { + return new Promise\RejectedPromise(new CredentialsException($msg)); + } + + /** + * @param $filename + * @return string + */ + private static function getFileName($filename) + { + if (!isset($filename)) { + $filename = getenv(self::ENV_SHARED_CREDENTIALS_FILE) ?: + (self::getHomeDir() . '/.aws/credentials'); + } + return $filename; + } + + /** + * @return boolean + */ + public static function shouldUseEcs() + { + //Check for relative uri. if not, then full uri. + //fall back to server for each as getenv is not thread-safe. + return !empty(getenv(EcsCredentialProvider::ENV_URI)) + || !empty($_SERVER[EcsCredentialProvider::ENV_URI]) + || !empty(getenv(EcsCredentialProvider::ENV_FULL_URI)) + || !empty($_SERVER[EcsCredentialProvider::ENV_FULL_URI]); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Credentials/Credentials.php b/vendor/aws/aws-sdk-php/src/Credentials/Credentials.php new file mode 100644 index 0000000..aca605a --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/Credentials.php @@ -0,0 +1,113 @@ +<?php +namespace Aws\Credentials; + +/** + * Basic implementation of the AWS Credentials interface that allows callers to + * pass in the AWS Access Key and AWS Secret Access Key in the constructor. + */ +class Credentials implements CredentialsInterface, \Serializable +{ + private $key; + private $secret; + private $token; + private $expires; + + /** + * Constructs a new BasicAWSCredentials object, with the specified AWS + * access key and AWS secret key + * + * @param string $key AWS access key ID + * @param string $secret AWS secret access key + * @param string $token Security token to use + * @param int $expires UNIX timestamp for when credentials expire + */ + public function __construct($key, $secret, $token = null, $expires = null) + { + $this->key = trim($key); + $this->secret = trim($secret); + $this->token = $token; + $this->expires = $expires; + } + + public static function __set_state(array $state) + { + return new self( + $state['key'], + $state['secret'], + $state['token'], + $state['expires'] + ); + } + + public function getAccessKeyId() + { + return $this->key; + } + + public function getSecretKey() + { + return $this->secret; + } + + public function getSecurityToken() + { + return $this->token; + } + + public function getExpiration() + { + return $this->expires; + } + + public function isExpired() + { + return $this->expires !== null && time() >= $this->expires; + } + + public function toArray() + { + return [ + 'key' => $this->key, + 'secret' => $this->secret, + 'token' => $this->token, + 'expires' => $this->expires + ]; + } + + public function serialize() + { + return json_encode($this->__serialize()); + } + + public function unserialize($serialized) + { + $data = json_decode($serialized, true); + + $this->__unserialize($data); + } + + public function __serialize() + { + return $this->toArray(); + } + + public function __unserialize($data) + { + $this->key = $data['key']; + $this->secret = $data['secret']; + $this->token = $data['token']; + $this->expires = $data['expires']; + } + + public function extendExpiration() { + $extension = mt_rand(5, 10); + $this->expires = time() + $extension * 60; + + $message = <<<EOT +Attempting credential expiration extension due to a credential service +availability issue. A refresh of these credentials will be attempted again +after {$extension} minutes.\n +EOT; + error_log($message); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Credentials/CredentialsInterface.php b/vendor/aws/aws-sdk-php/src/Credentials/CredentialsInterface.php new file mode 100644 index 0000000..86fac9d --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/CredentialsInterface.php @@ -0,0 +1,52 @@ +<?php +namespace Aws\Credentials; + +/** + * Provides access to the AWS credentials used for accessing AWS services: AWS + * access key ID, secret access key, and security token. These credentials are + * used to securely sign requests to AWS services. + */ +interface CredentialsInterface +{ + /** + * Returns the AWS access key ID for this credentials object. + * + * @return string + */ + public function getAccessKeyId(); + + /** + * Returns the AWS secret access key for this credentials object. + * + * @return string + */ + public function getSecretKey(); + + /** + * Get the associated security token if available + * + * @return string|null + */ + public function getSecurityToken(); + + /** + * Get the UNIX timestamp in which the credentials will expire + * + * @return int|null + */ + public function getExpiration(); + + /** + * Check if the credentials are expired + * + * @return bool + */ + public function isExpired(); + + /** + * Converts the credentials to an associative array. + * + * @return array + */ + public function toArray(); +} diff --git a/vendor/aws/aws-sdk-php/src/Credentials/EcsCredentialProvider.php b/vendor/aws/aws-sdk-php/src/Credentials/EcsCredentialProvider.php new file mode 100644 index 0000000..5c61a11 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/EcsCredentialProvider.php @@ -0,0 +1,134 @@ +<?php +namespace Aws\Credentials; + +use Aws\Exception\CredentialsException; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Promise\PromiseInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Credential provider that fetches credentials with GET request. + * ECS environment variable is used in constructing request URI. + */ +class EcsCredentialProvider +{ + const SERVER_URI = 'http://169.254.170.2'; + const ENV_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; + const ENV_FULL_URI = "AWS_CONTAINER_CREDENTIALS_FULL_URI"; + const ENV_AUTH_TOKEN = "AWS_CONTAINER_AUTHORIZATION_TOKEN"; + const ENV_TIMEOUT = 'AWS_METADATA_SERVICE_TIMEOUT'; + + /** @var callable */ + private $client; + + /** @var float|mixed */ + private $timeout; + + /** + * The constructor accepts following options: + * - timeout: (optional) Connection timeout, in seconds, default 1.0 + * - client: An EcsClient to make request from + * + * @param array $config Configuration options + */ + public function __construct(array $config = []) + { + $timeout = getenv(self::ENV_TIMEOUT); + + if (!$timeout) { + $timeout = isset($_SERVER[self::ENV_TIMEOUT]) + ? $_SERVER[self::ENV_TIMEOUT] + : (isset($config['timeout']) ? $config['timeout'] : 1.0); + } + + $this->timeout = (float) $timeout; + $this->client = isset($config['client']) + ? $config['client'] + : \Aws\default_http_handler(); + } + + /** + * Load ECS credentials + * + * @return PromiseInterface + */ + public function __invoke() + { + $client = $this->client; + $request = new Request('GET', self::getEcsUri()); + + $headers = $this->setHeaderForAuthToken(); + return $client( + $request, + [ + 'timeout' => $this->timeout, + 'proxy' => '', + 'headers' => $headers + ] + )->then(function (ResponseInterface $response) { + $result = $this->decodeResult((string) $response->getBody()); + return new Credentials( + $result['AccessKeyId'], + $result['SecretAccessKey'], + $result['Token'], + strtotime($result['Expiration']) + ); + })->otherwise(function ($reason) { + $reason = is_array($reason) ? $reason['exception'] : $reason; + $msg = $reason->getMessage(); + throw new CredentialsException( + "Error retrieving credential from ECS ($msg)" + ); + }); + } + + private function getEcsAuthToken() + { + return getenv(self::ENV_AUTH_TOKEN); + } + + public function setHeaderForAuthToken(){ + $authToken = self::getEcsAuthToken(); + $headers = []; + if(!empty($authToken)) + $headers = ['Authorization' => $authToken]; + + return $headers; + } + + /** + * Fetch credential URI from ECS environment variable + * + * @return string Returns ECS URI + */ + private function getEcsUri() + { + $credsUri = getenv(self::ENV_URI); + + if ($credsUri === false) { + $credsUri = isset($_SERVER[self::ENV_URI]) ? $_SERVER[self::ENV_URI] : ''; + } + + if(empty($credsUri)){ + $credFullUri = getenv(self::ENV_FULL_URI); + if($credFullUri === false){ + $credFullUri = isset($_SERVER[self::ENV_FULL_URI]) ? $_SERVER[self::ENV_FULL_URI] : ''; + } + + if(!empty($credFullUri)) + return $credFullUri; + } + + return self::SERVER_URI . $credsUri; + } + + private function decodeResult($response) + { + $result = json_decode($response, true); + + if (!isset($result['AccessKeyId'])) { + throw new CredentialsException('Unexpected ECS credential value'); + } + return $result; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Credentials/InstanceProfileProvider.php b/vendor/aws/aws-sdk-php/src/Credentials/InstanceProfileProvider.php new file mode 100644 index 0000000..0f1fcb1 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Credentials/InstanceProfileProvider.php @@ -0,0 +1,299 @@ +<?php +namespace Aws\Credentials; + +use Aws\Exception\CredentialsException; +use Aws\Exception\InvalidJsonException; +use Aws\Sdk; +use GuzzleHttp\Exception\TransferException; +use GuzzleHttp\Promise; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Promise\PromiseInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Credential provider that provides credentials from the EC2 metadata service. + */ +class InstanceProfileProvider +{ + const SERVER_URI = 'http://169.254.169.254/latest/'; + const CRED_PATH = 'meta-data/iam/security-credentials/'; + const TOKEN_PATH = 'api/token'; + + const ENV_DISABLE = 'AWS_EC2_METADATA_DISABLED'; + const ENV_TIMEOUT = 'AWS_METADATA_SERVICE_TIMEOUT'; + const ENV_RETRIES = 'AWS_METADATA_SERVICE_NUM_ATTEMPTS'; + + /** @var string */ + private $profile; + + /** @var callable */ + private $client; + + /** @var int */ + private $retries; + + /** @var int */ + private $attempts; + + /** @var float|mixed */ + private $timeout; + + /** @var bool */ + private $secureMode = true; + + /** + * The constructor accepts the following options: + * + * - timeout: Connection timeout, in seconds. + * - profile: Optional EC2 profile name, if known. + * - retries: Optional number of retries to be attempted. + * + * @param array $config Configuration options. + */ + public function __construct(array $config = []) + { + $this->timeout = (float) getenv(self::ENV_TIMEOUT) ?: (isset($config['timeout']) ? $config['timeout'] : 1.0); + $this->profile = isset($config['profile']) ? $config['profile'] : null; + $this->retries = (int) getenv(self::ENV_RETRIES) ?: (isset($config['retries']) ? $config['retries'] : 3); + $this->client = isset($config['client']) + ? $config['client'] // internal use only + : \Aws\default_http_handler(); + } + + /** + * Loads instance profile credentials. + * + * @return PromiseInterface + */ + public function __invoke($previousCredentials = null) + { + $this->attempts = 0; + return Promise\Coroutine::of(function () use ($previousCredentials) { + + // Retrieve token or switch out of secure mode + $token = null; + while ($this->secureMode && is_null($token)) { + try { + $token = (yield $this->request( + self::TOKEN_PATH, + 'PUT', + [ + 'x-aws-ec2-metadata-token-ttl-seconds' => 21600 + ] + )); + } catch (TransferException $e) { + if ($this->getExceptionStatusCode($e) === 500 + && $previousCredentials instanceof Credentials + ) { + goto generateCredentials; + } + else if (!method_exists($e, 'getResponse') + || empty($e->getResponse()) + || !in_array( + $e->getResponse()->getStatusCode(), + [400, 500, 502, 503, 504] + ) + ) { + $this->secureMode = false; + } else { + $this->handleRetryableException( + $e, + [], + $this->createErrorMessage( + 'Error retrieving metadata token' + ) + ); + } + } + $this->attempts++; + } + + // Set token header only for secure mode + $headers = []; + if ($this->secureMode) { + $headers = [ + 'x-aws-ec2-metadata-token' => $token + ]; + } + + // Retrieve profile + while (!$this->profile) { + try { + $this->profile = (yield $this->request( + self::CRED_PATH, + 'GET', + $headers + )); + } catch (TransferException $e) { + // 401 indicates insecure flow not supported, switch to + // attempting secure mode for subsequent calls + if (!empty($this->getExceptionStatusCode($e)) + && $this->getExceptionStatusCode($e) === 401 + ) { + $this->secureMode = true; + } + $this->handleRetryableException( + $e, + [ 'blacklist' => [401, 403] ], + $this->createErrorMessage($e->getMessage()) + ); + } + + $this->attempts++; + } + + // Retrieve credentials + $result = null; + while ($result == null) { + try { + $json = (yield $this->request( + self::CRED_PATH . $this->profile, + 'GET', + $headers + )); + $result = $this->decodeResult($json); + } catch (InvalidJsonException $e) { + $this->handleRetryableException( + $e, + [ 'blacklist' => [401, 403] ], + $this->createErrorMessage( + 'Invalid JSON response, retries exhausted' + ) + ); + } catch (TransferException $e) { + // 401 indicates insecure flow not supported, switch to + // attempting secure mode for subsequent calls + if (($this->getExceptionStatusCode($e) === 500 + || strpos($e->getMessage(), "cURL error 28") !== false) + && $previousCredentials instanceof Credentials + ) { + goto generateCredentials; + } else if (!empty($this->getExceptionStatusCode($e)) + && $this->getExceptionStatusCode($e) === 401 + ) { + $this->secureMode = true; + } + $this->handleRetryableException( + $e, + [ 'blacklist' => [401, 403] ], + $this->createErrorMessage($e->getMessage()) + ); + } + $this->attempts++; + } + generateCredentials: + + if (!isset($result)) { + $credentials = $previousCredentials; + } else { + $credentials = new Credentials( + $result['AccessKeyId'], + $result['SecretAccessKey'], + $result['Token'], + strtotime($result['Expiration']) + ); + } + + if ($credentials->isExpired()) { + $credentials->extendExpiration(); + } + + yield $credentials; + }); + } + + /** + * @param string $url + * @param string $method + * @param array $headers + * @return PromiseInterface Returns a promise that is fulfilled with the + * body of the response as a string. + */ + private function request($url, $method = 'GET', $headers = []) + { + $disabled = getenv(self::ENV_DISABLE) ?: false; + if (strcasecmp($disabled, 'true') === 0) { + throw new CredentialsException( + $this->createErrorMessage('EC2 metadata service access disabled') + ); + } + + $fn = $this->client; + $request = new Request($method, self::SERVER_URI . $url); + $userAgent = 'aws-sdk-php/' . Sdk::VERSION; + if (defined('HHVM_VERSION')) { + $userAgent .= ' HHVM/' . HHVM_VERSION; + } + $userAgent .= ' ' . \Aws\default_user_agent(); + $request = $request->withHeader('User-Agent', $userAgent); + foreach ($headers as $key => $value) { + $request = $request->withHeader($key, $value); + } + + return $fn($request, ['timeout' => $this->timeout]) + ->then(function (ResponseInterface $response) { + return (string) $response->getBody(); + })->otherwise(function (array $reason) { + $reason = $reason['exception']; + if ($reason instanceof TransferException) { + throw $reason; + } + $msg = $reason->getMessage(); + throw new CredentialsException( + $this->createErrorMessage($msg) + ); + }); + } + + private function handleRetryableException( + \Exception $e, + $retryOptions, + $message + ) { + $isRetryable = true; + if (!empty($status = $this->getExceptionStatusCode($e)) + && isset($retryOptions['blacklist']) + && in_array($status, $retryOptions['blacklist']) + ) { + $isRetryable = false; + } + if ($isRetryable && $this->attempts < $this->retries) { + sleep((int) pow(1.2, $this->attempts)); + } else { + throw new CredentialsException($message); + } + } + + private function getExceptionStatusCode(\Exception $e) + { + if (method_exists($e, 'getResponse') + && !empty($e->getResponse()) + ) { + return $e->getResponse()->getStatusCode(); + } + return null; + } + + private function createErrorMessage($previous) + { + return "Error retrieving credentials from the instance profile " + . "metadata service. ({$previous})"; + } + + private function decodeResult($response) + { + $result = json_decode($response, true); + + if (json_last_error() > 0) { + throw new InvalidJsonException(); + } + + if ($result['Code'] !== 'Success') { + throw new CredentialsException('Unexpected instance profile ' + . 'response code: ' . $result['Code']); + } + + return $result; + } +} |