mirror of
https://gh.wpcy.net/https://github.com/pixelabs-wp/whitelabel-plugins-update-server.git
synced 2026-04-18 04:02:15 +08:00
209 lines
7.7 KiB
PHP
209 lines
7.7 KiB
PHP
<?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 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)));
|
|
}
|
|
}
|