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/Token/SsoTokenProvider.php |
initial
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Token/SsoTokenProvider.php')
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/SsoTokenProvider.php | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Token/SsoTokenProvider.php b/vendor/aws/aws-sdk-php/src/Token/SsoTokenProvider.php new file mode 100644 index 0000000..7d8f4b4 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/SsoTokenProvider.php @@ -0,0 +1,213 @@ +<?php +namespace Aws\Token; + +use Aws\Exception\TokenException; +use GuzzleHttp\Promise; + +/** + * Token that comes from the SSO provider + */ +class SsoTokenProvider implements RefreshableTokenProviderInterface +{ + use ParsesIniTrait; + + const ENV_PROFILE = 'AWS_PROFILE'; + + private $ssoProfileName; + private $filename; + private $ssoOidcClient; + + /** + * Constructs a new SSO token object, with the specified AWS + * token + * + * @param string $token Security token to use + * @param int $expires UNIX timestamp for when the token expires + */ + public function __construct($ssoProfileName, $filename = null, $ssoOidcClient = null) { + $profileName = getenv(self::ENV_PROFILE) ?: 'default'; + $this->ssoProfileName = !empty($ssoProfileName) ? $ssoProfileName : $profileName; + $this->filename = !empty($filename) + ? $filename : + self::getHomeDir() . '/.aws/config'; + $this->ssoOidcClient = $ssoOidcClient; + } + + /* + * Loads cached sso credentials + * + * @return PromiseInterface + */ + public function __invoke() + { + return Promise\Coroutine::of(function () { + if (!@is_readable($this->filename)) { + throw new TokenException("Cannot read token from $this->filename"); + } + $profiles = self::loadProfiles($this->filename); + if (!isset($profiles[$this->ssoProfileName])) { + throw new TokenException("Profile {$this->ssoProfileName} does not exist in {$this->filename}."); + } + $ssoProfile = $profiles[$this->ssoProfileName]; + if (empty($ssoProfile['sso_session'])) { + throw new TokenException( + "Profile {$this->ssoProfileName} in {$this->filename} must contain an sso_session." + ); + } + + $sessionProfileName = 'sso-session ' . $ssoProfile['sso_session']; + if (empty($profiles[$sessionProfileName])) { + throw new TokenException( + "Profile {$this->ssoProfileName} does not exist in {$this->filename}" + ); + } + + $sessionProfileData = $profiles[$sessionProfileName]; + if (empty($sessionProfileData['sso_start_url']) + || empty($sessionProfileData['sso_region']) + ) { + throw new TokenException( + "Profile {$this->ssoProfileName} in {$this->filename} must contain the following keys: " + . "sso_start_url and sso_region." + ); + } + + $tokenLocation = self::getTokenLocation($ssoProfile['sso_session']); + if (!@is_readable($tokenLocation)) { + throw new TokenException("Unable to read token file at $tokenLocation"); + } + $tokenData = $this->getTokenData($tokenLocation); + $this->validateTokenData($tokenLocation, $tokenData); + yield new SsoToken( + $tokenData['accessToken'], + $tokenData['expiresAt'], + isset($tokenData['refreshToken']) ? $tokenData['refreshToken'] : null, + isset($tokenData['clientId']) ? $tokenData['clientId'] : null, + isset($tokenData['clientSecret']) ? $tokenData['clientSecret'] : null, + isset($tokenData['registrationExpiresAt']) ? $tokenData['registrationExpiresAt'] : null, + isset($tokenData['region']) ? $tokenData['region'] : null, + isset($tokenData['startUrl']) ? $tokenData['startUrl'] : null + ); + }); + } + + /** + * Refreshes the token + * @return mixed|null + */ + public function refresh() { + try { + //try to reload from disk + $token = $this(); + if ( + $token instanceof SsoToken + && !$token->shouldAttemptRefresh() + ) { + return $token; + } + } finally { + //if reload from disk fails, try refreshing + $tokenLocation = self::getTokenLocation($this->ssoProfileName); + $tokenData = $this->getTokenData($tokenLocation); + if ( + empty($this->ssoOidcClient) + || empty($tokenData['startUrl']) + ) { + throw new TokenException( + "Cannot refresh this token without an 'ssooidcClient' " + . "and a 'start_url'" + ); + } + $response = $this->ssoOidcClient->createToken([ + 'clientId' => $tokenData['clientId'], + 'clientSecret' => $tokenData['clientSecret'], + 'grantType' => 'refresh_token', // REQUIRED + 'refreshToken' => $tokenData['refreshToken'], + ]); + if ($response['@metadata']['statusCode'] == 200) { + $tokenData['accessToken'] = $response['accessToken']; + $tokenData['expiresAt'] = time () + $response['expiresIn']; + $tokenData['refreshToken'] = $response['refreshToken']; + $token = new SsoToken( + $tokenData['accessToken'], + $tokenData['expiresAt'], + $tokenData['refreshToken'], + isset($tokenData['clientId']) ? $tokenData['clientId'] : null, + isset($tokenData['clientSecret']) ? $tokenData['clientSecret'] : null, + isset($tokenData['registrationExpiresAt']) ? $tokenData['registrationExpiresAt'] : null, + isset($tokenData['region']) ? $tokenData['region'] : null, + isset($tokenData['startUrl']) ? $tokenData['startUrl'] : null ); + + $this->writeNewTokenDataToDisk($tokenData, $tokenLocation); + + return $token; + } + } + } + + public function shouldAttemptRefresh() + { + $tokenLocation = self::getTokenLocation($this->ssoProfileName); + $tokenData = $this->getTokenData($tokenLocation); + return strtotime("-10 minutes") >= strtotime($tokenData['expiresAt']); + } + + /** + * @param $sso_session + * @return string + */ + public static function getTokenLocation($sso_session) + { + return self::getHomeDir() + . '/.aws/sso/cache/' + . utf8_encode(sha1($sso_session)) + . ".json"; + } + + /** + * @param $tokenLocation + * @return array + */ + function getTokenData($tokenLocation) + { + return json_decode(file_get_contents($tokenLocation), true); + } + + /** + * @param $tokenData + * @param $tokenLocation + * @return mixed + */ + private function validateTokenData($tokenLocation, $tokenData) + { + if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) { + throw new TokenException( + "Token file at {$tokenLocation} must contain an access token and an expiration" + ); + } + + try { + $expiration = strtotime($tokenData['expiresAt']); + } catch (\Exception $e) { + throw new TokenException("Cached SSO token returned an invalid expiration"); + } + if ($expiration > time()) { + throw new TokenException("Cached SSO token returned an expired token"); + } + return $tokenData; + } + + /** + * @param array $tokenData + * @param string $tokenLocation + * @return void + */ + private function writeNewTokenDataToDisk(array $tokenData, $tokenLocation) + { + $tokenData['expiresAt'] = gmdate( + 'Y-m-d\TH:i:s\Z', + $tokenData['expiresAt'] + ); + file_put_contents($tokenLocation, json_encode(array_filter($tokenData))); + } +} |