| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991 | <?phpnamespace 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(                $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 = 'default',                               $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])) {                $ssoProfile = $profiles[$ssoProfileName];            } elseif (isset($profiles['profile ' . $ssoProfileName])) {                $ssoProfileName = 'profile ' . $ssoProfileName;                $ssoProfile = $profiles[$ssoProfileName];            } else {                return self::reject("Profile {$ssoProfileName} does not exist in {$filename}.");            }            if (!empty($ssoProfile['sso_session'])) {                return CredentialProvider::getSsoCredentials($profiles, $ssoProfileName, $filename, $config);            } else {                return CredentialProvider::getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config);            }        };    }    /**     * 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]);    }    /**     * @param $profiles     * @param $ssoProfileName     * @param $filename     * @param $config     * @return Promise\PromiseInterface     */    private static function getSsoCredentials($profiles, $ssoProfileName, $filename, $config)    {        if (empty($config['ssoOidcClient'])) {            $ssoProfile = $profiles[$ssoProfileName];            $sessionName = $ssoProfile['sso_session'];            if (empty($profiles['sso-session ' . $sessionName])) {                return self::reject(                    "Could not find sso-session {$sessionName} in {$filename}"                );            }            $ssoSession = $profiles['sso-session ' . $ssoProfile['sso_session']];            $ssoOidcClient = new Aws\SSOOIDC\SSOOIDCClient([                'region' => $ssoSession['sso_region'],                'version' => '2019-06-10',                'credentials' => false            ]);        } else {            $ssoOidcClient = $config['ssoClient'];        }        $tokenPromise = new Aws\Token\SsoTokenProvider(            $ssoProfileName,            $filename,            $ssoOidcClient        );        $token = $tokenPromise()->wait();        $ssoCredentials = CredentialProvider::getCredentialsFromSsoService(            $ssoProfile,            $ssoSession['sso_region'],            $token->getToken(),            $config        );        $expiration = $ssoCredentials['expiration'];        return Promise\Create::promiseFor(            new Credentials(                $ssoCredentials['accessKeyId'],                $ssoCredentials['secretAccessKey'],                $ssoCredentials['sessionToken'],                $expiration            )        );    }    /**     * @param $profiles     * @param $ssoProfileName     * @param $filename     * @param $config     * @return Promise\PromiseInterface     */    private static function getSsoCredentialsLegacy($profiles, $ssoProfileName, $filename, $config)    {        $ssoProfile = $profiles[$ssoProfileName];        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");        }        $ssoCredentials = CredentialProvider::getCredentialsFromSsoService(            $ssoProfile,            $ssoProfile['sso_region'],            $tokenData['accessToken'],            $config        );        return Promise\Create::promiseFor(            new Credentials(                $ssoCredentials['accessKeyId'],                $ssoCredentials['secretAccessKey'],                $ssoCredentials['sessionToken'],                $expiration            )        );    }    /**     * @param array $ssoProfile     * @param string $clientRegion     * @param string $accessToken     * @param array $config     * @return array|null     */    private static function getCredentialsFromSsoService($ssoProfile, $clientRegion, $accessToken, $config)    {        if (empty($config['ssoClient'])) {            $ssoClient = new Aws\SSO\SSOClient([                'region' => $clientRegion,                'version' => '2019-06-10',                'credentials' => false            ]);        } else {            $ssoClient = $config['ssoClient'];        }        $ssoResponse = $ssoClient->getRoleCredentials([            'accessToken' => $accessToken,            'accountId' => $ssoProfile['sso_account_id'],            'roleName' => $ssoProfile['sso_role_name']        ]);        $ssoCredentials = $ssoResponse['roleCredentials'];        return $ssoCredentials;    }}
 |