Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?phpnamespace 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 SsoTokenProvider object, which will fetch a token from an authenticated SSO profile* @param string $ssoProfileName The name of the profile that contains the sso_session key* @param int $filename Name of the config file to sso profile from*/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 profiles 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/'. mb_convert_encoding(sha1($sso_session), "UTF-8"). ".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");}$expiration = strtotime($tokenData['expiresAt']);if ($expiration === false) {throw new TokenException("Cached SSO token returned an invalid expiration");} elseif ($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)));}}