feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)
Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.
Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
- Provider key: s3compatible
- Reads user-configured endpoint URL from settings
- Uses path-style URL access (required by most S3-compatible services)
- Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
- Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
This commit is contained in:
29
vendor/Aws3/Aws/Token/BearerTokenAuthorization.php
vendored
Normal file
29
vendor/Aws3/Aws/Token/BearerTokenAuthorization.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\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}");
|
||||
}
|
||||
}
|
||||
41
vendor/Aws3/Aws/Token/ParsesIniTrait.php
vendored
Normal file
41
vendor/Aws3/Aws/Token/ParsesIniTrait.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token;
|
||||
|
||||
trait ParsesIniTrait
|
||||
{
|
||||
/**
|
||||
* Gets profiles from specified $filename, or default ini files.
|
||||
*/
|
||||
private static function loadProfiles($filename)
|
||||
{
|
||||
$profileData = \DeliciousBrains\WP_Offload_Media\Aws3\Aws\parse_ini_file($filename, \true, \INI_SCANNER_RAW);
|
||||
$configFilename = self::getHomeDir() . '/.aws/config';
|
||||
if (\is_readable($configFilename)) {
|
||||
$configProfiles = \DeliciousBrains\WP_Offload_Media\Aws3\Aws\parse_ini_file($configFilename, \true, \INI_SCANNER_RAW);
|
||||
$profileData = \array_merge($configProfiles, $profileData);
|
||||
}
|
||||
foreach ($profileData as $name => $profile) {
|
||||
// standardize config profile names
|
||||
$name = \str_replace('profile ', '', $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;
|
||||
}
|
||||
}
|
||||
23
vendor/Aws3/Aws/Token/RefreshableTokenProviderInterface.php
vendored
Normal file
23
vendor/Aws3/Aws/Token/RefreshableTokenProviderInterface.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\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();
|
||||
}
|
||||
102
vendor/Aws3/Aws/Token/SsoToken.php
vendored
Normal file
102
vendor/Aws3/Aws/Token/SsoToken.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\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;
|
||||
}
|
||||
/**
|
||||
* Creates an instance of SsoToken from a token data.
|
||||
*
|
||||
* @param $tokenData
|
||||
*
|
||||
* @return SsoToken
|
||||
*/
|
||||
public static function fromTokenData($tokenData) : SsoToken
|
||||
{
|
||||
return new SsoToken($tokenData['accessToken'], \strtotime($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);
|
||||
}
|
||||
}
|
||||
225
vendor/Aws3/Aws/Token/SsoTokenProvider.php
vendored
Normal file
225
vendor/Aws3/Aws/Token/SsoTokenProvider.php
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token;
|
||||
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Exception\TokenException;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\SSOOIDC\SSOOIDCClient;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\GuzzleHttp\Promise;
|
||||
/**
|
||||
* Token that comes from the SSO provider
|
||||
*/
|
||||
class SsoTokenProvider implements RefreshableTokenProviderInterface
|
||||
{
|
||||
use ParsesIniTrait;
|
||||
const ENV_PROFILE = 'AWS_PROFILE';
|
||||
const REFRESH_WINDOW_IN_SECS = 300;
|
||||
const REFRESH_ATTEMPT_WINDOW_IN_SECS = 30;
|
||||
/** @var string $profileName */
|
||||
private $profileName;
|
||||
/** @var string $configFilePath */
|
||||
private $configFilePath;
|
||||
/** @var SSOOIDCClient $ssoOidcClient */
|
||||
private $ssoOidcClient;
|
||||
/** @var string $ssoSessionName */
|
||||
private $ssoSessionName;
|
||||
/**
|
||||
* Constructs a new SsoTokenProvider object, which will fetch a token from an authenticated SSO profile
|
||||
* @param string $profileName The name of the profile that contains the sso_session key
|
||||
* @param string|null $configFilePath Name of the config file to sso profile from
|
||||
* @param SSOOIDCClient|null $ssoOidcClient The sso client for generating a new token
|
||||
*/
|
||||
public function __construct($profileName, $configFilePath = null, SSOOIDCClient $ssoOidcClient = null)
|
||||
{
|
||||
$this->profileName = $this->resolveProfileName($profileName);
|
||||
$this->configFilePath = $this->resolveConfigFile($configFilePath);
|
||||
$this->ssoOidcClient = $ssoOidcClient;
|
||||
}
|
||||
/**
|
||||
* This method resolves the profile name to be used. The
|
||||
* profile provided as instantiation argument takes precedence,
|
||||
* followed by AWS_PROFILE env variable, otherwise `default` is
|
||||
* used.
|
||||
*
|
||||
* @param string|null $argProfileName The profile provided as argument.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function resolveProfileName($argProfileName) : string
|
||||
{
|
||||
if (empty($argProfileName)) {
|
||||
return \getenv(self::ENV_PROFILE) ?: 'default';
|
||||
} else {
|
||||
return $argProfileName;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method resolves the config file from where the profiles
|
||||
* are going to be loaded from. If $argFileName is not empty then,
|
||||
* it takes precedence over the default config file location.
|
||||
*
|
||||
* @param string|null $argConfigFilePath The config path provided as argument.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function resolveConfigFile($argConfigFilePath) : string
|
||||
{
|
||||
if (empty($argConfigFilePath)) {
|
||||
return self::getHomeDir() . '/.aws/config';
|
||||
} else {
|
||||
return $argConfigFilePath;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Loads cached sso credentials.
|
||||
*
|
||||
* @return Promise\PromiseInterface
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return Promise\Coroutine::of(function () {
|
||||
if (empty($this->configFilePath) || !\is_readable($this->configFilePath)) {
|
||||
throw new TokenException("Cannot read profiles from {$this->configFilePath}");
|
||||
}
|
||||
$profiles = self::loadProfiles($this->configFilePath);
|
||||
if (!isset($profiles[$this->profileName])) {
|
||||
throw new TokenException("Profile `{$this->profileName}` does not exist in {$this->configFilePath}.");
|
||||
}
|
||||
$profile = $profiles[$this->profileName];
|
||||
if (empty($profile['sso_session'])) {
|
||||
throw new TokenException("Profile `{$this->profileName}` in {$this->configFilePath} must contain an sso_session.");
|
||||
}
|
||||
$ssoSessionName = $profile['sso_session'];
|
||||
$this->ssoSessionName = $ssoSessionName;
|
||||
$profileSsoSession = 'sso-session ' . $ssoSessionName;
|
||||
if (empty($profiles[$profileSsoSession])) {
|
||||
throw new TokenException("Sso session `{$ssoSessionName}` does not exist in {$this->configFilePath}");
|
||||
}
|
||||
$sessionProfileData = $profiles[$profileSsoSession];
|
||||
foreach (['sso_start_url', 'sso_region'] as $requiredProp) {
|
||||
if (empty($sessionProfileData[$requiredProp])) {
|
||||
throw new TokenException("Sso session `{$ssoSessionName}` in {$this->configFilePath} is missing the required property `{$requiredProp}`");
|
||||
}
|
||||
}
|
||||
$tokenData = $this->refresh();
|
||||
$tokenLocation = self::getTokenLocation($ssoSessionName);
|
||||
$this->validateTokenData($tokenLocation, $tokenData);
|
||||
$ssoToken = SsoToken::fromTokenData($tokenData);
|
||||
// To make sure the token is not expired
|
||||
if ($ssoToken->isExpired()) {
|
||||
throw new TokenException("Cached SSO token returned an expired token.");
|
||||
}
|
||||
(yield $ssoToken);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* This method attempt to refresh when possible.
|
||||
* If a refresh is not possible then it just returns
|
||||
* the current token data as it is.
|
||||
*
|
||||
* @return array
|
||||
* @throws TokenException
|
||||
*/
|
||||
public function refresh() : array
|
||||
{
|
||||
$tokenLocation = self::getTokenLocation($this->ssoSessionName);
|
||||
$tokenData = $this->getTokenData($tokenLocation);
|
||||
if (!$this->shouldAttemptRefresh()) {
|
||||
return $tokenData;
|
||||
}
|
||||
if (null === $this->ssoOidcClient) {
|
||||
throw new TokenException("Cannot refresh this token without an 'ssooidcClient' ");
|
||||
}
|
||||
foreach (['clientId', 'clientSecret', 'refreshToken'] as $requiredProp) {
|
||||
if (empty($tokenData[$requiredProp])) {
|
||||
throw new TokenException("Cannot refresh this token without `{$requiredProp}` being set");
|
||||
}
|
||||
}
|
||||
$response = $this->ssoOidcClient->createToken([
|
||||
'clientId' => $tokenData['clientId'],
|
||||
'clientSecret' => $tokenData['clientSecret'],
|
||||
'grantType' => 'refresh_token',
|
||||
// REQUIRED
|
||||
'refreshToken' => $tokenData['refreshToken'],
|
||||
]);
|
||||
if ($response['@metadata']['statusCode'] !== 200) {
|
||||
throw new TokenException('Unable to create a new sso token');
|
||||
}
|
||||
$tokenData['accessToken'] = $response['accessToken'];
|
||||
$tokenData['expiresAt'] = \time() + $response['expiresIn'];
|
||||
$tokenData['refreshToken'] = $response['refreshToken'];
|
||||
return $this->writeNewTokenDataToDisk($tokenData, $tokenLocation);
|
||||
}
|
||||
/**
|
||||
* This method checks for whether a token refresh should happen.
|
||||
* It will return true just if more than 30 seconds has happened
|
||||
* since last refresh, and if the expiration is within a 5-minutes
|
||||
* window from the current time.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldAttemptRefresh() : bool
|
||||
{
|
||||
$tokenLocation = self::getTokenLocation($this->ssoSessionName);
|
||||
$tokenData = $this->getTokenData($tokenLocation);
|
||||
if (empty($tokenData['expiresAt'])) {
|
||||
throw new TokenException("Token file at {$tokenLocation} must contain an expiration date");
|
||||
}
|
||||
$tokenExpiresAt = \strtotime($tokenData['expiresAt']);
|
||||
$lastRefreshAt = \filemtime($tokenLocation);
|
||||
$now = \time();
|
||||
// If last refresh happened after 30 seconds
|
||||
// and if the token expiration is in the 5 minutes window
|
||||
return $now - $lastRefreshAt > self::REFRESH_ATTEMPT_WINDOW_IN_SECS && $tokenExpiresAt - $now < self::REFRESH_WINDOW_IN_SECS;
|
||||
}
|
||||
/**
|
||||
* @param $sso_session
|
||||
* @return string
|
||||
*/
|
||||
public static function getTokenLocation($sso_session) : string
|
||||
{
|
||||
return self::getHomeDir() . '/.aws/sso/cache/' . \mb_convert_encoding(\sha1($sso_session), "UTF-8") . ".json";
|
||||
}
|
||||
/**
|
||||
* @param $tokenLocation
|
||||
* @return array
|
||||
*/
|
||||
function getTokenData($tokenLocation) : array
|
||||
{
|
||||
if (empty($tokenLocation) || !\is_readable($tokenLocation)) {
|
||||
throw new TokenException("Unable to read token file at {$tokenLocation}");
|
||||
}
|
||||
return \json_decode(\file_get_contents($tokenLocation), \true);
|
||||
}
|
||||
/**
|
||||
* @param $tokenData
|
||||
* @param $tokenLocation
|
||||
* @return mixed
|
||||
*/
|
||||
private function validateTokenData($tokenLocation, $tokenData)
|
||||
{
|
||||
foreach (['accessToken', 'expiresAt'] as $requiredProp) {
|
||||
if (empty($tokenData[$requiredProp])) {
|
||||
throw new TokenException("Token file at {$tokenLocation} must contain the required property `{$requiredProp}`");
|
||||
}
|
||||
}
|
||||
$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 array
|
||||
*/
|
||||
private function writeNewTokenDataToDisk(array $tokenData, $tokenLocation) : array
|
||||
{
|
||||
$tokenData['expiresAt'] = \gmdate('Y-m-d\\TH:i:s\\Z', $tokenData['expiresAt']);
|
||||
\file_put_contents($tokenLocation, \json_encode(\array_filter($tokenData)));
|
||||
return $tokenData;
|
||||
}
|
||||
}
|
||||
94
vendor/Aws3/Aws/Token/Token.php
vendored
Normal file
94
vendor/Aws3/Aws/Token/Token.php
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token;
|
||||
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Identity\BearerTokenIdentity;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token\TokenInterface;
|
||||
/**
|
||||
* Basic implementation of the AWS Token interface that allows callers to
|
||||
* pass in an AWS token in the constructor.
|
||||
*/
|
||||
class Token extends BearerTokenIdentity 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'];
|
||||
}
|
||||
}
|
||||
21
vendor/Aws3/Aws/Token/TokenAuthorization.php
vendored
Normal file
21
vendor/Aws3/Aws/Token/TokenAuthorization.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token;
|
||||
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\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);
|
||||
}
|
||||
34
vendor/Aws3/Aws/Token/TokenInterface.php
vendored
Normal file
34
vendor/Aws3/Aws/Token/TokenInterface.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\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();
|
||||
}
|
||||
211
vendor/Aws3/Aws/Token/TokenProvider.php
vendored
Normal file
211
vendor/Aws3/Aws/Token/TokenProvider.php
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace DeliciousBrains\WP_Offload_Media\Aws3\Aws\Token;
|
||||
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Api\DateTimeResult;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\CacheInterface;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\Aws\Exception\TokenException;
|
||||
use DeliciousBrains\WP_Offload_Media\Aws3\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'])) {
|
||||
$foundToken = $found['token'];
|
||||
if ($foundToken instanceof TokenInterface) {
|
||||
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 = \DeliciousBrains\WP_Offload_Media\Aws3\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 $profileName the name of the ini profile name
|
||||
* @param string $filename the location of the ini file
|
||||
* @param array $config configuration options
|
||||
*
|
||||
* @return SsoTokenProvider
|
||||
* @see Aws\Token\SsoTokenProvider for $config details.
|
||||
*/
|
||||
public static function sso($profileName, $filename, $config = [])
|
||||
{
|
||||
$ssoClient = isset($config['ssoClient']) ? $config['ssoClient'] : null;
|
||||
return new SsoTokenProvider($profileName, $filename, $ssoClient);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user