| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 | <?phpnamespace 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);        });    }}
 |