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 |
initial
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Token')
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/BearerTokenAuthorization.php | 33 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/ParsesIniTrait.php | 44 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/RefreshableTokenProviderInterface.php | 23 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/SsoToken.php | 108 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/SsoTokenProvider.php | 213 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/Token.php | 110 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/TokenAuthorization.php | 24 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/TokenInterface.php | 36 | ||||
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Token/TokenProvider.php | 268 |
9 files changed, 859 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Token/BearerTokenAuthorization.php b/vendor/aws/aws-sdk-php/src/Token/BearerTokenAuthorization.php new file mode 100644 index 0000000..7d830f3 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/BearerTokenAuthorization.php @@ -0,0 +1,33 @@ +<?php +namespace Aws\Token; + +use InvalidArgumentException; +use Psr\Http\Message\RequestInterface; + +/** + * Interface used to provide interchangeable strategies for adding authorization + * to requests using the various AWS signature protocols. + */ +class BearerTokenAuthorization implements TokenAuthorization +{ + /** + * Adds the specified token to a request by adding the required headers. + * + * @param RequestInterface $request Request to sign + * @param TokenInterface $token Token + * + * @return RequestInterface Returns the modified request. + */ + public function authorizeRequest( + RequestInterface $request, + TokenInterface $token + ) { + if (empty($token) || empty($token->getToken())) { + throw new InvalidArgumentException( + "Cannot authorize a request with an empty token" + ); + } + $accessToken = $token->getToken(); + return $request->withHeader('Authorization', "Bearer {$accessToken}"); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Token/ParsesIniTrait.php b/vendor/aws/aws-sdk-php/src/Token/ParsesIniTrait.php new file mode 100644 index 0000000..64afdb5 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/ParsesIniTrait.php @@ -0,0 +1,44 @@ +<?php +namespace Aws\Token; + +trait ParsesIniTrait +{ + /** + * Gets profiles from specified $filename, or default ini files. + */ + private static function loadProfiles($filename) + { + $profileData = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW); + + $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 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; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Token/RefreshableTokenProviderInterface.php b/vendor/aws/aws-sdk-php/src/Token/RefreshableTokenProviderInterface.php new file mode 100644 index 0000000..4c88f3f --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/RefreshableTokenProviderInterface.php @@ -0,0 +1,23 @@ +<?php +namespace Aws\Token; + +/** + * Provides access to an AWS token used for accessing AWS services + * + */ +interface RefreshableTokenProviderInterface +{ + /** + * Attempts to refresh this token object + * + * @return Token | Exception + */ + public function refresh(); + + /** + * Check if a refresh should be attempted + * + * @return boolean + */ + public function shouldAttemptRefresh(); +} diff --git a/vendor/aws/aws-sdk-php/src/Token/SsoToken.php b/vendor/aws/aws-sdk-php/src/Token/SsoToken.php new file mode 100644 index 0000000..4b453ef --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/SsoToken.php @@ -0,0 +1,108 @@ +<?php +namespace Aws\Token; + +/** + * Token that comes from the SSO provider + */ +class SsoToken extends Token +{ + private $refreshToken; + private $clientId; + private $clientSecret; + private $registrationExpiresAt; + private $region; + private $startUrl; + + /** + * 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 + * @param int $refreshToken An opaque string returned by the sso-oidc service + * @param int $clientId The client ID generated when performing the registration portion of the OIDC authorization flow + * @param int $clientSecret The client secret generated when performing the registration portion of the OIDC authorization flow + * @param int $registrationExpiresAt The expiration time of the client registration (clientId and clientSecret) + * @param int $region The configured sso_region for the profile that credentials are being resolved for + * @param int $startUrl The configured sso_start_url for the profile that credentials are being resolved for + */ + public function __construct( + $token, + $expires, + $refreshToken = null, + $clientId = null, + $clientSecret = null, + $registrationExpiresAt = null, + $region = null, + $startUrl = null + ) { + parent::__construct($token, $expires); + $this->refreshToken = $refreshToken; + $this->clientId = $clientId; + $this->clientSecret = $clientSecret; + $this->registrationExpiresAt = $registrationExpiresAt; + $this->region = $region; + $this->startUrl = $startUrl; + } + + /** + * @return bool + */ + public function isExpired() + { + if (isset($this->registrationExpiresAt) + && time() >= $this->registrationExpiresAt + ) { + return false; + } + return $this->expires !== null && time() >= $this->expires; + } + + /** + * @return string|null + */ + public function getRefreshToken() + { + return $this->refreshToken; + } + + /** + * @return string|null + */ + public function getClientId() + { + return $this->clientId; + } + + /** + * @return string|null + */ + public function getClientSecret() + { + return $this->clientSecret; + } + + /** + * @return int|null + */ + public function getRegistrationExpiresAt() + { + return $this->registrationExpiresAt; + } + + /** + * @return string|null + */ + public function getRegion() + { + return $this->region; + } + + /** + * @return string|null + */ + public function getStartUrl() + { + return $this->startUrl; + } +} 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))); + } +} diff --git a/vendor/aws/aws-sdk-php/src/Token/Token.php b/vendor/aws/aws-sdk-php/src/Token/Token.php new file mode 100644 index 0000000..6d2c566 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/Token.php @@ -0,0 +1,110 @@ +<?php +namespace Aws\Token; + +use Aws\Token\TokenInterface; + +/** + * Basic implementation of the AWS Token interface that allows callers to + * pass in an AWS token in the constructor. + */ +class Token implements TokenInterface, \Serializable +{ + protected $token; + protected $expires; + + /** + * Constructs a new basic 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($token, $expires = null) + { + $this->token = $token; + $this->expires = $expires; + } + + /** + * Sets the state of a token object + * + * @param array $state array containing 'token' and 'expires' + */ + public static function __set_state(array $state) + { + return new self( + $state['token'], + $state['expires'] + ); + } + + /** + * @return string + */ + public function getToken() + { + return $this->token; + } + + /** + * @return int + */ + public function getExpiration() + { + return $this->expires; + } + + /** + * @return bool + */ + public function isExpired() + { + return $this->expires !== null && time() >= $this->expires; + } + + /** + * @return array + */ + public function toArray() + { + return [ + 'token' => $this->token, + 'expires' => $this->expires + ]; + } + + /** + * @return string + */ + public function serialize() + { + return json_encode($this->__serialize()); + } + + /** + * Sets the state of the object from serialized json data + */ + public function unserialize($serialized) + { + $data = json_decode($serialized, true); + + $this->__unserialize($data); + } + + /** + * @return array + */ + public function __serialize() + { + return $this->toArray(); + } + + /** + * Sets the state of this object from an array + */ + public function __unserialize($data) + { + $this->token = $data['token']; + $this->expires = $data['expires']; + } +} diff --git a/vendor/aws/aws-sdk-php/src/Token/TokenAuthorization.php b/vendor/aws/aws-sdk-php/src/Token/TokenAuthorization.php new file mode 100644 index 0000000..3fab516 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/TokenAuthorization.php @@ -0,0 +1,24 @@ +<?php +namespace Aws\Token; + +use Psr\Http\Message\RequestInterface; + +/** + * Interface used to provide interchangeable strategies for adding authorization + * to requests using the various AWS signature protocols. + */ +interface TokenAuthorization +{ + /** + * Adds the specified token to a request by adding the required headers. + * + * @param RequestInterface $request Request to sign + * @param TokenInterface $token Token + * + * @return RequestInterface Returns the modified request. + */ + public function authorizeRequest( + RequestInterface $request, + TokenInterface $token + ); +} diff --git a/vendor/aws/aws-sdk-php/src/Token/TokenInterface.php b/vendor/aws/aws-sdk-php/src/Token/TokenInterface.php new file mode 100644 index 0000000..9b95d85 --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/TokenInterface.php @@ -0,0 +1,36 @@ +<?php +namespace Aws\Token; + +/** + * Provides access to an AWS token used for accessing AWS services + */ +interface TokenInterface +{ + /** + * Returns the token this token object. + * + * @return string + */ + public function getToken(); + + /** + * Get the UNIX timestamp in which the token will expire + * + * @return int|null + */ + public function getExpiration(); + + /** + * Check if the token are expired + * + * @return bool + */ + public function isExpired(); + + /** + * Converts the token to an associative array. + * + * @return array + */ + public function toArray(); +} diff --git a/vendor/aws/aws-sdk-php/src/Token/TokenProvider.php b/vendor/aws/aws-sdk-php/src/Token/TokenProvider.php new file mode 100644 index 0000000..1d7014c --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Token/TokenProvider.php @@ -0,0 +1,268 @@ +<?php +namespace Aws\Token; + +use Aws; +use Aws\Api\DateTimeResult; +use Aws\CacheInterface; +use Aws\Exception\TokenException; +use GuzzleHttp\Promise; + +/** + * Token providers are functions that accept no arguments and return a + * promise that is fulfilled with an {@see \Aws\Token\TokenInterface} + * or rejected with an {@see \Aws\Exception\TokenException}. + * + * <code> + * use Aws\Token\TokenProvider; + * $provider = TokenProvider::defaultProvider(); + * // Returns a TokenInterface or throws. + * $token = $provider()->wait(); + * </code> + * + * Token providers can be composed to create a token using conditional + * logic that can create different tokens in different environments. You + * can compose multiple providers into a single provider using + * {@see Aws\Token\TokenProvider::chain}. This function accepts + * providers as variadic arguments and returns a new function that will invoke + * each provider until a token is successfully returned. + */ +class TokenProvider +{ + use ParsesIniTrait; + const ENV_PROFILE = 'AWS_PROFILE'; + + /** + * Create a default token provider tha checks for cached a SSO token from + * the CLI + * + * This provider is automatically wrapped in a memoize function that caches + * previously provided tokens. + * + * @param array $config Optional array of token provider options. + * + * @return callable + */ + public static function defaultProvider(array $config = []) + { + + $cacheable = [ + 'sso', + ]; + + $defaultChain = []; + + if ( + !isset($config['use_aws_shared_config_files']) + || $config['use_aws_shared_config_files'] !== false + ) { + $profileName = getenv(self::ENV_PROFILE) ?: 'default'; + $defaultChain['sso'] = self::sso( + $profileName, + self::getHomeDir() . '/.aws/config', + $config + ); + } + + if (isset($config['token']) + && $config['token'] instanceof CacheInterface + ) { + foreach ($cacheable as $provider) { + if (isset($defaultChain[$provider])) { + $defaultChain[$provider] = self::cache( + $defaultChain[$provider], + $config['token'], + 'aws_cached_' . $provider . '_token' + ); + } + } + } + + return self::memoize( + call_user_func_array( + [TokenProvider::class, 'chain'], + array_values($defaultChain) + ) + ); + } + + /** + * Create a token provider function from a static token. + * + * @param TokenInterface $token + * + * @return callable + */ + public static function fromToken(TokenInterface $token) + { + $promise = Promise\Create::promiseFor($token); + + return function () use ($promise) { + return $promise; + }; + } + + /** + * Creates an aggregate token provider that invokes the provided + * variadic providers one after the other until a provider returns + * a token. + * + * @return callable + */ + public static function chain() + { + $links = func_get_args(); + //Common use case for when aws_shared_config_files is false + if (empty($links)) { + return function () { + return Promise\Create::promiseFor(false); + }; + } + + return function () use ($links) { + /** @var callable $parent */ + $parent = array_shift($links); + $promise = $parent(); + while ($next = array_shift($links)) { + $promise = $promise->otherwise($next); + } + return $promise; + }; + } + + /** + * Wraps a token provider and caches a previously provided token. + * Ensures that cached tokens are refreshed when they expire. + * + * @param callable $provider Token provider function to wrap. + * @return callable + */ + public static function memoize(callable $provider) + { + return function () use ($provider) { + static $result; + static $isConstant; + + // Constant tokens 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 a token that could expire and refresh when needed. + return $result + ->then(function (TokenInterface $token) use ($provider, &$isConstant, &$result) { + // Determine if the token is constant. + if (!$token->getExpiration()) { + $isConstant = true; + return $token; + } + + if (!$token->isExpired()) { + return $token; + } + return $result = $provider(); + }) + ->otherwise(function($reason) use (&$result) { + // Cleanup rejected promise. + $result = null; + return Promise\Create::promiseFor(null); + }); + }; + } + + /** + * Wraps a token provider and saves provided token in an + * instance of Aws\CacheInterface. Forwards calls when no token found + * in cache and updates cache with the results. + * + * @param callable $provider Token provider function to wrap + * @param CacheInterface $cache Cache to store the token + * @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_token'; + + return function () use ($provider, $cache, $cacheKey) { + $found = $cache->get($cacheKey); + if (is_array($found) && isset($found['token'])) { + if (isset($found['token']) && $found['token'] instanceof TokenInterface) { + $foundToken = $found['token']; + if (!$foundToken->isExpired()) { + return Promise\Create::promiseFor($foundToken); + } + if (isset($found['refreshMethod']) && is_callable($found['refreshMethod'])) { + return Promise\Create::promiseFor($found['refreshMethod']()); + } + } + } + + return $provider() + ->then(function (TokenInterface $token) use ( + $cache, + $cacheKey + ) { + $cache->set( + $cacheKey, + $token, + null === $token->getExpiration() ? + 0 : $token->getExpiration() - time() + ); + + return $token; + }); + }; + } + + /** + * Gets profiles from the ~/.aws/config ini file + */ + private static function loadDefaultProfiles() { + $profiles = []; + $configFile = self::getHomeDir() . '/.aws/config'; + + 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; + } + + private static function reject($msg) + { + return new Promise\RejectedPromise(new TokenException($msg)); + } + + /** + * Token provider that creates a token from cached sso credentials + * + * @param string $ssoProfileName the name of the ini profile name + * @param string $filename the location of the ini file + * @param array $config configuration options + * + * @return SsoToken + * @see Aws\Token\SsoToken for $config details. + */ + public static function sso($profileName, $filename, $config = []) + { + return new SsoTokenProvider($profileName, $filename, $config); + } +} + |